@lobehub/chat 0.157.2 → 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.
- package/.eslintignore +1 -2
- package/CHANGELOG.md +25 -0
- package/locales/ar/auth.json +2 -0
- package/locales/ar/common.json +1 -0
- package/locales/bg-BG/auth.json +2 -0
- package/locales/bg-BG/common.json +1 -0
- package/locales/bg-BG/error.json +3 -3
- package/locales/de-DE/auth.json +2 -0
- package/locales/de-DE/common.json +1 -0
- package/locales/en-US/auth.json +2 -0
- package/locales/en-US/common.json +1 -0
- package/locales/es-ES/auth.json +2 -0
- package/locales/es-ES/common.json +1 -0
- package/locales/fr-FR/auth.json +2 -0
- package/locales/fr-FR/common.json +1 -0
- package/locales/it-IT/auth.json +2 -0
- package/locales/it-IT/common.json +1 -0
- package/locales/ja-JP/auth.json +2 -0
- package/locales/ja-JP/common.json +1 -0
- package/locales/ko-KR/auth.json +2 -0
- package/locales/ko-KR/common.json +1 -0
- package/locales/nl-NL/auth.json +2 -0
- package/locales/nl-NL/common.json +50 -49
- package/locales/pl-PL/auth.json +2 -0
- package/locales/pl-PL/common.json +1 -0
- package/locales/pl-PL/error.json +3 -3
- package/locales/pt-BR/auth.json +2 -0
- package/locales/pt-BR/common.json +1 -0
- package/locales/ru-RU/auth.json +2 -0
- package/locales/ru-RU/common.json +1 -0
- package/locales/ru-RU/error.json +3 -3
- package/locales/tr-TR/auth.json +2 -0
- package/locales/tr-TR/common.json +1 -0
- package/locales/vi-VN/auth.json +2 -0
- package/locales/vi-VN/common.json +1 -0
- package/locales/zh-CN/auth.json +2 -0
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-TW/auth.json +2 -0
- package/locales/zh-TW/common.json +1 -0
- package/package.json +1 -1
- package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +80 -0
- package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +116 -0
- package/src/app/(main)/(mobile)/me/(home)/features/Category.tsx +15 -0
- package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +37 -0
- package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +95 -0
- package/src/app/(main)/(mobile)/me/{page.tsx → (home)/page.tsx} +6 -10
- package/src/app/(main)/(mobile)/me/data/features/Category.tsx +48 -0
- package/src/app/(main)/(mobile)/me/data/features/Header.tsx +33 -0
- package/src/app/(main)/(mobile)/me/data/layout.tsx +13 -0
- package/src/app/(main)/(mobile)/me/data/loading.tsx +5 -0
- package/src/app/(main)/(mobile)/me/data/page.tsx +17 -0
- package/src/app/(main)/(mobile)/me/profile/features/Category.tsx +45 -0
- package/src/app/(main)/(mobile)/me/profile/features/Header.tsx +33 -0
- package/src/app/(main)/(mobile)/me/profile/layout.tsx +16 -0
- package/src/app/(main)/(mobile)/me/profile/loading.tsx +5 -0
- package/src/app/(main)/(mobile)/me/profile/page.tsx +17 -0
- package/src/app/(main)/(mobile)/me/settings/features/Category.tsx +15 -0
- package/src/app/(main)/(mobile)/me/settings/features/Header.tsx +33 -0
- package/src/app/(main)/(mobile)/me/settings/features/useCategory.tsx +57 -0
- package/src/app/(main)/(mobile)/me/settings/layout.tsx +13 -0
- package/src/app/(main)/(mobile)/me/settings/loading.tsx +5 -0
- package/src/app/(main)/(mobile)/me/settings/page.tsx +17 -0
- package/src/app/(main)/_layout/Mobile.tsx +5 -4
- package/src/app/(main)/profile/[[...slugs]]/Client.tsx +74 -0
- package/src/app/(main)/profile/[[...slugs]]/page.tsx +18 -0
- package/src/app/(main)/profile/_layout/Mobile/Header.tsx +26 -0
- package/src/app/(main)/profile/_layout/Mobile/index.tsx +16 -0
- package/src/app/(main)/profile/layout.tsx +20 -0
- package/src/app/(main)/profile/loading.tsx +23 -0
- package/src/app/(main)/settings/_layout/Mobile/Header.tsx +2 -1
- package/src/app/(main)/settings/hooks/useCategory.tsx +7 -13
- package/src/app/@modal/layout.tsx +3 -0
- package/src/components/Cell/Divider.tsx +3 -2
- package/src/components/Cell/index.tsx +28 -18
- package/src/features/User/DataStatistics.tsx +3 -1
- package/src/features/User/UserLoginOrSignup.tsx +2 -2
- package/src/features/User/UserPanel/PanelContent.tsx +9 -3
- package/src/features/User/UserPanel/useMenu.tsx +29 -29
- package/src/features/User/__tests__/PanelContent.test.tsx +7 -0
- package/src/features/User/__tests__/useMenu.test.tsx +142 -0
- package/src/layout/AuthProvider/Clerk/useAppearance.ts +5 -4
- package/src/locales/default/auth.ts +2 -0
- package/src/locales/default/common.ts +1 -0
- package/src/store/user/slices/auth/selectors.ts +2 -1
- package/src/app/(auth)/profile/[[...slugs]]/PageTitle.tsx +0 -13
- package/src/app/(auth)/profile/[[...slugs]]/page.tsx +0 -14
- package/src/app/(main)/(mobile)/me/features/Cate.tsx +0 -33
- package/src/app/(main)/(mobile)/me/features/ExtraCate.tsx +0 -24
- package/src/app/(main)/(mobile)/me/features/useExtraCate.tsx +0 -62
- /package/src/app/(main)/(mobile)/me/{features → (home)/features}/Header.tsx +0 -0
- /package/src/app/(main)/(mobile)/me/{layout.tsx → (home)/layout.tsx} +0 -0
- /package/src/app/(main)/(mobile)/me/{loading.tsx → (home)/loading.tsx} +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { redirect } from 'next/navigation';
|
|
2
|
+
|
|
3
|
+
import { isMobileDevice } from '@/utils/responsive';
|
|
4
|
+
|
|
5
|
+
import Category from './features/Category';
|
|
6
|
+
|
|
7
|
+
const Page = () => {
|
|
8
|
+
const mobile = isMobileDevice();
|
|
9
|
+
|
|
10
|
+
if (!mobile) return redirect('/settings/common');
|
|
11
|
+
|
|
12
|
+
return <Category />;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
Page.displayName = 'MeSettings';
|
|
16
|
+
|
|
17
|
+
export default Page;
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { usePathname } from 'next/navigation';
|
|
4
|
+
import qs from 'query-string';
|
|
4
5
|
import { memo } from 'react';
|
|
5
6
|
|
|
6
7
|
import { useQuery } from '@/hooks/useQuery';
|
|
7
8
|
|
|
8
9
|
import { LayoutProps } from './type';
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
+
const MOBILE_NAV_ROUTES = new Set(['/chat', '/market', '/me']);
|
|
11
12
|
|
|
12
13
|
const Layout = memo(({ children, nav }: LayoutProps) => {
|
|
13
14
|
const { showMobileWorkspace } = useQuery();
|
|
14
15
|
const pathname = usePathname();
|
|
15
|
-
const
|
|
16
|
-
|
|
16
|
+
const { url } = qs.parseUrl(pathname);
|
|
17
|
+
const showNav = !showMobileWorkspace && MOBILE_NAV_ROUTES.has(url);
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<>
|
|
20
21
|
{children}
|
|
21
|
-
{
|
|
22
|
+
{showNav && nav}
|
|
22
23
|
</>
|
|
23
24
|
);
|
|
24
25
|
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { UserProfile } from '@clerk/nextjs';
|
|
4
|
+
import { ElementsConfig } from '@clerk/types';
|
|
5
|
+
import { createStyles } from 'antd-style';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
|
|
8
|
+
export const useStyles = createStyles(
|
|
9
|
+
({ css, token, cx }, mobile: boolean) =>
|
|
10
|
+
({
|
|
11
|
+
cardBox: css`
|
|
12
|
+
width: 100%;
|
|
13
|
+
max-width: unset;
|
|
14
|
+
height: 100%;
|
|
15
|
+
|
|
16
|
+
border: unset;
|
|
17
|
+
border-radius: unset;
|
|
18
|
+
box-shadow: unset;
|
|
19
|
+
`,
|
|
20
|
+
footer: cx(
|
|
21
|
+
mobile &&
|
|
22
|
+
css`
|
|
23
|
+
display: none;
|
|
24
|
+
`,
|
|
25
|
+
),
|
|
26
|
+
navbar: css`
|
|
27
|
+
flex: none;
|
|
28
|
+
|
|
29
|
+
width: 280px;
|
|
30
|
+
max-width: unset;
|
|
31
|
+
margin-right: 0;
|
|
32
|
+
padding: 24px 12px 16px;
|
|
33
|
+
|
|
34
|
+
background: ${token.colorBgContainer};
|
|
35
|
+
border-right: 1px solid ${token.colorSplit};
|
|
36
|
+
`,
|
|
37
|
+
navbarMobileMenuRow: cx(
|
|
38
|
+
mobile &&
|
|
39
|
+
css`
|
|
40
|
+
display: none;
|
|
41
|
+
`,
|
|
42
|
+
),
|
|
43
|
+
pageScrollBox: css`
|
|
44
|
+
align-self: center;
|
|
45
|
+
width: 100%;
|
|
46
|
+
max-width: 1024px;
|
|
47
|
+
`,
|
|
48
|
+
rootBox: css`
|
|
49
|
+
width: 100%;
|
|
50
|
+
height: 100%;
|
|
51
|
+
`,
|
|
52
|
+
scrollBox: css`
|
|
53
|
+
background: ${token.colorBgLayout};
|
|
54
|
+
border: unset;
|
|
55
|
+
border-radius: unset;
|
|
56
|
+
`,
|
|
57
|
+
}) as Partial<{
|
|
58
|
+
[k in keyof ElementsConfig]: any;
|
|
59
|
+
}>,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const Client = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
63
|
+
const { styles } = useStyles(mobile);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<UserProfile
|
|
67
|
+
appearance={{
|
|
68
|
+
elements: styles,
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export default Client;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { translation } from '@/server/translation';
|
|
2
|
+
import { isMobileDevice } from '@/utils/responsive';
|
|
3
|
+
|
|
4
|
+
import Client from './Client';
|
|
5
|
+
|
|
6
|
+
export const generateMetadata = async () => {
|
|
7
|
+
const { t } = await translation('common');
|
|
8
|
+
return {
|
|
9
|
+
title: t('userButton.profile'),
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const Page = () => {
|
|
14
|
+
const mobile = isMobileDevice();
|
|
15
|
+
return <Client mobile={mobile} />;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default Page;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
4
|
+
import { usePathname, useRouter } from 'next/navigation';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
|
9
|
+
|
|
10
|
+
const Header = memo(() => {
|
|
11
|
+
const { t } = useTranslation('auth');
|
|
12
|
+
|
|
13
|
+
const router = useRouter();
|
|
14
|
+
const pathname = usePathname();
|
|
15
|
+
const isSecurity = pathname.startsWith('/prifile/security');
|
|
16
|
+
return (
|
|
17
|
+
<MobileNavBar
|
|
18
|
+
center={<MobileNavBarTitle title={t(isSecurity ? 'security' : 'profile')} />}
|
|
19
|
+
onBackClick={() => router.push('/me/profile')}
|
|
20
|
+
showBackButton
|
|
21
|
+
style={mobileHeaderSticky}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export default Header;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
|
|
3
|
+
import Header from './Header';
|
|
4
|
+
|
|
5
|
+
const Layout = ({ children }: PropsWithChildren) => {
|
|
6
|
+
return (
|
|
7
|
+
<>
|
|
8
|
+
<Header />
|
|
9
|
+
{children}
|
|
10
|
+
</>
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
Layout.displayName = 'ProfileMobileLayout';
|
|
15
|
+
|
|
16
|
+
export default Layout;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { notFound } from 'next/navigation';
|
|
2
|
+
import { PropsWithChildren } from 'react';
|
|
3
|
+
|
|
4
|
+
import { enableClerk } from '@/const/auth';
|
|
5
|
+
import { isMobileDevice } from '@/utils/responsive';
|
|
6
|
+
|
|
7
|
+
import MobileLayout from './_layout/Mobile';
|
|
8
|
+
|
|
9
|
+
const Layout = ({ children }: PropsWithChildren) => {
|
|
10
|
+
if (!enableClerk) return notFound();
|
|
11
|
+
|
|
12
|
+
const mobile = isMobileDevice();
|
|
13
|
+
if (mobile) return <MobileLayout>{children}</MobileLayout>;
|
|
14
|
+
|
|
15
|
+
return children;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
Layout.displayName = 'ProfileLayout';
|
|
19
|
+
|
|
20
|
+
export default Layout;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Flexbox } from 'react-layout-kit';
|
|
2
|
+
|
|
3
|
+
import SkeletonLoading from '@/components/SkeletonLoading';
|
|
4
|
+
import { isMobileDevice } from '@/utils/responsive';
|
|
5
|
+
|
|
6
|
+
const Loading = () => {
|
|
7
|
+
const mobile = isMobileDevice();
|
|
8
|
+
if (mobile) return <SkeletonLoading paragraph={{ rows: 8 }} />;
|
|
9
|
+
return (
|
|
10
|
+
<Flexbox horizontal style={{ position: 'relative' }} width={'100%'}>
|
|
11
|
+
<Flexbox padding={24} width={256}>
|
|
12
|
+
<SkeletonLoading paragraph={{ rows: 8 }} />;
|
|
13
|
+
</Flexbox>
|
|
14
|
+
<Flexbox align={'center'} flex={1}>
|
|
15
|
+
<Flexbox padding={24} style={{ maxWidth: 1024 }} width={'100%'}>
|
|
16
|
+
<SkeletonLoading paragraph={{ rows: 8 }} />;
|
|
17
|
+
</Flexbox>
|
|
18
|
+
</Flexbox>
|
|
19
|
+
</Flexbox>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default Loading;
|
|
@@ -7,6 +7,7 @@ import { memo } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { Flexbox } from 'react-layout-kit';
|
|
9
9
|
|
|
10
|
+
import { enableAuth } from '@/const/auth';
|
|
10
11
|
import { useActiveSettingsKey } from '@/hooks/useActiveSettingsKey';
|
|
11
12
|
import { SettingsTabs } from '@/store/global/initialState';
|
|
12
13
|
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
|
@@ -32,7 +33,7 @@ const Header = memo(() => {
|
|
|
32
33
|
}
|
|
33
34
|
/>
|
|
34
35
|
}
|
|
35
|
-
onBackClick={() => router.push('/me')}
|
|
36
|
+
onBackClick={() => router.push(enableAuth ? '/me/settings' : '/me')}
|
|
36
37
|
showBackButton
|
|
37
38
|
style={mobileHeaderSticky}
|
|
38
39
|
/>
|
|
@@ -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
|
-
|
|
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}
|
|
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}
|
|
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}
|
|
37
|
+
icon: <Icon icon={Brain} />,
|
|
44
38
|
key: SettingsTabs.LLM,
|
|
45
39
|
label: t('tab.llm'),
|
|
46
40
|
},
|
|
47
|
-
{ icon: <Icon icon={Mic2}
|
|
41
|
+
{ icon: <Icon icon={Mic2} />, key: SettingsTabs.TTS, label: t('tab.tts') },
|
|
48
42
|
{
|
|
49
|
-
icon: <Icon icon={Bot}
|
|
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}
|
|
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,
|
|
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
|
-
|
|
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
|
|
23
|
-
|
|
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
|
-
<
|
|
32
|
-
|
|
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
|
-
|
|
42
|
+
padding={16}
|
|
37
43
|
>
|
|
38
|
-
<
|
|
39
|
-
|
|
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 '
|
|
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={
|
|
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
|
-
|
|
48
|
+
<>
|
|
49
|
+
<UserInfo />
|
|
50
|
+
<DataStatistics />
|
|
51
|
+
</>
|
|
49
52
|
) : isLoginWithAuth ? (
|
|
50
|
-
|
|
53
|
+
<>
|
|
54
|
+
<UserInfo onClick={handleOpenProfile} />
|
|
55
|
+
<DataStatistics />
|
|
56
|
+
</>
|
|
51
57
|
) : (
|
|
52
58
|
<UserLoginOrSignup onClick={handleSignIn} />
|
|
53
59
|
)}
|
|
54
|
-
|
|
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
|
|
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
|
|
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
|
-
...
|
|
196
|
-
...(
|
|
197
|
-
...
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|