Skip to content

Commit

Permalink
✨ feat: 支持用户设置
Browse files Browse the repository at this point in the history
  • Loading branch information
rdmclin2 committed May 18, 2024
1 parent ac50d24 commit da8b343
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 60 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
},
"dependencies": {
"@ant-design/icons": "^5.3.7",
"@ant-design/pro-card": "^2.6.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/utilities": "^3.2.2",
"@gltf-transform/core": "^3.10.1",
Expand Down
1 change: 1 addition & 0 deletions src/constants/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export const DEFAULT_SETTINGS: Config = {
},
primaryColor: DEFAULT_PRIMARY_COLOR,
avatar: DEFAULT_USER_AVATAR,
nickName: '',
};
2 changes: 1 addition & 1 deletion src/features/AvatarWithUpload/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const AvatarWithUpload = memo<AvatarWithUploadProps>(
<div className={styles} id={id} style={{ maxHeight: size, maxWidth: size, ...style }}>
<Upload beforeUpload={handleUploadAvatar} itemRender={() => void 0} maxCount={1}>
<NextImage
alt={avatar ? 'userAvatar' : 'LobeChat'}
alt={avatar ? 'userAvatar' : 'LobeVidol'}
height={size}
src={!!avatar ? avatar : DEFAULT_USER_AVATAR_URL}
unoptimized
Expand Down
6 changes: 5 additions & 1 deletion src/features/ChatList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import AutoScroll from './AutoScroll';
import { useStyles } from './style';

const itemContent = (index: number, id: string) => {
return index === 0 ? <div style={{ height: 24 }} /> : <Item id={id} index={index - 1} />;
return index === 0 ? (
<div style={{ height: 24 }} />
) : (
<Item id={id} index={index - 1} showTitle />
);
};

interface VirtualizedListProps {
Expand Down
101 changes: 51 additions & 50 deletions src/features/Settings/common.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import { CheckCard } from '@ant-design/pro-card';
import {
Form,
FormGroup,
FormItem,
PrimaryColors,
Swatches,
findCustomThemeName,
} from '@lobehub/ui';
import { App, Button, Segmented } from 'antd';
import { ThemeMode, createStyles, useTheme } from 'antd-style';
import { Form, FormGroup, FormItem } from '@lobehub/ui';
import { App, Button, Input, Segmented } from 'antd';
import { ThemeMode, createStyles } from 'antd-style';
import classNames from 'classnames';
import { Monitor, Settings2 } from 'lucide-react';
import { isEqual } from 'lodash-es';
import { Monitor, Settings2, User2Icon } from 'lucide-react';
import React from 'react';

import AvatarWithUpload from '@/features/AvatarWithUpload';
import ThemeSwatchesPrimary from '@/features/Settings/features/ThemeSwatchesPrimary';
import { useSyncSettings } from '@/features/Settings/useSyncSettings';
import { useAgentStore } from '@/store/agent';
import { useConfigStore } from '@/store/config';
import { useSessionStore } from '@/store/session';
Expand All @@ -40,18 +35,17 @@ const useStyles = createStyles(({ css }) => ({
const CommonConfig = (props: CommonConfigProps) => {
const { style, className } = props;
const { styles } = useStyles();
const [primaryColor, backgroundEffect] = useConfigStore((s) => [
s.config.primaryColor,
s.config.backgroundEffect,
]);
const [config, setConfig] = useConfigStore((s) => [s.config, s.setConfig], isEqual);
const clearAgentStorage = useAgentStore((s) => s.clearAgentStorage);
const [themeMode, setThemeMode] = useThemeStore((s) => [s.themeMode, s.setThemeMode]);
const setConfig = useConfigStore((s) => s.setConfig);
const theme = useTheme();
const clearSessions = useSessionStore((s) => s.clearSessions);
const resetConfig = useConfigStore((s) => s.resetConfig);
const { message, modal } = App.useApp();

const [form] = Form.useForm();

useSyncSettings(form);

const handleClear = () => {
modal.confirm({
cancelText: '取消',
Expand Down Expand Up @@ -89,46 +83,53 @@ const CommonConfig = (props: CommonConfigProps) => {

return (
<div className={classNames(styles.config, className)} style={style}>
<Form style={{ display: 'flex', flexGrow: 1 }}>
<FormGroup icon={Settings2} title={'主题设置'}>
<FormItem desc={'头像'} divider label={'自定义头像'} name={'avatar'}>
<Form
style={{ display: 'flex', flexGrow: 1 }}
initialValues={config}
form={form}
onValuesChange={setConfig}
>
<FormGroup icon={User2Icon} title={'用户设置'}>
<FormItem desc={'自定义头像'} divider label={'头像'} name={'avatar'}>
<AvatarWithUpload />
</FormItem>
<FormItem desc={'主题色'} divider label={'自定义主题色'} name={'primaryColor'}>
<Swatches
activeColor={primaryColor}
colors={[
theme.red,
theme.orange,
theme.gold,
theme.yellow,
theme.lime,
theme.green,
theme.cyan,
theme.blue,
theme.geekblue,
theme.purple,
theme.magenta,
theme.volcano,
]}
onSelect={(color: any) => {
const name = findCustomThemeName('primary', color) as PrimaryColors;
setConfig({ primaryColor: name || '' });
<FormItem desc={'自定义昵称'} divider label={'昵称'} name={'nickName'}>
<Input
defaultValue={config.nickName}
placeholder={'请输入昵称'}
maxLength={20}
showCount
onChange={(e) => {
setConfig({ nickName: e.target.value });
}}
/>
</FormItem>
</FormGroup>
<FormGroup icon={Settings2} title={'主题设置'}>
<FormItem desc={'主题色'} divider label={'自定义主题色'} name={'primaryColor'}>
<ThemeSwatchesPrimary />
</FormItem>
<FormItem desc={'自定义主题模式'} divider label={'主题模式'} name={'themeMode'}>
<CheckCard.Group
<Segmented
defaultValue={themeMode}
onChange={(value) => {
onChange={(value: ThemeMode) => {
setThemeMode(value as ThemeMode);
}}
size="small"
>
<CheckCard className={styles.effect} title="🔆 亮色模式" value="light" />
<CheckCard className={styles.effect} title="🌙 暗色模式" value="dark" />
<CheckCard className={styles.effect} title="💻 跟随系统" value="auto" />
</CheckCard.Group>
options={[
{
label: '🔆 亮色模式',
value: 'light',
},
{
label: '🌙 暗色模式',
value: 'dark',
},
{
label: '💻 跟随系统',
value: 'auto',
},
]}
/>
</FormItem>
<FormItem
desc={'自定义背景效果,可关闭以提升性能'}
Expand All @@ -137,7 +138,7 @@ const CommonConfig = (props: CommonConfigProps) => {
name={'backgroundEffect'}
>
<Segmented
defaultValue={backgroundEffect}
defaultValue={config.backgroundEffect}
onChange={(value: BackgroundEffect) => {
setConfig({ backgroundEffect: value });
}}
Expand Down
29 changes: 29 additions & 0 deletions src/features/Settings/features/ThemeSwatchesPrimary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
PrimaryColors,
Swatches,
findCustomThemeName,
primaryColors,
primaryColorsSwatches,
} from '@lobehub/ui';
import { memo } from 'react';

import { useConfigStore } from '@/store/config';

const ThemeSwatchesPrimary = memo(() => {
const [primaryColor, setConfig] = useConfigStore((s) => [s.config.primaryColor, s.setConfig]);

const handleSelect = (v: any) => {
const name = findCustomThemeName('primary', v) as PrimaryColors;
setConfig({ primaryColor: name || '' });
};

return (
<Swatches
activeColor={primaryColor ? primaryColors[primaryColor] : undefined}
colors={primaryColorsSwatches}
onSelect={handleSelect}
/>
);
});

export default ThemeSwatchesPrimary;
23 changes: 23 additions & 0 deletions src/features/Settings/useSyncSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FormInstance } from 'antd/es/form/hooks/useForm';
import { useEffect } from 'react';

import { useConfigStore } from '@/store/config';

export const useSyncSettings = (form: FormInstance) => {
useEffect(() => {
// set the first time
form.setFieldsValue(useConfigStore.getState().config);

// sync with later updated settings
const unsubscribe = useConfigStore.subscribe(
(s) => s.config,
(config) => {
form.setFieldsValue(config);
},
);

return () => {
unsubscribe();
};
}, []);
};
14 changes: 8 additions & 6 deletions src/store/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { produce } from 'immer';
import { isEqual, merge } from 'lodash-es';
import { devtools, persist } from 'zustand/middleware';
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware';
import { shallow } from 'zustand/shallow';
import { createWithEqualityFn } from 'zustand/traditional';
import { StateCreator } from 'zustand/vanilla';
Expand Down Expand Up @@ -106,11 +106,13 @@ const createStore: StateCreator<ConfigStore, [['zustand/devtools', never]]> = (s
});

export const useConfigStore = createWithEqualityFn<ConfigStore>()(
persist(
devtools(createStore, {
name: 'VIDOL_CONFIG_STORE',
}),
{ name: CONFIG_STORAGE_KEY },
subscribeWithSelector(
persist(
devtools(createStore, {
name: 'VIDOL_CONFIG_STORE',
}),
{ name: CONFIG_STORAGE_KEY },
),
),
shallow,
);
Expand Down
3 changes: 2 additions & 1 deletion src/store/session/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,13 @@ const currentChats = (s: SessionStore): ChatMessage[] => {
const { messages } = session;
return messages?.map((message) => {
const userAvatar = useConfigStore.getState().config.avatar;
const userNickName = useConfigStore.getState().config.nickName;
return {
...message,
meta: {
avatar: message.role === 'user' ? (userAvatar ? userAvatar : DEFAULT_USER_AVATAR) : avatar,
description: message.role === 'user' ? undefined : description,
title: message.role === 'user' ? '你' : name,
title: message.role === 'user' ? (userNickName ? userNickName : '你') : name,
},
};
});
Expand Down
7 changes: 7 additions & 0 deletions src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@ export interface PanelConfig {
export type PanelKey = keyof PanelConfig;

export interface CommonConfig {
/**
* 用户头像
*/
avatar: string;
/**
* 背景类型
*/
backgroundEffect: BackgroundEffect;
/**
* 用户昵称
*/
nickName: string;
/**
* 主题色
*/
Expand Down

0 comments on commit da8b343

Please sign in to comment.