@lobehub/chat 1.42.6 → 1.43.1
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 +83 -4
- package/locales/bg-BG/auth.json +82 -3
- package/locales/de-DE/auth.json +85 -6
- package/locales/en-US/auth.json +85 -6
- package/locales/es-ES/auth.json +82 -3
- package/locales/fa-IR/auth.json +84 -5
- package/locales/fr-FR/auth.json +85 -6
- package/locales/it-IT/auth.json +83 -4
- package/locales/ja-JP/auth.json +83 -4
- package/locales/ko-KR/auth.json +82 -3
- package/locales/nl-NL/auth.json +83 -4
- package/locales/pl-PL/auth.json +83 -4
- package/locales/pt-BR/auth.json +83 -4
- package/locales/ru-RU/auth.json +82 -3
- package/locales/tr-TR/auth.json +82 -3
- package/locales/vi-VN/auth.json +82 -3
- package/locales/zh-CN/auth.json +82 -3
- package/locales/zh-TW/auth.json +82 -3
- package/package.json +4 -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/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 +104 -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 +76 -4
- package/src/database/server/models/topic.ts +44 -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 +81 -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
@@ -56,9 +56,19 @@ export const topicRouter = router({
|
|
56
56
|
return data.topic.id;
|
57
57
|
}),
|
58
58
|
|
59
|
-
countTopics: topicProcedure
|
60
|
-
|
61
|
-
|
59
|
+
countTopics: topicProcedure
|
60
|
+
.input(
|
61
|
+
z
|
62
|
+
.object({
|
63
|
+
endDate: z.string().optional(),
|
64
|
+
range: z.tuple([z.string(), z.string()]).optional(),
|
65
|
+
startDate: z.string().optional(),
|
66
|
+
})
|
67
|
+
.optional(),
|
68
|
+
)
|
69
|
+
.query(async ({ ctx, input }) => {
|
70
|
+
return ctx.topicModel.count(input);
|
71
|
+
}),
|
62
72
|
|
63
73
|
createTopic: topicProcedure
|
64
74
|
.input(
|
@@ -100,6 +110,10 @@ export const topicRouter = router({
|
|
100
110
|
return (await ctx.topicModel.count()) === 0;
|
101
111
|
}),
|
102
112
|
|
113
|
+
rankTopics: topicProcedure.input(z.number().optional()).query(async ({ ctx, input }) => {
|
114
|
+
return ctx.topicModel.rank(input);
|
115
|
+
}),
|
116
|
+
|
103
117
|
removeAllTopics: topicProcedure.mutation(async ({ ctx }) => {
|
104
118
|
return ctx.topicModel.deleteAll();
|
105
119
|
}),
|
@@ -20,6 +20,10 @@ const userProcedure = authedProcedure.use(async (opts) => {
|
|
20
20
|
});
|
21
21
|
|
22
22
|
export const userRouter = router({
|
23
|
+
getUserRegistrationDuration: userProcedure.query(async ({ ctx }) => {
|
24
|
+
return ctx.userModel.getUserRegistrationDuration();
|
25
|
+
}),
|
26
|
+
|
23
27
|
getUserState: userProcedure.query(async ({ ctx }): Promise<UserInitializationState> => {
|
24
28
|
let state: Awaited<ReturnType<UserModel['getUserState']>> | undefined;
|
25
29
|
|
@@ -54,7 +54,7 @@ export class ChangelogService {
|
|
54
54
|
return this.mergeChangelogs(data.cloud, data.community).slice(0, 5);
|
55
55
|
} catch (e) {
|
56
56
|
const cause = (e as Error).cause as { code: string };
|
57
|
-
if (cause
|
57
|
+
if (cause?.code.includes('ETIMEDOUT')) {
|
58
58
|
console.warn(
|
59
59
|
'[ChangelogFetchTimeout] fail to fetch changelog lists due to network timeout. Please check your network connection.',
|
60
60
|
);
|
@@ -10,6 +10,7 @@ import {
|
|
10
10
|
ChatTTS,
|
11
11
|
ChatTranslate,
|
12
12
|
CreateMessageParams,
|
13
|
+
ModelRankItem,
|
13
14
|
} from '@/types/message';
|
14
15
|
|
15
16
|
import { IMessageService } from './type';
|
@@ -56,6 +57,21 @@ export class ClientService implements IMessageService {
|
|
56
57
|
return MessageModel.count();
|
57
58
|
}
|
58
59
|
|
60
|
+
// @ts-ignore
|
61
|
+
async rankModels(): Promise<ModelRankItem[]> {
|
62
|
+
throw new Error('Method not implemented.');
|
63
|
+
}
|
64
|
+
|
65
|
+
// @ts-ignore
|
66
|
+
async countWords(): Promise<number> {
|
67
|
+
throw new Error('Method not implemented.');
|
68
|
+
}
|
69
|
+
|
70
|
+
// @ts-ignore
|
71
|
+
async getHeatmaps() {
|
72
|
+
throw new Error('Method not implemented.');
|
73
|
+
}
|
74
|
+
|
59
75
|
async countTodayMessages() {
|
60
76
|
const topics = await MessageModel.queryAll();
|
61
77
|
return topics.filter(
|
@@ -350,24 +350,6 @@ describe('MessageClientService', () => {
|
|
350
350
|
});
|
351
351
|
});
|
352
352
|
|
353
|
-
describe('countTodayMessages', () => {
|
354
|
-
it('should count the number of messages created today', async () => {
|
355
|
-
// Setup
|
356
|
-
const mockMessages = [
|
357
|
-
{ ...mockMessage, id: undefined, createdAt: new Date(), userId },
|
358
|
-
{ ...mockMessage, id: undefined, createdAt: new Date(), userId },
|
359
|
-
{ ...mockMessage, id: undefined, createdAt: new Date('2023-01-01'), userId },
|
360
|
-
];
|
361
|
-
await clientDB.insert(messages).values(mockMessages);
|
362
|
-
|
363
|
-
// Execute
|
364
|
-
const count = await messageService.countTodayMessages();
|
365
|
-
|
366
|
-
// Assert
|
367
|
-
expect(count).toBe(2);
|
368
|
-
});
|
369
|
-
});
|
370
|
-
|
371
353
|
describe('updateMessageTTS', () => {
|
372
354
|
it('should update the TTS field of a message', async () => {
|
373
355
|
// Setup
|
@@ -1,5 +1,3 @@
|
|
1
|
-
import dayjs from 'dayjs';
|
2
|
-
|
3
1
|
import { INBOX_SESSION_ID } from '@/const/session';
|
4
2
|
import { clientDB } from '@/database/client/db';
|
5
3
|
import { MessageModel } from '@/database/server/models/message';
|
@@ -52,15 +50,20 @@ export class ClientService extends BaseClientService implements IMessageService
|
|
52
50
|
return data as unknown as ChatMessage[];
|
53
51
|
};
|
54
52
|
|
55
|
-
countMessages: IMessageService['countMessages'] = async () => {
|
56
|
-
return this.messageModel.count();
|
53
|
+
countMessages: IMessageService['countMessages'] = async (params) => {
|
54
|
+
return this.messageModel.count(params);
|
55
|
+
};
|
56
|
+
|
57
|
+
countWords: IMessageService['countWords'] = async (params) => {
|
58
|
+
return this.messageModel.countWords(params);
|
59
|
+
};
|
60
|
+
|
61
|
+
rankModels: IMessageService['rankModels'] = async () => {
|
62
|
+
return this.messageModel.rankModels();
|
57
63
|
};
|
58
64
|
|
59
|
-
|
60
|
-
|
61
|
-
return topics.filter(
|
62
|
-
(item) => dayjs(item.createdAt).format('YYYY-MM-DD') === dayjs().format('YYYY-MM-DD'),
|
63
|
-
).length;
|
65
|
+
getHeatmaps: IMessageService['getHeatmaps'] = async () => {
|
66
|
+
return this.messageModel.getHeatmaps();
|
64
67
|
};
|
65
68
|
|
66
69
|
getAllMessagesInSession: IMessageService['getAllMessagesInSession'] = async (sessionId) => {
|
@@ -36,12 +36,20 @@ export class ServerService implements IMessageService {
|
|
36
36
|
});
|
37
37
|
};
|
38
38
|
|
39
|
-
countMessages: IMessageService['countMessages'] = async () => {
|
40
|
-
return lambdaClient.message.count.query();
|
39
|
+
countMessages: IMessageService['countMessages'] = async (params) => {
|
40
|
+
return lambdaClient.message.count.query(params);
|
41
41
|
};
|
42
42
|
|
43
|
-
|
44
|
-
return lambdaClient.message.
|
43
|
+
countWords: IMessageService['countWords'] = async (params) => {
|
44
|
+
return lambdaClient.message.countWords.query(params);
|
45
|
+
};
|
46
|
+
|
47
|
+
rankModels: IMessageService['rankModels'] = async () => {
|
48
|
+
return lambdaClient.message.rankModels.query();
|
49
|
+
};
|
50
|
+
|
51
|
+
getHeatmaps: IMessageService['getHeatmaps'] = async () => {
|
52
|
+
return lambdaClient.message.getHeatmaps.query();
|
45
53
|
};
|
46
54
|
|
47
55
|
updateMessageError: IMessageService['updateMessageError'] = async (id, error) => {
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import type { HeatmapsProps } from '@lobehub/charts';
|
2
|
+
|
1
3
|
import { MessageItem } from '@/database/schemas';
|
2
4
|
import {
|
3
5
|
ChatMessage,
|
@@ -5,6 +7,7 @@ import {
|
|
5
7
|
ChatTTS,
|
6
8
|
ChatTranslate,
|
7
9
|
CreateMessageParams,
|
10
|
+
ModelRankItem,
|
8
11
|
} from '@/types/message';
|
9
12
|
|
10
13
|
/* eslint-disable typescript-sort-keys/interface */
|
@@ -16,9 +19,18 @@ export interface IMessageService {
|
|
16
19
|
getMessages(sessionId: string, topicId?: string): Promise<ChatMessage[]>;
|
17
20
|
getAllMessages(): Promise<ChatMessage[]>;
|
18
21
|
getAllMessagesInSession(sessionId: string): Promise<ChatMessage[]>;
|
19
|
-
countMessages(
|
20
|
-
|
21
|
-
|
22
|
+
countMessages(params?: {
|
23
|
+
endDate?: string;
|
24
|
+
range?: [string, string];
|
25
|
+
startDate?: string;
|
26
|
+
}): Promise<number>;
|
27
|
+
countWords(params?: {
|
28
|
+
endDate?: string;
|
29
|
+
range?: [string, string];
|
30
|
+
startDate?: string;
|
31
|
+
}): Promise<number>;
|
32
|
+
rankModels(): Promise<ModelRankItem[]>;
|
33
|
+
getHeatmaps(): Promise<HeatmapsProps['data']>;
|
22
34
|
updateMessageError(id: string, error: ChatMessageError): Promise<any>;
|
23
35
|
updateMessage(id: string, message: Partial<MessageItem>): Promise<any>;
|
24
36
|
updateMessageTTS(id: string, tts: Partial<ChatTTS> | false): Promise<any>;
|
@@ -82,6 +82,11 @@ export class ClientService implements ISessionService {
|
|
82
82
|
return SessionModel.count();
|
83
83
|
}
|
84
84
|
|
85
|
+
// @ts-ignore
|
86
|
+
async rankSessions() {
|
87
|
+
throw new Error('Method not implemented.');
|
88
|
+
}
|
89
|
+
|
85
90
|
async hasSessions() {
|
86
91
|
return (await this.countSessions()) !== 0;
|
87
92
|
}
|
@@ -74,8 +74,12 @@ export class ClientService extends BaseClientService implements ISessionService
|
|
74
74
|
}
|
75
75
|
};
|
76
76
|
|
77
|
-
countSessions: ISessionService['countSessions'] = async () => {
|
78
|
-
return this.sessionModel.count();
|
77
|
+
countSessions: ISessionService['countSessions'] = async (params) => {
|
78
|
+
return this.sessionModel.count(params);
|
79
|
+
};
|
80
|
+
|
81
|
+
rankSessions: ISessionService['rankSessions'] = async (limit) => {
|
82
|
+
return this.sessionModel.rank(limit);
|
79
83
|
};
|
80
84
|
|
81
85
|
searchSessions: ISessionService['searchSessions'] = async (keyword) => {
|
@@ -36,8 +36,12 @@ export class ServerService implements ISessionService {
|
|
36
36
|
return lambdaClient.session.getGroupedSessions.query();
|
37
37
|
};
|
38
38
|
|
39
|
-
countSessions: ISessionService['countSessions'] = () => {
|
40
|
-
return lambdaClient.session.countSessions.query();
|
39
|
+
countSessions: ISessionService['countSessions'] = async (params) => {
|
40
|
+
return lambdaClient.session.countSessions.query(params);
|
41
|
+
};
|
42
|
+
|
43
|
+
rankSessions: ISessionService['rankSessions'] = async (limit) => {
|
44
|
+
return lambdaClient.session.rankSessions.query(limit);
|
41
45
|
};
|
42
46
|
|
43
47
|
updateSession: ISessionService['updateSession'] = (id, data) => {
|
@@ -11,6 +11,7 @@ import {
|
|
11
11
|
LobeSessions,
|
12
12
|
SessionGroupItem,
|
13
13
|
SessionGroups,
|
14
|
+
SessionRankItem,
|
14
15
|
UpdateSessionParams,
|
15
16
|
} from '@/types/session';
|
16
17
|
|
@@ -31,7 +32,12 @@ export interface ISessionService {
|
|
31
32
|
* @deprecated
|
32
33
|
*/
|
33
34
|
getSessionsByType(type?: 'agent' | 'group' | 'all'): Promise<LobeSessions>;
|
34
|
-
countSessions(
|
35
|
+
countSessions(params?: {
|
36
|
+
endDate?: string;
|
37
|
+
range?: [string, string];
|
38
|
+
startDate?: string;
|
39
|
+
}): Promise<number>;
|
40
|
+
rankSessions(limit?: number): Promise<SessionRankItem[]>;
|
35
41
|
searchSessions(keyword: string): Promise<LobeSessions>;
|
36
42
|
|
37
43
|
updateSession(id: string, data: Partial<UpdateSessionParams>): Promise<any>;
|
@@ -38,6 +38,11 @@ export class ClientService implements ITopicService {
|
|
38
38
|
return TopicModel.count();
|
39
39
|
}
|
40
40
|
|
41
|
+
// @ts-ignore
|
42
|
+
async rankTopics() {
|
43
|
+
throw new Error('Method not implemented.');
|
44
|
+
}
|
45
|
+
|
41
46
|
async updateTopicFavorite(id: string, favorite?: boolean) {
|
42
47
|
return this.updateTopic(id, { favorite });
|
43
48
|
}
|
@@ -55,8 +55,12 @@ export class ClientService extends BaseClientService implements ITopicService {
|
|
55
55
|
return data as unknown as Promise<ChatTopic[]>;
|
56
56
|
};
|
57
57
|
|
58
|
-
countTopics: ITopicService['countTopics'] = async () => {
|
59
|
-
return this.topicModel.count();
|
58
|
+
countTopics: ITopicService['countTopics'] = async (params) => {
|
59
|
+
return this.topicModel.count(params);
|
60
|
+
};
|
61
|
+
|
62
|
+
rankTopics: ITopicService['rankTopics'] = async (limit) => {
|
63
|
+
return this.topicModel.rank(limit);
|
60
64
|
};
|
61
65
|
|
62
66
|
updateTopic: ITopicService['updateTopic'] = async (id, data) => {
|
@@ -24,7 +24,13 @@ export class ServerService implements ITopicService {
|
|
24
24
|
getAllTopics: ITopicService['getAllTopics'] = () =>
|
25
25
|
lambdaClient.topic.getAllTopics.query() as any;
|
26
26
|
|
27
|
-
countTopics: ITopicService['countTopics'] = async () =>
|
27
|
+
countTopics: ITopicService['countTopics'] = async (params) => {
|
28
|
+
return lambdaClient.topic.countTopics.query(params);
|
29
|
+
};
|
30
|
+
|
31
|
+
rankTopics: ITopicService['rankTopics'] = async (limit) => {
|
32
|
+
return lambdaClient.topic.rankTopics.query(limit);
|
33
|
+
};
|
28
34
|
|
29
35
|
searchTopics: ITopicService['searchTopics'] = (keywords, sessionId) =>
|
30
36
|
lambdaClient.topic.searchTopics.query({
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/* eslint-disable typescript-sort-keys/interface */
|
2
2
|
import { BatchTaskResult } from '@/types/service';
|
3
|
-
import { ChatTopic } from '@/types/topic';
|
3
|
+
import { ChatTopic, TopicRankItem } from '@/types/topic';
|
4
4
|
|
5
5
|
export interface CreateTopicParams {
|
6
6
|
favorite?: boolean;
|
@@ -22,7 +22,12 @@ export interface ITopicService {
|
|
22
22
|
|
23
23
|
getTopics(params: QueryTopicParams): Promise<ChatTopic[]>;
|
24
24
|
getAllTopics(): Promise<ChatTopic[]>;
|
25
|
-
countTopics(
|
25
|
+
countTopics(params?: {
|
26
|
+
endDate?: string;
|
27
|
+
range?: [string, string];
|
28
|
+
startDate?: string;
|
29
|
+
}): Promise<number>;
|
30
|
+
rankTopics(limit?: number): Promise<TopicRankItem[]>;
|
26
31
|
searchTopics(keyword: string, sessionId?: string): Promise<ChatTopic[]>;
|
27
32
|
|
28
33
|
updateTopic(id: string, data: Partial<ChatTopic>): Promise<any>;
|
@@ -16,6 +16,10 @@ export class ClientService implements IUserService {
|
|
16
16
|
this.preferenceStorage = new AsyncLocalStorage('LOBE_PREFERENCE');
|
17
17
|
}
|
18
18
|
|
19
|
+
getUserRegistrationDuration = async () => {
|
20
|
+
throw new Error('Method not implemented.');
|
21
|
+
};
|
22
|
+
|
19
23
|
async getUserState(): Promise<UserInitializationState> {
|
20
24
|
const user = await UserModel.getUser();
|
21
25
|
const messageCount = await MessageModel.count();
|
@@ -27,6 +27,10 @@ export class ClientService extends BaseClientService implements IUserService {
|
|
27
27
|
this.preferenceStorage = new AsyncLocalStorage('LOBE_PREFERENCE');
|
28
28
|
}
|
29
29
|
|
30
|
+
getUserRegistrationDuration: IUserService['getUserRegistrationDuration'] = async () => {
|
31
|
+
return this.userModel.getUserRegistrationDuration();
|
32
|
+
};
|
33
|
+
|
30
34
|
getUserState: IUserService['getUserState'] = async () => {
|
31
35
|
// if user not exist in the db, create one to make sure the user exist
|
32
36
|
await this.makeSureUserExist();
|
@@ -2,6 +2,10 @@ import { lambdaClient } from '@/libs/trpc/client';
|
|
2
2
|
import { IUserService } from '@/services/user/type';
|
3
3
|
|
4
4
|
export class ServerService implements IUserService {
|
5
|
+
getUserRegistrationDuration: IUserService['getUserRegistrationDuration'] = async () => {
|
6
|
+
return lambdaClient.user.getUserRegistrationDuration.query();
|
7
|
+
};
|
8
|
+
|
5
9
|
getUserState: IUserService['getUserState'] = async () => {
|
6
10
|
return lambdaClient.user.getUserState.query();
|
7
11
|
};
|
@@ -4,6 +4,11 @@ import { UserGuide, UserInitializationState, UserPreference } from '@/types/user
|
|
4
4
|
import { UserSettings } from '@/types/user/settings';
|
5
5
|
|
6
6
|
export interface IUserService {
|
7
|
+
getUserRegistrationDuration: () => Promise<{
|
8
|
+
createdAt: string;
|
9
|
+
duration: number;
|
10
|
+
updatedAt: string;
|
11
|
+
}>;
|
7
12
|
getUserState: () => Promise<UserInitializationState>;
|
8
13
|
resetUserSettings: () => Promise<any>;
|
9
14
|
updateGuide: (guide: Partial<UserGuide>) => Promise<any>;
|
@@ -31,6 +31,12 @@ export enum SettingsTabs {
|
|
31
31
|
TTS = 'tts',
|
32
32
|
}
|
33
33
|
|
34
|
+
export enum ProfileTabs {
|
35
|
+
Profile = 'profile',
|
36
|
+
Security = 'security',
|
37
|
+
Stats = 'stats',
|
38
|
+
}
|
39
|
+
|
34
40
|
export interface SystemStatus {
|
35
41
|
// which sessionGroup should expand
|
36
42
|
expandSessionGroupKeys: string[];
|
@@ -1,11 +1,8 @@
|
|
1
|
-
import { act, renderHook
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
2
2
|
import { mutate } from 'swr';
|
3
3
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
4
|
-
import { withSWR } from '~test-utils';
|
5
4
|
|
6
|
-
import { userService } from '@/services/user';
|
7
5
|
import { useUserStore } from '@/store/user';
|
8
|
-
import { switchLang } from '@/utils/client/switchLang';
|
9
6
|
|
10
7
|
vi.mock('zustand/traditional');
|
11
8
|
|
@@ -170,33 +167,4 @@ describe('createAuthSlice', () => {
|
|
170
167
|
expect(signIn).not.toHaveBeenCalled();
|
171
168
|
});
|
172
169
|
});
|
173
|
-
|
174
|
-
describe('openUserProfile', () => {
|
175
|
-
it('should call clerkOpenUserProfile when Clerk is enabled', async () => {
|
176
|
-
enableClerk = true;
|
177
|
-
|
178
|
-
const clerkOpenUserProfileMock = vi.fn();
|
179
|
-
useUserStore.setState({ clerkOpenUserProfile: clerkOpenUserProfileMock });
|
180
|
-
|
181
|
-
const { result } = renderHook(() => useUserStore());
|
182
|
-
|
183
|
-
await act(async () => {
|
184
|
-
await result.current.openUserProfile();
|
185
|
-
});
|
186
|
-
|
187
|
-
expect(clerkOpenUserProfileMock).toHaveBeenCalled();
|
188
|
-
});
|
189
|
-
it('should not call clerkOpenUserProfile when Clerk is disabled', async () => {
|
190
|
-
const clerkOpenUserProfileMock = vi.fn();
|
191
|
-
useUserStore.setState({ clerkOpenUserProfile: clerkOpenUserProfileMock });
|
192
|
-
|
193
|
-
const { result } = renderHook(() => useUserStore());
|
194
|
-
|
195
|
-
await act(async () => {
|
196
|
-
await result.current.openUserProfile();
|
197
|
-
});
|
198
|
-
|
199
|
-
expect(clerkOpenUserProfileMock).not.toHaveBeenCalled();
|
200
|
-
});
|
201
|
-
});
|
202
170
|
});
|
@@ -14,7 +14,6 @@ export interface UserAuthAction {
|
|
14
14
|
* universal login method
|
15
15
|
*/
|
16
16
|
openLogin: () => Promise<void>;
|
17
|
-
openUserProfile: () => Promise<void>;
|
18
17
|
}
|
19
18
|
|
20
19
|
export const createAuthSlice: StateCreator<
|
@@ -63,12 +62,4 @@ export const createAuthSlice: StateCreator<
|
|
63
62
|
signIn();
|
64
63
|
}
|
65
64
|
},
|
66
|
-
|
67
|
-
openUserProfile: async () => {
|
68
|
-
if (enableClerk) {
|
69
|
-
get().clerkOpenUserProfile?.();
|
70
|
-
|
71
|
-
return;
|
72
|
-
}
|
73
|
-
},
|
74
65
|
});
|
@@ -34,7 +34,7 @@ describe('createCommonSlice', () => {
|
|
34
34
|
describe('updateAvatar', () => {
|
35
35
|
it('should update avatar', async () => {
|
36
36
|
const { result } = renderHook(() => useUserStore());
|
37
|
-
const avatar = '
|
37
|
+
const avatar = 'data:image/png;base64,';
|
38
38
|
|
39
39
|
const spyOn = vi.spyOn(result.current, 'refreshUserState');
|
40
40
|
const updateAvatarSpy = vi
|
@@ -45,7 +45,7 @@ describe('createCommonSlice', () => {
|
|
45
45
|
await result.current.updateAvatar(avatar);
|
46
46
|
});
|
47
47
|
|
48
|
-
expect(updateAvatarSpy).toHaveBeenCalledWith(
|
48
|
+
expect(updateAvatarSpy).toHaveBeenCalledWith('data:image/png;base64,');
|
49
49
|
expect(spyOn).toHaveBeenCalled();
|
50
50
|
});
|
51
51
|
});
|
package/src/types/topic/topic.ts
CHANGED
@@ -44,3 +44,10 @@ export interface ChatTopic extends Omit<BaseDataModel, 'meta'> {
|
|
44
44
|
}
|
45
45
|
|
46
46
|
export type ChatTopicMap = Record<string, ChatTopic>;
|
47
|
+
|
48
|
+
export interface TopicRankItem {
|
49
|
+
count: number;
|
50
|
+
id: string;
|
51
|
+
sessionId: string | null;
|
52
|
+
title: string | null;
|
53
|
+
}
|
package/src/utils/format.ts
CHANGED
@@ -88,7 +88,7 @@ export const formatNumber = (num: any, fractionDigits?: number) => {
|
|
88
88
|
return `${numeral(a).format('0,0')}.${b}`;
|
89
89
|
};
|
90
90
|
|
91
|
-
export const formatIntergerNumber = (num
|
91
|
+
export const formatIntergerNumber = (num?: any) => {
|
92
92
|
if (!num && num !== 0) return '--';
|
93
93
|
|
94
94
|
return numeral(num).format('0,0');
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import dayjs, { Dayjs } from 'dayjs';
|
2
|
+
|
3
|
+
const getQuarterStart = (date: Dayjs) => {
|
4
|
+
const month = date.month();
|
5
|
+
const quarterStartMonth = Math.floor(month / 3) * 3;
|
6
|
+
return date.month(quarterStartMonth).startOf('month');
|
7
|
+
};
|
8
|
+
|
9
|
+
export const today = () => dayjs().startOf('day');
|
10
|
+
export const thisWeek = () => dayjs().startOf('week');
|
11
|
+
export const thisMonth = () => dayjs().startOf('month');
|
12
|
+
export const thisQuarter = () => getQuarterStart(today());
|
13
|
+
export const thisYear = () => dayjs().startOf('year');
|
14
|
+
|
15
|
+
export const hoursAgo = (hours: number) => dayjs().subtract(hours, 'hours').startOf('hours');
|
16
|
+
|
17
|
+
export const daysAgo = (days: number) => dayjs().subtract(days, 'days').startOf('day');
|
18
|
+
|
19
|
+
export const weeksAgo = (weeks: number) => dayjs().subtract(weeks, 'week').startOf('week');
|
20
|
+
|
21
|
+
export const monthsAgo = (months: number) => dayjs().subtract(months, 'month').startOf('month');
|
22
|
+
|
23
|
+
export const lastMonth = () => monthsAgo(1).endOf('month');
|
@@ -1,76 +0,0 @@
|
|
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-inline-end: 0;
|
32
|
-
padding-block: 24px 16px;
|
33
|
-
padding-inline: 12px;
|
34
|
-
|
35
|
-
background: ${token.colorBgContainer};
|
36
|
-
border-inline-end: 1px solid ${token.colorSplit};
|
37
|
-
`,
|
38
|
-
navbarMobileMenuRow: cx(
|
39
|
-
mobile &&
|
40
|
-
css`
|
41
|
-
display: none;
|
42
|
-
`,
|
43
|
-
),
|
44
|
-
pageScrollBox: css`
|
45
|
-
align-self: center;
|
46
|
-
width: 100%;
|
47
|
-
max-width: 1024px;
|
48
|
-
`,
|
49
|
-
rootBox: css`
|
50
|
-
width: 100%;
|
51
|
-
height: 100%;
|
52
|
-
`,
|
53
|
-
scrollBox: css`
|
54
|
-
background: ${token.colorBgLayout};
|
55
|
-
border: unset;
|
56
|
-
border-radius: unset;
|
57
|
-
`,
|
58
|
-
}) as Partial<{
|
59
|
-
// eslint-disable-next-line unused-imports/no-unused-vars
|
60
|
-
[k in keyof ElementsConfig]: any;
|
61
|
-
}>,
|
62
|
-
);
|
63
|
-
|
64
|
-
const Client = memo<{ mobile?: boolean }>(({ mobile }) => {
|
65
|
-
const { styles } = useStyles(mobile);
|
66
|
-
|
67
|
-
return (
|
68
|
-
<UserProfile
|
69
|
-
appearance={{
|
70
|
-
elements: styles,
|
71
|
-
}}
|
72
|
-
/>
|
73
|
-
);
|
74
|
-
});
|
75
|
-
|
76
|
-
export default Client;
|