@lobehub/chat 1.42.5 → 1.43.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/CHANGELOG.md +58 -0
- package/changelog/v1.json +21 -0
- package/docs/.cdn.cache.json +1 -0
- package/docs/changelog/2025-01-03-user-profile.mdx +27 -0
- package/docs/changelog/2025-01-03-user-profile.zh-CN.mdx +26 -0
- package/docs/self-hosting/advanced/auth/next-auth/wechat.mdx +3 -1
- package/docs/self-hosting/advanced/auth/next-auth/wechat.zh-CN.mdx +2 -2
- package/locales/ar/auth.json +76 -4
- package/locales/bg-BG/auth.json +75 -3
- package/locales/de-DE/auth.json +78 -6
- package/locales/en-US/auth.json +78 -6
- package/locales/es-ES/auth.json +75 -3
- package/locales/fa-IR/auth.json +77 -5
- package/locales/fr-FR/auth.json +78 -6
- package/locales/it-IT/auth.json +76 -4
- package/locales/ja-JP/auth.json +76 -4
- package/locales/ko-KR/auth.json +75 -3
- package/locales/nl-NL/auth.json +76 -4
- package/locales/pl-PL/auth.json +76 -4
- package/locales/pt-BR/auth.json +76 -4
- package/locales/ru-RU/auth.json +75 -3
- package/locales/tr-TR/auth.json +74 -3
- package/locales/vi-VN/auth.json +75 -3
- package/locales/zh-CN/auth.json +75 -3
- package/locales/zh-TW/auth.json +75 -3
- package/package.json +13 -3
- package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +4 -0
- package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +0 -46
- package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +11 -14
- package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +6 -21
- package/src/app/(main)/(mobile)/me/profile/features/Category.tsx +38 -21
- package/src/app/(main)/(mobile)/me/profile/layout.tsx +0 -3
- package/src/app/(main)/(mobile)/me/profile/page.tsx +3 -3
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx +1 -1
- package/src/app/(main)/chat/loading.tsx +2 -2
- package/src/app/(main)/discover/loading.tsx +2 -8
- package/src/app/(main)/files/loading.tsx +2 -2
- package/src/app/(main)/profile/(home)/Client.tsx +53 -0
- package/src/app/(main)/profile/(home)/[[...slugs]]/page.tsx +38 -0
- package/src/app/(main)/profile/@category/default.tsx +9 -0
- package/src/app/(main)/profile/@category/features/CategoryContent.tsx +38 -0
- package/src/app/(main)/profile/_layout/Desktop/Header.tsx +85 -0
- package/src/app/(main)/profile/_layout/Desktop/SideBar.tsx +42 -0
- package/src/app/(main)/profile/_layout/Desktop/index.tsx +48 -0
- package/src/app/(main)/profile/_layout/Mobile/Header.tsx +23 -5
- package/src/app/(main)/profile/_layout/Mobile/index.tsx +12 -5
- package/src/app/(main)/profile/_layout/type.ts +6 -0
- package/src/app/(main)/profile/error.tsx +5 -0
- package/src/app/(main)/profile/features/ClerkProfile.tsx +72 -0
- package/src/app/(main)/profile/hooks/useCategory.tsx +51 -0
- package/src/app/(main)/profile/layout.tsx +7 -17
- package/src/app/(main)/profile/loading.tsx +2 -22
- package/src/app/(main)/profile/not-found.tsx +3 -0
- package/src/app/(main)/profile/security/page.tsx +34 -0
- package/src/app/(main)/profile/stats/Client.tsx +52 -0
- package/src/app/(main)/profile/stats/features/AiHeatmaps.tsx +130 -0
- package/src/app/(main)/profile/stats/features/AssistantsRank.tsx +115 -0
- package/src/app/(main)/profile/stats/features/ModelsRank.tsx +84 -0
- package/src/app/(main)/profile/stats/features/ShareButton/Preview.tsx +159 -0
- package/src/app/(main)/profile/stats/features/ShareButton/ShareModal.tsx +87 -0
- package/src/app/(main)/profile/stats/features/ShareButton/TotalCard.tsx +39 -0
- package/src/app/(main)/profile/stats/features/ShareButton/index.tsx +26 -0
- package/src/app/(main)/profile/stats/features/TimeLabel.tsx +30 -0
- package/src/app/(main)/profile/stats/features/TopicsRank.tsx +103 -0
- package/src/app/(main)/profile/stats/features/TotalAssistants.tsx +56 -0
- package/src/app/(main)/profile/stats/features/TotalMessages.tsx +56 -0
- package/src/app/(main)/profile/stats/features/TotalTopics.tsx +53 -0
- package/src/app/(main)/profile/stats/features/TotalWords.tsx +54 -0
- package/src/app/(main)/profile/stats/features/Welcome.tsx +86 -0
- package/src/app/(main)/profile/{[[...slugs]] → stats}/page.tsx +4 -5
- package/src/app/(main)/repos/[id]/evals/dataset/page.tsx +2 -2
- package/src/app/(main)/repos/[id]/evals/evaluation/page.tsx +2 -2
- package/src/app/(main)/settings/@category/features/CategoryContent.tsx +1 -1
- package/src/app/(main)/settings/_layout/Desktop/index.tsx +1 -1
- package/src/app/(main)/settings/_layout/Mobile/Header.tsx +1 -1
- package/src/app/(main)/settings/_layout/Mobile/index.tsx +2 -0
- package/src/app/(main)/settings/common/features/Theme/index.tsx +2 -17
- package/src/app/(main)/settings/loading.tsx +2 -2
- package/src/components/Loading/BrandTextLoading/index.tsx +2 -2
- package/src/components/Statistic/index.tsx +15 -0
- package/src/components/StatisticCard/TitleWithPercentage.tsx +80 -0
- package/src/components/StatisticCard/growthPercentage.tsx +8 -0
- package/src/components/StatisticCard/index.tsx +209 -0
- package/src/const/url.ts +3 -3
- package/src/database/server/models/__tests__/message.test.ts +346 -35
- package/src/database/server/models/__tests__/session.test.ts +185 -2
- package/src/database/server/models/__tests__/topic.test.ts +136 -0
- package/src/database/server/models/__tests__/user.test.ts +140 -1
- package/src/database/server/models/message.ts +109 -14
- package/src/database/server/models/session.ts +75 -4
- package/src/database/server/models/topic.ts +43 -3
- package/src/database/server/models/user.ts +22 -0
- package/src/database/utils/genWhere.ts +39 -0
- package/src/features/ShareModal/ShareImage/index.tsx +11 -24
- package/src/features/ShareModal/ShareImage/type.ts +1 -6
- package/src/features/User/DataStatistics.tsx +21 -14
- package/src/features/User/UserPanel/PanelContent.tsx +12 -16
- package/src/features/User/UserPanel/useMenu.tsx +4 -6
- package/src/features/User/__tests__/PanelContent.test.tsx +4 -0
- package/src/features/User/__tests__/useMenu.test.tsx +1 -21
- package/src/hooks/useActiveTabKey.ts +34 -1
- package/src/{features/ShareModal/ShareImage → hooks}/useScreenshot.ts +51 -6
- package/src/locales/default/auth.ts +74 -2
- package/src/server/ld.test.ts +1 -1
- package/src/server/modules/AssistantStore/index.ts +3 -2
- package/src/server/routers/lambda/message.ts +35 -6
- package/src/server/routers/lambda/session.ts +17 -3
- package/src/server/routers/lambda/topic.ts +17 -3
- package/src/server/routers/lambda/user.ts +4 -0
- package/src/server/services/changelog/index.ts +1 -1
- package/src/services/message/_deprecated.ts +16 -0
- package/src/services/message/client.test.ts +0 -18
- package/src/services/message/client.ts +12 -9
- package/src/services/message/server.ts +12 -4
- package/src/services/message/type.ts +15 -3
- package/src/services/session/_deprecated.ts +5 -0
- package/src/services/session/client.ts +6 -2
- package/src/services/session/server.ts +6 -2
- package/src/services/session/type.ts +7 -1
- package/src/services/topic/_deprecated.ts +5 -0
- package/src/services/topic/client.ts +6 -2
- package/src/services/topic/server.ts +7 -1
- package/src/services/topic/type.ts +7 -2
- package/src/services/user/_deprecated.ts +4 -0
- package/src/services/user/client.ts +4 -0
- package/src/services/user/server.ts +4 -0
- package/src/services/user/type.ts +5 -0
- package/src/store/global/initialState.ts +6 -0
- package/src/store/user/slices/auth/action.test.ts +1 -33
- package/src/store/user/slices/auth/action.ts +0 -9
- package/src/store/user/slices/common/action.test.ts +2 -2
- package/src/types/message/index.ts +5 -0
- package/src/types/session/index.ts +8 -0
- package/src/types/topic/topic.ts +7 -0
- package/src/utils/format.ts +1 -1
- package/src/utils/time.ts +23 -0
- package/src/app/(main)/profile/[[...slugs]]/Client.tsx +0 -76
- package/src/components/Loading/BrandTextLoading/LobeChatText/SVG.tsx +0 -44
- package/src/components/Loading/BrandTextLoading/LobeChatText/index.tsx +0 -6
- package/src/components/Loading/BrandTextLoading/LobeChatText/style.css +0 -32
- package/src/hooks/useActiveSettingsKey.ts +0 -20
@@ -1,43 +1,60 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
|
-
import { LogOut, ShieldCheck, UserCircle } from 'lucide-react';
|
3
|
+
import { ChartColumnBigIcon, LogOut, ShieldCheck, UserCircle } from 'lucide-react';
|
4
4
|
import { useRouter } from 'next/navigation';
|
5
5
|
import { memo } from 'react';
|
6
6
|
import { useTranslation } from 'react-i18next';
|
7
7
|
|
8
8
|
import Cell, { CellProps } from '@/components/Cell';
|
9
|
+
import { isDeprecatedEdition } from '@/const/version';
|
10
|
+
import { ProfileTabs } from '@/store/global/initialState';
|
9
11
|
import { useUserStore } from '@/store/user';
|
12
|
+
import { authSelectors } from '@/store/user/slices/auth/selectors';
|
10
13
|
|
11
14
|
const Category = memo(() => {
|
15
|
+
const [isLogin, enableAuth, isLoginWithClerk, signOut] = useUserStore((s) => [
|
16
|
+
authSelectors.isLogin(s),
|
17
|
+
authSelectors.enabledAuth(s),
|
18
|
+
authSelectors.isLoginWithClerk(s),
|
19
|
+
s.logout,
|
20
|
+
]);
|
12
21
|
const router = useRouter();
|
13
22
|
const { t } = useTranslation('auth');
|
14
|
-
const signOut = useUserStore((s) => s.logout);
|
15
23
|
const items: CellProps[] = [
|
16
24
|
{
|
17
25
|
icon: UserCircle,
|
18
|
-
key:
|
19
|
-
label: t('profile'),
|
26
|
+
key: ProfileTabs.Profile,
|
27
|
+
label: t('tab.profile'),
|
20
28
|
onClick: () => router.push('/profile'),
|
21
29
|
},
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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');
|
30
|
+
enableAuth &&
|
31
|
+
isLoginWithClerk && {
|
32
|
+
icon: ShieldCheck,
|
33
|
+
key: ProfileTabs.Security,
|
34
|
+
label: t('tab.security'),
|
35
|
+
onClick: () => router.push('/profile/security'),
|
38
36
|
},
|
37
|
+
!isDeprecatedEdition && {
|
38
|
+
icon: ChartColumnBigIcon,
|
39
|
+
key: ProfileTabs.Stats,
|
40
|
+
label: t('tab.stats'),
|
41
|
+
onClick: () => router.push('/profile/stats'),
|
39
42
|
},
|
40
|
-
|
43
|
+
enableAuth &&
|
44
|
+
isLogin && {
|
45
|
+
type: 'divider',
|
46
|
+
},
|
47
|
+
enableAuth &&
|
48
|
+
isLogin && {
|
49
|
+
icon: LogOut,
|
50
|
+
key: 'logout',
|
51
|
+
label: t('signout', { ns: 'auth' }),
|
52
|
+
onClick: () => {
|
53
|
+
signOut();
|
54
|
+
router.push('/login');
|
55
|
+
},
|
56
|
+
},
|
57
|
+
].filter(Boolean) as CellProps[];
|
41
58
|
|
42
59
|
return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
|
43
60
|
});
|
@@ -1,13 +1,10 @@
|
|
1
|
-
import { notFound } from 'next/navigation';
|
2
1
|
import { PropsWithChildren } from 'react';
|
3
2
|
|
4
3
|
import MobileContentLayout from '@/components/server/MobileNavLayout';
|
5
|
-
import { enableClerk } from '@/const/auth';
|
6
4
|
|
7
5
|
import Header from './features/Header';
|
8
6
|
|
9
7
|
const Layout = ({ children }: PropsWithChildren) => {
|
10
|
-
if (!enableClerk) return notFound();
|
11
8
|
return <MobileContentLayout header={<Header />}>{children}</MobileContentLayout>;
|
12
9
|
};
|
13
10
|
|
@@ -7,10 +7,10 @@ import { isMobileDevice } from '@/utils/server/responsive';
|
|
7
7
|
import Category from './features/Category';
|
8
8
|
|
9
9
|
export const generateMetadata = async () => {
|
10
|
-
const { t } = await translation('
|
10
|
+
const { t } = await translation('auth');
|
11
11
|
return metadataModule.generate({
|
12
|
-
description: t('
|
13
|
-
title: t('
|
12
|
+
description: t('header.desc'),
|
13
|
+
title: t('header.title'),
|
14
14
|
url: '/me/profile',
|
15
15
|
});
|
16
16
|
};
|
package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx
CHANGED
@@ -54,7 +54,7 @@ const ByTimeMode = memo(() => {
|
|
54
54
|
|
55
55
|
const groupContent = useCallback(
|
56
56
|
(index: number) => {
|
57
|
-
if (index === 0) return
|
57
|
+
if (index === 0) return <div style={{ height: 1 }} />;
|
58
58
|
|
59
59
|
const topicGroup = groups[index];
|
60
60
|
return <TopicGroupItem {...topicGroup} />;
|
@@ -1,3 +1,3 @@
|
|
1
|
-
import
|
1
|
+
import Loading from '@/components/Loading/BrandTextLoading';
|
2
2
|
|
3
|
-
export default () => <
|
3
|
+
export default () => <Loading />;
|
@@ -1,9 +1,3 @@
|
|
1
|
-
import
|
1
|
+
import Loading from '@/components/Loading/BrandTextLoading';
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
export default () => (
|
6
|
-
<Center height={'90vh'} width={'100%'}>
|
7
|
-
<CircleLoading />
|
8
|
-
</Center>
|
9
|
-
);
|
3
|
+
export default () => <Loading />;
|
@@ -1,3 +1,3 @@
|
|
1
|
-
import
|
1
|
+
import Loading from '@/components/Loading/BrandTextLoading';
|
2
2
|
|
3
|
-
export default () => <
|
3
|
+
export default () => <Loading />;
|
@@ -0,0 +1,53 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Form, type ItemGroup } from '@lobehub/ui';
|
4
|
+
import { memo } from 'react';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
|
7
|
+
import { FORM_STYLE } from '@/const/layoutTokens';
|
8
|
+
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
9
|
+
import UserAvatar from '@/features/User/UserAvatar';
|
10
|
+
import { useUserStore } from '@/store/user';
|
11
|
+
import { authSelectors, userProfileSelectors } from '@/store/user/selectors';
|
12
|
+
|
13
|
+
type SettingItemGroup = ItemGroup;
|
14
|
+
|
15
|
+
const Client = memo<{ mobile?: boolean }>(() => {
|
16
|
+
const [isLoginWithNextAuth] = useUserStore((s) => [authSelectors.isLoginWithNextAuth(s)]);
|
17
|
+
const [enableAuth, nickname, username, userProfile] = useUserStore((s) => [
|
18
|
+
s.enableAuth(),
|
19
|
+
userProfileSelectors.nickName(s),
|
20
|
+
userProfileSelectors.username(s),
|
21
|
+
userProfileSelectors.userProfile(s),
|
22
|
+
]);
|
23
|
+
|
24
|
+
const [form] = Form.useForm();
|
25
|
+
const { t } = useTranslation('auth');
|
26
|
+
|
27
|
+
const profile: SettingItemGroup = {
|
28
|
+
children: [
|
29
|
+
{
|
30
|
+
children: enableAuth && isLoginWithNextAuth ? <UserAvatar /> : <AvatarWithUpload />,
|
31
|
+
label: t('profile.avatar'),
|
32
|
+
minWidth: undefined,
|
33
|
+
},
|
34
|
+
{
|
35
|
+
children: nickname || username,
|
36
|
+
label: t('profile.username'),
|
37
|
+
minWidth: undefined,
|
38
|
+
},
|
39
|
+
{
|
40
|
+
children: userProfile?.email || '--',
|
41
|
+
hidden: !isLoginWithNextAuth || !userProfile?.email,
|
42
|
+
label: t('profile.email'),
|
43
|
+
minWidth: undefined,
|
44
|
+
},
|
45
|
+
],
|
46
|
+
title: t('tab.profile'),
|
47
|
+
};
|
48
|
+
return (
|
49
|
+
<Form form={form} items={[profile]} itemsType={'group'} variant={'pure'} {...FORM_STYLE} />
|
50
|
+
);
|
51
|
+
});
|
52
|
+
|
53
|
+
export default Client;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { Skeleton } from 'antd';
|
2
|
+
import dynamic from 'next/dynamic';
|
3
|
+
|
4
|
+
import { enableClerk } from '@/const/auth';
|
5
|
+
import { metadataModule } from '@/server/metadata';
|
6
|
+
import { translation } from '@/server/translation';
|
7
|
+
import { isMobileDevice } from '@/utils/server/responsive';
|
8
|
+
|
9
|
+
import Client from '../Client';
|
10
|
+
|
11
|
+
// 为了兼容 ClerkProfile, 需要使用 [[...slug]]
|
12
|
+
|
13
|
+
const ClerkProfile = dynamic(() => import('../../features/ClerkProfile'), {
|
14
|
+
loading: () => (
|
15
|
+
<div style={{ flex: 1 }}>
|
16
|
+
<Skeleton paragraph={{ rows: 8 }} title={false} />
|
17
|
+
</div>
|
18
|
+
),
|
19
|
+
});
|
20
|
+
|
21
|
+
export const generateMetadata = async () => {
|
22
|
+
const { t } = await translation('auth');
|
23
|
+
return metadataModule.generate({
|
24
|
+
description: t('header.desc'),
|
25
|
+
title: t('tab.profile'),
|
26
|
+
url: '/profile',
|
27
|
+
});
|
28
|
+
};
|
29
|
+
|
30
|
+
const Page = async () => {
|
31
|
+
const mobile = await isMobileDevice();
|
32
|
+
|
33
|
+
if (enableClerk) return <ClerkProfile mobile={mobile} />;
|
34
|
+
|
35
|
+
return <Client mobile={mobile} />;
|
36
|
+
};
|
37
|
+
|
38
|
+
export default Page;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { memo } from 'react';
|
4
|
+
import urlJoin from 'url-join';
|
5
|
+
|
6
|
+
import Menu from '@/components/Menu';
|
7
|
+
import { useActiveSettingsKey } from '@/hooks/useActiveTabKey';
|
8
|
+
import { useQuery } from '@/hooks/useQuery';
|
9
|
+
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
10
|
+
import { ProfileTabs, SettingsTabs } from '@/store/global/initialState';
|
11
|
+
|
12
|
+
import { useCategory } from '../../hooks/useCategory';
|
13
|
+
|
14
|
+
const CategoryContent = memo<{ modal?: boolean }>(({ modal }) => {
|
15
|
+
const activeTab = useActiveSettingsKey();
|
16
|
+
const { tab = SettingsTabs.Common } = useQuery();
|
17
|
+
const cateItems = useCategory();
|
18
|
+
const router = useQueryRoute();
|
19
|
+
|
20
|
+
return (
|
21
|
+
<Menu
|
22
|
+
items={cateItems}
|
23
|
+
onClick={({ key }) => {
|
24
|
+
const activeKey = key === ProfileTabs.Profile ? '/' : key;
|
25
|
+
if (modal) {
|
26
|
+
router.replace('/profile/modal', { query: { tab: activeKey } });
|
27
|
+
} else {
|
28
|
+
router.push(urlJoin('/profile', activeKey));
|
29
|
+
}
|
30
|
+
}}
|
31
|
+
selectable
|
32
|
+
selectedKeys={[modal ? tab : (activeTab as any)]}
|
33
|
+
variant={'compact'}
|
34
|
+
/>
|
35
|
+
);
|
36
|
+
});
|
37
|
+
|
38
|
+
export default CategoryContent;
|
@@ -0,0 +1,85 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { ActionIcon, ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
|
4
|
+
import { Drawer, type DrawerProps } from 'antd';
|
5
|
+
import { createStyles } from 'antd-style';
|
6
|
+
import { Menu } from 'lucide-react';
|
7
|
+
import { ReactNode, memo, useState } from 'react';
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
9
|
+
|
10
|
+
import BrandWatermark from '@/components/BrandWatermark';
|
11
|
+
|
12
|
+
const useStyles = createStyles(({ token, css }) => ({
|
13
|
+
container: css`
|
14
|
+
position: relative;
|
15
|
+
flex: none;
|
16
|
+
height: 54px;
|
17
|
+
background: ${token.colorBgContainer};
|
18
|
+
`,
|
19
|
+
title: css`
|
20
|
+
font-size: 18px;
|
21
|
+
font-weight: 700;
|
22
|
+
line-height: 1.2;
|
23
|
+
`,
|
24
|
+
}));
|
25
|
+
|
26
|
+
interface HeaderProps extends Pick<DrawerProps, 'getContainer'> {
|
27
|
+
children: ReactNode;
|
28
|
+
title: ReactNode;
|
29
|
+
}
|
30
|
+
|
31
|
+
const Header = memo<HeaderProps>(({ children, getContainer, title }) => {
|
32
|
+
const [open, setOpen] = useState(false);
|
33
|
+
const { styles, theme } = useStyles();
|
34
|
+
|
35
|
+
return (
|
36
|
+
<>
|
37
|
+
<ChatHeader
|
38
|
+
className={styles.container}
|
39
|
+
left={
|
40
|
+
<ChatHeaderTitle
|
41
|
+
title={
|
42
|
+
<Flexbox align={'center'} className={styles.title} gap={4} horizontal>
|
43
|
+
<ActionIcon
|
44
|
+
color={theme.colorText}
|
45
|
+
icon={Menu}
|
46
|
+
onClick={() => setOpen(true)}
|
47
|
+
size={{ blockSize: 32, fontSize: 18 }}
|
48
|
+
/>
|
49
|
+
{title}
|
50
|
+
</Flexbox>
|
51
|
+
}
|
52
|
+
/>
|
53
|
+
}
|
54
|
+
/>
|
55
|
+
<Drawer
|
56
|
+
bodyStyle={{
|
57
|
+
display: 'flex',
|
58
|
+
flexDirection: 'column',
|
59
|
+
gap: 20,
|
60
|
+
justifyContent: 'space-between',
|
61
|
+
padding: 16,
|
62
|
+
}}
|
63
|
+
getContainer={getContainer}
|
64
|
+
headerStyle={{ display: 'none' }}
|
65
|
+
maskStyle={{ background: 'transparent' }}
|
66
|
+
onClick={() => setOpen(false)}
|
67
|
+
onClose={() => setOpen(false)}
|
68
|
+
open={open}
|
69
|
+
placement={'left'}
|
70
|
+
rootStyle={{ position: 'absolute' }}
|
71
|
+
style={{
|
72
|
+
background: theme.colorBgContainer,
|
73
|
+
borderRight: `1px solid ${theme.colorSplit}`,
|
74
|
+
}}
|
75
|
+
width={260}
|
76
|
+
zIndex={10}
|
77
|
+
>
|
78
|
+
{children}
|
79
|
+
<BrandWatermark paddingInline={12} />
|
80
|
+
</Drawer>
|
81
|
+
</>
|
82
|
+
);
|
83
|
+
});
|
84
|
+
|
85
|
+
export default Header;
|
@@ -0,0 +1,42 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { createStyles } from 'antd-style';
|
4
|
+
import { useTranslation } from 'react-i18next';
|
5
|
+
import { Flexbox, FlexboxProps } from 'react-layout-kit';
|
6
|
+
|
7
|
+
import BrandWatermark from '@/components/BrandWatermark';
|
8
|
+
import PanelTitle from '@/components/PanelTitle';
|
9
|
+
|
10
|
+
const useStyles = createStyles(({ token, css }) => ({
|
11
|
+
container: css`
|
12
|
+
padding-block: 0 16px;
|
13
|
+
padding-inline: 12px;
|
14
|
+
background: ${token.colorBgContainer};
|
15
|
+
border-inline-end: 1px solid ${token.colorBorder};
|
16
|
+
`,
|
17
|
+
}));
|
18
|
+
|
19
|
+
interface SidebarLayoutProps extends FlexboxProps {
|
20
|
+
desc?: string;
|
21
|
+
title?: string;
|
22
|
+
}
|
23
|
+
|
24
|
+
const SidebarLayout = ({ children, className, title, desc, ...rest }: SidebarLayoutProps) => {
|
25
|
+
const { cx, styles } = useStyles();
|
26
|
+
const { t } = useTranslation('auth');
|
27
|
+
return (
|
28
|
+
<Flexbox
|
29
|
+
className={cx(styles.container, className)}
|
30
|
+
flex={'none'}
|
31
|
+
gap={20}
|
32
|
+
width={280}
|
33
|
+
{...rest}
|
34
|
+
>
|
35
|
+
<PanelTitle desc={desc || t('header.desc')} title={title || t('header.title')} />
|
36
|
+
{children}
|
37
|
+
<BrandWatermark paddingInline={12} />
|
38
|
+
</Flexbox>
|
39
|
+
);
|
40
|
+
};
|
41
|
+
|
42
|
+
export default SidebarLayout;
|
@@ -0,0 +1,48 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { useResponsive } from 'antd-style';
|
4
|
+
import { memo, useRef } from 'react';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
7
|
+
|
8
|
+
import InitClientDB from '@/features/InitClientDB';
|
9
|
+
import Footer from '@/features/Setting/Footer';
|
10
|
+
import SettingContainer from '@/features/Setting/SettingContainer';
|
11
|
+
import { useActiveProfileKey } from '@/hooks/useActiveTabKey';
|
12
|
+
|
13
|
+
import { LayoutProps } from '../type';
|
14
|
+
import Header from './Header';
|
15
|
+
import SideBar from './SideBar';
|
16
|
+
|
17
|
+
const Layout = memo<LayoutProps>(({ children, category }) => {
|
18
|
+
const ref = useRef<any>(null);
|
19
|
+
const { md = true } = useResponsive();
|
20
|
+
const { t } = useTranslation('auth');
|
21
|
+
const activeKey = useActiveProfileKey();
|
22
|
+
|
23
|
+
return (
|
24
|
+
<>
|
25
|
+
<Flexbox
|
26
|
+
height={'100%'}
|
27
|
+
horizontal={md}
|
28
|
+
ref={ref}
|
29
|
+
style={{ position: 'relative' }}
|
30
|
+
width={'100%'}
|
31
|
+
>
|
32
|
+
{md ? (
|
33
|
+
<SideBar>{category}</SideBar>
|
34
|
+
) : (
|
35
|
+
<Header getContainer={() => ref.current} title={<>{t(`tab.${activeKey}`)}</>}>
|
36
|
+
{category}
|
37
|
+
</Header>
|
38
|
+
)}
|
39
|
+
<SettingContainer addonAfter={<Footer />}>{children}</SettingContainer>
|
40
|
+
</Flexbox>
|
41
|
+
<InitClientDB />
|
42
|
+
</>
|
43
|
+
);
|
44
|
+
});
|
45
|
+
|
46
|
+
Layout.displayName = 'DesktopProfileLayout';
|
47
|
+
|
48
|
+
export default Layout;
|
@@ -1,22 +1,40 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
3
|
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
4
|
-
import {
|
4
|
+
import { useRouter } from 'next/navigation';
|
5
5
|
import { memo } from 'react';
|
6
6
|
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
7
8
|
|
9
|
+
import { useActiveProfileKey } from '@/hooks/useActiveTabKey';
|
8
10
|
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
9
11
|
|
12
|
+
import ShareButton from '../../stats/features/ShareButton';
|
13
|
+
|
10
14
|
const Header = memo(() => {
|
11
15
|
const { t } = useTranslation('auth');
|
12
16
|
|
13
17
|
const router = useRouter();
|
14
|
-
const
|
15
|
-
const
|
18
|
+
const activeSettingsKey = useActiveProfileKey();
|
19
|
+
const isStats = activeSettingsKey === 'stats';
|
20
|
+
|
21
|
+
const handleBackClick = () => {
|
22
|
+
router.push('/me/profile');
|
23
|
+
};
|
24
|
+
|
16
25
|
return (
|
17
26
|
<MobileNavBar
|
18
|
-
center={
|
19
|
-
|
27
|
+
center={
|
28
|
+
<MobileNavBarTitle
|
29
|
+
title={
|
30
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
31
|
+
<span style={{ lineHeight: 1.2 }}> {t(`tab.${activeSettingsKey}`)}</span>
|
32
|
+
</Flexbox>
|
33
|
+
}
|
34
|
+
/>
|
35
|
+
}
|
36
|
+
onBackClick={handleBackClick}
|
37
|
+
right={isStats ? <ShareButton mobile /> : undefined}
|
20
38
|
showBackButton
|
21
39
|
style={mobileHeaderSticky}
|
22
40
|
/>
|
@@ -1,16 +1,23 @@
|
|
1
|
-
import
|
1
|
+
import MobileContentLayout from '@/components/server/MobileNavLayout';
|
2
|
+
import InitClientDB from '@/features/InitClientDB';
|
3
|
+
import Footer from '@/features/Setting/Footer';
|
2
4
|
|
5
|
+
import { LayoutProps } from '../type';
|
3
6
|
import Header from './Header';
|
4
7
|
|
5
|
-
const Layout = ({ children }:
|
8
|
+
const Layout = ({ children }: LayoutProps) => {
|
6
9
|
return (
|
7
10
|
<>
|
8
|
-
<Header />
|
9
|
-
|
11
|
+
<MobileContentLayout header={<Header />}>
|
12
|
+
{children}
|
13
|
+
<div style={{ flex: 1 }} />
|
14
|
+
<Footer />
|
15
|
+
</MobileContentLayout>
|
16
|
+
<InitClientDB />
|
10
17
|
</>
|
11
18
|
);
|
12
19
|
};
|
13
20
|
|
14
|
-
Layout.displayName = '
|
21
|
+
Layout.displayName = 'MobileProfileLayout';
|
15
22
|
|
16
23
|
export default Layout;
|
@@ -0,0 +1,72 @@
|
|
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, responsive, token }) =>
|
10
|
+
({
|
11
|
+
cardBox: css`
|
12
|
+
width: 100%;
|
13
|
+
min-width: 100%;
|
14
|
+
background: transparent;
|
15
|
+
`,
|
16
|
+
footer: css`
|
17
|
+
display: none !important;
|
18
|
+
`,
|
19
|
+
headerTitle: css`
|
20
|
+
${responsive.mobile} {
|
21
|
+
margin: 0;
|
22
|
+
padding: 16px;
|
23
|
+
|
24
|
+
font-size: 14px;
|
25
|
+
font-weight: 400;
|
26
|
+
line-height: 24px;
|
27
|
+
|
28
|
+
opacity: 0.5;
|
29
|
+
}
|
30
|
+
`,
|
31
|
+
navbar: css`
|
32
|
+
display: none !important;
|
33
|
+
`,
|
34
|
+
navbarMobileMenuRow: css`
|
35
|
+
display: none !important;
|
36
|
+
`,
|
37
|
+
pageScrollBox: css`
|
38
|
+
padding: 0;
|
39
|
+
`,
|
40
|
+
profileSection: css`
|
41
|
+
${responsive.mobile} {
|
42
|
+
padding-inline: 16px;
|
43
|
+
background: ${token.colorBgContainer};
|
44
|
+
}
|
45
|
+
`,
|
46
|
+
rootBox: css`
|
47
|
+
width: 100%;
|
48
|
+
height: 100%;
|
49
|
+
`,
|
50
|
+
scrollBox: css`
|
51
|
+
background: transparent;
|
52
|
+
`,
|
53
|
+
}) as Partial<{
|
54
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
55
|
+
[k in keyof ElementsConfig]: any;
|
56
|
+
}>,
|
57
|
+
);
|
58
|
+
|
59
|
+
const Client = memo<{ mobile?: boolean }>(({ mobile }) => {
|
60
|
+
const { styles } = useStyles(mobile);
|
61
|
+
|
62
|
+
return (
|
63
|
+
<UserProfile
|
64
|
+
appearance={{
|
65
|
+
elements: styles,
|
66
|
+
}}
|
67
|
+
path={'/profile'}
|
68
|
+
/>
|
69
|
+
);
|
70
|
+
});
|
71
|
+
|
72
|
+
export default Client;
|