Skip to content

Commit

Permalink
feat: 添加主题切换和配置持久化
Browse files Browse the repository at this point in the history
  • Loading branch information
Sioncovy committed Apr 16, 2024
1 parent 3787cc4 commit cb25676
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 55 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"axios-hooks": "^5.0.2",
"dayjs": "^1.11.10",
"dotenv": "^16.4.1",
"immer": "^10.0.4",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"qs": "^6.11.2",
Expand All @@ -32,7 +33,7 @@
"rxdb": "^15.14.0",
"rxjs": "^7.8.1",
"socket.io-client": "^4.7.5",
"zustand": "^4.5.0"
"zustand": "^4.5.2"
},
"devDependencies": {
"@antfu/eslint-config": "^2.6.4",
Expand Down
16 changes: 12 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/components/ContactInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function ContactInfo(props: ContactInfoProps) {
block
onClick={async () => {
const chat = await mailpenDatabase.chats.findOne({ selector: { _id: username } }).exec()
if (!chat) {
if (!chat && username) {
await mailpenDatabase.chats.insert({
_id: username,
name: contact.remark || contact.nickname || contact.username,
Expand All @@ -54,6 +54,7 @@ function ContactInfo(props: ContactInfoProps) {
count: 0,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
pinned: false,
})
}
navigate(`/chat/${username}`)
Expand Down
44 changes: 44 additions & 0 deletions src/components/Setting/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Flex, Radio, Typography } from 'antd'
import type { SettingConfig, SettingType } from '@/pages/SettingPage'
import { useThemeToken } from '@/hooks'

interface SettingProps {
config: SettingType
}

function Setting({ config }: SettingProps) {
console.log('✨ ~ Setting ~ config:', config)
const { token } = useThemeToken()

const renderSetting = (setting: SettingConfig) => {
switch (setting.type) {
case 'radio-group':
return (
<Radio.Group
{...setting.props}
/>
)
default:
return null
}
}

return (
<Flex vertical gap={12} style={{ padding: token.padding }}>
<Flex vertical>
<Typography.Title level={3}>{config.title}</Typography.Title>
<Typography.Text>{config.content}</Typography.Text>
</Flex>
{config.settings.map((setting) => {
return (
<Flex vertical key={setting.key}>
<Typography.Title level={5}>{setting.label}</Typography.Title>
{renderSetting(setting)}
</Flex>
)
})}
</Flex>
)
}

export default Setting
98 changes: 72 additions & 26 deletions src/components/Sider/ChatSider/ChatItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,96 @@
import { Badge, Flex, Typography } from 'antd'
import { Badge, Dropdown, Flex, Modal, Typography } from 'antd'
import { useNavigate, useParams } from 'react-router-dom'
import { useMeasure } from 'react-use'
import styles from './index.module.less'
import type { Chat, Message } from '@/typings'
import { useThemeToken, useTime } from '@/hooks'
import { mailpenDatabase } from '@/storages'

interface ChatItemProps {
chat: Chat
}

function ChatItem({ chat }: ChatItemProps) {
const [modalApi, modalContextHolder] = Modal.useModal()
const [ref, { width }] = useMeasure<HTMLDivElement>()
const { token } = useThemeToken()
const time = useTime()
const { updatedAt, avatar, count, name, message } = chat
const { updatedAt, avatar, count, name, message, pinned } = chat
const { content } = message || {} as Message
const navigate = useNavigate()
const { username } = useParams()
const isActive = username === chat._id

return (
<Flex
ref={ref}
onClick={() => {
navigate(`/chat/${chat._id}`)
}}
className={styles.chatItem}
style={{ backgroundColor: isActive ? token.colorPrimaryActive : undefined, padding: 10 }}
gap={10}
>
<div style={{ minWidth: 50, height: 50, borderRadius: '50%', overflow: 'hidden' }}>
<img src={avatar} />
</div>
<Flex vertical style={{ width: width - 60 }} justify="space-between">
<Flex justify="space-between" align="center">
<Typography.Text ellipsis style={{ fontSize: token.fontSizeLG }}>{name}</Typography.Text>
<Typography.Text type="secondary" style={{ fontSize: token.fontSizeSM, flexShrink: 0 }}>{time(updatedAt).fromNow()}</Typography.Text>
<>
{modalContextHolder}
<Dropdown
menu={{
items: [
{
key: 'pinned',
label: pinned ? '取消置顶' : '置顶会话',
onClick: () => {
mailpenDatabase.chats.findOne({
selector: {
_id: chat._id,
},
}).update({
$set: {
pinned: !pinned,
},
})
},
},
{
key: 'delete',
label: '删除会话',
onClick: () => {
modalApi.confirm({
title: '删除会话',
content: '确定删除该会话吗?',
onOk: async () => {
await mailpenDatabase.chats.findOne({
selector: {
_id: chat._id,
},
}).remove()
navigate('/chat')
},
})
},
},
],
}}
trigger={['contextMenu']}
>
<Flex
ref={ref}
onClick={() => {
navigate(`/chat/${chat._id}`)
}}
className={styles.chatItem}
style={{ backgroundColor: isActive ? token.colorPrimaryActive : undefined, padding: 10 }}
gap={10}
>
<div style={{ minWidth: 50, height: 50, borderRadius: '50%', overflow: 'hidden' }}>
<img src={avatar} />
</div>
<Flex vertical style={{ width: width - 60 }} justify="space-between">
<Flex justify="space-between" align="center">
<Typography.Text ellipsis style={{ fontSize: token.fontSizeLG }}>{name}</Typography.Text>
<Typography.Text type="secondary" style={{ fontSize: token.fontSizeSM, flexShrink: 0 }}>{time(updatedAt).fromNow()}</Typography.Text>
</Flex>
<Flex justify="space-between" align="center">
<Typography.Text ellipsis>
{content}
</Typography.Text>
<Badge style={{ boxShadow: 'none' }} count={count} />
</Flex>
</Flex>
</Flex>
<Flex justify="space-between" align="center">
<Typography.Text ellipsis>
{content}
</Typography.Text>
<Badge style={{ boxShadow: 'none' }} count={count} />
</Flex>
</Flex>
</Flex>
</Dropdown>
</>
)
}

Expand Down
4 changes: 3 additions & 1 deletion src/components/Sider/ChatSider/index.module.less
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
@import url("@/themes/index.less");

.sider {
display: flex;
flex-direction: column;
height: 100%;

.topBar {
padding: 20px 10px;
background-color: #fff;
background-color: @colorBgContainer;
}

.list {
Expand Down
23 changes: 21 additions & 2 deletions src/components/Sider/SettingSider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
import React from 'react'
import { Menu } from 'antd'
import { useNavigate } from 'react-router-dom'

function SettingSider() {
const navigate = useNavigate()
return (
<div>SettingSider</div>
<Menu items={[{
key: 'theme',
label: '主题',
onClick: () => navigate('/setting/theme'),
}, {
key: 'account',
label: '账号',
onClick: () => navigate('/setting/account'),
}, {
key: 'security',
label: '安全',
onClick: () => navigate('/setting/security'),
}, {
key: 'about',
label: '关于',
onClick: () => navigate('/setting/about'),
}]}
/>
)
}

Expand Down
13 changes: 12 additions & 1 deletion src/hooks/useAppStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
import type { Contact, UserPublic } from '@/typings'
import { Language, Theme } from '@/typings'

Expand All @@ -18,7 +20,7 @@ interface Actions {

type Store = State & Actions

export const useAppStore = create<Store>(set => ({
export const useAppStore = create(persist(immer<Store>(set => ({
theme: Theme.Light,
language: Language.Zh,
userInfo: {} as UserPublic,
Expand All @@ -32,4 +34,13 @@ export const useAppStore = create<Store>(set => ({
contactList.forEach(contact => contactMap.set(contact._id, contact))
set({ contactList, contactMap })
},
})), {
name: 'app-store',
storage: createJSONStorage(() => localStorage),
partialize(state) {
return {
theme: state.theme,
language: state.language,
}
},
}))
4 changes: 3 additions & 1 deletion src/layouts/BasicLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { MessageOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons'
import type { GlobalToken } from 'antd'
import { ConfigProvider, Layout, Menu } from 'antd'
import { ConfigProvider, Layout, Menu, theme as themeAntd } from 'antd'
import { useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { socket, useAppStore, useThemeToken } from '@/hooks'
import { AUTH_TOKEN_KEY } from '@/config'
import Loading from '@/components/Loading'
import { queryContactList, queryProfile } from '@/apis'
import { Theme } from '@/typings'

function AddThemeToVars() {
const { token: realToken } = useThemeToken()
Expand Down Expand Up @@ -70,6 +71,7 @@ function BasicLayout(props: any) {
token: {
colorPrimary: '#bde0fe',
},
algorithm: theme === Theme.Light ? themeAntd.defaultAlgorithm : themeAntd.darkAlgorithm,
}}
>
<AddThemeToVars />
Expand Down
1 change: 1 addition & 0 deletions src/pages/ChatPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function ChatPage() {
count: 1,
createdAt: now,
updatedAt: now,
pinned: false,
})
}
else {
Expand Down
13 changes: 0 additions & 13 deletions src/pages/Setting/index.tsx

This file was deleted.

Loading

0 comments on commit cb25676

Please sign in to comment.