@lobehub/chat 0.161.13 → 0.161.15
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/.github/ISSUE_TEMPLATE/1_bug_report.yml +20 -23
- package/.github/ISSUE_TEMPLATE/1_bug_report_cn.yml +20 -23
- package/CHANGELOG.md +50 -0
- package/README.md +8 -8
- package/README.zh-CN.md +2 -2
- package/package.json +4 -4
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/SystemRoleContent.tsx +2 -1
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +2 -1
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +7 -6
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx +2 -1
- package/src/app/(main)/chat/@session/features/SessionListContent/DefaultMode.tsx +5 -5
- package/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx +5 -3
- package/src/database/client/models/__tests__/sessionGroup.test.ts +7 -0
- package/src/features/PWAInstall/index.tsx +51 -1
- package/src/hooks/useSyncData.ts +2 -2
- package/src/layout/AuthProvider/Clerk/UserUpdater.tsx +0 -1
- package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -2
- package/src/store/global/action.test.ts +23 -22
- package/src/store/global/action.ts +28 -29
- package/src/store/global/initialState.ts +19 -23
- package/src/store/global/selectors.ts +20 -3
- package/src/store/user/slices/auth/initialState.ts +0 -6
- package/src/store/user/slices/auth/selectors.ts +2 -2
- package/src/store/user/slices/common/action.test.ts +4 -3
- package/src/store/user/slices/common/action.ts +7 -2
- package/src/types/user/index.ts +1 -1
- package/src/utils/localStorage.ts +1 -1
|
@@ -3,16 +3,15 @@ description: 'Report an bug'
|
|
|
3
3
|
title: '[Bug] '
|
|
4
4
|
labels: ['🐛 Bug']
|
|
5
5
|
body:
|
|
6
|
-
- type:
|
|
6
|
+
- type: checkboxes
|
|
7
7
|
attributes:
|
|
8
8
|
label: '📦 Environment'
|
|
9
9
|
options:
|
|
10
|
-
-
|
|
11
|
-
- Official
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- Other
|
|
10
|
+
- label: 'Official'
|
|
11
|
+
- label: 'Official Preview'
|
|
12
|
+
- label: 'Vercel / Zeabur / Sealos'
|
|
13
|
+
- label: 'Docker'
|
|
14
|
+
- label: 'Other'
|
|
16
15
|
validations:
|
|
17
16
|
required: true
|
|
18
17
|
- type: input
|
|
@@ -21,30 +20,28 @@ body:
|
|
|
21
20
|
validations:
|
|
22
21
|
required: true
|
|
23
22
|
|
|
24
|
-
- type:
|
|
23
|
+
- type: checkboxes
|
|
25
24
|
attributes:
|
|
26
25
|
label: '💻 Operating System'
|
|
27
26
|
options:
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
- Other
|
|
27
|
+
- label: 'Windows'
|
|
28
|
+
- label: 'macOS'
|
|
29
|
+
- label: 'Ubuntu'
|
|
30
|
+
- label: 'Other Linux'
|
|
31
|
+
- label: 'iOS'
|
|
32
|
+
- label: 'Android'
|
|
33
|
+
- label: 'Other'
|
|
36
34
|
validations:
|
|
37
35
|
required: true
|
|
38
|
-
- type:
|
|
36
|
+
- type: checkboxes
|
|
39
37
|
attributes:
|
|
40
38
|
label: '🌐 Browser'
|
|
41
39
|
options:
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
- Other
|
|
40
|
+
- label: 'Chrome'
|
|
41
|
+
- label: 'Edge'
|
|
42
|
+
- label: 'Safari'
|
|
43
|
+
- label: 'Firefox'
|
|
44
|
+
- label: 'Other'
|
|
48
45
|
validations:
|
|
49
46
|
required: true
|
|
50
47
|
- type: textarea
|
|
@@ -3,16 +3,15 @@ description: '反馈一个问题缺陷'
|
|
|
3
3
|
title: '[Bug] '
|
|
4
4
|
labels: ['🐛 Bug']
|
|
5
5
|
body:
|
|
6
|
-
- type:
|
|
6
|
+
- type: checkboxes
|
|
7
7
|
attributes:
|
|
8
8
|
label: '📦 部署环境'
|
|
9
9
|
options:
|
|
10
|
-
-
|
|
11
|
-
- Official
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- Other
|
|
10
|
+
- label: 'Official'
|
|
11
|
+
- label: 'Official Preview'
|
|
12
|
+
- label: 'Vercel / Zeabur / Sealos'
|
|
13
|
+
- label: 'Docker'
|
|
14
|
+
- label: 'Other'
|
|
16
15
|
validations:
|
|
17
16
|
required: true
|
|
18
17
|
- type: input
|
|
@@ -21,30 +20,28 @@ body:
|
|
|
21
20
|
validations:
|
|
22
21
|
required: true
|
|
23
22
|
|
|
24
|
-
- type:
|
|
23
|
+
- type: checkboxes
|
|
25
24
|
attributes:
|
|
26
25
|
label: '💻 系统环境'
|
|
27
26
|
options:
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
- Other
|
|
27
|
+
- label: 'Windows'
|
|
28
|
+
- label: 'macOS'
|
|
29
|
+
- label: 'Ubuntu'
|
|
30
|
+
- label: 'Other Linux'
|
|
31
|
+
- label: 'iOS'
|
|
32
|
+
- label: 'Android'
|
|
33
|
+
- label: 'Other'
|
|
36
34
|
validations:
|
|
37
35
|
required: true
|
|
38
|
-
- type:
|
|
36
|
+
- type: checkboxes
|
|
39
37
|
attributes:
|
|
40
38
|
label: '🌐 浏览器'
|
|
41
39
|
options:
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
- Other
|
|
40
|
+
- label: 'Chrome'
|
|
41
|
+
- label: 'Edge'
|
|
42
|
+
- label: 'Safari'
|
|
43
|
+
- label: 'Firefox'
|
|
44
|
+
- label: 'Other'
|
|
48
45
|
validations:
|
|
49
46
|
required: true
|
|
50
47
|
- type: textarea
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.161.15](https://github.com/lobehub/lobe-chat/compare/v0.161.14...v0.161.15)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-05-24**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix avatar missing on client DB mode.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix avatar missing on client DB mode, closes [#2645](https://github.com/lobehub/lobe-chat/issues/2645) ([12726c2](https://github.com/lobehub/lobe-chat/commit/12726c2))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.161.14](https://github.com/lobehub/lobe-chat/compare/v0.161.13...v0.161.14)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2024-05-24**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **misc**: Refactor the global app status and fix PWA installer.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### Code refactoring
|
|
44
|
+
|
|
45
|
+
- **misc**: Refactor the global app status and fix PWA installer, closes [#2637](https://github.com/lobehub/lobe-chat/issues/2637) ([1f70305](https://github.com/lobehub/lobe-chat/commit/1f70305))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 0.161.13](https://github.com/lobehub/lobe-chat/compare/v0.161.12...v0.161.13)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2024-05-24**</sup>
|
package/README.md
CHANGED
|
@@ -262,14 +262,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
|
262
262
|
|
|
263
263
|
<!-- AGENT LIST -->
|
|
264
264
|
|
|
265
|
-
| Recent Submits | Description
|
|
266
|
-
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
267
|
-
| [
|
|
268
|
-
| [
|
|
269
|
-
| [
|
|
270
|
-
| [
|
|
271
|
-
|
|
272
|
-
> 📊 Total agents: [<kbd>**
|
|
265
|
+
| Recent Submits | Description |
|
|
266
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
267
|
+
| [Minecraft Command Instructor](https://chat-preview.lobehub.com/market?agent=mcse-helper)<br/><sup>By **[CLOT-LIU](https://github.com/CLOT-LIU)** on **2024-05-24**</sup> | Specializes in explaining and demonstrating Minecraft commands<br/>`minecraft` `commands` `explanation` `examples` |
|
|
268
|
+
| [Bahasa/English Translator](https://chat-preview.lobehub.com/market?agent=bahasa-translation)<br/><sup>By **[xenstar](https://github.com/xenstar)** on **2024-05-22**</sup> | Translates text into Bahasa or English, as needed<br/>`english` `translation` `writing` `bahasa` |
|
|
269
|
+
| [Zen Master](https://chat-preview.lobehub.com/market?agent=buddhism-master)<br/><sup>By **[epochaudio](https://github.com/epochaudio)** on **2024-05-22**</sup> | Well-versed in classics, adept at using Buddhism to guide life<br/>`buddhist-studies` `zen-buddhism` `buddhist-scripture-interpretation` `wisdom-q-a` |
|
|
270
|
+
| [Chinese Historian](https://chat-preview.lobehub.com/market?agent=chinese-historian)<br/><sup>By **[epochaudio](https://github.com/epochaudio)** on **2024-05-22**</sup> | Specialized in Chinese historical research, adept at applying ancient wisdom to analyze modern issues.<br/>`historical-research` `chinese-history` |
|
|
271
|
+
|
|
272
|
+
> 📊 Total agents: [<kbd>**270**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
273
273
|
|
|
274
274
|
<!-- AGENT LIST -->
|
|
275
275
|
|
package/README.zh-CN.md
CHANGED
|
@@ -252,12 +252,12 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
252
252
|
|
|
253
253
|
| 最近新增 | 助手说明 |
|
|
254
254
|
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
255
|
+
| [我的世界指令导师](https://chat-preview.lobehub.com/market?agent=mcse-helper)<br/><sup>By **[CLOT-LIU](https://github.com/CLOT-LIU)** on **2024-05-24**</sup> | 擅长解释和示范 “我的世界” 指令<br/>`我的世界` `指令` `解释` `示例` |
|
|
255
256
|
| [Bahasa/English Translator](https://chat-preview.lobehub.com/market?agent=bahasa-translation)<br/><sup>By **[xenstar](https://github.com/xenstar)** on **2024-05-22**</sup> | 根据需要将文本翻译成马来语或英语<br/>`英语` `翻译` `写作` `马来语` |
|
|
256
257
|
| [禅定法师](https://chat-preview.lobehub.com/market?agent=buddhism-master)<br/><sup>By **[epochaudio](https://github.com/epochaudio)** on **2024-05-22**</sup> | 熟读经典,善于运用佛法来指导人生<br/>`佛法佛法研究` `禅宗` `佛经解读` `智慧问答` |
|
|
257
258
|
| [中国历史学者](https://chat-preview.lobehub.com/market?agent=chinese-historian)<br/><sup>By **[epochaudio](https://github.com/epochaudio)** on **2024-05-22**</sup> | 专精于中国历史研究,擅长将古代智慧应用于现代问题分析<br/>`历史研究` `中国历史` |
|
|
258
|
-
| [儒家学者](https://chat-preview.lobehub.com/market?agent=confucian-sage)<br/><sup>By **[epochaudio](https://github.com/epochaudio)** on **2024-05-22**</sup> | 一名精通儒家经典且注重弘扬道义的学者<br/>`儒家学者` `道义弘扬者` |
|
|
259
259
|
|
|
260
|
-
> 📊 Total agents: [<kbd>**
|
|
260
|
+
> 📊 Total agents: [<kbd>**270**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
261
261
|
|
|
262
262
|
<!-- AGENT LIST -->
|
|
263
263
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.161.
|
|
3
|
+
"version": "0.161.15",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -95,10 +95,10 @@
|
|
|
95
95
|
"@google/generative-ai": "^0.11.3",
|
|
96
96
|
"@icons-pack/react-simple-icons": "^9.5.0",
|
|
97
97
|
"@khmyznikov/pwa-install": "^0.3.9",
|
|
98
|
-
"@lobehub/chat-plugin-sdk": "
|
|
99
|
-
"@lobehub/chat-plugins-gateway": "
|
|
98
|
+
"@lobehub/chat-plugin-sdk": "^1.32.3",
|
|
99
|
+
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
|
100
100
|
"@lobehub/icons": "^1.22.0",
|
|
101
|
-
"@lobehub/tts": "
|
|
101
|
+
"@lobehub/tts": "^1.24.1",
|
|
102
102
|
"@lobehub/ui": "^1.139.0",
|
|
103
103
|
"@microsoft/fetch-event-source": "^2.0.1",
|
|
104
104
|
"@next/third-parties": "^14.2.3",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
HEADER_HEIGHT,
|
|
11
11
|
} from '@/const/layoutTokens';
|
|
12
12
|
import { useGlobalStore } from '@/store/global';
|
|
13
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
13
14
|
|
|
14
15
|
import Footer from './Footer';
|
|
15
16
|
import Head from './Header';
|
|
@@ -19,8 +20,8 @@ const DesktopChatInput = memo(() => {
|
|
|
19
20
|
const [expand, setExpand] = useState<boolean>(false);
|
|
20
21
|
|
|
21
22
|
const [inputHeight, updatePreference] = useGlobalStore((s) => [
|
|
22
|
-
|
|
23
|
-
s.
|
|
23
|
+
systemStatusSelectors.inputHeight(s),
|
|
24
|
+
s.updateSystemStatus,
|
|
24
25
|
]);
|
|
25
26
|
|
|
26
27
|
return (
|
|
@@ -15,6 +15,7 @@ import { useAgentStore } from '@/store/agent';
|
|
|
15
15
|
import { agentSelectors } from '@/store/agent/selectors';
|
|
16
16
|
import { useGlobalStore } from '@/store/global';
|
|
17
17
|
import { ChatSettingsTabs } from '@/store/global/initialState';
|
|
18
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
18
19
|
import { useSessionStore } from '@/store/session';
|
|
19
20
|
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
20
21
|
|
|
@@ -35,7 +36,7 @@ const SystemRole = memo(() => {
|
|
|
35
36
|
]);
|
|
36
37
|
|
|
37
38
|
const [showSystemRole, toggleSystemRole] = useGlobalStore((s) => [
|
|
38
|
-
|
|
39
|
+
systemStatusSelectors.showSystemRole(s),
|
|
39
40
|
s.toggleSystemRole,
|
|
40
41
|
]);
|
|
41
42
|
|
|
@@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
7
7
|
|
|
8
8
|
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
9
9
|
import { useGlobalStore } from '@/store/global';
|
|
10
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
10
11
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
11
12
|
|
|
12
13
|
import SettingButton from '../../../features/SettingButton';
|
|
@@ -16,7 +17,7 @@ const HeaderAction = memo(() => {
|
|
|
16
17
|
const { t } = useTranslation('chat');
|
|
17
18
|
|
|
18
19
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
19
|
-
|
|
20
|
+
systemStatusSelectors.showChatSideBar(s),
|
|
20
21
|
s.toggleChatSideBar,
|
|
21
22
|
]);
|
|
22
23
|
|
|
@@ -7,13 +7,14 @@ import { Suspense, memo } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { Flexbox } from 'react-layout-kit';
|
|
9
9
|
|
|
10
|
-
import { useInitAgentConfig } from '@/app/(main)/chat/(workspace)/_layout/useInitAgentConfig';
|
|
11
10
|
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
12
11
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
13
12
|
import { useGlobalStore } from '@/store/global';
|
|
13
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
14
14
|
import { useSessionStore } from '@/store/session';
|
|
15
15
|
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
16
16
|
|
|
17
|
+
import { useInitAgentConfig } from '../../useInitAgentConfig';
|
|
17
18
|
import Tags from './Tags';
|
|
18
19
|
|
|
19
20
|
const Main = memo(() => {
|
|
@@ -34,7 +35,8 @@ const Main = memo(() => {
|
|
|
34
35
|
|
|
35
36
|
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
36
37
|
const displayDesc = isInbox ? t('inbox.desc') : description;
|
|
37
|
-
const showSessionPanel = useGlobalStore(
|
|
38
|
+
const showSessionPanel = useGlobalStore(systemStatusSelectors.showSessionPanel);
|
|
39
|
+
const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
|
|
38
40
|
|
|
39
41
|
return !init ? (
|
|
40
42
|
<Flexbox horizontal>
|
|
@@ -52,10 +54,9 @@ const Main = memo(() => {
|
|
|
52
54
|
aria-label={t('agentsAndConversations')}
|
|
53
55
|
icon={showSessionPanel ? PanelLeftClose : PanelLeftOpen}
|
|
54
56
|
onClick={() => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
showSessionPanel: !currentShowSessionPanel,
|
|
57
|
+
updateSystemStatus({
|
|
58
|
+
sessionsWidth: showSessionPanel ? 0 : 320,
|
|
59
|
+
showSessionPanel: !showSessionPanel,
|
|
59
60
|
});
|
|
60
61
|
}}
|
|
61
62
|
size={DESKTOP_HEADER_ICON_SIZE}
|
|
@@ -8,6 +8,7 @@ import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
|
|
8
8
|
import SafeSpacing from '@/components/SafeSpacing';
|
|
9
9
|
import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
|
|
10
10
|
import { useGlobalStore } from '@/store/global';
|
|
11
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
11
12
|
|
|
12
13
|
const useStyles = createStyles(({ css, token }) => ({
|
|
13
14
|
content: css`
|
|
@@ -28,10 +29,10 @@ const TopicPanel = memo(({ children }: PropsWithChildren) => {
|
|
|
28
29
|
const { styles } = useStyles();
|
|
29
30
|
const { md = true, lg = true } = useResponsive();
|
|
30
31
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
31
|
-
|
|
32
|
+
systemStatusSelectors.showChatSideBar(s),
|
|
32
33
|
s.toggleChatSideBar,
|
|
33
|
-
s.isPreferenceInit,
|
|
34
34
|
]);
|
|
35
|
+
|
|
35
36
|
const [cacheExpand, setCacheExpand] = useState<boolean>(Boolean(showAgentSettings));
|
|
36
37
|
|
|
37
38
|
const handleExpand = (expand: boolean) => {
|
|
@@ -5,12 +5,13 @@ import { PropsWithChildren, memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { useGlobalStore } from '@/store/global';
|
|
8
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
8
9
|
|
|
9
10
|
import { useWorkspaceModal } from '../../features/useWorkspaceModal';
|
|
10
11
|
|
|
11
12
|
const Topics = memo(({ children }: PropsWithChildren) => {
|
|
12
13
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
13
|
-
|
|
14
|
+
systemStatusSelectors.mobileShowTopic(s),
|
|
14
15
|
s.toggleMobileTopic,
|
|
15
16
|
]);
|
|
16
17
|
const [open, setOpen] = useWorkspaceModal(showAgentSettings, toggleConfig);
|
|
@@ -4,7 +4,7 @@ import { memo, useMemo, useState } from 'react';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { useGlobalStore } from '@/store/global';
|
|
7
|
-
import {
|
|
7
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
8
8
|
import { useSessionStore } from '@/store/session';
|
|
9
9
|
import { sessionSelectors } from '@/store/session/selectors';
|
|
10
10
|
import { SessionDefaultGroup } from '@/types/session';
|
|
@@ -30,9 +30,9 @@ const DefaultMode = memo(() => {
|
|
|
30
30
|
const customSessionGroups = useSessionStore(sessionSelectors.customSessionGroups, isEqual);
|
|
31
31
|
const pinnedSessions = useSessionStore(sessionSelectors.pinnedSessions, isEqual);
|
|
32
32
|
|
|
33
|
-
const [sessionGroupKeys,
|
|
34
|
-
|
|
35
|
-
s.
|
|
33
|
+
const [sessionGroupKeys, updateSystemStatus] = useGlobalStore((s) => [
|
|
34
|
+
systemStatusSelectors.sessionGroupKeys(s),
|
|
35
|
+
s.updateSystemStatus,
|
|
36
36
|
]);
|
|
37
37
|
|
|
38
38
|
const items = useMemo(
|
|
@@ -80,7 +80,7 @@ const DefaultMode = memo(() => {
|
|
|
80
80
|
onChange={(keys) => {
|
|
81
81
|
const expandSessionGroupKeys = typeof keys === 'string' ? [keys] : keys;
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
updateSystemStatus({ expandSessionGroupKeys });
|
|
84
84
|
}}
|
|
85
85
|
/>
|
|
86
86
|
{activeGroupId && (
|
|
@@ -7,6 +7,7 @@ import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { FOLDER_WIDTH } from '@/const/layoutTokens';
|
|
9
9
|
import { useGlobalStore } from '@/store/global';
|
|
10
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
10
11
|
|
|
11
12
|
export const useStyles = createStyles(({ css, token }) => ({
|
|
12
13
|
panel: css`
|
|
@@ -21,10 +22,11 @@ const SessionPanel = memo<PropsWithChildren>(({ children }) => {
|
|
|
21
22
|
|
|
22
23
|
const { styles } = useStyles();
|
|
23
24
|
const [sessionsWidth, sessionExpandable, updatePreference] = useGlobalStore((s) => [
|
|
24
|
-
s
|
|
25
|
-
|
|
26
|
-
s.
|
|
25
|
+
systemStatusSelectors.sessionWidth(s),
|
|
26
|
+
systemStatusSelectors.showSessionPanel(s),
|
|
27
|
+
s.updateSystemStatus,
|
|
27
28
|
]);
|
|
29
|
+
|
|
28
30
|
const [cacheExpand, setCacheExpand] = useState<boolean>(Boolean(sessionExpandable));
|
|
29
31
|
const [tmpWidth, setWidth] = useState(sessionsWidth);
|
|
30
32
|
if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth);
|
|
@@ -6,6 +6,8 @@ import { SessionGroups } from '@/types/session';
|
|
|
6
6
|
import { DB_SessionGroup } from '../../schemas/sessionGroup';
|
|
7
7
|
import { SessionGroupModel } from '../sessionGroup';
|
|
8
8
|
|
|
9
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
|
|
9
11
|
describe('SessionGroupModel', () => {
|
|
10
12
|
let sessionGroupData: DB_SessionGroup;
|
|
11
13
|
|
|
@@ -135,9 +137,14 @@ describe('SessionGroupModel', () => {
|
|
|
135
137
|
|
|
136
138
|
it('should return session groups sorted by sort field first and then by createdAt', async () => {
|
|
137
139
|
const group0 = await SessionGroupModel.create('group0');
|
|
140
|
+
await sleep(10);
|
|
138
141
|
const group1 = await SessionGroupModel.create('group1', 1);
|
|
142
|
+
await sleep(10);
|
|
139
143
|
const group2 = await SessionGroupModel.create('group2');
|
|
144
|
+
await sleep(10);
|
|
140
145
|
const group3 = await SessionGroupModel.create('group3', 2);
|
|
146
|
+
await sleep(10);
|
|
147
|
+
|
|
141
148
|
const fetchedGroups = await SessionGroupModel.query();
|
|
142
149
|
expect(fetchedGroups[0].id).toEqual(group1.id);
|
|
143
150
|
expect(fetchedGroups[1].id).toEqual(group3.id);
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import dynamic from 'next/dynamic';
|
|
4
|
-
import { memo } from 'react';
|
|
4
|
+
import { memo, useEffect, useLayoutEffect } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { PWA_INSTALL_ID } from '@/const/layoutTokens';
|
|
8
|
+
import { usePWAInstall } from '@/hooks/usePWAInstall';
|
|
8
9
|
import { usePlatform } from '@/hooks/usePlatform';
|
|
10
|
+
import { useGlobalStore } from '@/store/global';
|
|
11
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
12
|
+
import { useUserStore } from '@/store/user';
|
|
9
13
|
|
|
10
14
|
// @ts-ignore
|
|
11
15
|
const PWA: any = dynamic(() => import('@khmyznikov/pwa-install/dist/pwa-install.react.js'), {
|
|
@@ -15,6 +19,52 @@ const PWA: any = dynamic(() => import('@khmyznikov/pwa-install/dist/pwa-install.
|
|
|
15
19
|
const PWAInstall = memo(() => {
|
|
16
20
|
const { t } = useTranslation('metadata');
|
|
17
21
|
const { isPWA } = usePlatform();
|
|
22
|
+
|
|
23
|
+
const { install, canInstall } = usePWAInstall();
|
|
24
|
+
|
|
25
|
+
const isShowPWAGuide = useUserStore((s) => s.isShowPWAGuide);
|
|
26
|
+
const [hidePWAInstaller, updateSystemStatus] = useGlobalStore((s) => [
|
|
27
|
+
systemStatusSelectors.hidePWAInstaller(s),
|
|
28
|
+
s.updateSystemStatus,
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
// we need to make the pwa installer hidden by default
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
sessionStorage.setItem('pwa-hide-install', 'true');
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const pwaInstall =
|
|
37
|
+
// eslint-disable-next-line unicorn/prefer-query-selector
|
|
38
|
+
typeof window === 'undefined' ? undefined : document.getElementById(PWA_INSTALL_ID);
|
|
39
|
+
|
|
40
|
+
// add an event listener to control the user close installer action
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!pwaInstall) return;
|
|
43
|
+
|
|
44
|
+
const handler = (e: Event) => {
|
|
45
|
+
const event = e as CustomEvent;
|
|
46
|
+
|
|
47
|
+
// it means user hide installer
|
|
48
|
+
if (event.detail.message === 'dismissed') {
|
|
49
|
+
updateSystemStatus({ hidePWAInstaller: true });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
pwaInstall.addEventListener('pwa-user-choice-result-event', handler);
|
|
54
|
+
return () => {
|
|
55
|
+
pwaInstall.removeEventListener('pwa-user-choice-result-event', handler);
|
|
56
|
+
};
|
|
57
|
+
}, [pwaInstall]);
|
|
58
|
+
|
|
59
|
+
// trigger the PWA guide on demand
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!canInstall || hidePWAInstaller) return;
|
|
62
|
+
|
|
63
|
+
if (isShowPWAGuide) {
|
|
64
|
+
install();
|
|
65
|
+
}
|
|
66
|
+
}, [canInstall, hidePWAInstaller, isShowPWAGuide]);
|
|
67
|
+
|
|
18
68
|
if (isPWA) return null;
|
|
19
69
|
return <PWA description={t('chat.description')} id={PWA_INSTALL_ID} />;
|
|
20
70
|
});
|
package/src/hooks/useSyncData.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { useChatStore } from '@/store/chat';
|
|
|
4
4
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
5
5
|
import { useSessionStore } from '@/store/session';
|
|
6
6
|
import { useUserStore } from '@/store/user';
|
|
7
|
-
import { syncSettingsSelectors } from '@/store/user/selectors';
|
|
7
|
+
import { syncSettingsSelectors, userProfileSelectors } from '@/store/user/selectors';
|
|
8
8
|
|
|
9
9
|
export const useSyncEvent = () => {
|
|
10
10
|
const [refreshMessages, refreshTopic] = useChatStore((s) => [s.refreshMessages, s.refreshTopic]);
|
|
@@ -38,7 +38,7 @@ export const useSyncEvent = () => {
|
|
|
38
38
|
|
|
39
39
|
export const useEnabledDataSync = () => {
|
|
40
40
|
const [userId, userEnableSync, useEnabledSync] = useUserStore((s) => [
|
|
41
|
-
|
|
41
|
+
userProfileSelectors.userId(s),
|
|
42
42
|
syncSettingsSelectors.enableWebRTC(s),
|
|
43
43
|
s.useEnabledSync,
|
|
44
44
|
]);
|
|
@@ -27,7 +27,6 @@ const UserUpdater = memo(() => {
|
|
|
27
27
|
useStoreUpdater('isLoaded', isLoaded);
|
|
28
28
|
useStoreUpdater('user', lobeUser);
|
|
29
29
|
useStoreUpdater('isSignedIn', isSignedIn);
|
|
30
|
-
useStoreUpdater('userId', user?.id);
|
|
31
30
|
|
|
32
31
|
useStoreUpdater('clerkUser', user);
|
|
33
32
|
useStoreUpdater('clerkSession', session);
|
|
@@ -24,11 +24,11 @@ const StoreInitialization = memo(() => {
|
|
|
24
24
|
|
|
25
25
|
const { serverConfig } = useServerConfigStore();
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const useInitSystemStatus = useGlobalStore((s) => s.useInitSystemStatus);
|
|
28
28
|
|
|
29
29
|
const useFetchDefaultAgentConfig = useAgentStore((s) => s.useFetchDefaultAgentConfig);
|
|
30
30
|
// init the system preference
|
|
31
|
-
|
|
31
|
+
useInitSystemStatus();
|
|
32
32
|
useFetchDefaultAgentConfig();
|
|
33
33
|
|
|
34
34
|
useInitUserState(isLogin, serverConfig, {
|
|
@@ -34,11 +34,11 @@ describe('createPreferenceSlice', () => {
|
|
|
34
34
|
const { result } = renderHook(() => useGlobalStore());
|
|
35
35
|
|
|
36
36
|
act(() => {
|
|
37
|
-
useGlobalStore.getState().
|
|
37
|
+
useGlobalStore.getState().updateSystemStatus({ showChatSideBar: false });
|
|
38
38
|
result.current.toggleChatSideBar();
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
expect(result.current.
|
|
41
|
+
expect(result.current.status.showChatSideBar).toBe(true);
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
44
|
|
|
@@ -48,10 +48,11 @@ describe('createPreferenceSlice', () => {
|
|
|
48
48
|
const groupId = 'group-id';
|
|
49
49
|
|
|
50
50
|
act(() => {
|
|
51
|
+
useGlobalStore.setState({ isStatusInit: true });
|
|
51
52
|
result.current.toggleExpandSessionGroup(groupId, true);
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
expect(result.current.
|
|
55
|
+
expect(result.current.status.expandSessionGroupKeys).toContain(groupId);
|
|
55
56
|
});
|
|
56
57
|
});
|
|
57
58
|
|
|
@@ -60,10 +61,11 @@ describe('createPreferenceSlice', () => {
|
|
|
60
61
|
const { result } = renderHook(() => useGlobalStore());
|
|
61
62
|
|
|
62
63
|
act(() => {
|
|
64
|
+
useGlobalStore.setState({ isStatusInit: true });
|
|
63
65
|
result.current.toggleMobileTopic();
|
|
64
66
|
});
|
|
65
67
|
|
|
66
|
-
expect(result.current.
|
|
68
|
+
expect(result.current.status.mobileShowTopic).toBe(true);
|
|
67
69
|
});
|
|
68
70
|
});
|
|
69
71
|
|
|
@@ -72,23 +74,24 @@ describe('createPreferenceSlice', () => {
|
|
|
72
74
|
const { result } = renderHook(() => useGlobalStore());
|
|
73
75
|
|
|
74
76
|
act(() => {
|
|
77
|
+
useGlobalStore.setState({ isStatusInit: true });
|
|
75
78
|
result.current.toggleSystemRole(true);
|
|
76
79
|
});
|
|
77
80
|
|
|
78
|
-
expect(result.current.
|
|
81
|
+
expect(result.current.status.showSystemRole).toBe(true);
|
|
79
82
|
});
|
|
80
83
|
});
|
|
81
84
|
|
|
82
85
|
describe('updatePreference', () => {
|
|
83
|
-
it('should update
|
|
86
|
+
it('should update status', () => {
|
|
84
87
|
const { result } = renderHook(() => useGlobalStore());
|
|
85
|
-
const
|
|
88
|
+
const status = { inputHeight: 200 };
|
|
86
89
|
|
|
87
90
|
act(() => {
|
|
88
|
-
result.current.
|
|
91
|
+
result.current.updateSystemStatus(status);
|
|
89
92
|
});
|
|
90
93
|
|
|
91
|
-
expect(result.current.
|
|
94
|
+
expect(result.current.status.inputHeight).toEqual(200);
|
|
92
95
|
});
|
|
93
96
|
});
|
|
94
97
|
|
|
@@ -144,13 +147,12 @@ describe('createPreferenceSlice', () => {
|
|
|
144
147
|
});
|
|
145
148
|
|
|
146
149
|
describe('useInitGlobalPreference', () => {
|
|
147
|
-
it('should init global
|
|
148
|
-
vi.spyOn(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
).mockReturnValueOnce({} as any);
|
|
150
|
+
it('should init global status if there is empty object', async () => {
|
|
151
|
+
vi.spyOn(useGlobalStore.getState().statusStorage, 'getFromLocalStorage').mockReturnValueOnce(
|
|
152
|
+
{} as any,
|
|
153
|
+
);
|
|
152
154
|
|
|
153
|
-
const { result } = renderHook(() => useGlobalStore().
|
|
155
|
+
const { result } = renderHook(() => useGlobalStore().useInitSystemStatus(), {
|
|
154
156
|
wrapper: withSWR,
|
|
155
157
|
});
|
|
156
158
|
|
|
@@ -158,17 +160,16 @@ describe('createPreferenceSlice', () => {
|
|
|
158
160
|
expect(result.current.data).toEqual({});
|
|
159
161
|
});
|
|
160
162
|
|
|
161
|
-
expect(useGlobalStore.getState().
|
|
163
|
+
expect(useGlobalStore.getState().status).toEqual(initialState.status);
|
|
162
164
|
});
|
|
163
165
|
|
|
164
166
|
it('should update with data', async () => {
|
|
165
167
|
const { result } = renderHook(() => useGlobalStore());
|
|
166
|
-
vi.spyOn(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
).mockReturnValueOnce({ inputHeight: 300 } as any);
|
|
168
|
+
vi.spyOn(useGlobalStore.getState().statusStorage, 'getFromLocalStorage').mockReturnValueOnce({
|
|
169
|
+
inputHeight: 300,
|
|
170
|
+
} as any);
|
|
170
171
|
|
|
171
|
-
const { result: hooks } = renderHook(() => result.current.
|
|
172
|
+
const { result: hooks } = renderHook(() => result.current.useInitSystemStatus(), {
|
|
172
173
|
wrapper: withSWR,
|
|
173
174
|
});
|
|
174
175
|
|
|
@@ -176,7 +177,7 @@ describe('createPreferenceSlice', () => {
|
|
|
176
177
|
expect(hooks.current.data).toEqual({ inputHeight: 300 });
|
|
177
178
|
});
|
|
178
179
|
|
|
179
|
-
expect(result.current.
|
|
180
|
+
expect(result.current.status.inputHeight).toEqual(300);
|
|
180
181
|
});
|
|
181
182
|
});
|
|
182
183
|
});
|
|
@@ -13,9 +13,9 @@ import type { GlobalStore } from '@/store/global/index';
|
|
|
13
13
|
import { merge } from '@/utils/merge';
|
|
14
14
|
import { setNamespace } from '@/utils/storeDebug';
|
|
15
15
|
|
|
16
|
-
import type {
|
|
16
|
+
import type { SystemStatus } from './initialState';
|
|
17
17
|
|
|
18
|
-
const n = setNamespace('
|
|
18
|
+
const n = setNamespace('g');
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* 设置操作
|
|
@@ -26,9 +26,9 @@ export interface GlobalStoreAction {
|
|
|
26
26
|
toggleExpandSessionGroup: (id: string, expand: boolean) => void;
|
|
27
27
|
toggleMobileTopic: (visible?: boolean) => void;
|
|
28
28
|
toggleSystemRole: (visible?: boolean) => void;
|
|
29
|
-
|
|
29
|
+
updateSystemStatus: (status: Partial<SystemStatus>, action?: any) => void;
|
|
30
30
|
useCheckLatestVersion: (enabledCheck?: boolean) => SWRResponse<string>;
|
|
31
|
-
|
|
31
|
+
useInitSystemStatus: () => SWRResponse;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export const globalActionSlice: StateCreator<
|
|
@@ -42,13 +42,13 @@ export const globalActionSlice: StateCreator<
|
|
|
42
42
|
},
|
|
43
43
|
toggleChatSideBar: (newValue) => {
|
|
44
44
|
const showChatSideBar =
|
|
45
|
-
typeof newValue === 'boolean' ? newValue : !get().
|
|
45
|
+
typeof newValue === 'boolean' ? newValue : !get().status.showChatSideBar;
|
|
46
46
|
|
|
47
|
-
get().
|
|
47
|
+
get().updateSystemStatus({ showChatSideBar }, n('toggleAgentPanel', newValue));
|
|
48
48
|
},
|
|
49
49
|
toggleExpandSessionGroup: (id, expand) => {
|
|
50
|
-
const {
|
|
51
|
-
const nextExpandSessionGroup = produce(
|
|
50
|
+
const { status } = get();
|
|
51
|
+
const nextExpandSessionGroup = produce(status.expandSessionGroupKeys, (draft: string[]) => {
|
|
52
52
|
if (expand) {
|
|
53
53
|
if (draft.includes(id)) return;
|
|
54
54
|
draft.push(id);
|
|
@@ -57,26 +57,29 @@ export const globalActionSlice: StateCreator<
|
|
|
57
57
|
if (index !== -1) draft.splice(index, 1);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
|
-
get().
|
|
60
|
+
get().updateSystemStatus({ expandSessionGroupKeys: nextExpandSessionGroup });
|
|
61
61
|
},
|
|
62
62
|
toggleMobileTopic: (newValue) => {
|
|
63
63
|
const mobileShowTopic =
|
|
64
|
-
typeof newValue === 'boolean' ? newValue : !get().
|
|
64
|
+
typeof newValue === 'boolean' ? newValue : !get().status.mobileShowTopic;
|
|
65
65
|
|
|
66
|
-
get().
|
|
66
|
+
get().updateSystemStatus({ mobileShowTopic }, n('toggleMobileTopic', newValue));
|
|
67
67
|
},
|
|
68
68
|
toggleSystemRole: (newValue) => {
|
|
69
|
-
const showSystemRole =
|
|
70
|
-
typeof newValue === 'boolean' ? newValue : !get().preference.mobileShowTopic;
|
|
69
|
+
const showSystemRole = typeof newValue === 'boolean' ? newValue : !get().status.mobileShowTopic;
|
|
71
70
|
|
|
72
|
-
get().
|
|
71
|
+
get().updateSystemStatus({ showSystemRole }, n('toggleMobileTopic', newValue));
|
|
73
72
|
},
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
updateSystemStatus: (status, action) => {
|
|
74
|
+
// Status cannot be modified when it is not initialized
|
|
75
|
+
if (!get().isStatusInit) return;
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
const nextStatus = merge(get().status, status);
|
|
78
|
+
if (isEqual(get().status, nextStatus)) return;
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
set({ status: nextStatus }, false, action || n('updateSystemStatus'));
|
|
81
|
+
|
|
82
|
+
get().statusStorage.saveToLocalStorage(nextStatus);
|
|
80
83
|
},
|
|
81
84
|
|
|
82
85
|
useCheckLatestVersion: (enabledCheck = true) =>
|
|
@@ -89,19 +92,15 @@ export const globalActionSlice: StateCreator<
|
|
|
89
92
|
},
|
|
90
93
|
}),
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
useClientDataSWR<
|
|
94
|
-
'
|
|
95
|
-
() => get().
|
|
95
|
+
useInitSystemStatus: () =>
|
|
96
|
+
useClientDataSWR<SystemStatus>(
|
|
97
|
+
'initSystemStatus',
|
|
98
|
+
() => get().statusStorage.getFromLocalStorage(),
|
|
96
99
|
{
|
|
97
|
-
onSuccess: (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
set({ isPreferenceInit: true });
|
|
101
|
-
|
|
102
|
-
if (isEqual(get().preference, nextPreference)) return;
|
|
100
|
+
onSuccess: (status) => {
|
|
101
|
+
set({ isStatusInit: true }, false, 'setStatusInit');
|
|
103
102
|
|
|
104
|
-
|
|
103
|
+
get().updateSystemStatus(status, 'initSystemStatus');
|
|
105
104
|
},
|
|
106
105
|
},
|
|
107
106
|
),
|
|
@@ -29,9 +29,10 @@ export enum SettingsTabs {
|
|
|
29
29
|
TTS = 'tts',
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export interface
|
|
32
|
+
export interface SystemStatus {
|
|
33
33
|
// which sessionGroup should expand
|
|
34
34
|
expandSessionGroupKeys: string[];
|
|
35
|
+
hidePWAInstaller?: boolean;
|
|
35
36
|
inputHeight: number;
|
|
36
37
|
mobileShowTopic?: boolean;
|
|
37
38
|
sessionsWidth: number;
|
|
@@ -40,37 +41,32 @@ export interface GlobalPreference {
|
|
|
40
41
|
showSystemRole?: boolean;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
export interface
|
|
44
|
-
/**
|
|
45
|
-
* the user preference, which only store in local storage
|
|
46
|
-
*/
|
|
47
|
-
preference: GlobalPreference;
|
|
48
|
-
preferenceStorage: AsyncLocalStorage<GlobalPreference>;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface GlobalCommonState {
|
|
44
|
+
export interface GlobalState {
|
|
52
45
|
hasNewVersion?: boolean;
|
|
53
46
|
isMobile?: boolean;
|
|
54
|
-
|
|
47
|
+
isStatusInit?: boolean;
|
|
55
48
|
latestVersion?: string;
|
|
56
49
|
router?: AppRouterInstance;
|
|
57
50
|
sidebarKey: SidebarTabKey;
|
|
51
|
+
status: SystemStatus;
|
|
52
|
+
statusStorage: AsyncLocalStorage<SystemStatus>;
|
|
58
53
|
}
|
|
59
54
|
|
|
60
|
-
export
|
|
55
|
+
export const INITIAL_STATUS = {
|
|
56
|
+
expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default],
|
|
57
|
+
hidePWAInstaller: false,
|
|
58
|
+
inputHeight: 200,
|
|
59
|
+
mobileShowTopic: false,
|
|
60
|
+
sessionsWidth: 320,
|
|
61
|
+
showChatSideBar: true,
|
|
62
|
+
showSessionPanel: true,
|
|
63
|
+
showSystemRole: false,
|
|
64
|
+
} satisfies SystemStatus;
|
|
61
65
|
|
|
62
66
|
export const initialState: GlobalState = {
|
|
63
67
|
isMobile: false,
|
|
64
|
-
|
|
65
|
-
preference: {
|
|
66
|
-
expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default],
|
|
67
|
-
inputHeight: 200,
|
|
68
|
-
mobileShowTopic: false,
|
|
69
|
-
sessionsWidth: 320,
|
|
70
|
-
showChatSideBar: true,
|
|
71
|
-
showSessionPanel: true,
|
|
72
|
-
showSystemRole: false,
|
|
73
|
-
},
|
|
74
|
-
preferenceStorage: new AsyncLocalStorage('LOBE_GLOBAL_PREFERENCE'),
|
|
68
|
+
isStatusInit: false,
|
|
75
69
|
sidebarKey: SidebarTabKey.Chat,
|
|
70
|
+
status: INITIAL_STATUS,
|
|
71
|
+
statusStorage: new AsyncLocalStorage('LOBE_SYSTEM_STATUS'),
|
|
76
72
|
};
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import { GlobalStore } from '@/store/global';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { INITIAL_STATUS } from './initialState';
|
|
3
4
|
|
|
4
5
|
const sessionGroupKeys = (s: GlobalStore): string[] =>
|
|
5
|
-
s.
|
|
6
|
+
s.status.expandSessionGroupKeys || INITIAL_STATUS.expandSessionGroupKeys;
|
|
7
|
+
|
|
8
|
+
const showSystemRole = (s: GlobalStore) => s.status.showSystemRole;
|
|
9
|
+
const mobileShowTopic = (s: GlobalStore) => s.status.mobileShowTopic;
|
|
10
|
+
const showChatSideBar = (s: GlobalStore) => s.status.showChatSideBar;
|
|
11
|
+
const showSessionPanel = (s: GlobalStore) => s.status.showSessionPanel;
|
|
12
|
+
const hidePWAInstaller = (s: GlobalStore) => s.status.hidePWAInstaller;
|
|
13
|
+
|
|
14
|
+
const sessionWidth = (s: GlobalStore) => s.status.sessionsWidth;
|
|
15
|
+
const inputHeight = (s: GlobalStore) => s.status.inputHeight;
|
|
6
16
|
|
|
7
|
-
export const
|
|
17
|
+
export const systemStatusSelectors = {
|
|
18
|
+
hidePWAInstaller,
|
|
19
|
+
inputHeight,
|
|
20
|
+
mobileShowTopic,
|
|
8
21
|
sessionGroupKeys,
|
|
22
|
+
sessionWidth,
|
|
23
|
+
showChatSideBar,
|
|
24
|
+
showSessionPanel,
|
|
25
|
+
showSystemRole,
|
|
9
26
|
};
|
|
@@ -10,11 +10,6 @@ import {
|
|
|
10
10
|
import { LobeUser } from '@/types/user';
|
|
11
11
|
|
|
12
12
|
export interface UserAuthState {
|
|
13
|
-
/**
|
|
14
|
-
* 未来收到 user.avatar 中
|
|
15
|
-
* @deprecated
|
|
16
|
-
*/
|
|
17
|
-
avatar?: string;
|
|
18
13
|
clerkOpenUserProfile?: (props?: UserProfileProps) => void;
|
|
19
14
|
|
|
20
15
|
clerkSession?: ActiveSessionResource;
|
|
@@ -28,7 +23,6 @@ export interface UserAuthState {
|
|
|
28
23
|
nextSession?: Session;
|
|
29
24
|
nextUser?: User;
|
|
30
25
|
user?: LobeUser;
|
|
31
|
-
userId?: string;
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
export const initialAuthState: UserAuthState = {};
|
|
@@ -24,8 +24,8 @@ const username = (s: UserStore) => {
|
|
|
24
24
|
|
|
25
25
|
export const userProfileSelectors = {
|
|
26
26
|
nickName,
|
|
27
|
-
userAvatar: (s: UserStore): string => s.user?.avatar ||
|
|
28
|
-
userId: (s: UserStore) => s.
|
|
27
|
+
userAvatar: (s: UserStore): string => s.user?.avatar || '',
|
|
28
|
+
userId: (s: UserStore) => s.user?.id,
|
|
29
29
|
userProfile: (s: UserStore): LobeUser | null | undefined => s.user,
|
|
30
30
|
username,
|
|
31
31
|
};
|
|
@@ -105,7 +105,7 @@ describe('createCommonSlice', () => {
|
|
|
105
105
|
await waitFor(() => expect(result.current.data).toEqual(mockUserState));
|
|
106
106
|
|
|
107
107
|
// 验证状态是否正确更新
|
|
108
|
-
expect(useUserStore.getState().avatar).toBe(mockUserState.avatar);
|
|
108
|
+
expect(useUserStore.getState().user?.avatar).toBe(mockUserState.avatar);
|
|
109
109
|
expect(useUserStore.getState().settings).toEqual(mockUserState.settings);
|
|
110
110
|
expect(successCallback).toHaveBeenCalledWith(mockUserState);
|
|
111
111
|
|
|
@@ -183,13 +183,14 @@ describe('createCommonSlice', () => {
|
|
|
183
183
|
});
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
-
it('should handle the case when user
|
|
186
|
+
it('should handle the case when user state have avatar', async () => {
|
|
187
187
|
const { result } = renderHook(() => useUserStore());
|
|
188
188
|
const mockUserState: UserInitializationState = {
|
|
189
189
|
userId: 'user-id',
|
|
190
190
|
isOnboard: true,
|
|
191
191
|
preference: undefined as any,
|
|
192
192
|
settings: null as any,
|
|
193
|
+
avatar: 'abc',
|
|
193
194
|
};
|
|
194
195
|
|
|
195
196
|
vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
|
|
@@ -202,7 +203,7 @@ describe('createCommonSlice', () => {
|
|
|
202
203
|
await waitFor(() => {
|
|
203
204
|
expect(result.current.isUserStateInit).toBeTruthy();
|
|
204
205
|
// 验证状态未被错误更新
|
|
205
|
-
expect(result.current.avatar).
|
|
206
|
+
expect(result.current.user?.avatar).toEqual('abc');
|
|
206
207
|
expect(result.current.settings).toEqual({});
|
|
207
208
|
});
|
|
208
209
|
});
|
|
@@ -88,6 +88,12 @@ export const createCommonSlice: StateCreator<
|
|
|
88
88
|
const isEmpty = Object.keys(data.preference || {}).length === 0;
|
|
89
89
|
const preference = isEmpty ? DEFAULT_PREFERENCE : data.preference;
|
|
90
90
|
|
|
91
|
+
// if there is avatar or userId (from client DB), update it into user
|
|
92
|
+
const user =
|
|
93
|
+
data.avatar || data.userId
|
|
94
|
+
? merge(get().user, { avatar: data.avatar, id: data.userId })
|
|
95
|
+
: get().user;
|
|
96
|
+
|
|
91
97
|
set(
|
|
92
98
|
{
|
|
93
99
|
defaultSettings,
|
|
@@ -97,11 +103,10 @@ export const createCommonSlice: StateCreator<
|
|
|
97
103
|
isUserCanEnableTrace: data.canEnableTrace,
|
|
98
104
|
isUserHasConversation: data.hasConversation,
|
|
99
105
|
isUserStateInit: true,
|
|
100
|
-
|
|
101
106
|
preference,
|
|
102
107
|
serverLanguageModel: serverConfig.languageModel,
|
|
103
108
|
settings: data.settings || {},
|
|
104
|
-
|
|
109
|
+
user,
|
|
105
110
|
},
|
|
106
111
|
false,
|
|
107
112
|
n('initUserState'),
|
package/src/types/user/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ const PREV_KEY = 'LOBE_GLOBAL';
|
|
|
2
2
|
|
|
3
3
|
// LOBE_PREFERENCE for userStore
|
|
4
4
|
// LOBE_GLOBAL_PREFERENCE for globalStore
|
|
5
|
-
type StorageKey = 'LOBE_PREFERENCE' | '
|
|
5
|
+
type StorageKey = 'LOBE_PREFERENCE' | 'LOBE_SYSTEM_STATUS';
|
|
6
6
|
|
|
7
7
|
export class AsyncLocalStorage<State> {
|
|
8
8
|
private storageKey: StorageKey;
|