@lobehub/chat 0.157.1 → 0.158.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/.eslintignore +1 -2
  2. package/CHANGELOG.md +50 -0
  3. package/README.md +14 -14
  4. package/README.zh-CN.md +14 -14
  5. package/locales/ar/auth.json +2 -0
  6. package/locales/ar/chat.json +1 -0
  7. package/locales/ar/common.json +1 -0
  8. package/locales/bg-BG/auth.json +2 -0
  9. package/locales/bg-BG/chat.json +1 -0
  10. package/locales/bg-BG/common.json +1 -0
  11. package/locales/bg-BG/error.json +3 -3
  12. package/locales/de-DE/auth.json +2 -0
  13. package/locales/de-DE/chat.json +1 -0
  14. package/locales/de-DE/common.json +1 -0
  15. package/locales/en-US/auth.json +2 -0
  16. package/locales/en-US/chat.json +1 -0
  17. package/locales/en-US/common.json +1 -0
  18. package/locales/es-ES/auth.json +2 -0
  19. package/locales/es-ES/chat.json +1 -0
  20. package/locales/es-ES/common.json +1 -0
  21. package/locales/fr-FR/auth.json +2 -0
  22. package/locales/fr-FR/chat.json +1 -0
  23. package/locales/fr-FR/common.json +1 -0
  24. package/locales/it-IT/auth.json +2 -0
  25. package/locales/it-IT/chat.json +1 -0
  26. package/locales/it-IT/common.json +1 -0
  27. package/locales/ja-JP/auth.json +2 -0
  28. package/locales/ja-JP/chat.json +1 -0
  29. package/locales/ja-JP/common.json +1 -0
  30. package/locales/ko-KR/auth.json +2 -0
  31. package/locales/ko-KR/chat.json +1 -0
  32. package/locales/ko-KR/common.json +1 -0
  33. package/locales/nl-NL/auth.json +2 -0
  34. package/locales/nl-NL/chat.json +1 -0
  35. package/locales/nl-NL/common.json +50 -49
  36. package/locales/pl-PL/auth.json +2 -0
  37. package/locales/pl-PL/chat.json +1 -0
  38. package/locales/pl-PL/common.json +1 -0
  39. package/locales/pl-PL/error.json +3 -3
  40. package/locales/pt-BR/auth.json +2 -0
  41. package/locales/pt-BR/chat.json +1 -0
  42. package/locales/pt-BR/common.json +1 -0
  43. package/locales/ru-RU/auth.json +2 -0
  44. package/locales/ru-RU/chat.json +1 -0
  45. package/locales/ru-RU/common.json +1 -0
  46. package/locales/ru-RU/error.json +3 -3
  47. package/locales/tr-TR/auth.json +2 -0
  48. package/locales/tr-TR/chat.json +1 -0
  49. package/locales/tr-TR/common.json +1 -0
  50. package/locales/vi-VN/auth.json +2 -0
  51. package/locales/vi-VN/chat.json +1 -0
  52. package/locales/vi-VN/common.json +1 -0
  53. package/locales/zh-CN/auth.json +2 -0
  54. package/locales/zh-CN/chat.json +1 -0
  55. package/locales/zh-CN/common.json +1 -0
  56. package/locales/zh-TW/auth.json +2 -0
  57. package/locales/zh-TW/chat.json +1 -0
  58. package/locales/zh-TW/common.json +1 -0
  59. package/package.json +2 -2
  60. package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +80 -0
  61. package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +116 -0
  62. package/src/app/(main)/(mobile)/me/(home)/features/Category.tsx +15 -0
  63. package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +37 -0
  64. package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +95 -0
  65. package/src/app/(main)/(mobile)/me/{page.tsx → (home)/page.tsx} +6 -10
  66. package/src/app/(main)/(mobile)/me/data/features/Category.tsx +48 -0
  67. package/src/app/(main)/(mobile)/me/data/features/Header.tsx +33 -0
  68. package/src/app/(main)/(mobile)/me/data/layout.tsx +13 -0
  69. package/src/app/(main)/(mobile)/me/data/loading.tsx +5 -0
  70. package/src/app/(main)/(mobile)/me/data/page.tsx +17 -0
  71. package/src/app/(main)/(mobile)/me/profile/features/Category.tsx +45 -0
  72. package/src/app/(main)/(mobile)/me/profile/features/Header.tsx +33 -0
  73. package/src/app/(main)/(mobile)/me/profile/layout.tsx +16 -0
  74. package/src/app/(main)/(mobile)/me/profile/loading.tsx +5 -0
  75. package/src/app/(main)/(mobile)/me/profile/page.tsx +17 -0
  76. package/src/app/(main)/(mobile)/me/settings/features/Category.tsx +15 -0
  77. package/src/app/(main)/(mobile)/me/settings/features/Header.tsx +33 -0
  78. package/src/app/(main)/(mobile)/me/settings/features/useCategory.tsx +57 -0
  79. package/src/app/(main)/(mobile)/me/settings/layout.tsx +13 -0
  80. package/src/app/(main)/(mobile)/me/settings/loading.tsx +5 -0
  81. package/src/app/(main)/(mobile)/me/settings/page.tsx +17 -0
  82. package/src/app/(main)/_layout/Mobile.tsx +5 -4
  83. package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +21 -1
  84. package/src/app/(main)/profile/[[...slugs]]/Client.tsx +74 -0
  85. package/src/app/(main)/profile/[[...slugs]]/page.tsx +18 -0
  86. package/src/app/(main)/profile/_layout/Mobile/Header.tsx +26 -0
  87. package/src/app/(main)/profile/_layout/Mobile/index.tsx +16 -0
  88. package/src/app/(main)/profile/layout.tsx +20 -0
  89. package/src/app/(main)/profile/loading.tsx +23 -0
  90. package/src/app/(main)/settings/_layout/Mobile/Header.tsx +2 -1
  91. package/src/app/(main)/settings/hooks/useCategory.tsx +7 -13
  92. package/src/app/@modal/layout.tsx +3 -0
  93. package/src/components/Cell/Divider.tsx +3 -2
  94. package/src/components/Cell/index.tsx +28 -18
  95. package/src/features/User/DataStatistics.tsx +3 -1
  96. package/src/features/User/UserLoginOrSignup.tsx +2 -2
  97. package/src/features/User/UserPanel/PanelContent.tsx +9 -3
  98. package/src/features/User/UserPanel/useMenu.tsx +29 -29
  99. package/src/features/User/__tests__/PanelContent.test.tsx +7 -0
  100. package/src/features/User/__tests__/useMenu.test.tsx +142 -0
  101. package/src/layout/AuthProvider/Clerk/useAppearance.ts +5 -4
  102. package/src/libs/agent-runtime/azureOpenai/index.test.ts +161 -27
  103. package/src/libs/agent-runtime/utils/streams/openai.ts +4 -0
  104. package/src/locales/default/auth.ts +2 -0
  105. package/src/locales/default/chat.ts +1 -0
  106. package/src/locales/default/common.ts +1 -0
  107. package/src/store/user/slices/auth/selectors.ts +2 -1
  108. package/src/app/(auth)/profile/[[...slugs]]/PageTitle.tsx +0 -13
  109. package/src/app/(auth)/profile/[[...slugs]]/page.tsx +0 -14
  110. package/src/app/(main)/(mobile)/me/features/Cate.tsx +0 -33
  111. package/src/app/(main)/(mobile)/me/features/ExtraCate.tsx +0 -24
  112. package/src/app/(main)/(mobile)/me/features/useExtraCate.tsx +0 -62
  113. /package/src/app/(main)/(mobile)/me/{features → (home)/features}/Header.tsx +0 -0
  114. /package/src/app/(main)/(mobile)/me/{layout.tsx → (home)/layout.tsx} +0 -0
  115. /package/src/app/(main)/(mobile)/me/{loading.tsx → (home)/loading.tsx} +0 -0
