@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,116 @@
|
|
|
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 { useCategory } from '../features/useCategory';
|
|
7
|
+
|
|
8
|
+
// Mock dependencies
|
|
9
|
+
vi.mock('next/navigation', () => ({
|
|
10
|
+
useRouter: vi.fn(() => ({
|
|
11
|
+
push: vi.fn(),
|
|
12
|
+
})),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock('react-i18next', () => ({
|
|
16
|
+
useTranslation: vi.fn(() => ({
|
|
17
|
+
t: vi.fn((key) => key),
|
|
18
|
+
})),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock('../../settings/features/useCategory', () => ({
|
|
22
|
+
useCategory: vi.fn(() => [{ key: 'extraSetting', label: 'Extra Setting' }]),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// 定义一个变量来存储 enableAuth 的值
|
|
26
|
+
let enableAuth = true;
|
|
27
|
+
let enableClerk = true;
|
|
28
|
+
// 模拟 @/const/auth 模块
|
|
29
|
+
vi.mock('@/const/auth', () => ({
|
|
30
|
+
get enableAuth() {
|
|
31
|
+
return enableAuth;
|
|
32
|
+
},
|
|
33
|
+
get enableClerk() {
|
|
34
|
+
return enableClerk;
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
enableAuth = true;
|
|
40
|
+
enableClerk = true;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('useCategory', () => {
|
|
44
|
+
it('should return correct items when the user is logged in with authentication', () => {
|
|
45
|
+
act(() => {
|
|
46
|
+
useUserStore.setState({ isSignedIn: true });
|
|
47
|
+
});
|
|
48
|
+
enableAuth = true;
|
|
49
|
+
enableClerk = false;
|
|
50
|
+
|
|
51
|
+
const { result } = renderHook(() => useCategory());
|
|
52
|
+
|
|
53
|
+
act(() => {
|
|
54
|
+
const items = result.current;
|
|
55
|
+
expect(items.some((item) => item.key === 'profile')).toBe(false);
|
|
56
|
+
expect(items.some((item) => item.key === 'setting')).toBe(true);
|
|
57
|
+
expect(items.some((item) => item.key === 'data')).toBe(true);
|
|
58
|
+
expect(items.some((item) => item.key === 'docs')).toBe(true);
|
|
59
|
+
expect(items.some((item) => item.key === 'feedback')).toBe(true);
|
|
60
|
+
expect(items.some((item) => item.key === 'discord')).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should return correct items when the user is logged in with Clerk', () => {
|
|
65
|
+
act(() => {
|
|
66
|
+
useUserStore.setState({ isSignedIn: true });
|
|
67
|
+
});
|
|
68
|
+
enableAuth = true;
|
|
69
|
+
enableClerk = true;
|
|
70
|
+
|
|
71
|
+
const { result } = renderHook(() => useCategory());
|
|
72
|
+
|
|
73
|
+
act(() => {
|
|
74
|
+
const items = result.current;
|
|
75
|
+
expect(items.some((item) => item.key === 'profile')).toBe(true);
|
|
76
|
+
expect(items.some((item) => item.key === 'setting')).toBe(true);
|
|
77
|
+
expect(items.some((item) => item.key === 'data')).toBe(true);
|
|
78
|
+
expect(items.some((item) => item.key === 'docs')).toBe(true);
|
|
79
|
+
expect(items.some((item) => item.key === 'feedback')).toBe(true);
|
|
80
|
+
expect(items.some((item) => item.key === 'discord')).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should return correct items when the user is not logged in', () => {
|
|
85
|
+
act(() => {
|
|
86
|
+
useUserStore.setState({ isSignedIn: false });
|
|
87
|
+
});
|
|
88
|
+
enableAuth = true;
|
|
89
|
+
|
|
90
|
+
const { result } = renderHook(() => useCategory());
|
|
91
|
+
|
|
92
|
+
act(() => {
|
|
93
|
+
const items = result.current;
|
|
94
|
+
expect(items.some((item) => item.key === 'profile')).toBe(false);
|
|
95
|
+
expect(items.some((item) => item.key === 'setting')).toBe(false);
|
|
96
|
+
expect(items.some((item) => item.key === 'data')).toBe(false);
|
|
97
|
+
expect(items.some((item) => item.key === 'docs')).toBe(true);
|
|
98
|
+
expect(items.some((item) => item.key === 'feedback')).toBe(true);
|
|
99
|
+
expect(items.some((item) => item.key === 'discord')).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should handle settings for non-authenticated users', () => {
|
|
104
|
+
act(() => {
|
|
105
|
+
useUserStore.setState({ isSignedIn: false });
|
|
106
|
+
});
|
|
107
|
+
enableAuth = false;
|
|
108
|
+
|
|
109
|
+
const { result } = renderHook(() => useCategory());
|
|
110
|
+
|
|
111
|
+
act(() => {
|
|
112
|
+
const items = result.current;
|
|
113
|
+
expect(items.some((item) => item.key === 'extraSetting')).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
|
|
5
|
+
import Cell from '@/components/Cell';
|
|
6
|
+
|
|
7
|
+
import { useCategory } from './useCategory';
|
|
8
|
+
|
|
9
|
+
const Category = memo(() => {
|
|
10
|
+
const items = useCategory();
|
|
11
|
+
|
|
12
|
+
return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default Category;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useRouter } from 'next/navigation';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
|
6
|
+
|
|
7
|
+
import { enableAuth } from '@/const/auth';
|
|
8
|
+
import DataStatistics from '@/features/User/DataStatistics';
|
|
9
|
+
import UserInfo from '@/features/User/UserInfo';
|
|
10
|
+
import UserLoginOrSignup from '@/features/User/UserLoginOrSignup';
|
|
11
|
+
import { useUserStore } from '@/store/user';
|
|
12
|
+
import { authSelectors } from '@/store/user/selectors';
|
|
13
|
+
|
|
14
|
+
const UserBanner = memo(() => {
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Flexbox gap={12} paddingBlock={8}>
|
|
20
|
+
{!enableAuth ? (
|
|
21
|
+
<>
|
|
22
|
+
<UserInfo />
|
|
23
|
+
<DataStatistics paddingInline={12} />
|
|
24
|
+
</>
|
|
25
|
+
) : isLoginWithAuth ? (
|
|
26
|
+
<>
|
|
27
|
+
<UserInfo onClick={() => router.push('/me/profile')} />
|
|
28
|
+
<DataStatistics paddingInline={12} />
|
|
29
|
+
</>
|
|
30
|
+
) : (
|
|
31
|
+
<UserLoginOrSignup onClick={() => router.push('/login')} />
|
|
32
|
+
)}
|
|
33
|
+
</Flexbox>
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export default UserBanner;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { DiscordIcon } from '@lobehub/ui';
|
|
2
|
+
import { Book, CircleUserRound, Database, Feather, Settings2 } from 'lucide-react';
|
|
3
|
+
import { useRouter } from 'next/navigation';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import { CellProps } from '@/components/Cell';
|
|
7
|
+
import { enableAuth } from '@/const/auth';
|
|
8
|
+
import { DISCORD, DOCUMENTS, FEEDBACK } from '@/const/url';
|
|
9
|
+
import { useUserStore } from '@/store/user';
|
|
10
|
+
import { authSelectors } from '@/store/user/slices/auth/selectors';
|
|
11
|
+
|
|
12
|
+
import { useCategory as useSettingsCategory } from '../../settings/features/useCategory';
|
|
13
|
+
|
|
14
|
+
export const useCategory = () => {
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
const { t } = useTranslation(['common', 'setting', 'auth']);
|
|
17
|
+
const [isLogin, isLoginWithAuth, isLoginWithClerk] = useUserStore((s) => [
|
|
18
|
+
authSelectors.isLogin(s),
|
|
19
|
+
authSelectors.isLoginWithAuth(s),
|
|
20
|
+
authSelectors.isLoginWithClerk(s),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const profile: CellProps[] = [
|
|
24
|
+
{
|
|
25
|
+
icon: CircleUserRound,
|
|
26
|
+
key: 'profile',
|
|
27
|
+
label: t('userPanel.profile'),
|
|
28
|
+
onClick: () => router.push('/me/profile'),
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const settings: CellProps[] = [
|
|
33
|
+
{
|
|
34
|
+
icon: Settings2,
|
|
35
|
+
key: 'setting',
|
|
36
|
+
label: t('userPanel.setting'),
|
|
37
|
+
onClick: () => router.push('/me/settings'),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: 'divider',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const settingsWithoutAuth = [
|
|
45
|
+
...useSettingsCategory(),
|
|
46
|
+
{
|
|
47
|
+
type: 'divider',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const data: CellProps[] = [
|
|
52
|
+
{
|
|
53
|
+
icon: Database,
|
|
54
|
+
key: 'data',
|
|
55
|
+
label: t('userPanel.data'),
|
|
56
|
+
onClick: () => router.push('/me/data'),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'divider',
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const helps: CellProps[] = [
|
|
64
|
+
{
|
|
65
|
+
icon: Book,
|
|
66
|
+
key: 'docs',
|
|
67
|
+
label: t('document'),
|
|
68
|
+
onClick: () => window.open(DOCUMENTS, '__blank'),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
icon: Feather,
|
|
72
|
+
key: 'feedback',
|
|
73
|
+
label: t('feedback'),
|
|
74
|
+
onClick: () => window.open(FEEDBACK, '__blank'),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
icon: DiscordIcon,
|
|
78
|
+
key: 'discord',
|
|
79
|
+
label: 'Discord',
|
|
80
|
+
onClick: () => window.open(DISCORD, '__blank'),
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const mainItems = [
|
|
85
|
+
{
|
|
86
|
+
type: 'divider',
|
|
87
|
+
},
|
|
88
|
+
...(isLoginWithClerk ? profile : []),
|
|
89
|
+
...(enableAuth ? (isLoginWithAuth ? settings : []) : settingsWithoutAuth),
|
|
90
|
+
...(isLogin ? data : []),
|
|
91
|
+
...helps,
|
|
92
|
+
].filter(Boolean) as CellProps[];
|
|
93
|
+
|
|
94
|
+
return mainItems;
|
|
95
|
+
};
|
|
@@ -2,13 +2,10 @@ import { redirect } from 'next/navigation';
|
|
|
2
2
|
import { Center } from 'react-layout-kit';
|
|
3
3
|
|
|
4
4
|
import BrandWatermark from '@/components/BrandWatermark';
|
|
5
|
-
import Divider from '@/components/Cell/Divider';
|
|
6
|
-
import DataStatistics from '@/features/User/DataStatistics';
|
|
7
|
-
import UserInfo from '@/features/User/UserInfo';
|
|
8
5
|
import { isMobileDevice } from '@/utils/responsive';
|
|
9
6
|
|
|
10
|
-
import
|
|
11
|
-
import
|
|
7
|
+
import Category from './features/Category';
|
|
8
|
+
import UserBanner from './features/UserBanner';
|
|
12
9
|
|
|
13
10
|
const Page = () => {
|
|
14
11
|
const mobile = isMobileDevice();
|
|
@@ -17,11 +14,8 @@ const Page = () => {
|
|
|
17
14
|
|
|
18
15
|
return (
|
|
19
16
|
<>
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
<Divider />
|
|
23
|
-
<Cate />
|
|
24
|
-
<ExtraCate />
|
|
17
|
+
<UserBanner />
|
|
18
|
+
<Category />
|
|
25
19
|
<Center padding={16}>
|
|
26
20
|
<BrandWatermark />
|
|
27
21
|
</Center>
|
|
@@ -29,4 +23,6 @@ const Page = () => {
|
|
|
29
23
|
);
|
|
30
24
|
};
|
|
31
25
|
|
|
26
|
+
Page.displayName = 'Me';
|
|
27
|
+
|
|
32
28
|
export default Page;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import Cell, { CellProps } from '@/components/Cell';
|
|
7
|
+
import DataImporter from '@/features/DataImporter';
|
|
8
|
+
import { configService } from '@/services/config';
|
|
9
|
+
|
|
10
|
+
const Category = memo(() => {
|
|
11
|
+
const { t } = useTranslation('common');
|
|
12
|
+
const items: CellProps[] = [
|
|
13
|
+
{
|
|
14
|
+
key: 'allAgent',
|
|
15
|
+
label: t('exportType.allAgent'),
|
|
16
|
+
onClick: configService.exportAgents,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: 'allAgentWithMessage',
|
|
20
|
+
label: t('exportType.allAgentWithMessage'),
|
|
21
|
+
onClick: configService.exportSessions,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
key: 'globalSetting',
|
|
25
|
+
label: t('exportType.globalSetting'),
|
|
26
|
+
onClick: configService.exportSettings,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'divider',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: 'all',
|
|
33
|
+
label: t('exportType.all'),
|
|
34
|
+
onClick: configService.exportAll,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'divider',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'import',
|
|
41
|
+
label: <DataImporter>{t('import')}</DataImporter>,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export default Category;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
|
8
|
+
|
|
9
|
+
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
|
10
|
+
|
|
11
|
+
const Header = memo(() => {
|
|
12
|
+
const { t } = useTranslation('common');
|
|
13
|
+
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
return (
|
|
16
|
+
<MobileNavBar
|
|
17
|
+
center={
|
|
18
|
+
<MobileNavBarTitle
|
|
19
|
+
title={
|
|
20
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
|
21
|
+
{t('userPanel.data')}
|
|
22
|
+
</Flexbox>
|
|
23
|
+
}
|
|
24
|
+
/>
|
|
25
|
+
}
|
|
26
|
+
onBackClick={() => router.push('/me')}
|
|
27
|
+
showBackButton
|
|
28
|
+
style={mobileHeaderSticky}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default Header;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
|
|
3
|
+
import MobileContentLayout from '@/components/server/MobileNavLayout';
|
|
4
|
+
|
|
5
|
+
import Header from './features/Header';
|
|
6
|
+
|
|
7
|
+
const Layout = ({ children }: PropsWithChildren) => {
|
|
8
|
+
return <MobileContentLayout header={<Header />}>{children}</MobileContentLayout>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
Layout.displayName = 'MeDataLayout';
|
|
12
|
+
|
|
13
|
+
export default Layout;
|
|
@@ -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('/chat');
|
|
11
|
+
|
|
12
|
+
return <Category />;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
Page.displayName = 'MeData';
|
|
16
|
+
|
|
17
|
+
export default Page;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { LogOut, ShieldCheck, UserCircle } from 'lucide-react';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
import Cell, { CellProps } from '@/components/Cell';
|
|
9
|
+
import { useUserStore } from '@/store/user';
|
|
10
|
+
|
|
11
|
+
const Category = memo(() => {
|
|
12
|
+
const router = useRouter();
|
|
13
|
+
const { t } = useTranslation('auth');
|
|
14
|
+
const signOut = useUserStore((s) => s.logout);
|
|
15
|
+
const items: CellProps[] = [
|
|
16
|
+
{
|
|
17
|
+
icon: UserCircle,
|
|
18
|
+
key: 'profile',
|
|
19
|
+
label: t('profile'),
|
|
20
|
+
onClick: () => router.push('/profile'),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
icon: ShieldCheck,
|
|
24
|
+
key: 'security',
|
|
25
|
+
label: t('security'),
|
|
26
|
+
onClick: () => router.push('/profile/security'),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'divider',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
icon: LogOut,
|
|
33
|
+
key: 'logout',
|
|
34
|
+
label: t('signout', { ns: 'auth' }),
|
|
35
|
+
onClick: () => {
|
|
36
|
+
signOut();
|
|
37
|
+
router.push('/login');
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export default Category;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
|
8
|
+
|
|
9
|
+
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
|
10
|
+
|
|
11
|
+
const Header = memo(() => {
|
|
12
|
+
const { t } = useTranslation('common');
|
|
13
|
+
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
return (
|
|
16
|
+
<MobileNavBar
|
|
17
|
+
center={
|
|
18
|
+
<MobileNavBarTitle
|
|
19
|
+
title={
|
|
20
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
|
21
|
+
{t('userPanel.profile')}
|
|
22
|
+
</Flexbox>
|
|
23
|
+
}
|
|
24
|
+
/>
|
|
25
|
+
}
|
|
26
|
+
onBackClick={() => router.push('/me')}
|
|
27
|
+
showBackButton
|
|
28
|
+
style={mobileHeaderSticky}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default Header;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { notFound } from 'next/navigation';
|
|
2
|
+
import { PropsWithChildren } from 'react';
|
|
3
|
+
|
|
4
|
+
import MobileContentLayout from '@/components/server/MobileNavLayout';
|
|
5
|
+
import { enableClerk } from '@/const/auth';
|
|
6
|
+
|
|
7
|
+
import Header from './features/Header';
|
|
8
|
+
|
|
9
|
+
const Layout = ({ children }: PropsWithChildren) => {
|
|
10
|
+
if (!enableClerk) return notFound();
|
|
11
|
+
return <MobileContentLayout header={<Header />}>{children}</MobileContentLayout>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
Layout.displayName = 'MeProfileLayout';
|
|
15
|
+
|
|
16
|
+
export default Layout;
|
|
@@ -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('/profile');
|
|
11
|
+
|
|
12
|
+
return <Category />;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
Page.displayName = 'MeProfile';
|
|
16
|
+
|
|
17
|
+
export default Page;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
|
|
5
|
+
import Cell from '@/components/Cell';
|
|
6
|
+
|
|
7
|
+
import { useCategory } from './useCategory';
|
|
8
|
+
|
|
9
|
+
const Category = memo(() => {
|
|
10
|
+
const items = useCategory();
|
|
11
|
+
|
|
12
|
+
return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default Category;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
|
8
|
+
|
|
9
|
+
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
|
10
|
+
|
|
11
|
+
const Header = memo(() => {
|
|
12
|
+
const { t } = useTranslation('common');
|
|
13
|
+
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
return (
|
|
16
|
+
<MobileNavBar
|
|
17
|
+
center={
|
|
18
|
+
<MobileNavBarTitle
|
|
19
|
+
title={
|
|
20
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
|
21
|
+
{t('userPanel.setting')}
|
|
22
|
+
</Flexbox>
|
|
23
|
+
}
|
|
24
|
+
/>
|
|
25
|
+
}
|
|
26
|
+
onBackClick={() => router.push('/me')}
|
|
27
|
+
showBackButton
|
|
28
|
+
style={mobileHeaderSticky}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default Header;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Tag } from 'antd';
|
|
2
|
+
import { Bot, Brain, Cloudy, Info, Mic2, Settings2 } from 'lucide-react';
|
|
3
|
+
import { useRouter } from 'next/navigation';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
|
6
|
+
import urlJoin from 'url-join';
|
|
7
|
+
|
|
8
|
+
import { CellProps } from '@/components/Cell';
|
|
9
|
+
import { SettingsTabs } from '@/store/global/initialState';
|
|
10
|
+
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
11
|
+
|
|
12
|
+
export const useCategory = () => {
|
|
13
|
+
const router = useRouter();
|
|
14
|
+
const { t } = useTranslation('setting');
|
|
15
|
+
const { enableWebrtc, showLLM } = useServerConfigStore(featureFlagsSelectors);
|
|
16
|
+
|
|
17
|
+
const items: CellProps[] = [
|
|
18
|
+
{
|
|
19
|
+
icon: Settings2,
|
|
20
|
+
key: SettingsTabs.Common,
|
|
21
|
+
label: t('tab.common'),
|
|
22
|
+
},
|
|
23
|
+
enableWebrtc && {
|
|
24
|
+
icon: Cloudy,
|
|
25
|
+
key: SettingsTabs.Sync,
|
|
26
|
+
label: (
|
|
27
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
|
28
|
+
{t('tab.sync')}
|
|
29
|
+
<Tag bordered={false} color={'warning'}>
|
|
30
|
+
{t('tab.experiment')}
|
|
31
|
+
</Tag>
|
|
32
|
+
</Flexbox>
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
showLLM && {
|
|
36
|
+
icon: Brain,
|
|
37
|
+
key: SettingsTabs.LLM,
|
|
38
|
+
label: t('tab.llm'),
|
|
39
|
+
},
|
|
40
|
+
{ icon: Mic2, key: SettingsTabs.TTS, label: t('tab.tts') },
|
|
41
|
+
{
|
|
42
|
+
icon: Bot,
|
|
43
|
+
key: SettingsTabs.Agent,
|
|
44
|
+
label: t('tab.agent'),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
icon: Info,
|
|
48
|
+
key: SettingsTabs.About,
|
|
49
|
+
label: t('tab.about'),
|
|
50
|
+
},
|
|
51
|
+
].filter(Boolean) as CellProps[];
|
|
52
|
+
|
|
53
|
+
return items.map((item) => ({
|
|
54
|
+
...item,
|
|
55
|
+
onClick: () => router.push(urlJoin('/settings', item.key as SettingsTabs)),
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
|
|
3
|
+
import MobileContentLayout from '@/components/server/MobileNavLayout';
|
|
4
|
+
|
|
5
|
+
import Header from './features/Header';
|
|
6
|
+
|
|
7
|
+
const Layout = ({ children }: PropsWithChildren) => {
|
|
8
|
+
return <MobileContentLayout header={<Header />}>{children}</MobileContentLayout>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
Layout.displayName = 'MeSettingsLayout';
|
|
12
|
+
|
|
13
|
+
export default Layout;
|