@lobehub/chat 1.26.21 → 1.27.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 +50 -0
- package/locales/ar/chat.json +1 -21
- package/locales/ar/error.json +4 -1
- package/locales/ar/topic.json +37 -0
- package/locales/bg-BG/chat.json +1 -21
- package/locales/bg-BG/error.json +4 -1
- package/locales/bg-BG/topic.json +37 -0
- package/locales/de-DE/chat.json +1 -21
- package/locales/de-DE/error.json +4 -1
- package/locales/de-DE/topic.json +37 -0
- package/locales/en-US/chat.json +1 -21
- package/locales/en-US/error.json +4 -1
- package/locales/en-US/topic.json +37 -0
- package/locales/es-ES/chat.json +1 -21
- package/locales/es-ES/error.json +4 -1
- package/locales/es-ES/topic.json +37 -0
- package/locales/fa-IR/chat.json +1 -21
- package/locales/fa-IR/components.json +1 -0
- package/locales/fa-IR/error.json +4 -1
- package/locales/fa-IR/topic.json +37 -0
- package/locales/fr-FR/chat.json +1 -21
- package/locales/fr-FR/error.json +4 -1
- package/locales/fr-FR/topic.json +37 -0
- package/locales/it-IT/chat.json +1 -21
- package/locales/it-IT/error.json +4 -1
- package/locales/it-IT/topic.json +37 -0
- package/locales/ja-JP/chat.json +1 -21
- package/locales/ja-JP/error.json +4 -1
- package/locales/ja-JP/topic.json +37 -0
- package/locales/ko-KR/chat.json +1 -21
- package/locales/ko-KR/error.json +4 -1
- package/locales/ko-KR/topic.json +37 -0
- package/locales/nl-NL/chat.json +1 -21
- package/locales/nl-NL/error.json +4 -1
- package/locales/nl-NL/topic.json +37 -0
- package/locales/pl-PL/chat.json +1 -21
- package/locales/pl-PL/error.json +4 -1
- package/locales/pl-PL/topic.json +37 -0
- package/locales/pt-BR/chat.json +1 -21
- package/locales/pt-BR/error.json +4 -1
- package/locales/pt-BR/topic.json +37 -0
- package/locales/ru-RU/chat.json +1 -21
- package/locales/ru-RU/error.json +4 -1
- package/locales/ru-RU/topic.json +37 -0
- package/locales/tr-TR/chat.json +1 -21
- package/locales/tr-TR/error.json +4 -1
- package/locales/tr-TR/topic.json +37 -0
- package/locales/vi-VN/chat.json +1 -21
- package/locales/vi-VN/error.json +4 -1
- package/locales/vi-VN/topic.json +37 -0
- package/locales/zh-CN/chat.json +1 -21
- package/locales/zh-CN/topic.json +37 -0
- package/locales/zh-TW/chat.json +1 -21
- package/locales/zh-TW/error.json +4 -1
- package/locales/zh-TW/topic.json +37 -0
- package/package.json +2 -2
- package/src/app/(backend)/webapi/plugin/store/route.ts +22 -8
- package/src/app/(main)/@nav/_layout/Desktop/TopActions.tsx +3 -2
- package/src/app/(main)/@nav/_layout/Desktop/index.tsx +4 -1
- package/src/app/(main)/chat/(workspace)/@topic/features/Header.tsx +27 -9
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/GroupItem.tsx +30 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx +72 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/DefaultContent.tsx +2 -2
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/FlatMode/index.tsx +60 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicContent.tsx +7 -7
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/index.tsx +17 -53
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicSearchBar/index.tsx +2 -2
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/ChatHeaderTitle.tsx +2 -2
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx +5 -2
- package/src/const/user.ts +2 -0
- package/src/hooks/useFetchTopics.ts +11 -0
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +7 -1
- package/src/locales/default/chat.ts +0 -20
- package/src/locales/default/index.ts +3 -1
- package/src/locales/default/topic.ts +37 -0
- package/src/store/chat/slices/topic/action.test.ts +1 -1
- package/src/store/chat/slices/topic/action.ts +4 -4
- package/src/store/chat/slices/topic/selectors.test.ts +78 -1
- package/src/store/chat/slices/topic/selectors.ts +28 -1
- package/src/store/user/slices/preference/selectors.ts +4 -0
- package/src/types/topic.ts +24 -0
- package/src/types/user/index.ts +2 -0
- package/src/utils/client/topic.test.ts +142 -0
- package/src/utils/client/topic.ts +86 -0
@@ -1,10 +1,16 @@
|
|
1
|
+
import { t } from 'i18next';
|
1
2
|
import { describe, expect, it } from 'vitest';
|
2
3
|
|
3
4
|
import { ChatStore } from '@/store/chat';
|
4
5
|
import { initialState } from '@/store/chat/initialState';
|
5
6
|
import { merge } from '@/utils/merge';
|
6
7
|
|
7
|
-
import { topicSelectors } from '
|
8
|
+
import { topicSelectors } from './selectors';
|
9
|
+
|
10
|
+
// Mock i18next
|
11
|
+
vi.mock('i18next', () => ({
|
12
|
+
t: vi.fn().mockImplementation((key) => key),
|
13
|
+
}));
|
8
14
|
|
9
15
|
const initialStore = initialState as ChatStore;
|
10
16
|
|
@@ -92,4 +98,75 @@ describe('topicSelectors', () => {
|
|
92
98
|
expect(topic).toEqual(topicMaps.test[0]);
|
93
99
|
});
|
94
100
|
});
|
101
|
+
|
102
|
+
describe('groupedTopicsSelector', () => {
|
103
|
+
it('should return empty array if there are no topics', () => {
|
104
|
+
const state = merge(initialStore, { activeId: 'test' });
|
105
|
+
const grouped = topicSelectors.groupedTopicsSelector(state);
|
106
|
+
expect(grouped).toEqual([]);
|
107
|
+
});
|
108
|
+
|
109
|
+
it('should return grouped topics by time when no favorites exist', () => {
|
110
|
+
const topics = [
|
111
|
+
{ id: 'topic1', name: 'Topic 1', favorite: false, createAt: '2023-01-01' },
|
112
|
+
{ id: 'topic2', name: 'Topic 2', favorite: false, createAt: '2023-01-01' },
|
113
|
+
];
|
114
|
+
|
115
|
+
const state = merge(initialStore, {
|
116
|
+
topicMaps: { test: topics },
|
117
|
+
activeId: 'test',
|
118
|
+
});
|
119
|
+
|
120
|
+
const grouped = topicSelectors.groupedTopicsSelector(state);
|
121
|
+
expect(grouped).toHaveLength(1); // One time-based group
|
122
|
+
expect(grouped[0].children).toEqual(topics);
|
123
|
+
});
|
124
|
+
|
125
|
+
it('should separate favorite and unfavorite topics into different groups', () => {
|
126
|
+
const topics = [
|
127
|
+
{ id: 'topic1', name: 'Topic 1', favorite: true, createAt: '2023-01-01' },
|
128
|
+
{ id: 'topic2', name: 'Topic 2', favorite: false, createAt: '2023-01-01' },
|
129
|
+
{ id: 'topic3', name: 'Topic 3', favorite: true, createAt: '2023-01-01' },
|
130
|
+
];
|
131
|
+
|
132
|
+
const state = merge(initialStore, {
|
133
|
+
topicMaps: { test: topics },
|
134
|
+
activeId: 'test',
|
135
|
+
});
|
136
|
+
|
137
|
+
const grouped = topicSelectors.groupedTopicsSelector(state);
|
138
|
+
|
139
|
+
expect(grouped).toHaveLength(2); // Favorite group + one time-based group
|
140
|
+
|
141
|
+
// Check favorite group
|
142
|
+
expect(grouped[0]).toEqual({
|
143
|
+
id: 'favorite',
|
144
|
+
title: 'favorite', // This matches the mocked t function return
|
145
|
+
children: topics.filter((t) => t.favorite),
|
146
|
+
});
|
147
|
+
|
148
|
+
// Check unfavorite group
|
149
|
+
expect(grouped[1].children).toEqual(topics.filter((t) => !t.favorite));
|
150
|
+
});
|
151
|
+
|
152
|
+
it('should only create time-based groups when there are no favorites', () => {
|
153
|
+
const topics = [
|
154
|
+
{ id: 'topic1', name: 'Topic 1', favorite: false, createAt: '2023-01-01' },
|
155
|
+
{ id: 'topic2', name: 'Topic 2', favorite: false, createAt: '2023-02-01' },
|
156
|
+
];
|
157
|
+
|
158
|
+
const state = merge(initialStore, {
|
159
|
+
topicMaps: { test: topics },
|
160
|
+
activeId: 'test',
|
161
|
+
});
|
162
|
+
|
163
|
+
const grouped = topicSelectors.groupedTopicsSelector(state);
|
164
|
+
|
165
|
+
// Should not have a favorites group
|
166
|
+
expect(grouped.find((g) => g.id === 'favorite')).toBeUndefined();
|
167
|
+
|
168
|
+
// Should have time-based groups
|
169
|
+
expect(grouped.every((g) => g.id !== 'favorite')).toBeTruthy();
|
170
|
+
});
|
171
|
+
});
|
95
172
|
});
|
@@ -1,4 +1,7 @@
|
|
1
|
-
import {
|
1
|
+
import { t } from 'i18next';
|
2
|
+
|
3
|
+
import { ChatTopic, GroupedTopic } from '@/types/topic';
|
4
|
+
import { groupTopicsByTime } from '@/utils/client/topic';
|
2
5
|
|
3
6
|
import { ChatStore } from '../../store';
|
4
7
|
|
@@ -12,6 +15,9 @@ const searchTopics = (s: ChatStore): ChatTopic[] => s.searchTopics;
|
|
12
15
|
const displayTopics = (s: ChatStore): ChatTopic[] | undefined =>
|
13
16
|
s.isSearchingTopic ? searchTopics(s) : currentTopics(s);
|
14
17
|
|
18
|
+
const currentFavTopics = (s: ChatStore): ChatTopic[] =>
|
19
|
+
currentTopics(s)?.filter((s) => s.favorite) || [];
|
20
|
+
|
15
21
|
const currentUnFavTopics = (s: ChatStore): ChatTopic[] =>
|
16
22
|
currentTopics(s)?.filter((s) => !s.favorite) || [];
|
17
23
|
|
@@ -21,8 +27,28 @@ const getTopicById =
|
|
21
27
|
(id: string) =>
|
22
28
|
(s: ChatStore): ChatTopic | undefined =>
|
23
29
|
currentTopics(s)?.find((topic) => topic.id === id);
|
30
|
+
|
24
31
|
const isCreatingTopic = (s: ChatStore) => s.creatingTopic;
|
25
32
|
|
33
|
+
const groupedTopicsSelector = (s: ChatStore): GroupedTopic[] => {
|
34
|
+
const topics = currentTopics(s);
|
35
|
+
|
36
|
+
if (!topics) return [];
|
37
|
+
const favTopics = currentFavTopics(s);
|
38
|
+
const unfavTopics = currentUnFavTopics(s);
|
39
|
+
|
40
|
+
return favTopics.length > 0
|
41
|
+
? [
|
42
|
+
{
|
43
|
+
children: favTopics,
|
44
|
+
id: 'favorite',
|
45
|
+
title: t('favorite', { ns: 'topic' }),
|
46
|
+
},
|
47
|
+
...groupTopicsByTime(unfavTopics),
|
48
|
+
]
|
49
|
+
: groupTopicsByTime(topics);
|
50
|
+
};
|
51
|
+
|
26
52
|
export const topicSelectors = {
|
27
53
|
currentActiveTopic,
|
28
54
|
currentTopicLength,
|
@@ -30,6 +56,7 @@ export const topicSelectors = {
|
|
30
56
|
currentUnFavTopics,
|
31
57
|
displayTopics,
|
32
58
|
getTopicById,
|
59
|
+
groupedTopicsSelector,
|
33
60
|
isCreatingTopic,
|
34
61
|
searchTopics,
|
35
62
|
};
|
@@ -1,6 +1,9 @@
|
|
1
|
+
import { DEFAULT_PREFERENCE } from '@/const/user';
|
1
2
|
import { UserStore } from '@/store/user';
|
2
3
|
|
3
4
|
const useCmdEnterToSend = (s: UserStore): boolean => s.preference.useCmdEnterToSend || false;
|
5
|
+
const topicDisplayMode = (s: UserStore) =>
|
6
|
+
s.preference.topicDisplayMode || DEFAULT_PREFERENCE.topicDisplayMode;
|
4
7
|
|
5
8
|
const userAllowTrace = (s: UserStore) => s.preference.telemetry;
|
6
9
|
|
@@ -22,6 +25,7 @@ export const preferenceSelectors = {
|
|
22
25
|
isPreferenceInit,
|
23
26
|
shouldTriggerFileInKnowledgeBaseTip,
|
24
27
|
showUploadFileInKnowledgeBaseTip,
|
28
|
+
topicDisplayMode,
|
25
29
|
useCmdEnterToSend,
|
26
30
|
userAllowTrace,
|
27
31
|
};
|
package/src/types/topic.ts
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
import { BaseDataModel } from '@/types/meta';
|
2
2
|
|
3
|
+
// 类型定义
|
4
|
+
export type TimeGroupId =
|
5
|
+
| 'today'
|
6
|
+
| 'yesterday'
|
7
|
+
| 'week'
|
8
|
+
| 'month'
|
9
|
+
| `${number}-${string}`
|
10
|
+
| `${number}`;
|
11
|
+
|
12
|
+
/* eslint-disable typescript-sort-keys/string-enum */
|
13
|
+
export enum TopicDisplayMode {
|
14
|
+
ByTime = 'byTime',
|
15
|
+
Flat = 'flat',
|
16
|
+
// AscMessages = 'ascMessages',
|
17
|
+
// DescMessages = 'descMessages',
|
18
|
+
}
|
19
|
+
/* eslint-enable */
|
20
|
+
|
21
|
+
export interface GroupedTopic {
|
22
|
+
children: ChatTopic[];
|
23
|
+
id: string;
|
24
|
+
title?: string;
|
25
|
+
}
|
26
|
+
|
3
27
|
export interface ChatTopic extends Omit<BaseDataModel, 'meta'> {
|
4
28
|
favorite?: boolean;
|
5
29
|
sessionId?: string;
|
package/src/types/user/index.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { DeepPartial } from 'utility-types';
|
2
2
|
import { z } from 'zod';
|
3
3
|
|
4
|
+
import { TopicDisplayMode } from '@/types/topic';
|
4
5
|
import { UserSettings } from '@/types/user/settings';
|
5
6
|
|
6
7
|
export interface LobeUser {
|
@@ -36,6 +37,7 @@ export interface UserPreference {
|
|
36
37
|
guide?: UserGuide;
|
37
38
|
hideSyncAlert?: boolean;
|
38
39
|
telemetry: boolean | null;
|
40
|
+
topicDisplayMode?: TopicDisplayMode;
|
39
41
|
/**
|
40
42
|
* whether to use cmd + enter to send message
|
41
43
|
*/
|
@@ -0,0 +1,142 @@
|
|
1
|
+
import dayjs from 'dayjs';
|
2
|
+
import { beforeAll, describe, expect, it } from 'vitest';
|
3
|
+
|
4
|
+
import { ChatTopic } from '@/types/topic';
|
5
|
+
|
6
|
+
import { groupTopicsByTime } from './topic';
|
7
|
+
|
8
|
+
// Mock current date to ensure consistent test results
|
9
|
+
const NOW = '2024-01-15T12:00:00Z';
|
10
|
+
|
11
|
+
beforeAll(() => {
|
12
|
+
// Mock the current date
|
13
|
+
vi.useFakeTimers();
|
14
|
+
vi.setSystemTime(new Date(NOW));
|
15
|
+
});
|
16
|
+
|
17
|
+
describe('groupTopicsByTime', () => {
|
18
|
+
afterAll(() => {
|
19
|
+
vi.useRealTimers();
|
20
|
+
});
|
21
|
+
|
22
|
+
// Helper function to create test topics
|
23
|
+
const createTopic = (createdAt: number, title: string = 'Test Topic'): ChatTopic => ({
|
24
|
+
id: createdAt.toString(),
|
25
|
+
title,
|
26
|
+
createdAt,
|
27
|
+
updatedAt: createdAt,
|
28
|
+
});
|
29
|
+
|
30
|
+
it('should return empty array for empty input', () => {
|
31
|
+
expect(groupTopicsByTime([])).toEqual([]);
|
32
|
+
});
|
33
|
+
|
34
|
+
it('should group topics created today', () => {
|
35
|
+
const today = dayjs().valueOf();
|
36
|
+
const topics = [createTopic(today)];
|
37
|
+
|
38
|
+
const result = groupTopicsByTime(topics);
|
39
|
+
|
40
|
+
expect(result).toHaveLength(1);
|
41
|
+
expect(result[0]).toEqual({
|
42
|
+
id: 'today',
|
43
|
+
children: topics,
|
44
|
+
});
|
45
|
+
});
|
46
|
+
|
47
|
+
it('should group topics created yesterday', () => {
|
48
|
+
const yesterday = dayjs().subtract(1, 'day').valueOf();
|
49
|
+
const topics = [createTopic(yesterday)];
|
50
|
+
|
51
|
+
const result = groupTopicsByTime(topics);
|
52
|
+
|
53
|
+
expect(result).toHaveLength(1);
|
54
|
+
expect(result[0]).toEqual({
|
55
|
+
id: 'yesterday',
|
56
|
+
children: topics,
|
57
|
+
});
|
58
|
+
});
|
59
|
+
|
60
|
+
it('should group topics created within the week', () => {
|
61
|
+
const threeDaysAgo = dayjs().subtract(3, 'day').valueOf();
|
62
|
+
const topics = [createTopic(threeDaysAgo)];
|
63
|
+
|
64
|
+
const result = groupTopicsByTime(topics);
|
65
|
+
|
66
|
+
expect(result).toHaveLength(1);
|
67
|
+
expect(result[0]).toEqual({
|
68
|
+
id: 'week',
|
69
|
+
children: topics,
|
70
|
+
});
|
71
|
+
});
|
72
|
+
|
73
|
+
it('should group topics created this month', () => {
|
74
|
+
const thisMonth = dayjs().startOf('month').add(1, 'day').valueOf();
|
75
|
+
const topics = [createTopic(thisMonth)];
|
76
|
+
|
77
|
+
const result = groupTopicsByTime(topics);
|
78
|
+
|
79
|
+
expect(result).toHaveLength(1);
|
80
|
+
expect(result[0]).toEqual({
|
81
|
+
id: 'month',
|
82
|
+
children: topics,
|
83
|
+
});
|
84
|
+
});
|
85
|
+
|
86
|
+
it('should group topics from previous years', () => {
|
87
|
+
const lastYear = dayjs().subtract(1, 'year').valueOf();
|
88
|
+
const topics = [createTopic(lastYear)];
|
89
|
+
|
90
|
+
const result = groupTopicsByTime(topics);
|
91
|
+
|
92
|
+
expect(result).toHaveLength(1);
|
93
|
+
expect(result[0]).toEqual({
|
94
|
+
id: dayjs(lastYear).year().toString(),
|
95
|
+
children: topics,
|
96
|
+
});
|
97
|
+
});
|
98
|
+
|
99
|
+
it('should sort groups in correct order', () => {
|
100
|
+
const today = dayjs().valueOf();
|
101
|
+
const yesterday = dayjs().subtract(1, 'day').valueOf();
|
102
|
+
const lastWeek = dayjs().subtract(5, 'day').valueOf();
|
103
|
+
const lastMonth = dayjs().subtract(1, 'month').valueOf();
|
104
|
+
const lastYear = dayjs().subtract(1, 'year').valueOf();
|
105
|
+
|
106
|
+
const topics = [
|
107
|
+
createTopic(lastYear, 'Last Year'),
|
108
|
+
createTopic(lastMonth, 'Last Month'),
|
109
|
+
createTopic(lastWeek, 'Last Week'),
|
110
|
+
createTopic(yesterday, 'Yesterday'),
|
111
|
+
createTopic(today, 'Today'),
|
112
|
+
];
|
113
|
+
|
114
|
+
const result = groupTopicsByTime(topics);
|
115
|
+
|
116
|
+
// Verify order of groups
|
117
|
+
expect(result.map((g) => g.id)).toEqual([
|
118
|
+
'today',
|
119
|
+
'yesterday',
|
120
|
+
'week',
|
121
|
+
dayjs(lastYear).year().toString(),
|
122
|
+
]);
|
123
|
+
});
|
124
|
+
|
125
|
+
it('should sort topics within groups by createdAt in descending order', () => {
|
126
|
+
const today1 = dayjs().hour(9).valueOf();
|
127
|
+
const today2 = dayjs().hour(10).valueOf();
|
128
|
+
const today3 = dayjs().hour(11).valueOf();
|
129
|
+
|
130
|
+
const topics = [
|
131
|
+
createTopic(today1, 'Morning'),
|
132
|
+
createTopic(today2, 'Midday'),
|
133
|
+
createTopic(today3, 'Afternoon'),
|
134
|
+
];
|
135
|
+
|
136
|
+
const result = groupTopicsByTime(topics);
|
137
|
+
|
138
|
+
expect(result).toHaveLength(1);
|
139
|
+
expect(result[0].id).toBe('today');
|
140
|
+
expect(result[0].children.map((t) => t.title)).toEqual(['Afternoon', 'Midday', 'Morning']);
|
141
|
+
});
|
142
|
+
});
|
@@ -0,0 +1,86 @@
|
|
1
|
+
import dayjs from 'dayjs';
|
2
|
+
import isToday from 'dayjs/plugin/isToday';
|
3
|
+
import isYesterday from 'dayjs/plugin/isYesterday';
|
4
|
+
|
5
|
+
import { ChatTopic, GroupedTopic, TimeGroupId } from '@/types/topic';
|
6
|
+
|
7
|
+
// 初始化 dayjs 插件
|
8
|
+
dayjs.extend(isToday);
|
9
|
+
dayjs.extend(isYesterday);
|
10
|
+
|
11
|
+
const getTopicGroupId = (timestamp: number): TimeGroupId => {
|
12
|
+
const date = dayjs(timestamp);
|
13
|
+
const now = dayjs();
|
14
|
+
|
15
|
+
if (date.isToday()) {
|
16
|
+
return 'today';
|
17
|
+
}
|
18
|
+
|
19
|
+
if (date.isYesterday()) {
|
20
|
+
return 'yesterday';
|
21
|
+
}
|
22
|
+
|
23
|
+
// 7天内(不包括今天和昨天)
|
24
|
+
const weekAgo = now.subtract(7, 'day');
|
25
|
+
if (date.isAfter(weekAgo) && !date.isToday() && !date.isYesterday()) {
|
26
|
+
return 'week';
|
27
|
+
}
|
28
|
+
|
29
|
+
// 当前月份(不包括前面已经分组的日期)
|
30
|
+
// 使用原生的月份和年份比较
|
31
|
+
if (date.month() === now.month() && date.year() === now.year()) {
|
32
|
+
return 'month';
|
33
|
+
}
|
34
|
+
|
35
|
+
// 当年的其他月份
|
36
|
+
if (date.year() === now.year()) {
|
37
|
+
return `${date.year()}-${(date.month() + 1).toString().padStart(2, '0')}`;
|
38
|
+
}
|
39
|
+
|
40
|
+
// 更早的年份
|
41
|
+
return `${date.year()}`;
|
42
|
+
};
|
43
|
+
|
44
|
+
// 确保分组的排序
|
45
|
+
const sortGroups = (groups: GroupedTopic[]): GroupedTopic[] => {
|
46
|
+
const orderMap = new Map<string, number>();
|
47
|
+
|
48
|
+
// 设置固定分组的顺序
|
49
|
+
orderMap.set('today', 0);
|
50
|
+
orderMap.set('yesterday', 1);
|
51
|
+
orderMap.set('week', 2);
|
52
|
+
orderMap.set('month', 3);
|
53
|
+
|
54
|
+
return groups.sort((a, b) => {
|
55
|
+
const orderA = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
|
56
|
+
const orderB = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
|
57
|
+
|
58
|
+
if (orderA !== Number.MAX_SAFE_INTEGER || orderB !== Number.MAX_SAFE_INTEGER) {
|
59
|
+
return orderA - orderB;
|
60
|
+
}
|
61
|
+
|
62
|
+
// 对于年月格式和年份格式的分组,按时间倒序排序
|
63
|
+
return b.id.localeCompare(a.id);
|
64
|
+
});
|
65
|
+
};
|
66
|
+
|
67
|
+
// 时间分组的具体实现
|
68
|
+
export const groupTopicsByTime = (topics: ChatTopic[]): GroupedTopic[] => {
|
69
|
+
if (!topics.length) return [];
|
70
|
+
|
71
|
+
const sortedTopics = [...topics].sort((a, b) => b.createdAt - a.createdAt);
|
72
|
+
const groupsMap = new Map<TimeGroupId, ChatTopic[]>();
|
73
|
+
|
74
|
+
sortedTopics.forEach((topic) => {
|
75
|
+
const groupId = getTopicGroupId(topic.createdAt);
|
76
|
+
const existingGroup = groupsMap.get(groupId) || [];
|
77
|
+
groupsMap.set(groupId, [...existingGroup, topic]);
|
78
|
+
});
|
79
|
+
|
80
|
+
const result = Array.from(groupsMap.entries()).map(([id, children]) => ({
|
81
|
+
children,
|
82
|
+
id,
|
83
|
+
}));
|
84
|
+
|
85
|
+
return sortGroups(result);
|
86
|
+
};
|