@@ -9,26 +9,20 @@ import type { MenuProps } from '@/components/Menu';
9
9
  import { SettingsTabs } from '@/store/global/initialState';
10
10
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
11
11
 
12
- interface UseCategoryOptions {
13
- mobile?: boolean;
14
- }
15
-
16
- export const useCategory = ({ mobile }: UseCategoryOptions = {}) => {
12
+ export const useCategory = () => {
17
13
  const { t } = useTranslation('setting');
18
14
  const { enableWebrtc, showLLM } = useServerConfigStore(featureFlagsSelectors);
19
15
 
20
- const iconSize = mobile ? { fontSize: 20 } : undefined;
21
-
22
16
  const cateItems: MenuProps['items'] = useMemo(
23
17
  () =>
24
18
  [
25
19
  {
26
- icon: <Icon icon={Settings2} size={iconSize} />,
20
+ icon: <Icon icon={Settings2} />,
27
21
  key: SettingsTabs.Common,
28
22
  label: t('tab.common'),
29
23
  },
30
24
  enableWebrtc && {
31
- icon: <Icon icon={Cloudy} size={iconSize} />,
25
+ icon: <Icon icon={Cloudy} />,
32
26
  key: SettingsTabs.Sync,
33
27
  label: (
34
28
  <Flexbox align={'center'} gap={8} horizontal>
@@ -40,18 +34,18 @@ export const useCategory = ({ mobile }: UseCategoryOptions = {}) => {
40
34
  ),
41
35
  },
42
36
  showLLM && {
43
- icon: <Icon icon={Brain} size={iconSize} />,
37
+ icon: <Icon icon={Brain} />,
44
38
  key: SettingsTabs.LLM,
45
39
  label: t('tab.llm'),
46
40
  },
47
- { icon: <Icon icon={Mic2} size={iconSize} />, key: SettingsTabs.TTS, label: t('tab.tts') },
41
+ { icon: <Icon icon={Mic2} />, key: SettingsTabs.TTS, label: t('tab.tts') },
48
42
  {
49
- icon: <Icon icon={Bot} size={iconSize} />,
43
+ icon: <Icon icon={Bot} />,
50
44
  key: SettingsTabs.Agent,
51
45
  label: t('tab.agent'),
52
46
  },
53
47
  {
54
- icon: <Icon icon={Info} size={iconSize} />,
48
+ icon: <Icon icon={Info} />,
55
49
  key: SettingsTabs.About,
56
50
  label: t('tab.about'),
57
51
  },
@@ -1,12 +1,14 @@
1
1
  'use client';
2
2
 
3
3
  import { Modal } from '@lobehub/ui';
4
+ import { useTheme } from 'antd-style';
4
5
  import { useRouter } from 'next/navigation';
5
6
  import { PropsWithChildren, memo, useState } from 'react';
6
7
 
7
8
  const SessionSettingsModal = memo<PropsWithChildren>(({ children }) => {
8
9
  const [open, setOpen] = useState(true);
9
10
  const router = useRouter();
11
+ const theme = useTheme();
10
12
 
11
13
  return (
12
14
  <Modal
@@ -18,6 +20,7 @@ const SessionSettingsModal = memo<PropsWithChildren>(({ children }) => {
18
20
  open={open}
19
21
  styles={{
20
22
  body: { display: 'flex', minHeight: 'min(75vh, 750px)', overflow: 'hidden', padding: 0 },
23
+ content: { border: 'none', boxShadow: `0 0 0 1px ${theme.colorBorderSecondary}` },
21
24
  }}
22
25
  title={false}
23
26
  width={1024}
@@ -1,14 +1,15 @@
1
1
  'use client';
2
2
 
3
3
  import { createStyles } from 'antd-style';
4
+ import { rgba } from 'polished';
4
5
  import { memo } from 'react';
5
6
 
6
7
  const useStyles = createStyles(
7
- ({ css, token }) => css`
8
+ ({ css, token, isDarkMode }) => css`
8
9
  flex: none;
9
10
  width: 100%;
10
11
  height: 6px;
11
- background: ${token.colorFillTertiary};
12
+ background: ${isDarkMode ? rgba(token.colorFillTertiary, 0.04) : token.colorFillTertiary};
12
13
  `,
13
14
  );
14
15
 
@@ -1,42 +1,52 @@
1
- import { Icon, List } from '@lobehub/ui';
1
+ import { Icon, IconProps } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import { ChevronRight } from 'lucide-react';
4
4
  import { ReactNode, memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
5
6
 
6
- const { Item } = List;
7
+ import Divider from './Divider';
7
8
 
8
9
  const useStyles = createStyles(({ css, token }) => ({
9
10
  container: css`
10
11
  position: relative;
11
-
12
- gap: 12px;
13
-
14
- padding: 16px !important;
15
-
16
- background: ${token.colorBgLayout};
12
+ font-size: 15px;
17
13
  border-radius: 0;
14
+
15
+ &:active {
16
+ background: ${token.colorFillTertiary};
17
+ }
18
18
  `,
19
19
  }));
20
20
 
21
21
  export interface CellProps {
22
- icon: ReactNode;
23
- label: string | ReactNode;
22
+ icon?: IconProps['icon'];
23
+ key?: string | number;
24
+ label?: string | ReactNode;
24
25
  onClick?: () => void;
26
+ type?: 'divider';
25
27
  }
26
28
 
27
- const Cell = memo<CellProps>(({ label, icon, onClick }) => {
28
- const { cx, styles } = useStyles();
29
+ const Cell = memo<CellProps>(({ label, icon, onClick, type }) => {
30
+ const { cx, styles, theme } = useStyles();
31
+
32
+ if (type === 'divider') return <Divider />;
29
33
 
30
34
  return (
31
- <Item
32
- active={false}
33
- avatar={icon}
35
+ <Flexbox
36
+ align={'center'}
34
37
  className={cx(styles.container)}
38
+ gap={12}
39
+ horizontal
40
+ justify={'space-between'}
35
41
  onClick={onClick}
36
- title={label as string}
42
+ padding={16}
37
43
  >
38
- <Icon icon={ChevronRight} size={{ fontSize: 16 }} />
39
- </Item>
44
+ <Flexbox align={'center'} gap={12} horizontal>
45
+ {icon && <Icon color={theme.colorPrimaryBorder} icon={icon} size={{ fontSize: 20 }} />}
46
+ {label}
47
+ </Flexbox>
48
+ <Icon color={theme.colorBorder} icon={ChevronRight} size={{ fontSize: 16 }} />
49
+ </Flexbox>
40
50
  );
41
51
  });
42
52
 
@@ -13,6 +13,7 @@ import useSWR from 'swr';
13
13
  import { messageService } from '@/services/message';
14
14
  import { sessionService } from '@/services/session';
15
15
  import { topicService } from '@/services/topic';
16
+ import { useServerConfigStore } from '@/store/serverConfig';
16
17
 
17
18
  const useStyles = createStyles(({ css, token }) => ({
18
19
  card: css`
@@ -53,6 +54,7 @@ const formatNumber = (num: any) => {
53
54
  };
54
55
 
55
56
  const DataStatistics = memo<Omit<FlexboxProps, 'children'>>(({ style, ...rest }) => {
57
+ const mobile = useServerConfigStore((s) => s.isMobile);
56
58
  // sessions
57
59
  const { data: sessions, isLoading: sessionsLoading } = useSWR(
58
60
  'count-sessions',
@@ -111,7 +113,7 @@ const DataStatistics = memo<Omit<FlexboxProps, 'children'>>(({ style, ...rest })
111
113
  <Flexbox
112
114
  align={'center'}
113
115
  className={styles.card}
114
- flex={showBadge ? 2 : 1}
116
+ flex={showBadge && !mobile ? 2 : 1}
115
117
  gap={4}
116
118
  horizontal
117
119
  justify={'space-between'}
@@ -3,7 +3,7 @@ import { memo } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
 
6
- import UserInfo from '@/features/User/UserInfo';
6
+ import UserInfo from './UserInfo';
7
7
 
8
8
  const UserLoginOrSignup = memo<{ onClick: () => void }>(({ onClick }) => {
9
9
  const { t } = useTranslation('auth');
@@ -11,7 +11,7 @@ const UserLoginOrSignup = memo<{ onClick: () => void }>(({ onClick }) => {
11
11
  return (
12
12
  <>
13
13
  <UserInfo />
14
- <Flexbox paddingBlock={'0 12px'} paddingInline={16} width={'100%'}>
14
+ <Flexbox paddingBlock={12} paddingInline={16} width={'100%'}>
15
15
  <Button block onClick={onClick} type={'primary'}>
16
16
  {t('loginOrSignup')}
17
17
  </Button>
@@ -45,13 +45,19 @@ const PanelContent = memo<{ closePopover: () => void }>(({ closePopover }) => {
45
45
  return (
46
46
  <Flexbox gap={2} style={{ minWidth: 300 }}>
47
47
  {!enableAuth ? (
48
- <UserInfo />
48
+ <>
49
+ <UserInfo />
50
+ <DataStatistics />
51
+ </>
49
52
  ) : isLoginWithAuth ? (
50
- <UserInfo onClick={handleOpenProfile} />
53
+ <>
54
+ <UserInfo onClick={handleOpenProfile} />
55
+ <DataStatistics />
56
+ </>
51
57
  ) : (
52
58
  <UserLoginOrSignup onClick={handleSignIn} />
53
59
  )}
54
- <DataStatistics />
60
+
55
61
  <Menu items={mainItems} onClick={closePopover} />
56
62
  <Flexbox
57
63
  align={'center'}
@@ -57,7 +57,21 @@ export const useMenu = () => {
57
57
  const hasNewVersion = useNewVersion();
58
58
  const openSettings = useOpenSettings();
59
59
  const { t } = useTranslation(['common', 'setting', 'auth']);
60
- const isSignedIn = useUserStore(authSelectors.isLoginWithAuth);
60
+ const [isLogin, isLoginWithAuth, isLoginWithClerk, openUserProfile] = useUserStore((s) => [
61
+ authSelectors.isLogin(s),
62
+ authSelectors.isLoginWithAuth(s),
63
+ authSelectors.isLoginWithClerk(s),
64
+ s.openUserProfile,
65
+ ]);
66
+
67
+ const profile: MenuProps['items'] = [
68
+ {
69
+ icon: <Icon icon={CircleUserRound} />,
70
+ key: 'profile',
71
+ label: t('userPanel.profile'),
72
+ onClick: () => openUserProfile(),
73
+ },
74
+ ];
61
75
 
62
76
  const settings: MenuProps['items'] = [
63
77
  {
@@ -82,7 +96,7 @@ export const useMenu = () => {
82
96
  },
83
97
  ];
84
98
 
85
- const exports: MenuProps['items'] = [
99
+ const data: MenuProps['items'] = [
86
100
  {
87
101
  icon: <Icon icon={HardDriveUpload} />,
88
102
  key: 'import',
@@ -123,22 +137,6 @@ export const useMenu = () => {
123
137
  },
124
138
  ];
125
139
 
126
- const openUserProfile = useUserStore((s) => s.openUserProfile);
127
-
128
- const planAndBilling: MenuProps['items'] = [
129
- {
130
- icon: <Icon icon={CircleUserRound} />,
131
- key: 'profile',
132
- label: t('userPanel.profile'),
133
- onClick: () => {
134
- openUserProfile();
135
- },
136
- },
137
- {
138
- type: 'divider',
139
- },
140
- ];
141
-
142
140
  const helps: MenuProps['items'] = [
143
141
  {
144
142
  icon: <Icon icon={DiscordIcon} />,
@@ -192,19 +190,21 @@ export const useMenu = () => {
192
190
  {
193
191
  type: 'divider',
194
192
  },
195
- ...settings,
196
- ...(isSignedIn ? planAndBilling : []),
197
- ...exports,
193
+ ...(isLoginWithClerk ? profile : []),
194
+ ...(isLogin ? settings : []),
195
+ ...(isLogin ? data : []),
198
196
  ...helps,
199
197
  ].filter(Boolean) as MenuProps['items'];
200
198
 
201
- const logoutItems: MenuProps['items'] = [
202
- {
203
- icon: <Icon icon={LogOut} />,
204
- key: 'logout',
205
- label: <span>{t('signout', { ns: 'auth' })}</span>,
206
- },
207
- ];
199
+ const logoutItems: MenuProps['items'] = isLoginWithAuth
200
+ ? [
201
+ {
202
+ icon: <Icon icon={LogOut} />,
203
+ key: 'logout',
204
+ label: <span>{t('signout', { ns: 'auth' })}</span>,
205
+ },
206
+ ]
207
+ : [];
208
208
 
209
209
  return { logoutItems, mainItems };
210
- };
210
+ };
@@ -57,6 +57,10 @@ vi.mock('../UserLoginOrSignup', () => ({
57
57
  )),
58
58
  }));
59
59
 
60
+ vi.mock('../DataStatistics', () => ({
61
+ default: vi.fn(() => <div>Mocked DataStatistics</div>),
62
+ }));
63
+
60
64
  // 定义一个变量来存储 enableAuth 的值
61
65
  let enableAuth = true;
62
66
 
@@ -83,6 +87,7 @@ describe('PanelContent', () => {
83
87
  render(<PanelContent closePopover={closePopover} />);
84
88
 
85
89
  expect(screen.getByText('Mocked UserInfo')).toBeInTheDocument();
90
+ expect(screen.getByText('Mocked DataStatistics')).toBeInTheDocument();
86
91
  expect(screen.queryByText('Mocked SignInBlock')).not.toBeInTheDocument();
87
92
  });
88
93
 
@@ -94,6 +99,7 @@ describe('PanelContent', () => {
94
99
  render(<PanelContent closePopover={closePopover} />);
95
100
 
96
101
  expect(screen.getByText('Mocked SignInBlock')).toBeInTheDocument();
102
+ expect(screen.queryByText('Mocked DataStatistics')).not.toBeInTheDocument();
97
103
  expect(screen.queryByText('Mocked UserInfo')).not.toBeInTheDocument();
98
104
  });
99
105
 
@@ -127,6 +133,7 @@ describe('PanelContent', () => {
127
133
  render(<PanelContent closePopover={closePopover} />);
128
134
 
129
135
  expect(screen.getByText('Mocked UserInfo')).toBeInTheDocument();
136
+ expect(screen.getByText('Mocked DataStatistics')).toBeInTheDocument();
130
137
  expect(screen.queryByText('Mocked SignInBlock')).not.toBeInTheDocument();
131
138
  });
132
139
 
@@ -0,0 +1,142 @@
1
+ import { act, renderHook } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { useUserStore } from '@/store/user';
5
+
6
+ import { useMenu } from '../UserPanel/useMenu';
7
+
8
+ // Mock dependencies
9
+ vi.mock('next/link', () => ({
10
+ default: vi.fn(({ children }) => <div>{children}</div>),
11
+ }));
12
+
13
+ vi.mock('@/hooks/useQueryRoute', () => ({
14
+ useQueryRoute: vi.fn(() => ({
15
+ push: vi.fn(),
16
+ })),
17
+ }));
18
+
19
+ vi.mock('@/hooks/useInterceptingRoutes', () => ({
20
+ useOpenSettings: vi.fn(() => vi.fn()),
21
+ }));
22
+
23
+ vi.mock('@/features/DataImporter', () => ({
24
+ default: vi.fn(({ children }) => <div>{children}</div>),
25
+ }));
26
+
27
+ vi.mock('react-i18next', () => ({
28
+ useTranslation: vi.fn(() => ({
29
+ t: vi.fn((key) => key),
30
+ })),
31
+ }));
32
+
33
+ vi.mock('@/services/config', () => ({
34
+ configService: {
35
+ exportAgents: vi.fn(),
36
+ exportAll: vi.fn(),
37
+ exportSessions: vi.fn(),
38
+ exportSettings: vi.fn(),
39
+ },
40
+ }));
41
+
42
+ vi.mock('./useNewVersion', () => ({
43
+ useNewVersion: vi.fn(() => false),
44
+ }));
45
+
46
+ // 定义一个变量来存储 enableAuth 的值
47
+ let enableAuth = true;
48
+ let enableClerk = true;
49
+ // 模拟 @/const/auth 模块
50
+ vi.mock('@/const/auth', () => ({
51
+ get enableAuth() {
52
+ return enableAuth;
53
+ },
54
+ get enableClerk() {
55
+ return enableClerk;
56
+ },
57
+ }));
58
+
59
+ afterEach(() => {
60
+ enableAuth = true;
61
+ enableClerk = true;
62
+ });
63
+
64
+ describe('useMenu', () => {
65
+ it('should provide correct menu items when user is logged in with auth', () => {
66
+ act(() => {
67
+ useUserStore.setState({ isSignedIn: true });
68
+ });
69
+ enableAuth = true;
70
+ enableClerk = false;
71
+
72
+ const { result } = renderHook(() => useMenu());
73
+
74
+ act(() => {
75
+ const { mainItems, logoutItems } = result.current;
76
+ expect(mainItems?.some((item) => item?.key === 'profile')).toBe(false);
77
+ expect(mainItems?.some((item) => item?.key === 'setting')).toBe(true);
78
+ expect(mainItems?.some((item) => item?.key === 'import')).toBe(true);
79
+ expect(mainItems?.some((item) => item?.key === 'export')).toBe(true);
80
+ expect(mainItems?.some((item) => item?.key === 'discord')).toBe(true);
81
+ expect(logoutItems.some((item) => item?.key === 'logout')).toBe(true);
82
+ });
83
+ });
84
+
85
+ it('should provide correct menu items when user is logged in with Clerk', () => {
86
+ act(() => {
87
+ useUserStore.setState({ isSignedIn: true });
88
+ });
89
+ enableAuth = true;
90
+ enableClerk = true;
91
+
92
+ const { result } = renderHook(() => useMenu());
93
+
94
+ act(() => {
95
+ const { mainItems, logoutItems } = result.current;
96
+ expect(mainItems?.some((item) => item?.key === 'profile')).toBe(true);
97
+ expect(mainItems?.some((item) => item?.key === 'setting')).toBe(true);
98
+ expect(mainItems?.some((item) => item?.key === 'import')).toBe(true);
99
+ expect(mainItems?.some((item) => item?.key === 'export')).toBe(true);
100
+ expect(mainItems?.some((item) => item?.key === 'discord')).toBe(true);
101
+ expect(logoutItems.some((item) => item?.key === 'logout')).toBe(true);
102
+ });
103
+ });
104
+
105
+ it('should provide correct menu items when user is logged in without auth', () => {
106
+ act(() => {
107
+ useUserStore.setState({ isSignedIn: false });
108
+ });
109
+ enableAuth = false;
110
+
111
+ const { result } = renderHook(() => useMenu());
112
+
113
+ act(() => {
114
+ const { mainItems, logoutItems } = result.current;
115
+ expect(mainItems?.some((item) => item?.key === 'profile')).toBe(false);
116
+ expect(mainItems?.some((item) => item?.key === 'setting')).toBe(true);
117
+ expect(mainItems?.some((item) => item?.key === 'import')).toBe(true);
118
+ expect(mainItems?.some((item) => item?.key === 'export')).toBe(true);
119
+ expect(mainItems?.some((item) => item?.key === 'discord')).toBe(true);
120
+ expect(logoutItems.some((item) => item?.key === 'logout')).toBe(false);
121
+ });
122
+ });
123
+
124
+ it('should provide correct menu items when user is not logged in', () => {
125
+ act(() => {
126
+ useUserStore.setState({ isSignedIn: false });
127
+ });
128
+ enableAuth = true;
129
+
130
+ const { result } = renderHook(() => useMenu());
131
+
132
+ act(() => {
133
+ const { mainItems, logoutItems } = result.current;
134
+ expect(mainItems?.some((item) => item?.key === 'profile')).toBe(false);
135
+ expect(mainItems?.some((item) => item?.key === 'setting')).toBe(false);
136
+ expect(mainItems?.some((item) => item?.key === 'import')).toBe(false);
137
+ expect(mainItems?.some((item) => item?.key === 'export')).toBe(false);
138
+ expect(mainItems?.some((item) => item?.key === 'discord')).toBe(true);
139
+ expect(logoutItems.some((item) => item?.key === 'logout')).toBe(false);
140
+ });
141
+ });
142
+ });
@@ -7,13 +7,14 @@ import { createStyles, useThemeMode } from 'antd-style';
7
7
  const prefixCls = 'cl';
8
8
 
9
9
  export const useStyles = createStyles(
10
- ({ css, token }) =>
10
+ ({ css, token, isDarkMode }) =>
11
11
  ({
12
12
  avatarBox: css`
13
13
  width: 40px;
14
14
  height: 40px;
15
15
  `,
16
16
  cardBox: css`
17
+ background: ${token.colorBgContainer};
17
18
  border-radius: ${token.borderRadiusLG}px;
18
19
  box-shadow: 0 0 0 1px ${token.colorBorderSecondary};
19
20
  `,
@@ -42,7 +43,7 @@ export const useStyles = createStyles(
42
43
  }
43
44
  `,
44
45
  navbar: css`
45
- background: ${token.colorBgContainer};
46
+ background: ${isDarkMode ? token.colorBgContainer : token.colorFillTertiary};
46
47
  `,
47
48
  navbarButton: css`
48
49
  line-height: 2;
@@ -68,10 +69,10 @@ export const useStyles = createStyles(
68
69
  }
69
70
  `,
70
71
  scrollBox: css`
71
- background: ${token.colorBgLayout};
72
+ background: ${isDarkMode ? token.colorFillQuaternary : token.colorBgElevated};
72
73
  border: unset;
73
74
  border-radius: unset;
74
- box-shadow: 0 1px 0 1px ${token.colorSplit};
75
+ box-shadow: 0 1px 0 1px ${token.colorFillTertiary};
75
76
  `,
76
77
  socialButtons: css`
77
78
  display: flex;