From 19117388f621b65c812b6328ccb9266bd9b17236 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Sun, 28 Apr 2024 23:28:17 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20rename=20globa?= =?UTF-8?q?lStore=20to=20userStore=20(#2264)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ refactor: rename globalStore to userStore * ♻️ refactor: refactor the globalStore usage * ♻️ refactor: clean the user preference * 🐛 fix: fix preference init * ♻️ refactor: refactor the global Store * 🎨 chore: improve code --- .../(desktop)/features/ChatHeader/Tags.tsx | 6 +- .../features/ChatInput/Footer/DragUpload.tsx | 6 +- .../features/ChatInput/Footer/SendMore.tsx | 6 +- .../features/ChatInput/Footer/index.tsx | 6 +- .../features/ChatInput/TextArea.test.tsx | 10 +- .../(desktop)/features/ChatInput/TextArea.tsx | 6 +- .../chat/(mobile)/features/SessionHeader.tsx | 6 +- .../chat/features/ShareButton/ShareModal.tsx | 6 +- .../features/TelemetryNotification/index.tsx | 4 +- .../features/TopicListContent/Topic/index.tsx | 4 +- .../SubmitAgentButton/SubmitAgentModal.tsx | 6 +- src/app/settings/(mobile)/index.tsx | 6 +- src/app/settings/about/Analytics.tsx | 8 +- src/app/settings/agent/Agent.tsx | 8 +- src/app/settings/common/Common.tsx | 8 +- src/app/settings/common/Theme.tsx | 8 +- .../ThemeSwatches/ThemeSwatchesNeutral.tsx | 6 +- .../ThemeSwatches/ThemeSwatchesPrimary.tsx | 6 +- src/app/settings/hooks/useSyncSettings.ts | 6 +- src/app/settings/llm/Azure/index.tsx | 6 +- .../llm/components/ProviderConfig/index.tsx | 6 +- .../ProviderModelList/CustomModelOption.tsx | 8 +- .../ProviderModelList/ModelConfigModal.tsx | 8 +- .../ProviderModelList/ModelFetcher.tsx | 12 +- .../components/ProviderModelList/Option.tsx | 6 +- .../components/ProviderModelList/index.tsx | 12 +- src/app/settings/sync/Alert.tsx | 6 +- .../settings/sync/DeviceInfo/DeviceName.tsx | 6 +- src/app/settings/sync/WebRTC/index.tsx | 4 +- src/app/settings/tts/TTS/index.tsx | 8 +- src/chains/__tests__/summaryAgentName.test.ts | 4 +- .../__tests__/summaryDescription.test.ts | 4 +- src/chains/__tests__/summaryTags.test.ts | 4 +- src/chains/__tests__/summaryTitle.test.ts | 4 +- src/chains/summaryAgentName.ts | 2 +- src/chains/summaryDescription.ts | 2 +- src/chains/summaryTags.ts | 2 +- src/chains/summaryTitle.ts | 2 +- .../AgentSetting/AgentConfig/ModelSelect.tsx | 9 +- src/features/AgentSetting/AgentMeta/index.tsx | 6 +- .../AgentSetting/AgentPrompt/TokenTag.tsx | 8 +- src/features/AgentSetting/AgentTTS/index.tsx | 6 +- src/features/AvatarWithUpload/index.tsx | 6 +- .../ChatInput/ActionBar/FileUpload.tsx | 6 +- .../ChatInput/ActionBar/Token/TokenTag.tsx | 8 +- .../ChatInput/ActionBar/Token/index.tsx | 6 +- .../ChatInput/ActionBar/Tools/index.tsx | 6 +- src/features/ChatInput/STT/browser.tsx | 8 +- src/features/ChatInput/STT/index.tsx | 6 +- src/features/ChatInput/STT/openai.tsx | 8 +- src/features/ChatInput/useChatInput.ts | 6 +- .../Conversation/Error/APIKeyForm/Bedrock.tsx | 6 +- .../Error/APIKeyForm/ProviderApiKeyForm.tsx | 6 +- .../Conversation/Error/AccessCodeForm.tsx | 6 +- .../Conversation/Extras/TTS/index.tsx | 6 +- .../Plugins/Render/MarkdownType/index.tsx | 6 +- .../components/ChatItem/index.tsx | 6 +- src/features/ModelSwitchPanel/index.tsx | 9 +- src/features/PluginDevModal/LocalForm.tsx | 6 +- .../SyncStatusInspector/DisableSync.tsx | 6 +- .../SyncStatusInspector/EnableSync.tsx | 8 +- src/features/SyncStatusInspector/index.tsx | 4 +- src/hooks/_header.ts | 8 +- src/hooks/useSyncData.ts | 6 +- src/hooks/useTTS.ts | 8 +- .../Desktop/SideBar/BottomActions.tsx | 4 +- .../Desktop/SideBar/TopActions.tsx | 4 +- src/layout/DefaultLayout/Mobile/index.tsx | 2 +- src/layout/GlobalProvider/AppTheme.tsx | 8 +- .../GlobalProvider/StoreInitialization.tsx | 7 +- src/services/__tests__/chat.test.ts | 37 +++--- src/services/__tests__/tool.test.ts | 4 +- src/services/_auth.test.ts | 4 +- src/services/_auth.ts | 14 +-- src/services/_header.ts | 8 +- src/services/chat.ts | 26 ++--- src/services/config.ts | 8 +- src/services/models.ts | 6 +- src/services/ollama.ts | 6 +- src/services/session/client.ts | 4 +- src/services/tool.ts | 2 +- src/services/trace.ts | 6 +- src/store/agent/slices/chat/selectors.test.ts | 4 +- .../chat/slices/message/selectors.test.ts | 2 +- src/store/chat/slices/message/selectors.ts | 6 +- .../{slices/preference => }/action.test.ts | 78 ++++++++++--- .../global/{slices/preference => }/action.ts | 46 +++++--- src/store/global/initialState.ts | 66 +++++++++-- src/store/global/selectors.ts | 17 +-- .../global/slices/common/initialState.ts | 42 ------- src/store/global/slices/common/selectors.ts | 6 - .../global/slices/preference/initialState.ts | 51 -------- .../global/slices/preference/selectors.ts | 18 --- .../global/slices/settings/selectors/sync.ts | 14 --- src/store/global/store.ts | 10 +- src/store/market/action.ts | 2 +- src/store/session/slices/session/action.ts | 6 +- src/store/{global => user}/helpers.ts | 4 +- src/store/user/index.ts | 1 + src/store/user/initialState.ts | 11 ++ src/store/user/selectors.ts | 8 ++ .../slices/common/action.test.ts | 110 +++++------------- .../{global => user}/slices/common/action.ts | 22 +--- src/store/user/slices/common/initialState.ts | 18 +++ src/store/user/slices/common/selectors.ts | 6 + .../user/slices/preference/action.test.ts | 41 +++++++ src/store/user/slices/preference/action.ts | 50 ++++++++ .../user/slices/preference/initialState.ts | 33 ++++++ src/store/user/slices/preference/selectors.ts | 13 +++ .../slices/settings/actions/general.test.ts | 12 +- .../slices/settings/actions/general.ts | 4 +- .../slices/settings/actions/index.ts | 4 +- .../slices/settings/actions/llm.test.ts | 25 ++-- .../slices/settings/actions/llm.ts | 4 +- .../slices/settings/initialState.ts | 4 +- .../settings/reducers/customModelCard.test.ts | 0 .../settings/reducers/customModelCard.ts | 0 .../__snapshots__/selectors.test.ts.snap | 0 .../slices/settings/selectors/index.ts | 0 .../settings/selectors/modelConfig.test.ts | 16 +-- .../slices/settings/selectors/modelConfig.ts | 24 ++-- .../settings/selectors/modelProvider.test.ts | 34 +++--- .../settings/selectors/modelProvider.ts | 39 +++---- .../settings/selectors/selectors.test.ts | 16 +-- .../slices/settings/selectors/settings.ts | 24 ++-- .../user/slices/settings/selectors/sync.ts | 14 +++ src/store/user/store.ts | 33 ++++++ src/tools/dalle/Render/ToolBar.tsx | 6 +- src/utils/localStorage.ts | 4 +- 129 files changed, 820 insertions(+), 686 deletions(-) rename src/store/global/{slices/preference => }/action.test.ts (50%) rename src/store/global/{slices/preference => }/action.ts (65%) delete mode 100644 src/store/global/slices/common/initialState.ts delete mode 100644 src/store/global/slices/common/selectors.ts delete mode 100644 src/store/global/slices/preference/initialState.ts delete mode 100644 src/store/global/slices/preference/selectors.ts delete mode 100644 src/store/global/slices/settings/selectors/sync.ts rename src/store/{global => user}/helpers.ts (71%) create mode 100644 src/store/user/index.ts create mode 100644 src/store/user/initialState.ts create mode 100644 src/store/user/selectors.ts rename src/store/{global => user}/slices/common/action.test.ts (70%) rename src/store/{global => user}/slices/common/action.ts (87%) create mode 100644 src/store/user/slices/common/initialState.ts create mode 100644 src/store/user/slices/common/selectors.ts create mode 100644 src/store/user/slices/preference/action.test.ts create mode 100644 src/store/user/slices/preference/action.ts create mode 100644 src/store/user/slices/preference/initialState.ts create mode 100644 src/store/user/slices/preference/selectors.ts rename src/store/{global => user}/slices/settings/actions/general.test.ts (90%) rename src/store/{global => user}/slices/settings/actions/general.ts (96%) rename src/store/{global => user}/slices/settings/actions/index.ts (87%) rename src/store/{global => user}/slices/settings/actions/llm.test.ts (86%) rename src/store/{global => user}/slices/settings/actions/llm.ts (99%) rename src/store/{global => user}/slices/settings/initialState.ts (89%) rename src/store/{global => user}/slices/settings/reducers/customModelCard.test.ts (100%) rename src/store/{global => user}/slices/settings/reducers/customModelCard.ts (100%) rename src/store/{global => user}/slices/settings/selectors/__snapshots__/selectors.test.ts.snap (100%) rename src/store/{global => user}/slices/settings/selectors/index.ts (100%) rename src/store/{global => user}/slices/settings/selectors/modelConfig.test.ts (87%) rename src/store/{global => user}/slices/settings/selectors/modelConfig.ts (70%) rename src/store/{global => user}/slices/settings/selectors/modelProvider.test.ts (83%) rename src/store/{global => user}/slices/settings/selectors/modelProvider.ts (72%) rename src/store/{global => user}/slices/settings/selectors/selectors.test.ts (93%) rename src/store/{global => user}/slices/settings/selectors/settings.ts (57%) create mode 100644 src/store/user/slices/settings/selectors/sync.ts create mode 100644 src/store/user/store.ts diff --git a/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx b/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx index 15e9abe2e289f..acc32e75ad241 100644 --- a/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx +++ b/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx @@ -5,8 +5,8 @@ import ModelTag from '@/components/ModelTag'; import ModelSwitchPanel from '@/features/ModelSwitchPanel'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import PluginTag from '../../../features/PluginTag'; @@ -16,7 +16,7 @@ const TitleTags = memo(() => { agentSelectors.currentAgentPlugins(s), ]); - const showPlugin = useGlobalStore(modelProviderSelectors.isModelEnabledFunctionCall(model)); + const showPlugin = useUserStore(modelProviderSelectors.isModelEnabledFunctionCall(model)); return ( diff --git a/src/app/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx b/src/app/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx index f65a85a150c1b..9b275cc9de25c 100644 --- a/src/app/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx +++ b/src/app/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx @@ -9,8 +9,8 @@ import { Center, Flexbox } from 'react-layout-kit'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; import { useFileStore } from '@/store/file'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; const useStyles = createStyles(({ css, token, stylish }) => { return { @@ -77,7 +77,7 @@ const DragUpload = memo(() => { const model = useAgentStore(agentSelectors.currentAgentModel); - const enabledFiles = useGlobalStore(modelProviderSelectors.isModelEnabledFiles(model)); + const enabledFiles = useUserStore(modelProviderSelectors.isModelEnabledFiles(model)); const uploadImages = async (fileList: FileList | undefined) => { if (!fileList || fileList.length === 0) return; diff --git a/src/app/chat/(desktop)/features/ChatInput/Footer/SendMore.tsx b/src/app/chat/(desktop)/features/ChatInput/Footer/SendMore.tsx index 9a73e43dbf46b..ec59f22b92bdc 100644 --- a/src/app/chat/(desktop)/features/ChatInput/Footer/SendMore.tsx +++ b/src/app/chat/(desktop)/features/ChatInput/Footer/SendMore.tsx @@ -11,8 +11,8 @@ import HotKeys from '@/components/HotKeys'; import { ALT_KEY } from '@/const/hotkeys'; import { useSendMessage } from '@/features/ChatInput/useSend'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { preferenceSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { preferenceSelectors } from '@/store/user/selectors'; import { isMacOS } from '@/utils/platform'; const useStyles = createStyles(({ css, prefixCls }) => { @@ -32,7 +32,7 @@ const SendMore = memo(() => { const { styles } = useStyles(); - const [useCmdEnterToSend, updatePreference] = useGlobalStore((s) => [ + const [useCmdEnterToSend, updatePreference] = useUserStore((s) => [ preferenceSelectors.useCmdEnterToSend(s), s.updatePreference, ]); diff --git a/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx b/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx index 17ad26c997ea6..30da5ea3a1854 100644 --- a/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +++ b/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx @@ -13,8 +13,8 @@ import { useSendMessage } from '@/features/ChatInput/useSend'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors, preferenceSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors, preferenceSelectors } from '@/store/user/selectors'; import { isMacOS } from '@/utils/platform'; import DragUpload from './DragUpload'; @@ -66,7 +66,7 @@ const Footer = memo(({ setExpand }) => { const model = useAgentStore(agentSelectors.currentAgentModel); - const [useCmdEnterToSend, canUpload] = useGlobalStore((s) => [ + const [useCmdEnterToSend, canUpload] = useUserStore((s) => [ preferenceSelectors.useCmdEnterToSend(s), modelProviderSelectors.isModelEnabledUpload(model)(s), ]); diff --git a/src/app/chat/(desktop)/features/ChatInput/TextArea.test.tsx b/src/app/chat/(desktop)/features/ChatInput/TextArea.test.tsx index ef2d89c8b3961..e286a7ea29535 100644 --- a/src/app/chat/(desktop)/features/ChatInput/TextArea.test.tsx +++ b/src/app/chat/(desktop)/features/ChatInput/TextArea.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import InputArea from './TextArea'; @@ -232,7 +232,7 @@ describe('', () => { inputMessage: '123', sendMessage: sendMessageMock, }); - useGlobalStore.getState().updatePreference({ useCmdEnterToSend: true }); + useUserStore.getState().updatePreference({ useCmdEnterToSend: true }); }); render(); @@ -252,7 +252,7 @@ describe('', () => { sendMessage: sendMessageMock, updateInputMessage: updateInputMessageMock, }); - useGlobalStore.getState().updatePreference({ useCmdEnterToSend: false }); + useUserStore.getState().updatePreference({ useCmdEnterToSend: false }); }); render(); @@ -275,7 +275,7 @@ describe('', () => { inputMessage: '123', sendMessage: sendMessageMock, }); - useGlobalStore.getState().updatePreference({ useCmdEnterToSend: true }); + useUserStore.getState().updatePreference({ useCmdEnterToSend: true }); }); render(); @@ -300,7 +300,7 @@ describe('', () => { sendMessage: sendMessageMock, updateInputMessage: updateInputMessageMock, }); - useGlobalStore.getState().updatePreference({ useCmdEnterToSend: false }); + useUserStore.getState().updatePreference({ useCmdEnterToSend: false }); }); render(); diff --git a/src/app/chat/(desktop)/features/ChatInput/TextArea.tsx b/src/app/chat/(desktop)/features/ChatInput/TextArea.tsx index 04ac6083cbf6e..2bf6bb0147d99 100644 --- a/src/app/chat/(desktop)/features/ChatInput/TextArea.tsx +++ b/src/app/chat/(desktop)/features/ChatInput/TextArea.tsx @@ -6,8 +6,8 @@ import { useTranslation } from 'react-i18next'; import { useSendMessage } from '@/features/ChatInput/useSend'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { preferenceSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { preferenceSelectors } from '@/store/user/selectors'; import { isCommandPressed } from '@/utils/keyboard'; import { useAutoFocus } from './useAutoFocus'; @@ -47,7 +47,7 @@ const InputArea = memo(({ setExpand }) => { s.updateInputMessage, ]); - const useCmdEnterToSend = useGlobalStore(preferenceSelectors.useCmdEnterToSend); + const useCmdEnterToSend = useUserStore(preferenceSelectors.useCmdEnterToSend); const sendMessage = useSendMessage(); diff --git a/src/app/chat/(mobile)/features/SessionHeader.tsx b/src/app/chat/(mobile)/features/SessionHeader.tsx index f0bb69a478494..185cc0e4cb260 100644 --- a/src/app/chat/(mobile)/features/SessionHeader.tsx +++ b/src/app/chat/(mobile)/features/SessionHeader.tsx @@ -7,10 +7,10 @@ import { Flexbox } from 'react-layout-kit'; import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens'; import SyncStatusInspector from '@/features/SyncStatusInspector'; -import { useGlobalStore } from '@/store/global'; -import { commonSelectors } from '@/store/global/selectors'; import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; import { useSessionStore } from '@/store/session'; +import { useUserStore } from '@/store/user'; +import { commonSelectors } from '@/store/user/selectors'; import { mobileHeaderSticky } from '@/styles/mobileHeader'; export const useStyles = createStyles(({ css, token }) => ({ @@ -26,7 +26,7 @@ export const useStyles = createStyles(({ css, token }) => ({ const Header = memo(() => { const [createSession] = useSessionStore((s) => [s.createSession]); const router = useRouter(); - const avatar = useGlobalStore(commonSelectors.userAvatar); + const avatar = useUserStore(commonSelectors.userAvatar); const { showCreateSession } = useServerConfigStore(featureFlagsSelectors); return ( diff --git a/src/app/chat/features/ShareButton/ShareModal.tsx b/src/app/chat/features/ShareButton/ShareModal.tsx index 2f11193881fea..2b692eaf55d54 100644 --- a/src/app/chat/features/ShareButton/ShareModal.tsx +++ b/src/app/chat/features/ShareButton/ShareModal.tsx @@ -6,8 +6,8 @@ import { Flexbox } from 'react-layout-kit'; import { FORM_STYLE } from '@/const/layoutTokens'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { commonSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { commonSelectors } from '@/store/user/selectors'; import Preview from './Preview'; import { FieldType, ImageType } from './type'; @@ -49,7 +49,7 @@ const ShareModal = memo(({ onCancel, open }) => { const [fieldValue, setFieldValue] = useState(DEFAULT_FIELD_VALUE); const [tab, setTab] = useState(Tab.Screenshot); const { t } = useTranslation('chat'); - const avatar = useGlobalStore(commonSelectors.userAvatar); + const avatar = useUserStore(commonSelectors.userAvatar); const [shareLoading, shareToShareGPT] = useChatStore((s) => [s.shareLoading, s.shareToShareGPT]); const { loading, onDownload, title } = useScreenshot(fieldValue.imageType); diff --git a/src/app/chat/features/TelemetryNotification/index.tsx b/src/app/chat/features/TelemetryNotification/index.tsx index bfb0b84f6d6f6..02b6869189bdc 100644 --- a/src/app/chat/features/TelemetryNotification/index.tsx +++ b/src/app/chat/features/TelemetryNotification/index.tsx @@ -9,9 +9,9 @@ import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import { PRIVACY_URL } from '@/const/url'; -import { useGlobalStore } from '@/store/global'; import { useServerConfigStore } from '@/store/serverConfig'; import { serverConfigSelectors } from '@/store/serverConfig/selectors'; +import { useUserStore } from '@/store/user'; const useStyles = createStyles(({ css, token, isDarkMode }) => ({ container: css` @@ -58,7 +58,7 @@ const TelemetryNotification = memo<{ mobile?: boolean }>(({ mobile }) => { const { t } = useTranslation('common'); const shouldCheck = useServerConfigStore(serverConfigSelectors.enabledTelemetryChat); - const [useCheckTrace, updatePreference] = useGlobalStore((s) => [ + const [useCheckTrace, updatePreference] = useUserStore((s) => [ s.useCheckTrace, s.updatePreference, ]); diff --git a/src/app/chat/features/TopicListContent/Topic/index.tsx b/src/app/chat/features/TopicListContent/Topic/index.tsx index 4c842d7a182ee..8c9f591008e4a 100644 --- a/src/app/chat/features/TopicListContent/Topic/index.tsx +++ b/src/app/chat/features/TopicListContent/Topic/index.tsx @@ -9,7 +9,7 @@ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'; import { imageUrl } from '@/const/url'; import { useChatStore } from '@/store/chat'; import { topicSelectors } from '@/store/chat/selectors'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import { ChatTopic } from '@/types/topic'; import { Placeholder, SkeletonList } from './SkeletonList'; @@ -30,7 +30,7 @@ export const Topic = memo(() => { s.activeTopicId, topicSelectors.currentTopicLength(s), ]); - const [visible, updateGuideState] = useGlobalStore((s) => [ + const [visible, updateGuideState] = useUserStore((s) => [ s.preference.guide?.topic, s.updateGuideState, ]); diff --git a/src/app/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx b/src/app/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx index 730dfff10d566..847276c7b2100 100644 --- a/src/app/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx +++ b/src/app/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx @@ -12,10 +12,10 @@ import { AGENTS_INDEX_GITHUB_ISSUE } from '@/const/url'; import AgentInfo from '@/features/AgentInfo'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; import { useSessionStore } from '@/store/session'; import { sessionMetaSelectors } from '@/store/session/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; const SubmitAgentModal = memo(({ open, onCancel }) => { const { t } = useTranslation('setting'); @@ -23,7 +23,7 @@ const SubmitAgentModal = memo(({ open, onCancel }) => { const systemRole = useAgentStore(agentSelectors.currentAgentSystemRole); const theme = useTheme(); const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual); - const language = useGlobalStore((s) => settingsSelectors.currentSettings(s).language); + const language = useUserStore((s) => settingsSelectors.currentSettings(s).language); const isMetaPass = Boolean( meta && meta.title && meta.description && (meta.tags as string[])?.length > 0 && meta.avatar, diff --git a/src/app/settings/(mobile)/index.tsx b/src/app/settings/(mobile)/index.tsx index f2fbbe1c36e25..d06aa856bec98 100644 --- a/src/app/settings/(mobile)/index.tsx +++ b/src/app/settings/(mobile)/index.tsx @@ -7,8 +7,8 @@ import { Center, Flexbox } from 'react-layout-kit'; import { CURRENT_VERSION } from '@/const/version'; import AvatarWithUpload from '@/features/AvatarWithUpload'; -import { useGlobalStore } from '@/store/global'; -import { commonSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { commonSelectors } from '@/store/user/selectors'; import SettingList from '../features/SettingList'; import AvatarBanner from './features/AvatarBanner'; @@ -26,7 +26,7 @@ const useStyles = createStyles(({ css, token }) => ({ })); const Setting = memo(() => { - const avatar = useGlobalStore(commonSelectors.userAvatar); + const avatar = useUserStore(commonSelectors.userAvatar); const { styles } = useStyles(); return ( diff --git a/src/app/settings/about/Analytics.tsx b/src/app/settings/about/Analytics.tsx index 78064449ff080..a4fc39b8c35a1 100644 --- a/src/app/settings/about/Analytics.tsx +++ b/src/app/settings/about/Analytics.tsx @@ -4,14 +4,14 @@ import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import { useStyles } from '@/app/settings/about/style'; -import { useGlobalStore } from '@/store/global'; -import { preferenceSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { preferenceSelectors } from '@/store/user/selectors'; const Analytics = memo(() => { const { t } = useTranslation('setting'); const { styles } = useStyles(); - const checked = useGlobalStore(preferenceSelectors.userAllowTrace); - const [updatePreference] = useGlobalStore((s) => [s.updatePreference]); + const checked = useUserStore(preferenceSelectors.userAllowTrace); + const [updatePreference] = useUserStore((s) => [s.updatePreference]); return (
diff --git a/src/app/settings/agent/Agent.tsx b/src/app/settings/agent/Agent.tsx index 25235825d4e9a..cccbe003fefd0 100644 --- a/src/app/settings/agent/Agent.tsx +++ b/src/app/settings/agent/Agent.tsx @@ -5,13 +5,13 @@ import { INBOX_SESSION_ID } from '@/const/session'; import AgentSetting from '@/features/AgentSetting'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; const Agent = memo(() => { const config = useAgentStore(agentSelectors.defaultAgentConfig, isEqual); - const meta = useGlobalStore(settingsSelectors.defaultAgentMeta, isEqual); - const [updateAgent] = useGlobalStore((s) => [s.updateDefaultAgent]); + const meta = useUserStore(settingsSelectors.defaultAgentMeta, isEqual); + const [updateAgent] = useUserStore((s) => [s.updateDefaultAgent]); return ( (({ showAccessCodeConfig, showOAuthLogin ]); const [removeAllFiles] = useFileStore((s) => [s.removeAllFiles]); const removeAllPlugins = useToolStore((s) => s.removeAllPlugins); - const settings = useGlobalStore(settingsSelectors.currentSettings, isEqual); - const [setSettings, resetSettings] = useGlobalStore((s) => [s.setSettings, s.resetSettings]); + const settings = useUserStore(settingsSelectors.currentSettings, isEqual); + const [setSettings, resetSettings] = useUserStore((s) => [s.setSettings, s.resetSettings]); const { message, modal } = App.useApp(); diff --git a/src/app/settings/common/Theme.tsx b/src/app/settings/common/Theme.tsx index 4832a679f28bb..8fec892b259c5 100644 --- a/src/app/settings/common/Theme.tsx +++ b/src/app/settings/common/Theme.tsx @@ -10,8 +10,8 @@ import { FORM_STYLE } from '@/const/layoutTokens'; import { imageUrl } from '@/const/url'; import AvatarWithUpload from '@/features/AvatarWithUpload'; import { localeOptions } from '@/locales/resources'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { switchLang } from '@/utils/client/switchLang'; import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from '../features/ThemeSwatches'; @@ -22,8 +22,8 @@ const Theme = memo(() => { const { t } = useTranslation('setting'); const [form] = AntForm.useForm(); - const settings = useGlobalStore(settingsSelectors.currentSettings, isEqual); - const [setThemeMode, setSettings] = useGlobalStore((s) => [s.switchThemeMode, s.setSettings]); + const settings = useUserStore(settingsSelectors.currentSettings, isEqual); + const [setThemeMode, setSettings] = useUserStore((s) => [s.switchThemeMode, s.setSettings]); useSyncSettings(form); diff --git a/src/app/settings/features/ThemeSwatches/ThemeSwatchesNeutral.tsx b/src/app/settings/features/ThemeSwatches/ThemeSwatchesNeutral.tsx index 5be247955bae0..ee6a9757079e8 100644 --- a/src/app/settings/features/ThemeSwatches/ThemeSwatchesNeutral.tsx +++ b/src/app/settings/features/ThemeSwatches/ThemeSwatchesNeutral.tsx @@ -7,11 +7,11 @@ import { } from '@lobehub/ui'; import { memo } from 'react'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; const ThemeSwatchesNeutral = memo(() => { - const [neutralColor, setSettings] = useGlobalStore((s) => [ + const [neutralColor, setSettings] = useUserStore((s) => [ settingsSelectors.currentSettings(s).neutralColor, s.setSettings, ]); diff --git a/src/app/settings/features/ThemeSwatches/ThemeSwatchesPrimary.tsx b/src/app/settings/features/ThemeSwatches/ThemeSwatchesPrimary.tsx index 941a891604538..d2bd3c9020938 100644 --- a/src/app/settings/features/ThemeSwatches/ThemeSwatchesPrimary.tsx +++ b/src/app/settings/features/ThemeSwatches/ThemeSwatchesPrimary.tsx @@ -7,11 +7,11 @@ import { } from '@lobehub/ui'; import { memo } from 'react'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; const ThemeSwatchesPrimary = memo(() => { - const [primaryColor, setSettings] = useGlobalStore((s) => [ + const [primaryColor, setSettings] = useUserStore((s) => [ settingsSelectors.currentSettings(s).primaryColor, s.setSettings, ]); diff --git a/src/app/settings/hooks/useSyncSettings.ts b/src/app/settings/hooks/useSyncSettings.ts index 7e73a2e8cb711..224f2775c950c 100644 --- a/src/app/settings/hooks/useSyncSettings.ts +++ b/src/app/settings/hooks/useSyncSettings.ts @@ -1,15 +1,15 @@ import { FormInstance } from 'antd/es/form/hooks/useForm'; import { useEffect } from 'react'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; export const useSyncSettings = (form: FormInstance) => { useEffect(() => { // set the first time - form.setFieldsValue(useGlobalStore.getState().settings); + form.setFieldsValue(useUserStore.getState().settings); // sync with later updated settings - const unsubscribe = useGlobalStore.subscribe( + const unsubscribe = useUserStore.subscribe( (s) => s.settings, (settings) => { form.setFieldsValue(settings); diff --git a/src/app/settings/llm/Azure/index.tsx b/src/app/settings/llm/Azure/index.tsx index 4456c6ba588d4..331cbb6799b66 100644 --- a/src/app/settings/llm/Azure/index.tsx +++ b/src/app/settings/llm/Azure/index.tsx @@ -7,8 +7,8 @@ import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import { ModelProvider } from '@/libs/agent-runtime'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import ProviderConfig from '../components/ProviderConfig'; import { LLMProviderApiTokenKey, LLMProviderBaseUrlKey, LLMProviderConfigKey } from '../const'; @@ -33,7 +33,7 @@ const AzureOpenAIProvider = memo(() => { const { styles } = useStyles(); // Get the first model card's deployment name as the check model - const checkModel = useGlobalStore((s) => { + const checkModel = useUserStore((s) => { const chatModelCards = modelProviderSelectors.getModelCardsById(providerKey)(s); if (chatModelCards.length > 0) { diff --git a/src/app/settings/llm/components/ProviderConfig/index.tsx b/src/app/settings/llm/components/ProviderConfig/index.tsx index cc6bf58748447..e4df3532e88d4 100644 --- a/src/app/settings/llm/components/ProviderConfig/index.tsx +++ b/src/app/settings/llm/components/ProviderConfig/index.tsx @@ -12,8 +12,8 @@ import { LLMProviderModelListKey, } from '@/app/settings/llm/const'; import { FORM_STYLE } from '@/const/layoutTokens'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors } from '@/store/user/selectors'; import { GlobalLLMProviderKey } from '@/types/settings'; import Checker from '../Checker'; @@ -59,7 +59,7 @@ const ProviderConfig = memo( enabled, isFetchOnClient, isProviderEndpointNotEmpty, - ] = useGlobalStore((s) => [ + ] = useUserStore((s) => [ s.toggleProviderEnabled, s.setSettings, modelConfigSelectors.isProviderEnabled(provider)(s), diff --git a/src/app/settings/llm/components/ProviderModelList/CustomModelOption.tsx b/src/app/settings/llm/components/ProviderModelList/CustomModelOption.tsx index 25776cea4968f..e295f21c2d4cc 100644 --- a/src/app/settings/llm/components/ProviderModelList/CustomModelOption.tsx +++ b/src/app/settings/llm/components/ProviderModelList/CustomModelOption.tsx @@ -8,8 +8,8 @@ import { Flexbox } from 'react-layout-kit'; import ModelIcon from '@/components/ModelIcon'; import { ModelInfoTags } from '@/components/ModelSelect'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors } from '@/store/user/selectors'; import { GlobalLLMProviderKey } from '@/types/settings'; interface CustomModelOptionProps { @@ -23,12 +23,12 @@ const CustomModelOption = memo(({ id, provider }) => { const { modal } = App.useApp(); const [dispatchCustomModelCards, toggleEditingCustomModelCard, removeEnabledModels] = - useGlobalStore((s) => [ + useUserStore((s) => [ s.dispatchCustomModelCards, s.toggleEditingCustomModelCard, s.removeEnabledModels, ]); - const modelCard = useGlobalStore( + const modelCard = useUserStore( modelConfigSelectors.getCustomModelCard({ id, provider }), isEqual, ); diff --git a/src/app/settings/llm/components/ProviderModelList/ModelConfigModal.tsx b/src/app/settings/llm/components/ProviderModelList/ModelConfigModal.tsx index 32d3eefb74c1d..ade6f00961f8d 100644 --- a/src/app/settings/llm/components/ProviderModelList/ModelConfigModal.tsx +++ b/src/app/settings/llm/components/ProviderModelList/ModelConfigModal.tsx @@ -4,8 +4,8 @@ import isEqual from 'fast-deep-equal'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors } from '@/store/global/slices/settings/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors } from '@/store/user/selectors'; import MaxTokenSlider from './MaxTokenSlider'; @@ -18,7 +18,7 @@ const ModelConfigModal = memo(({ showAzureDeployName, pro const { t } = useTranslation('setting'); const [open, id, editingProvider, dispatchCustomModelCards, toggleEditingCustomModelCard] = - useGlobalStore((s) => [ + useUserStore((s) => [ !!s.editingCustomCardModel && provider === s.editingCustomCardModel?.provider, s.editingCustomCardModel?.id, s.editingCustomCardModel?.provider, @@ -26,7 +26,7 @@ const ModelConfigModal = memo(({ showAzureDeployName, pro s.toggleEditingCustomModelCard, ]); - const modelCard = useGlobalStore( + const modelCard = useUserStore( modelConfigSelectors.getCustomModelCard({ id, provider: editingProvider }), isEqual, ); diff --git a/src/app/settings/llm/components/ProviderModelList/ModelFetcher.tsx b/src/app/settings/llm/components/ProviderModelList/ModelFetcher.tsx index 9337c9e85801e..a99323782419e 100644 --- a/src/app/settings/llm/components/ProviderModelList/ModelFetcher.tsx +++ b/src/app/settings/llm/components/ProviderModelList/ModelFetcher.tsx @@ -7,12 +7,12 @@ import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import { modelConfigSelectors, modelProviderSelectors, settingsSelectors, -} from '@/store/global/selectors'; +} from '@/store/user/selectors'; import { GlobalLLMProviderKey } from '@/types/settings'; const useStyles = createStyles(({ css, token }) => ({ @@ -36,15 +36,15 @@ interface ModelFetcherProps { const ModelFetcher = memo(({ provider }) => { const { styles } = useStyles(); const { t } = useTranslation('setting'); - const [useFetchProviderModelList] = useGlobalStore((s) => [ + const [useFetchProviderModelList] = useUserStore((s) => [ s.useFetchProviderModelList, s.setModelProviderConfig, ]); - const enabledAutoFetch = useGlobalStore(modelConfigSelectors.isAutoFetchModelsEnabled(provider)); - const latestFetchTime = useGlobalStore( + const enabledAutoFetch = useUserStore(modelConfigSelectors.isAutoFetchModelsEnabled(provider)); + const latestFetchTime = useUserStore( (s) => settingsSelectors.providerConfig(provider)(s)?.latestFetchTime, ); - const totalModels = useGlobalStore( + const totalModels = useUserStore( (s) => modelProviderSelectors.getModelCardsById(provider)(s).length, ); diff --git a/src/app/settings/llm/components/ProviderModelList/Option.tsx b/src/app/settings/llm/components/ProviderModelList/Option.tsx index 004413bada08a..401499b22eea7 100644 --- a/src/app/settings/llm/components/ProviderModelList/Option.tsx +++ b/src/app/settings/llm/components/ProviderModelList/Option.tsx @@ -5,8 +5,8 @@ import { Flexbox } from 'react-layout-kit'; import ModelIcon from '@/components/ModelIcon'; import { ModelInfoTags } from '@/components/ModelSelect'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import { GlobalLLMProviderKey } from '@/types/settings'; import CustomModelOption from './CustomModelOption'; @@ -18,7 +18,7 @@ interface OptionRenderProps { provider: GlobalLLMProviderKey; } const OptionRender = memo(({ displayName, id, provider, isAzure }) => { - const model = useGlobalStore((s) => modelProviderSelectors.getModelCardById(id)(s), isEqual); + const model = useUserStore((s) => modelProviderSelectors.getModelCardById(id)(s), isEqual); // if there is isCustom, it means it is a user defined custom model if (model?.isCustom || isAzure) return ; diff --git a/src/app/settings/llm/components/ProviderModelList/index.tsx b/src/app/settings/llm/components/ProviderModelList/index.tsx index 9632f007ab7b4..93c3375416562 100644 --- a/src/app/settings/llm/components/ProviderModelList/index.tsx +++ b/src/app/settings/llm/components/ProviderModelList/index.tsx @@ -7,8 +7,8 @@ import { ReactNode, memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import { GlobalLLMProviderKey } from '@/types/settings'; import ModelConfigModal from './ModelConfigModal'; @@ -44,22 +44,22 @@ const ProviderModelListSelect = memo( ({ showModelFetcher = false, provider, showAzureDeployName, notFoundContent, placeholder }) => { const { t } = useTranslation('common'); const { t: transSetting } = useTranslation('setting'); - const [setModelProviderConfig, dispatchCustomModelCards] = useGlobalStore((s) => [ + const [setModelProviderConfig, dispatchCustomModelCards] = useUserStore((s) => [ s.setModelProviderConfig, s.dispatchCustomModelCards, s.useFetchProviderModelList, ]); - const chatModelCards = useGlobalStore( + const chatModelCards = useUserStore( modelProviderSelectors.getModelCardsById(provider), isEqual, ); - const defaultEnableModel = useGlobalStore( + const defaultEnableModel = useUserStore( modelProviderSelectors.getDefaultEnabledModelsById(provider), isEqual, ); - const enabledModels = useGlobalStore( + const enabledModels = useUserStore( modelProviderSelectors.getEnableModelsById(provider), isEqual, ); diff --git a/src/app/settings/sync/Alert.tsx b/src/app/settings/sync/Alert.tsx index ecb518aa80b8b..88842a6a8c121 100644 --- a/src/app/settings/sync/Alert.tsx +++ b/src/app/settings/sync/Alert.tsx @@ -6,15 +6,15 @@ import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import { MAX_WIDTH } from '@/const/layoutTokens'; -import { useGlobalStore } from '@/store/global'; -import { preferenceSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { preferenceSelectors } from '@/store/user/selectors'; interface ExperimentAlertProps { mobile?: boolean; } const ExperimentAlert = memo(({ mobile }) => { const { t } = useTranslation('setting'); - const [hideSyncAlert, updatePreference] = useGlobalStore((s) => [ + const [hideSyncAlert, updatePreference] = useUserStore((s) => [ preferenceSelectors.hideSyncAlert(s), s.updatePreference, ]); diff --git a/src/app/settings/sync/DeviceInfo/DeviceName.tsx b/src/app/settings/sync/DeviceInfo/DeviceName.tsx index fd85acb67261f..450cfb168fd1f 100644 --- a/src/app/settings/sync/DeviceInfo/DeviceName.tsx +++ b/src/app/settings/sync/DeviceInfo/DeviceName.tsx @@ -6,13 +6,13 @@ import { memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; -import { useGlobalStore } from '@/store/global'; -import { syncSettingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { syncSettingsSelectors } from '@/store/user/selectors'; const DeviceName = memo(() => { const { t } = useTranslation('setting'); - const [deviceName, setSettings] = useGlobalStore((s) => [ + const [deviceName, setSettings] = useUserStore((s) => [ syncSettingsSelectors.deviceName(s), s.setSettings, ]); diff --git a/src/app/settings/sync/WebRTC/index.tsx b/src/app/settings/sync/WebRTC/index.tsx index 76319e33813f3..428c5bf600579 100644 --- a/src/app/settings/sync/WebRTC/index.tsx +++ b/src/app/settings/sync/WebRTC/index.tsx @@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit'; import { FORM_STYLE } from '@/const/layoutTokens'; import SyncStatusInspector from '@/features/SyncStatusInspector'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import { useSyncSettings } from '../../hooks/useSyncSettings'; import ChannelNameInput from './ChannelNameInput'; @@ -20,7 +20,7 @@ const WebRTC = memo(() => { const { t } = useTranslation('setting'); const [form] = AntForm.useForm(); - const [setSettings] = useGlobalStore((s) => [s.setSettings]); + const [setSettings] = useUserStore((s) => [s.setSettings]); useSyncSettings(form); diff --git a/src/app/settings/tts/TTS/index.tsx b/src/app/settings/tts/TTS/index.tsx index f5dae48d96e1c..db6a8b2e2f76e 100644 --- a/src/app/settings/tts/TTS/index.tsx +++ b/src/app/settings/tts/TTS/index.tsx @@ -7,8 +7,8 @@ import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FORM_STYLE } from '@/const/layoutTokens'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { opeanaiSTTOptions, opeanaiTTSOptions, sttOptions } from './options'; @@ -19,8 +19,8 @@ const TTS_SETTING_KEY = 'tts'; const TTS = memo(() => { const { t } = useTranslation('setting'); const [form] = AntForm.useForm(); - const settings = useGlobalStore(settingsSelectors.currentSettings, isEqual); - const [setSettings] = useGlobalStore((s) => [s.setSettings]); + const settings = useUserStore(settingsSelectors.currentSettings, isEqual); + const [setSettings] = useUserStore((s) => [s.setSettings]); const stt: SettingItemGroup = { children: [ diff --git a/src/chains/__tests__/summaryAgentName.test.ts b/src/chains/__tests__/summaryAgentName.test.ts index 4e562e6c709b6..3c91662b77380 100644 --- a/src/chains/__tests__/summaryAgentName.test.ts +++ b/src/chains/__tests__/summaryAgentName.test.ts @@ -1,11 +1,11 @@ import { Mock, describe, expect, it } from 'vitest'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { chainSummaryAgentName } from '../summaryAgentName'; // Mock the getCurrentLanguage function -vi.mock('@/store/global/helpers', () => ({ +vi.mock('@/store/user/helpers', () => ({ globalHelpers: { getCurrentLanguage: vi.fn(), }, diff --git a/src/chains/__tests__/summaryDescription.test.ts b/src/chains/__tests__/summaryDescription.test.ts index 20dfaea522396..4fb16ae659881 100644 --- a/src/chains/__tests__/summaryDescription.test.ts +++ b/src/chains/__tests__/summaryDescription.test.ts @@ -1,11 +1,11 @@ import { Mock, describe, expect, it, vi } from 'vitest'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { chainSummaryDescription } from '../summaryDescription'; // Mock the globalHelpers.getCurrentLanguage function -vi.mock('@/store/global/helpers', () => ({ +vi.mock('@/store/user/helpers', () => ({ globalHelpers: { getCurrentLanguage: vi.fn(() => 'en-US'), }, diff --git a/src/chains/__tests__/summaryTags.test.ts b/src/chains/__tests__/summaryTags.test.ts index 8b5b790076ff6..ac722456850a1 100644 --- a/src/chains/__tests__/summaryTags.test.ts +++ b/src/chains/__tests__/summaryTags.test.ts @@ -1,11 +1,11 @@ import { Mock, describe, expect, it } from 'vitest'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { chainSummaryTags } from '../summaryTags'; // Mock the getCurrentLanguage function -vi.mock('@/store/global/helpers', () => ({ +vi.mock('@/store/user/helpers', () => ({ globalHelpers: { getCurrentLanguage: vi.fn(), }, diff --git a/src/chains/__tests__/summaryTitle.test.ts b/src/chains/__tests__/summaryTitle.test.ts index ebbd60955db14..487db4999ef07 100644 --- a/src/chains/__tests__/summaryTitle.test.ts +++ b/src/chains/__tests__/summaryTitle.test.ts @@ -1,13 +1,13 @@ import { Mock, describe, expect, it, vi } from 'vitest'; import { chatHelpers } from '@/store/chat/helpers'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { OpenAIChatMessage } from '@/types/openai/chat'; import { chainSummaryTitle } from '../summaryTitle'; // Mock the getCurrentLanguage function -vi.mock('@/store/global/helpers', () => ({ +vi.mock('@/store/user/helpers', () => ({ globalHelpers: { getCurrentLanguage: vi.fn(), }, diff --git a/src/chains/summaryAgentName.ts b/src/chains/summaryAgentName.ts index 987b529747368..19acedd4cf243 100644 --- a/src/chains/summaryAgentName.ts +++ b/src/chains/summaryAgentName.ts @@ -1,4 +1,4 @@ -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { ChatStreamPayload } from '@/types/openai/chat'; /** diff --git a/src/chains/summaryDescription.ts b/src/chains/summaryDescription.ts index 662de30d5c229..7cd3bb0d73f52 100644 --- a/src/chains/summaryDescription.ts +++ b/src/chains/summaryDescription.ts @@ -1,4 +1,4 @@ -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { ChatStreamPayload } from '@/types/openai/chat'; export const chainSummaryDescription = (content: string): Partial => ({ diff --git a/src/chains/summaryTags.ts b/src/chains/summaryTags.ts index 403418cb0d151..bbe7833a65dd6 100644 --- a/src/chains/summaryTags.ts +++ b/src/chains/summaryTags.ts @@ -1,4 +1,4 @@ -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { ChatStreamPayload } from '@/types/openai/chat'; export const chainSummaryTags = (content: string): Partial => ({ diff --git a/src/chains/summaryTitle.ts b/src/chains/summaryTitle.ts index ef3b0d98fcfbc..d7ec3deaa97a3 100644 --- a/src/chains/summaryTitle.ts +++ b/src/chains/summaryTitle.ts @@ -1,5 +1,5 @@ import { chatHelpers } from '@/store/chat/helpers'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { ChatStreamPayload, OpenAIChatMessage } from '@/types/openai/chat'; export const chainSummaryTitle = async ( diff --git a/src/features/AgentSetting/AgentConfig/ModelSelect.tsx b/src/features/AgentSetting/AgentConfig/ModelSelect.tsx index aa61124fac237..85120de6ff91e 100644 --- a/src/features/AgentSetting/AgentConfig/ModelSelect.tsx +++ b/src/features/AgentSetting/AgentConfig/ModelSelect.tsx @@ -4,8 +4,8 @@ import isEqual from 'fast-deep-equal'; import { memo, useMemo } from 'react'; import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import { ModelProviderCard } from '@/types/llm'; import { useStore } from '../store'; @@ -25,10 +25,7 @@ interface ModelOption { const ModelSelect = memo(() => { const [model, updateConfig] = useStore((s) => [s.config.model, s.setAgentConfig]); - const enabledList = useGlobalStore( - modelProviderSelectors.modelProviderListForModelSelect, - isEqual, - ); + const enabledList = useUserStore(modelProviderSelectors.modelProviderListForModelSelect, isEqual); const { styles } = useStyles(); diff --git a/src/features/AgentSetting/AgentMeta/index.tsx b/src/features/AgentSetting/AgentMeta/index.tsx index 9a990c35a9b0f..9829d79df3a73 100644 --- a/src/features/AgentSetting/AgentMeta/index.tsx +++ b/src/features/AgentSetting/AgentMeta/index.tsx @@ -8,8 +8,8 @@ import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { FORM_STYLE } from '@/const/layoutTokens'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { useStore } from '../store'; import { SessionLoadingState } from '../store/initialState'; @@ -28,7 +28,7 @@ const AgentMeta = memo(() => { s.autocompleteMeta, s.autocompleteAllMeta, ]); - const locale = useGlobalStore(settingsSelectors.currentLanguage); + const locale = useUserStore(settingsSelectors.currentLanguage); const loading = useStore((s) => s.autocompleteLoading); const meta = useStore((s) => s.meta, isEqual); diff --git a/src/features/AgentSetting/AgentPrompt/TokenTag.tsx b/src/features/AgentSetting/AgentPrompt/TokenTag.tsx index bec8458282d23..06be8efdf7257 100644 --- a/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +++ b/src/features/AgentSetting/AgentPrompt/TokenTag.tsx @@ -4,8 +4,8 @@ import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import { useTokenCount } from '@/hooks/useTokenCount'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import { useStore } from '../store'; @@ -14,8 +14,8 @@ const Tokens = memo(() => { const [systemRole, model] = useStore((s) => [s.config.systemRole, s.config.model]); const systemTokenCount = useTokenCount(systemRole); - const showTag = useGlobalStore(modelProviderSelectors.isModelHasMaxToken(model)); - const modelMaxTokens = useGlobalStore(modelProviderSelectors.modelMaxToken(model)); + const showTag = useUserStore(modelProviderSelectors.isModelHasMaxToken(model)); + const modelMaxTokens = useUserStore(modelProviderSelectors.modelMaxToken(model)); return ( diff --git a/src/features/AgentSetting/AgentTTS/index.tsx b/src/features/AgentSetting/AgentTTS/index.tsx index eadf23b6151f7..802b45d47332e 100644 --- a/src/features/AgentSetting/AgentTTS/index.tsx +++ b/src/features/AgentSetting/AgentTTS/index.tsx @@ -8,8 +8,8 @@ import { memo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { FORM_STYLE } from '@/const/layoutTokens'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { useStore } from '../store'; import SelectWithTTSPreview from './SelectWithTTSPreview'; @@ -22,7 +22,7 @@ const AgentTTS = memo(() => { const { t } = useTranslation('setting'); const updateConfig = useStore((s) => s.setAgentConfig); const [form] = AFrom.useForm(); - const voiceList = useGlobalStore((s) => { + const voiceList = useUserStore((s) => { const locale = settingsSelectors.currentLanguage(s); return (all?: boolean) => new VoiceList(all ? undefined : locale); }); diff --git a/src/features/AvatarWithUpload/index.tsx b/src/features/AvatarWithUpload/index.tsx index a3590173b1a62..b9c57f69aab16 100644 --- a/src/features/AvatarWithUpload/index.tsx +++ b/src/features/AvatarWithUpload/index.tsx @@ -4,8 +4,8 @@ import NextImage from 'next/image'; import { CSSProperties, memo, useCallback } from 'react'; import { DEFAULT_USER_AVATAR_URL } from '@/const/meta'; -import { useGlobalStore } from '@/store/global'; -import { commonSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { commonSelectors } from '@/store/user/selectors'; import { imageToBase64 } from '@/utils/imageToBase64'; import { createUploadImageHandler } from '@/utils/uploadFIle'; @@ -38,7 +38,7 @@ interface AvatarWithUploadProps { const AvatarWithUpload = memo( ({ size = 40, compressSize = 256, style, id }) => { const { styles } = useStyle(); - const [avatar, updateAvatar] = useGlobalStore((s) => [ + const [avatar, updateAvatar] = useUserStore((s) => [ commonSelectors.userAvatar(s), s.updateAvatar, ]); diff --git a/src/features/ChatInput/ActionBar/FileUpload.tsx b/src/features/ChatInput/ActionBar/FileUpload.tsx index e68ecaa4d64a5..a55d349fb4f36 100644 --- a/src/features/ChatInput/ActionBar/FileUpload.tsx +++ b/src/features/ChatInput/ActionBar/FileUpload.tsx @@ -9,8 +9,8 @@ import { Center } from 'react-layout-kit'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; import { useFileStore } from '@/store/file'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; const FileUpload = memo(() => { const { t } = useTranslation('chat'); @@ -19,7 +19,7 @@ const FileUpload = memo(() => { const upload = useFileStore((s) => s.uploadFile); const model = useAgentStore(agentSelectors.currentAgentModel); - const [canUpload, enabledFiles] = useGlobalStore((s) => [ + const [canUpload, enabledFiles] = useUserStore((s) => [ modelProviderSelectors.isModelEnabledUpload(model)(s), modelProviderSelectors.isModelEnabledFiles(model)(s), ]); diff --git a/src/features/ChatInput/ActionBar/Token/TokenTag.tsx b/src/features/ChatInput/ActionBar/Token/TokenTag.tsx index 47e1c8c288e07..ebcfb26dcdc92 100644 --- a/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +++ b/src/features/ChatInput/ActionBar/Token/TokenTag.tsx @@ -9,10 +9,10 @@ import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; import { useChatStore } from '@/store/chat'; import { chatSelectors } from '@/store/chat/selectors'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; import { useToolStore } from '@/store/tool'; import { toolSelectors } from '@/store/tool/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; const format = (number: number) => numeral(number).format('0,0'); @@ -29,10 +29,10 @@ const Token = memo(() => { agentSelectors.currentAgentModel(s) as string, ]); - const maxTokens = useGlobalStore(modelProviderSelectors.modelMaxToken(model)); + const maxTokens = useUserStore(modelProviderSelectors.modelMaxToken(model)); // Tool usage token - const canUseTool = useGlobalStore(modelProviderSelectors.isModelEnabledFunctionCall(model)); + const canUseTool = useUserStore(modelProviderSelectors.isModelEnabledFunctionCall(model)); const plugins = useAgentStore(agentSelectors.currentAgentPlugins); const toolsString = useToolStore((s) => { const pluginSystemRoles = toolSelectors.enabledSystemRoles(plugins)(s); diff --git a/src/features/ChatInput/ActionBar/Token/index.tsx b/src/features/ChatInput/ActionBar/Token/index.tsx index 4f3bfce468b8e..2ecc19806a0c4 100644 --- a/src/features/ChatInput/ActionBar/Token/index.tsx +++ b/src/features/ChatInput/ActionBar/Token/index.tsx @@ -3,14 +3,14 @@ import { memo } from 'react'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; const LargeTokenContent = dynamic(() => import('./TokenTag'), { ssr: false }); const Token = memo(() => { const model = useAgentStore(agentSelectors.currentAgentModel); - const showTag = useGlobalStore(modelProviderSelectors.isModelHasMaxToken(model)); + const showTag = useUserStore(modelProviderSelectors.isModelHasMaxToken(model)); return showTag && ; }); diff --git a/src/features/ChatInput/ActionBar/Tools/index.tsx b/src/features/ChatInput/ActionBar/Tools/index.tsx index 90940f1b82d85..0f39687ecbd1e 100644 --- a/src/features/ChatInput/ActionBar/Tools/index.tsx +++ b/src/features/ChatInput/ActionBar/Tools/index.tsx @@ -11,11 +11,11 @@ import { Flexbox } from 'react-layout-kit'; import PluginStore from '@/features/PluginStore'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; import { pluginHelpers, useToolStore } from '@/store/tool'; import { builtinToolSelectors, pluginSelectors } from '@/store/tool/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import ToolItem from './ToolItem'; @@ -49,7 +49,7 @@ const Tools = memo(() => { const { styles } = useStyles(); const model = useAgentStore(agentSelectors.currentAgentModel); - const enableFC = useGlobalStore(modelProviderSelectors.isModelEnabledFunctionCall(model)); + const enableFC = useUserStore(modelProviderSelectors.isModelEnabledFunctionCall(model)); const items: ItemType[] = [ (builtinList.length !== 0 && { diff --git a/src/features/ChatInput/STT/browser.tsx b/src/features/ChatInput/STT/browser.tsx index edfb3978239f3..856bbd537e9ee 100644 --- a/src/features/ChatInput/STT/browser.tsx +++ b/src/features/ChatInput/STT/browser.tsx @@ -7,8 +7,8 @@ import { SWRConfiguration } from 'swr'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { ChatMessageError } from '@/types/message'; import { getMessageError } from '@/utils/fetch'; @@ -19,9 +19,9 @@ interface STTConfig extends SWRConfiguration { } const useBrowserSTT = (config: STTConfig) => { - const ttsSettings = useGlobalStore(settingsSelectors.currentTTS, isEqual); + const ttsSettings = useUserStore(settingsSelectors.currentTTS, isEqual); const ttsAgentSettings = useAgentStore(agentSelectors.currentAgentTTS, isEqual); - const locale = useGlobalStore(settingsSelectors.currentLanguage); + const locale = useUserStore(settingsSelectors.currentLanguage); const autoStop = ttsSettings.sttAutoStop; const sttLocale = diff --git a/src/features/ChatInput/STT/index.tsx b/src/features/ChatInput/STT/index.tsx index 445856a107284..5e28c0675b7e8 100644 --- a/src/features/ChatInput/STT/index.tsx +++ b/src/features/ChatInput/STT/index.tsx @@ -1,14 +1,14 @@ import isEqual from 'fast-deep-equal'; import { memo } from 'react'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import BrowserSTT from './browser'; import OpenaiSTT from './openai'; const STT = memo<{ mobile?: boolean }>(({ mobile }) => { - const { sttServer } = useGlobalStore(settingsSelectors.currentTTS, isEqual); + const { sttServer } = useUserStore(settingsSelectors.currentTTS, isEqual); switch (sttServer) { case 'openai': { return ; diff --git a/src/features/ChatInput/STT/openai.tsx b/src/features/ChatInput/STT/openai.tsx index 986821336461d..5175f8db772ba 100644 --- a/src/features/ChatInput/STT/openai.tsx +++ b/src/features/ChatInput/STT/openai.tsx @@ -10,8 +10,8 @@ import { API_ENDPOINTS } from '@/services/_url'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { ChatMessageError } from '@/types/message'; import { getMessageError } from '@/utils/fetch'; @@ -22,9 +22,9 @@ interface STTConfig extends SWRConfiguration { } const useOpenaiSTT = (config: STTConfig) => { - const ttsSettings = useGlobalStore(settingsSelectors.currentTTS, isEqual); + const ttsSettings = useUserStore(settingsSelectors.currentTTS, isEqual); const ttsAgentSettings = useAgentStore(agentSelectors.currentAgentTTS, isEqual); - const locale = useGlobalStore(settingsSelectors.currentLanguage); + const locale = useUserStore(settingsSelectors.currentLanguage); const autoStop = ttsSettings.sttAutoStop; const sttLocale = diff --git a/src/features/ChatInput/useChatInput.ts b/src/features/ChatInput/useChatInput.ts index daf43a56045d3..dabf723b3a171 100644 --- a/src/features/ChatInput/useChatInput.ts +++ b/src/features/ChatInput/useChatInput.ts @@ -4,8 +4,8 @@ import { useCallback, useRef, useState } from 'react'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import { useSendMessage } from './useSend'; @@ -15,7 +15,7 @@ export const useChatInput = () => { const onSend = useSendMessage(); const model = useAgentStore(agentSelectors.currentAgentModel); - const canUpload = useGlobalStore(modelProviderSelectors.isModelEnabledUpload(model)); + const canUpload = useUserStore(modelProviderSelectors.isModelEnabledUpload(model)); const [loading, value, onInput, onStop] = useChatStore((s) => [ !!s.chatLoadingId, diff --git a/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx b/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx index b63a0c90f3449..1b274f024cf8f 100644 --- a/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx +++ b/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx @@ -7,8 +7,8 @@ import { memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ModelProvider } from '@/libs/agent-runtime'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors } from '@/store/user/selectors'; import { FormAction } from '../style'; @@ -16,7 +16,7 @@ const BedrockForm = memo(() => { const { t } = useTranslation('modelProvider'); const [showRegion, setShow] = useState(false); - const [accessKeyId, secretAccessKey, region, setConfig] = useGlobalStore((s) => [ + const [accessKeyId, secretAccessKey, region, setConfig] = useUserStore((s) => [ modelConfigSelectors.bedrockConfig(s).accessKeyId, modelConfigSelectors.bedrockConfig(s).secretAccessKey, modelConfigSelectors.bedrockConfig(s).region, diff --git a/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx b/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx index 66a2aafde8273..41b491e8b6e68 100644 --- a/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx +++ b/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx @@ -4,8 +4,8 @@ import { Network } from 'lucide-react'; import { ReactNode, memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { GlobalLLMProviderKey } from '@/types/settings'; import { FormAction } from '../style'; @@ -23,7 +23,7 @@ const ProviderApiKeyForm = memo( const { t: errorT } = useTranslation('error'); const [showProxy, setShow] = useState(false); - const [apiKey, proxyUrl, setConfig] = useGlobalStore((s) => [ + const [apiKey, proxyUrl, setConfig] = useUserStore((s) => [ settingsSelectors.providerConfig(provider)(s)?.apiKey, settingsSelectors.providerConfig(provider)(s)?.endpoint, s.setModelProviderConfig, diff --git a/src/features/Conversation/Error/AccessCodeForm.tsx b/src/features/Conversation/Error/AccessCodeForm.tsx index eb2610fdba457..a442f81e0b1b8 100644 --- a/src/features/Conversation/Error/AccessCodeForm.tsx +++ b/src/features/Conversation/Error/AccessCodeForm.tsx @@ -4,8 +4,8 @@ import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { FormAction } from './style'; @@ -15,7 +15,7 @@ interface AccessCodeFormProps { const AccessCodeForm = memo(({ id }) => { const { t } = useTranslation('error'); - const [password, setSettings] = useGlobalStore((s) => [ + const [password, setSettings] = useUserStore((s) => [ settingsSelectors.currentSettings(s).password, s.setSettings, ]); diff --git a/src/features/Conversation/Extras/TTS/index.tsx b/src/features/Conversation/Extras/TTS/index.tsx index 0e19632f74746..597efb0d602a8 100644 --- a/src/features/Conversation/Extras/TTS/index.tsx +++ b/src/features/Conversation/Extras/TTS/index.tsx @@ -3,8 +3,8 @@ import { Md5 } from 'ts-md5'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import FilePlayer from './FilePlayer'; import InitPlayer, { TTSProps } from './InitPlayer'; @@ -12,7 +12,7 @@ import InitPlayer, { TTSProps } from './InitPlayer'; const TTS = memo( (props) => { const { file, voice, content, contentMd5 } = props; - const lang = useGlobalStore(settingsSelectors.currentLanguage); + const lang = useUserStore(settingsSelectors.currentLanguage); const currentVoice = useAgentStore(agentSelectors.currentAgentTTSVoice(lang)); const md5 = useMemo(() => Md5.hashStr(content).toString(), [content]); diff --git a/src/features/Conversation/Plugins/Render/MarkdownType/index.tsx b/src/features/Conversation/Plugins/Render/MarkdownType/index.tsx index 6a6a3aa7e2c8b..76ebcd3d92699 100644 --- a/src/features/Conversation/Plugins/Render/MarkdownType/index.tsx +++ b/src/features/Conversation/Plugins/Render/MarkdownType/index.tsx @@ -1,8 +1,8 @@ import { Markdown } from '@lobehub/ui'; import { memo } from 'react'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import Loading from '../Loading'; @@ -12,7 +12,7 @@ export interface PluginMarkdownTypeProps { } const PluginMarkdownType = memo(({ content, loading }) => { - const fontSize = useGlobalStore((s) => settingsSelectors.currentSettings(s).fontSize); + const fontSize = useUserStore((s) => settingsSelectors.currentSettings(s).fontSize); if (loading) return ; return ( diff --git a/src/features/Conversation/components/ChatItem/index.tsx b/src/features/Conversation/components/ChatItem/index.tsx index 0ac1ec84ce767..e3c20e71468a2 100644 --- a/src/features/Conversation/components/ChatItem/index.tsx +++ b/src/features/Conversation/components/ChatItem/index.tsx @@ -8,10 +8,10 @@ import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; import { useChatStore } from '@/store/chat'; import { chatSelectors } from '@/store/chat/selectors'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; import { useSessionStore } from '@/store/session'; import { sessionMetaSelectors } from '@/store/session/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { ChatMessage } from '@/types/message'; import ErrorMessageExtra, { getErrorAlertConfig } from '../../Error'; @@ -38,7 +38,7 @@ export interface ChatListItemProps { } const Item = memo(({ index, id }) => { - const fontSize = useGlobalStore((s) => settingsSelectors.currentSettings(s).fontSize); + const fontSize = useUserStore((s) => settingsSelectors.currentSettings(s).fontSize); const { t } = useTranslation('common'); const { styles, cx } = useStyles(); const [editing, setEditing] = useState(false); diff --git a/src/features/ModelSwitchPanel/index.tsx b/src/features/ModelSwitchPanel/index.tsx index e3b7ed0c0e183..9235c46fef168 100644 --- a/src/features/ModelSwitchPanel/index.tsx +++ b/src/features/ModelSwitchPanel/index.tsx @@ -11,8 +11,8 @@ import { Flexbox } from 'react-layout-kit'; import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; -import { useGlobalStore } from '@/store/global'; -import { modelProviderSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelProviderSelectors } from '@/store/user/selectors'; import { ModelProviderCard } from '@/types/llm'; import { withBasePath } from '@/utils/basePath'; @@ -46,10 +46,7 @@ const ModelSwitchPanel = memo(({ children }) => { ]); const router = useRouter(); - const enabledList = useGlobalStore( - modelProviderSelectors.modelProviderListForModelSelect, - isEqual, - ); + const enabledList = useUserStore(modelProviderSelectors.modelProviderListForModelSelect, isEqual); const items = useMemo(() => { const getModelItems = (provider: ModelProviderCard) => { diff --git a/src/features/PluginDevModal/LocalForm.tsx b/src/features/PluginDevModal/LocalForm.tsx index a539bb40da3b6..d5104c27a14a1 100644 --- a/src/features/PluginDevModal/LocalForm.tsx +++ b/src/features/PluginDevModal/LocalForm.tsx @@ -4,16 +4,16 @@ import dynamic from 'next/dynamic'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; import { useToolStore } from '@/store/tool'; import { pluginSelectors } from '@/store/tool/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; const EmojiPicker = dynamic(() => import('@lobehub/ui/es/EmojiPicker'), { ssr: false }); const LocalForm = memo<{ form: FormInstance; mode?: 'edit' | 'create' }>(({ form, mode }) => { const isEditMode = mode === 'edit'; - const locale = useGlobalStore(settingsSelectors.currentLanguage); + const locale = useUserStore(settingsSelectors.currentLanguage); const { t } = useTranslation('plugin'); const pluginIds = useToolStore(pluginSelectors.storeAndInstallPluginsIdList); diff --git a/src/features/SyncStatusInspector/DisableSync.tsx b/src/features/SyncStatusInspector/DisableSync.tsx index 76a7f5c911c59..f537b1014fba3 100644 --- a/src/features/SyncStatusInspector/DisableSync.tsx +++ b/src/features/SyncStatusInspector/DisableSync.tsx @@ -7,8 +7,8 @@ import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; -import { useGlobalStore } from '@/store/global'; -import { syncSettingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { syncSettingsSelectors } from '@/store/user/selectors'; interface DisableSyncProps { noPopover?: boolean; @@ -17,7 +17,7 @@ interface DisableSyncProps { const DisableSync = memo(({ noPopover, placement = 'bottomLeft' }) => { const { t } = useTranslation('common'); - const [haveConfig, setSettings] = useGlobalStore((s) => [ + const [haveConfig, setSettings] = useUserStore((s) => [ !!syncSettingsSelectors.webrtcConfig(s).channelName, s.setSettings, ]); diff --git a/src/features/SyncStatusInspector/EnableSync.tsx b/src/features/SyncStatusInspector/EnableSync.tsx index 21f201a667047..87f65c339af60 100644 --- a/src/features/SyncStatusInspector/EnableSync.tsx +++ b/src/features/SyncStatusInspector/EnableSync.tsx @@ -9,8 +9,8 @@ import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; -import { useGlobalStore } from '@/store/global'; -import { syncSettingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { syncSettingsSelectors } from '@/store/user/selectors'; import { pathString } from '@/utils/url'; import EnableTag from './EnableTag'; @@ -39,7 +39,7 @@ const EnableSync = memo(({ hiddenActions, placement = 'bottomLe const { t } = useTranslation('common'); const { styles, theme } = useStyles(); - const [syncStatus, isSyncing, channelName, enableWebRTC, setSettings] = useGlobalStore((s) => [ + const [syncStatus, isSyncing, channelName, enableWebRTC, setSettings] = useUserStore((s) => [ s.syncStatus, s.syncStatus === 'syncing', syncSettingsSelectors.webrtcChannelName(s), @@ -47,7 +47,7 @@ const EnableSync = memo(({ hiddenActions, placement = 'bottomLe s.setSettings, ]); - const users = useGlobalStore((s) => s.syncAwareness, isEqual); + const users = useUserStore((s) => s.syncAwareness, isEqual); const switchSync = (enabled: boolean) => { setSettings({ sync: { webrtc: { enabled } } }); diff --git a/src/features/SyncStatusInspector/index.tsx b/src/features/SyncStatusInspector/index.tsx index 5de666c3bc3b4..5dcddfc7dc64d 100644 --- a/src/features/SyncStatusInspector/index.tsx +++ b/src/features/SyncStatusInspector/index.tsx @@ -1,7 +1,7 @@ import { TooltipPlacement } from 'antd/es/tooltip'; import { memo } from 'react'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import DisableSync from './DisableSync'; import EnableSync from './EnableSync'; @@ -14,7 +14,7 @@ interface SyncStatusTagProps { const SyncStatusTag = memo( ({ hiddenActions, placement, hiddenEnableGuide }) => { - const [enableSync] = useGlobalStore((s) => [s.syncEnabled]); + const [enableSync] = useUserStore((s) => [s.syncEnabled]); return enableSync ? ( diff --git a/src/hooks/_header.ts b/src/hooks/_header.ts index 96506da02308e..5d530a4fd7468 100644 --- a/src/hooks/_header.ts +++ b/src/hooks/_header.ts @@ -1,11 +1,11 @@ import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors, settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors, settingsSelectors } from '@/store/user/selectors'; // TODO: Need to be removed after tts refactor // eslint-disable-next-line no-undef export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => { - const openai = modelConfigSelectors.openAIConfig(useGlobalStore.getState()); + const openai = modelConfigSelectors.openAIConfig(useUserStore.getState()); const apiKey = openai.apiKey || ''; const endpoint = openai.endpoint || ''; @@ -13,7 +13,7 @@ export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => { // eslint-disable-next-line no-undef return { ...header, - [LOBE_CHAT_ACCESS_CODE]: settingsSelectors.password(useGlobalStore.getState()), + [LOBE_CHAT_ACCESS_CODE]: settingsSelectors.password(useUserStore.getState()), [OPENAI_API_KEY_HEADER_KEY]: apiKey, [OPENAI_END_POINT]: endpoint, }; diff --git a/src/hooks/useSyncData.ts b/src/hooks/useSyncData.ts index e3129446aa37b..5148b25e12cbb 100644 --- a/src/hooks/useSyncData.ts +++ b/src/hooks/useSyncData.ts @@ -1,9 +1,9 @@ import { useCallback } from 'react'; import { useChatStore } from '@/store/chat'; -import { useGlobalStore } from '@/store/global'; -import { syncSettingsSelectors } from '@/store/global/selectors'; import { useSessionStore } from '@/store/session'; +import { useUserStore } from '@/store/user'; +import { syncSettingsSelectors } from '@/store/user/selectors'; export const useSyncEvent = () => { const [refreshMessages, refreshTopic] = useChatStore((s) => [s.refreshMessages, s.refreshTopic]); @@ -36,7 +36,7 @@ export const useSyncEvent = () => { }; export const useEnabledDataSync = () => { - const [userId, userEnableSync, useEnabledSync] = useGlobalStore((s) => [ + const [userId, userEnableSync, useEnabledSync] = useUserStore((s) => [ s.userId, syncSettingsSelectors.enableWebRTC(s), s.useEnabledSync, diff --git a/src/hooks/useTTS.ts b/src/hooks/useTTS.ts index 39051af5ab2ef..1fada0539763e 100644 --- a/src/hooks/useTTS.ts +++ b/src/hooks/useTTS.ts @@ -13,8 +13,8 @@ import { createHeaderWithOpenAI } from '@/services/_header'; import { API_ENDPOINTS } from '@/services/_url'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { TTSServer } from '@/types/agent'; interface TTSConfig extends TTSOptions { @@ -24,9 +24,9 @@ interface TTSConfig extends TTSOptions { } export const useTTS = (content: string, config?: TTSConfig) => { - const ttsSettings = useGlobalStore(settingsSelectors.currentTTS, isEqual); + const ttsSettings = useUserStore(settingsSelectors.currentTTS, isEqual); const ttsAgentSettings = useAgentStore(agentSelectors.currentAgentTTS, isEqual); - const lang = useGlobalStore(settingsSelectors.currentLanguage); + const lang = useUserStore(settingsSelectors.currentLanguage); const voice = useAgentStore(agentSelectors.currentAgentTTSVoice(lang)); let useSelectedTTS; let options: any = {}; diff --git a/src/layout/DefaultLayout/Desktop/SideBar/BottomActions.tsx b/src/layout/DefaultLayout/Desktop/SideBar/BottomActions.tsx index 0f8b703e7aff3..d455f16868c0c 100644 --- a/src/layout/DefaultLayout/Desktop/SideBar/BottomActions.tsx +++ b/src/layout/DefaultLayout/Desktop/SideBar/BottomActions.tsx @@ -20,11 +20,11 @@ import { Flexbox } from 'react-layout-kit'; import { ABOUT, CHANGELOG, DISCORD, DOCUMENTS, FEEDBACK, GITHUB } from '@/const/url'; import DataImporter from '@/features/DataImporter'; import { configService } from '@/services/config'; -import { GlobalStore, useGlobalStore } from '@/store/global'; +import { useGlobalStore } from '@/store/global'; import { SidebarTabKey } from '@/store/global/initialState'; export interface BottomActionProps { - tab?: GlobalStore['sidebarKey']; + tab?: SidebarTabKey; } const BottomActions = memo(({ tab }) => { diff --git a/src/layout/DefaultLayout/Desktop/SideBar/TopActions.tsx b/src/layout/DefaultLayout/Desktop/SideBar/TopActions.tsx index 66f5a0abe75f8..8a98f5661d497 100644 --- a/src/layout/DefaultLayout/Desktop/SideBar/TopActions.tsx +++ b/src/layout/DefaultLayout/Desktop/SideBar/TopActions.tsx @@ -4,12 +4,12 @@ import Link from 'next/link'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { GlobalStore, useGlobalStore } from '@/store/global'; +import { useGlobalStore } from '@/store/global'; import { SidebarTabKey } from '@/store/global/initialState'; import { useSessionStore } from '@/store/session'; export interface TopActionProps { - tab?: GlobalStore['sidebarKey']; + tab?: SidebarTabKey; } const TopActions = memo(({ tab }) => { diff --git a/src/layout/DefaultLayout/Mobile/index.tsx b/src/layout/DefaultLayout/Mobile/index.tsx index 523cabb55b314..c1b8b2c307efb 100644 --- a/src/layout/DefaultLayout/Mobile/index.tsx +++ b/src/layout/DefaultLayout/Mobile/index.tsx @@ -7,7 +7,7 @@ import { CSSProperties, PropsWithChildren, memo } from 'react'; import { Flexbox } from 'react-layout-kit'; import SafeSpacing from '@/components/SafeSpacing'; -import { SidebarTabKey } from '@/store/global/slices/common/initialState'; +import { SidebarTabKey } from '@/store/global/initialState'; const MobileTabBar = dynamic(() => import('@/features/MobileTabBar')); diff --git a/src/layout/GlobalProvider/AppTheme.tsx b/src/layout/GlobalProvider/AppTheme.tsx index fa06627909445..8e85a0e83c674 100644 --- a/src/layout/GlobalProvider/AppTheme.tsx +++ b/src/layout/GlobalProvider/AppTheme.tsx @@ -13,8 +13,8 @@ import { LOBE_THEME_NEUTRAL_COLOR, LOBE_THEME_PRIMARY_COLOR, } from '@/const/theme'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { GlobalStyle } from '@/styles'; import { setCookie } from '@/utils/cookie'; @@ -83,9 +83,9 @@ const AppTheme = memo( // console.debug('server:appearance', defaultAppearance); // console.debug('server:primaryColor', defaultPrimaryColor); // console.debug('server:neutralColor', defaultNeutralColor); - const themeMode = useGlobalStore((s) => settingsSelectors.currentSettings(s).themeMode); + const themeMode = useUserStore((s) => settingsSelectors.currentSettings(s).themeMode); - const [primaryColor, neutralColor] = useGlobalStore((s) => [ + const [primaryColor, neutralColor] = useUserStore((s) => [ settingsSelectors.currentSettings(s).primaryColor, settingsSelectors.currentSettings(s).neutralColor, ]); diff --git a/src/layout/GlobalProvider/StoreInitialization.tsx b/src/layout/GlobalProvider/StoreInitialization.tsx index f3235293b220e..87ea868d123b2 100644 --- a/src/layout/GlobalProvider/StoreInitialization.tsx +++ b/src/layout/GlobalProvider/StoreInitialization.tsx @@ -8,16 +8,21 @@ import { useIsMobile } from '@/hooks/useIsMobile'; import { useEnabledDataSync } from '@/hooks/useSyncData'; import { useAgentStore } from '@/store/agent'; import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; const StoreInitialization = memo(() => { - const [useFetchServerConfig, useFetchUserConfig, useInitPreference] = useGlobalStore((s) => [ + const [useFetchServerConfig, useFetchUserConfig, useInitPreference] = useUserStore((s) => [ s.useFetchServerConfig, s.useFetchUserConfig, s.useInitPreference, ]); + const useInitGlobalPreference = useGlobalStore((s) => s.useInitGlobalPreference); + const useFetchDefaultAgentConfig = useAgentStore((s) => s.useFetchDefaultAgentConfig); // init the system preference useInitPreference(); + useInitGlobalPreference(); + useFetchDefaultAgentConfig(); const { isLoading } = useFetchServerConfig(); diff --git a/src/services/__tests__/chat.test.ts b/src/services/__tests__/chat.test.ts index e87c85f60cd4a..a33ea4676fbad 100644 --- a/src/services/__tests__/chat.test.ts +++ b/src/services/__tests__/chat.test.ts @@ -23,12 +23,9 @@ import { } from '@/libs/agent-runtime'; import { AgentRuntime } from '@/libs/agent-runtime'; import { useFileStore } from '@/store/file'; -import { GlobalStore } from '@/store/global'; -import { - GlobalSettingsState, - initialSettingsState, -} from '@/store/global/slices/settings/initialState'; import { useToolStore } from '@/store/tool'; +import { UserStore } from '@/store/user'; +import { UserSettingsState, initialSettingsState } from '@/store/user/slices/settings/initialState'; import { DalleManifest } from '@/tools/dalle'; import { ChatMessage } from '@/types/message'; import { ChatStreamPayload } from '@/types/openai/chat'; @@ -695,7 +692,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.OpenAI, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI); @@ -713,7 +710,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Azure, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeAzureOpenAI); @@ -728,7 +725,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Google, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeGoogleAI); @@ -743,7 +740,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Moonshot, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeMoonshotAI); @@ -760,7 +757,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Bedrock, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeBedrockAI); @@ -775,7 +772,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Ollama, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeOllamaAI); @@ -790,7 +787,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Perplexity, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobePerplexityAI); @@ -805,7 +802,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Anthropic, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeAnthropicAI); @@ -820,7 +817,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Mistral, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeMistralAI); @@ -835,7 +832,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.OpenRouter, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeOpenRouterAI); @@ -850,7 +847,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.TogetherAI, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeTogetherAI); @@ -865,7 +862,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.ZeroOne, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeZeroOneAI); @@ -880,7 +877,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.Groq, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeGroq); @@ -900,7 +897,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as any as GlobalSettingsState) as unknown as GlobalStore; + } as any as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore('unknown' as ModelProvider, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI); @@ -927,7 +924,7 @@ describe('AgentRuntimeOnClient', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const runtime = await initializeWithClientStore(ModelProvider.ZhiPu, {}); expect(runtime).toBeInstanceOf(AgentRuntime); expect(runtime['_runtime']).toBeInstanceOf(LobeZhipuAI); diff --git a/src/services/__tests__/tool.test.ts b/src/services/__tests__/tool.test.ts index 7935fabc43719..0c7da7947b682 100644 --- a/src/services/__tests__/tool.test.ts +++ b/src/services/__tests__/tool.test.ts @@ -1,6 +1,6 @@ import { Mock, beforeEach, describe, expect, it, vi } from 'vitest'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { toolService } from '../tool'; import openAPIV3 from './openai/OpenAPI_V3.json'; @@ -8,7 +8,7 @@ import OpenAIPlugin from './openai/plugin.json'; // Mocking modules and functions -vi.mock('@/store/global/helpers', () => ({ +vi.mock('@/store/user/helpers', () => ({ globalHelpers: { getCurrentLanguage: vi.fn(), }, diff --git a/src/services/_auth.test.ts b/src/services/_auth.test.ts index 4ad2ee339e92a..0a188b03bafec 100644 --- a/src/services/_auth.test.ts +++ b/src/services/_auth.test.ts @@ -2,7 +2,7 @@ import { act } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import { ModelProvider } from '@/libs/agent-runtime'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import { GlobalLLMConfig, GlobalLLMProviderKey } from '@/types/settings'; import { getProviderAuthPayload } from './_auth'; @@ -23,7 +23,7 @@ const setModelProviderConfig = ( provider: T, config: Partial, ) => { - useGlobalStore.setState({ + useUserStore.setState({ settings: { languageModel: { [provider]: config } }, }); }; diff --git a/src/services/_auth.ts b/src/services/_auth.ts index 32cfe25505a78..1c17ff8b3d24f 100644 --- a/src/services/_auth.ts +++ b/src/services/_auth.ts @@ -1,14 +1,14 @@ import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth'; import { ModelProvider } from '@/libs/agent-runtime'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors, settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors, settingsSelectors } from '@/store/user/selectors'; import { createJWT } from '@/utils/jwt'; export const getProviderAuthPayload = (provider: string) => { switch (provider) { case ModelProvider.Bedrock: { const { accessKeyId, region, secretAccessKey } = modelConfigSelectors.bedrockConfig( - useGlobalStore.getState(), + useUserStore.getState(), ); const awsSecretAccessKey = secretAccessKey; const awsAccessKeyId = accessKeyId; @@ -19,7 +19,7 @@ export const getProviderAuthPayload = (provider: string) => { } case ModelProvider.Azure: { - const azure = modelConfigSelectors.azureConfig(useGlobalStore.getState()); + const azure = modelConfigSelectors.azureConfig(useUserStore.getState()); return { apiKey: azure.apiKey, @@ -29,13 +29,13 @@ export const getProviderAuthPayload = (provider: string) => { } case ModelProvider.Ollama: { - const config = modelConfigSelectors.ollamaConfig(useGlobalStore.getState()); + const config = modelConfigSelectors.ollamaConfig(useUserStore.getState()); return { endpoint: config?.endpoint }; } default: { - const config = settingsSelectors.providerConfig(provider)(useGlobalStore.getState()); + const config = settingsSelectors.providerConfig(provider)(useUserStore.getState()); return { apiKey: config?.apiKey, endpoint: config?.endpoint }; } @@ -43,7 +43,7 @@ export const getProviderAuthPayload = (provider: string) => { }; const createAuthTokenWithPayload = async (payload = {}) => { - const accessCode = settingsSelectors.password(useGlobalStore.getState()); + const accessCode = settingsSelectors.password(useUserStore.getState()); return await createJWT({ accessCode, ...payload }); }; diff --git a/src/services/_header.ts b/src/services/_header.ts index fe7c547492bb2..3380f7114af98 100644 --- a/src/services/_header.ts +++ b/src/services/_header.ts @@ -1,6 +1,6 @@ import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors, settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors, settingsSelectors } from '@/store/user/selectors'; /** * TODO: Need to be removed after tts refactor @@ -8,12 +8,12 @@ import { modelConfigSelectors, settingsSelectors } from '@/store/global/selector */ // eslint-disable-next-line no-undef export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => { - const openAIConfig = modelConfigSelectors.openAIConfig(useGlobalStore.getState()); + const openAIConfig = modelConfigSelectors.openAIConfig(useUserStore.getState()); // eslint-disable-next-line no-undef return { ...header, - [LOBE_CHAT_ACCESS_CODE]: settingsSelectors.password(useGlobalStore.getState()), + [LOBE_CHAT_ACCESS_CODE]: settingsSelectors.password(useUserStore.getState()), [OPENAI_API_KEY_HEADER_KEY]: openAIConfig.apiKey || '', [OPENAI_END_POINT]: openAIConfig.endpoint || '', }; diff --git a/src/services/chat.ts b/src/services/chat.ts index c02f1a6209449..51059e767fd6c 100644 --- a/src/services/chat.ts +++ b/src/services/chat.ts @@ -7,17 +7,17 @@ import { DEFAULT_AGENT_CONFIG } from '@/const/settings'; import { TracePayload, TraceTagMap } from '@/const/trace'; import { AgentRuntime, ChatCompletionErrorPayload, ModelProvider } from '@/libs/agent-runtime'; import { filesSelectors, useFileStore } from '@/store/file'; -import { useGlobalStore } from '@/store/global'; +import { useSessionStore } from '@/store/session'; +import { sessionMetaSelectors } from '@/store/session/selectors'; +import { useToolStore } from '@/store/tool'; +import { pluginSelectors, toolSelectors } from '@/store/tool/selectors'; +import { useUserStore } from '@/store/user'; import { commonSelectors, modelConfigSelectors, modelProviderSelectors, preferenceSelectors, -} from '@/store/global/selectors'; -import { useSessionStore } from '@/store/session'; -import { sessionMetaSelectors } from '@/store/session/selectors'; -import { useToolStore } from '@/store/tool'; -import { pluginSelectors, toolSelectors } from '@/store/tool/selectors'; +} from '@/store/user/selectors'; import { ChatErrorType } from '@/types/fetch'; import { ChatMessage } from '@/types/message'; import type { ChatStreamPayload, OpenAIChatMessage } from '@/types/openai/chat'; @@ -195,7 +195,7 @@ class ChatService { // check this model can use function call const canUseFC = modelProviderSelectors.isModelEnabledFunctionCall(payload.model)( - useGlobalStore.getState(), + useUserStore.getState(), ); // the rule that model can use tools: // 1. tools is not empty @@ -241,7 +241,7 @@ class ChatService { // if the provider is Azure, get the deployment name as the request model if (provider === ModelProvider.Azure) { const chatModelCards = modelProviderSelectors.getModelCardsById(provider)( - useGlobalStore.getState(), + useUserStore.getState(), ); const deploymentName = chatModelCards.find((i) => i.id === model)?.deploymentName; @@ -257,7 +257,7 @@ class ChatService { * Use browser agent runtime */ const enableFetchOnClient = modelConfigSelectors.isProviderFetchOnClient(provider)( - useGlobalStore.getState(), + useUserStore.getState(), ); /** * Notes: @@ -391,7 +391,7 @@ class ChatService { if (imageList.length === 0) return m.content; const canUploadFile = modelProviderSelectors.isModelEnabledUpload(model)( - useGlobalStore.getState(), + useUserStore.getState(), ); if (!canUploadFile) { @@ -426,7 +426,7 @@ class ChatService { return produce(postMessages, (draft) => { if (!tools || tools.length === 0) return; const hasFC = modelProviderSelectors.isModelEnabledFunctionCall(model)( - useGlobalStore.getState(), + useUserStore.getState(), ); if (!hasFC) return; @@ -449,7 +449,7 @@ class ChatService { private mapTrace(trace?: TracePayload, tag?: TraceTagMap): TracePayload { const tags = sessionMetaSelectors.currentAgentMeta(useSessionStore.getState()).tags || []; - const enabled = preferenceSelectors.userAllowTrace(useGlobalStore.getState()); + const enabled = preferenceSelectors.userAllowTrace(useUserStore.getState()); if (!enabled) return { enabled: false }; @@ -457,7 +457,7 @@ class ChatService { ...trace, enabled: true, tags: [tag, ...(trace?.tags || []), ...tags].filter(Boolean) as string[], - userId: commonSelectors.userId(useGlobalStore.getState()), + userId: commonSelectors.userId(useUserStore.getState()), }; } diff --git a/src/services/config.ts b/src/services/config.ts index 3043afb27ccbd..89789c8f2440c 100644 --- a/src/services/config.ts +++ b/src/services/config.ts @@ -1,10 +1,10 @@ import { messageService } from '@/services/message'; import { sessionService } from '@/services/session'; import { topicService } from '@/services/topic'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; import { useSessionStore } from '@/store/session'; import { sessionSelectors } from '@/store/session/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { ConfigFile } from '@/types/exportConfig'; import { ChatMessage } from '@/types/message'; import { LobeSessions, SessionGroupItem } from '@/types/session'; @@ -36,7 +36,7 @@ class ConfigService { return messageService.batchCreateMessages(messages); }; importSettings = async (settings: GlobalSettings) => { - useGlobalStore.getState().importAppSettings(settings); + useUserStore.getState().importAppSettings(settings); }; importTopics = async (topics: ChatTopic[]) => { return topicService.batchCreateTopics(topics); @@ -183,7 +183,7 @@ class ConfigService { exportConfigFile(config, 'config'); }; - private getSettings = () => settingsSelectors.exportSettings(useGlobalStore.getState()); + private getSettings = () => settingsSelectors.exportSettings(useUserStore.getState()); private getSession = (id: string) => sessionSelectors.getSessionById(id)(useSessionStore.getState()); diff --git a/src/services/models.ts b/src/services/models.ts index 7ab495820cd2a..876d2ae35720b 100644 --- a/src/services/models.ts +++ b/src/services/models.ts @@ -1,6 +1,6 @@ import { createHeaderWithAuth } from '@/services/_auth'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors } from '@/store/user/selectors'; import { ChatModelCard } from '@/types/llm'; import { API_ENDPOINTS } from './_url'; @@ -17,7 +17,7 @@ class ModelsService { * Use browser agent runtime */ const enableFetchOnClient = modelConfigSelectors.isProviderFetchOnClient(provider)( - useGlobalStore.getState(), + useUserStore.getState(), ); if (enableFetchOnClient) { const agentRuntime = await initializeWithClientStore(provider, {}); diff --git a/src/services/ollama.ts b/src/services/ollama.ts index ccdbf6eaf5d93..669b06779c942 100644 --- a/src/services/ollama.ts +++ b/src/services/ollama.ts @@ -2,8 +2,8 @@ import { ListResponse, Ollama as OllamaBrowser, ProgressResponse } from 'ollama/ import { createErrorResponse } from '@/app/api/errorResponse'; import { ModelProvider } from '@/libs/agent-runtime'; -import { useGlobalStore } from '@/store/global'; -import { modelConfigSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { modelConfigSelectors } from '@/store/user/selectors'; import { ChatErrorType } from '@/types/fetch'; import { getMessageError } from '@/utils/fetch'; @@ -25,7 +25,7 @@ export class OllamaService { } getHost = (): string => { - const config = modelConfigSelectors.ollamaConfig(useGlobalStore.getState()); + const config = modelConfigSelectors.ollamaConfig(useUserStore.getState()); return config.endpoint || DEFAULT_BASE_URL; }; diff --git a/src/services/session/client.ts b/src/services/session/client.ts index 8e3c0d3e246b6..4e9532d9dafd6 100644 --- a/src/services/session/client.ts +++ b/src/services/session/client.ts @@ -4,7 +4,7 @@ import { INBOX_SESSION_ID } from '@/const/session'; import { SessionModel } from '@/database/client/models/session'; import { SessionGroupModel } from '@/database/client/models/sessionGroup'; import { UserModel } from '@/database/client/models/user'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import { LobeAgentConfig } from '@/types/agent'; import { ChatSessionList, @@ -97,7 +97,7 @@ export class ClientService implements ISessionService { async updateSessionConfig(activeId: string, config: DeepPartial) { if (activeId === INBOX_SESSION_ID) { - return useGlobalStore.getState().updateDefaultAgent({ config }); + return useUserStore.getState().updateDefaultAgent({ config }); } return SessionModel.updateConfig(activeId, config); diff --git a/src/services/tool.ts b/src/services/tool.ts index 26c6b52924172..365e69339cf36 100644 --- a/src/services/tool.ts +++ b/src/services/tool.ts @@ -4,7 +4,7 @@ import { pluginManifestSchema, } from '@lobehub/chat-plugin-sdk'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { OpenAIPluginManifest } from '@/types/openai/plugin'; import { API_ENDPOINTS } from './_url'; diff --git a/src/services/trace.ts b/src/services/trace.ts index 5c27ff9b266ec..68cbdfa432023 100644 --- a/src/services/trace.ts +++ b/src/services/trace.ts @@ -1,6 +1,6 @@ import { API_ENDPOINTS } from '@/services/_url'; -import { useGlobalStore } from '@/store/global'; -import { preferenceSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { preferenceSelectors } from '@/store/user/selectors'; import { TraceEventBasePayload, TraceEventPayloads } from '@/types/trace'; class TraceService { @@ -17,7 +17,7 @@ class TraceService { } async traceEvent(data: TraceEventPayloads & TraceEventBasePayload) { - const enabled = preferenceSelectors.userAllowTrace(useGlobalStore.getState()); + const enabled = preferenceSelectors.userAllowTrace(useUserStore.getState()); if (!enabled) return; diff --git a/src/store/agent/slices/chat/selectors.test.ts b/src/store/agent/slices/chat/selectors.test.ts index 3e0d3712fa812..c9dda20da94be 100644 --- a/src/store/agent/slices/chat/selectors.test.ts +++ b/src/store/agent/slices/chat/selectors.test.ts @@ -3,8 +3,8 @@ import { describe, expect, it } from 'vitest'; import { INBOX_SESSION_ID } from '@/const/session'; import { DEFAULT_AGENT_CONFIG, DEFAUTT_AGENT_TTS_CONFIG } from '@/const/settings'; import { AgentStore } from '@/store/agent'; -import { GlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/slices/settings/selectors'; +import { UserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/slices/settings/selectors'; import { LobeAgentConfig } from '@/types/agent'; import { agentSelectors } from './selectors'; diff --git a/src/store/chat/slices/message/selectors.test.ts b/src/store/chat/slices/message/selectors.test.ts index 07d04f684ad8b..5218510b15969 100644 --- a/src/store/chat/slices/message/selectors.test.ts +++ b/src/store/chat/slices/message/selectors.test.ts @@ -6,8 +6,8 @@ import { INBOX_SESSION_ID } from '@/const/session'; import { useAgentStore } from '@/store/agent'; import { ChatStore } from '@/store/chat'; import { initialState } from '@/store/chat/initialState'; -import { useGlobalStore } from '@/store/global'; import { useSessionStore } from '@/store/session'; +import { useUserStore } from '@/store/user'; import { LobeAgentConfig } from '@/types/agent'; import { ChatMessage } from '@/types/message'; import { MetaData } from '@/types/meta'; diff --git a/src/store/chat/slices/message/selectors.ts b/src/store/chat/slices/message/selectors.ts index f2ca5c707be74..67b31ea8d4a90 100644 --- a/src/store/chat/slices/message/selectors.ts +++ b/src/store/chat/slices/message/selectors.ts @@ -5,10 +5,10 @@ import { DEFAULT_INBOX_AVATAR, DEFAULT_USER_AVATAR } from '@/const/meta'; import { INBOX_SESSION_ID } from '@/const/session'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; -import { useGlobalStore } from '@/store/global'; -import { commonSelectors } from '@/store/global/selectors'; import { useSessionStore } from '@/store/session'; import { sessionMetaSelectors } from '@/store/session/selectors'; +import { useUserStore } from '@/store/user'; +import { commonSelectors } from '@/store/user/selectors'; import { ChatMessage } from '@/types/message'; import { MetaData } from '@/types/meta'; import { merge } from '@/utils/merge'; @@ -20,7 +20,7 @@ const getMeta = (message: ChatMessage) => { switch (message.role) { case 'user': { return { - avatar: commonSelectors.userAvatar(useGlobalStore.getState()) || DEFAULT_USER_AVATAR, + avatar: commonSelectors.userAvatar(useUserStore.getState()) || DEFAULT_USER_AVATAR, }; } diff --git a/src/store/global/slices/preference/action.test.ts b/src/store/global/action.test.ts similarity index 50% rename from src/store/global/slices/preference/action.test.ts rename to src/store/global/action.test.ts index ab46eccd9d375..e66df4cc76e3f 100644 --- a/src/store/global/slices/preference/action.test.ts +++ b/src/store/global/action.test.ts @@ -1,9 +1,23 @@ -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { withSWR } from '~test-utils'; -import { useGlobalStore } from '@/store/global'; +import { globalService } from '@/services/global'; +import { useGlobalStore } from '@/store/global/index'; -import { type Guide } from './initialState'; +vi.mock('zustand/traditional'); + +vi.mock('@/utils/client/switchLang', () => ({ + switchLang: vi.fn(), +})); + +vi.mock('swr', async (importOriginal) => { + const modules = await importOriginal(); + return { + ...(modules as any), + mutate: vi.fn(), + }; +}); beforeEach(() => { vi.clearAllMocks(); @@ -64,29 +78,67 @@ describe('createPreferenceSlice', () => { }); }); - describe('updateGuideState', () => { - it('should update guide state', () => { + describe('updatePreference', () => { + it('should update preference', () => { const { result } = renderHook(() => useGlobalStore()); - const guide: Guide = { topic: true }; + const preference = { inputHeight: 200 }; act(() => { - result.current.updateGuideState(guide); + result.current.updatePreference(preference); }); - expect(result.current.preference.guide).toEqual(guide); + expect(result.current.preference.inputHeight).toEqual(200); }); }); - describe('updatePreference', () => { - it('should update preference', () => { + describe('switchBackToChat', () => { + it('should switch back to chat', () => { const { result } = renderHook(() => useGlobalStore()); - const preference = { inputHeight: 200 }; + const sessionId = 'session-id'; + const router = { push: vi.fn() } as any; act(() => { - result.current.updatePreference(preference); + useGlobalStore.setState({ router }); + result.current.switchBackToChat(sessionId); }); - expect(result.current.preference.inputHeight).toEqual(200); + expect(router.push).toHaveBeenCalledWith('/chat?session=session-id'); + }); + }); + + describe('useCheckLatestVersion', () => { + it('should set hasNewVersion to false if there is no new version', async () => { + const latestVersion = '0.0.1'; + + vi.spyOn(globalService, 'getLatestVersion').mockResolvedValueOnce(latestVersion); + + const { result } = renderHook(() => useGlobalStore().useCheckLatestVersion(), { + wrapper: withSWR, + }); + + await waitFor(() => { + expect(result.current.data).toBe(latestVersion); + }); + + expect(useGlobalStore.getState().hasNewVersion).toBeUndefined(); + expect(useGlobalStore.getState().latestVersion).toBeUndefined(); + }); + + it('should set hasNewVersion to true if there is a new version', async () => { + const latestVersion = '10000000.0.0'; + + vi.spyOn(globalService, 'getLatestVersion').mockResolvedValueOnce(latestVersion); + + const { result } = renderHook(() => useGlobalStore().useCheckLatestVersion(), { + wrapper: withSWR, + }); + + await waitFor(() => { + expect(result.current.data).toBe(latestVersion); + }); + + expect(useGlobalStore.getState().hasNewVersion).toBe(true); + expect(useGlobalStore.getState().latestVersion).toBe(latestVersion); }); }); }); diff --git a/src/store/global/slices/preference/action.ts b/src/store/global/action.ts similarity index 65% rename from src/store/global/slices/preference/action.ts rename to src/store/global/action.ts index 058794bf592f8..b10e99f188449 100644 --- a/src/store/global/slices/preference/action.ts +++ b/src/store/global/action.ts @@ -1,35 +1,44 @@ import { produce } from 'immer'; -import { SWRResponse } from 'swr'; +import { gt } from 'semver'; +import useSWR, { SWRResponse } from 'swr'; import type { StateCreator } from 'zustand/vanilla'; +import { INBOX_SESSION_ID } from '@/const/session'; +import { SESSION_CHAT_URL } from '@/const/url'; +import { CURRENT_VERSION } from '@/const/version'; import { useClientDataSWR } from '@/libs/swr'; -import type { GlobalStore } from '@/store/global'; +import { globalService } from '@/services/global'; +import type { GlobalStore } from '@/store/global/index'; import { merge } from '@/utils/merge'; import { setNamespace } from '@/utils/storeDebug'; -import type { GlobalPreference, Guide } from './initialState'; +import type { GlobalPreference } from './initialState'; const n = setNamespace('preference'); /** * 设置操作 */ -export interface PreferenceAction { +export interface GlobalStoreAction { + switchBackToChat: (sessionId?: string) => void; toggleChatSideBar: (visible?: boolean) => void; toggleExpandSessionGroup: (id: string, expand: boolean) => void; toggleMobileTopic: (visible?: boolean) => void; toggleSystemRole: (visible?: boolean) => void; - updateGuideState: (guide: Partial) => void; updatePreference: (preference: Partial, action?: any) => void; - useInitPreference: () => SWRResponse; + useCheckLatestVersion: () => SWRResponse; + useInitGlobalPreference: () => SWRResponse; } -export const createPreferenceSlice: StateCreator< +export const globalActionSlice: StateCreator< GlobalStore, [['zustand/devtools', never]], [], - PreferenceAction + GlobalStoreAction > = (set, get) => ({ + switchBackToChat: (sessionId) => { + get().router?.push(SESSION_CHAT_URL(sessionId || INBOX_SESSION_ID, get().isMobile)); + }, toggleChatSideBar: (newValue) => { const showChatSideBar = typeof newValue === 'boolean' ? newValue : !get().preference.showChatSideBar; @@ -38,7 +47,7 @@ export const createPreferenceSlice: StateCreator< }, toggleExpandSessionGroup: (id, expand) => { const { preference } = get(); - const nextExpandSessionGroup = produce(preference.expandSessionGroupKeys, (draft) => { + const nextExpandSessionGroup = produce(preference.expandSessionGroupKeys, (draft: string[]) => { if (expand) { if (draft.includes(id)) return; draft.push(id); @@ -61,11 +70,6 @@ export const createPreferenceSlice: StateCreator< get().updatePreference({ showSystemRole }, n('toggleMobileTopic', newValue)); }, - updateGuideState: (guide) => { - const { updatePreference } = get(); - const nextGuide = merge(get().preference.guide, guide); - updatePreference({ guide: nextGuide }); - }, updatePreference: (preference, action) => { const nextPreference = merge(get().preference, preference); @@ -74,9 +78,19 @@ export const createPreferenceSlice: StateCreator< get().preferenceStorage.saveToLocalStorage(nextPreference); }, - useInitPreference: () => + useCheckLatestVersion: () => + useSWR('checkLatestVersion', globalService.getLatestVersion, { + // check latest version every 30 minutes + focusThrottleInterval: 1000 * 60 * 30, + onSuccess: (data: string) => { + if (gt(data, CURRENT_VERSION)) + set({ hasNewVersion: true, latestVersion: data }, false, n('checkLatestVersion')); + }, + }), + + useInitGlobalPreference: () => useClientDataSWR( - 'preference', + 'initGlobalPreference', () => get().preferenceStorage.getFromLocalStorage(), { onSuccess: (preference) => { diff --git a/src/store/global/initialState.ts b/src/store/global/initialState.ts index 0494ffe9073eb..dd7d23ddc179c 100644 --- a/src/store/global/initialState.ts +++ b/src/store/global/initialState.ts @@ -1,13 +1,63 @@ -import { GlobalCommonState, initialCommonState } from './slices/common/initialState'; -import { GlobalPreferenceState, initialPreferenceState } from './slices/preference/initialState'; -import { GlobalSettingsState, initialSettingsState } from './slices/settings/initialState'; +import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime'; -export { SettingsTabs, SidebarTabKey } from './slices/common/initialState'; +import { SessionDefaultGroup } from '@/types/session'; +import { AsyncLocalStorage } from '@/utils/localStorage'; -export type GlobalState = GlobalCommonState & GlobalSettingsState & GlobalPreferenceState; +export enum SidebarTabKey { + Chat = 'chat', + Market = 'market', + Setting = 'settings', +} + +export enum SettingsTabs { + About = 'about', + Agent = 'agent', + Common = 'common', + LLM = 'llm', + Sync = 'sync', + TTS = 'tts', +} + +export interface GlobalPreference { + // which sessionGroup should expand + expandSessionGroupKeys: string[]; + inputHeight: number; + mobileShowTopic?: boolean; + sessionsWidth: number; + showChatSideBar?: boolean; + showSessionPanel?: boolean; + showSystemRole?: boolean; +} + +export interface GlobalPreferenceState { + /** + * the user preference, which only store in local storage + */ + preference: GlobalPreference; + preferenceStorage: AsyncLocalStorage; +} + +export interface GlobalCommonState { + hasNewVersion?: boolean; + isMobile?: boolean; + latestVersion?: string; + router?: AppRouterInstance; + sidebarKey: SidebarTabKey; +} + +export type GlobalState = GlobalCommonState & GlobalPreferenceState; export const initialState: GlobalState = { - ...initialCommonState, - ...initialSettingsState, - ...initialPreferenceState, + isMobile: false, + preference: { + expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default], + inputHeight: 200, + mobileShowTopic: false, + sessionsWidth: 320, + showChatSideBar: true, + showSessionPanel: true, + showSystemRole: false, + }, + preferenceStorage: new AsyncLocalStorage('LOBE_GLOBAL_PREFERENCE'), + sidebarKey: SidebarTabKey.Chat, }; diff --git a/src/store/global/selectors.ts b/src/store/global/selectors.ts index eac9eb7b7cefd..1076b9d8f4fdf 100644 --- a/src/store/global/selectors.ts +++ b/src/store/global/selectors.ts @@ -1,8 +1,9 @@ -export { commonSelectors } from './slices/common/selectors'; -export { preferenceSelectors } from './slices/preference/selectors'; -export { - modelConfigSelectors, - modelProviderSelectors, - settingsSelectors, - syncSettingsSelectors, -} from './slices/settings/selectors'; +import { GlobalStore } from '@/store/global'; +import { SessionDefaultGroup } from '@/types/session'; + +const sessionGroupKeys = (s: GlobalStore): string[] => + s.preference.expandSessionGroupKeys || [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default]; + +export const preferenceSelectors = { + sessionGroupKeys, +}; diff --git a/src/store/global/slices/common/initialState.ts b/src/store/global/slices/common/initialState.ts deleted file mode 100644 index 3ee147c7bfb45..0000000000000 --- a/src/store/global/slices/common/initialState.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime'; - -import { PeerSyncStatus, SyncAwarenessState } from '@/types/sync'; - -export enum SidebarTabKey { - Chat = 'chat', - Market = 'market', - Setting = 'settings', -} - -export enum SettingsTabs { - About = 'about', - Agent = 'agent', - Common = 'common', - LLM = 'llm', - Sync = 'sync', - TTS = 'tts', -} - -export interface Guide { - // Topic 引导 - topic?: boolean; -} - -export interface GlobalCommonState { - hasNewVersion?: boolean; - isMobile?: boolean; - latestVersion?: string; - router?: AppRouterInstance; - sidebarKey: SidebarTabKey; - syncAwareness: SyncAwarenessState[]; - syncEnabled: boolean; - syncStatus: PeerSyncStatus; -} - -export const initialCommonState: GlobalCommonState = { - isMobile: false, - sidebarKey: SidebarTabKey.Chat, - syncAwareness: [], - syncEnabled: false, - syncStatus: PeerSyncStatus.Disabled, -}; diff --git a/src/store/global/slices/common/selectors.ts b/src/store/global/slices/common/selectors.ts deleted file mode 100644 index adf1eb90ebedd..0000000000000 --- a/src/store/global/slices/common/selectors.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { GlobalStore } from '@/store/global'; - -export const commonSelectors = { - userAvatar: (s: GlobalStore) => s.avatar || '', - userId: (s: GlobalStore) => s.userId, -}; diff --git a/src/store/global/slices/preference/initialState.ts b/src/store/global/slices/preference/initialState.ts deleted file mode 100644 index 0f96d3254f250..0000000000000 --- a/src/store/global/slices/preference/initialState.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { SessionDefaultGroup, SessionGroupId } from '@/types/session'; -import { AsyncLocalStorage } from '@/utils/localStorage'; - -export interface Guide { - // Topic 引导 - topic?: boolean; -} - -export interface GlobalPreference { - // which sessionGroup should expand - expandSessionGroupKeys: SessionGroupId[]; - guide?: Guide; - hideSyncAlert?: boolean; - inputHeight: number; - mobileShowTopic?: boolean; - - sessionsWidth: number; - showChatSideBar?: boolean; - showSessionPanel?: boolean; - showSystemRole?: boolean; - telemetry: boolean | null; - - /** - * whether to use cmd + enter to send message - */ - useCmdEnterToSend?: boolean; -} - -export interface GlobalPreferenceState { - /** - * the user preference, which only store in local storage - */ - preference: GlobalPreference; - preferenceStorage: AsyncLocalStorage; -} - -export const initialPreferenceState: GlobalPreferenceState = { - preference: { - expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default], - guide: {}, - inputHeight: 200, - mobileShowTopic: false, - sessionsWidth: 320, - showChatSideBar: true, - showSessionPanel: true, - showSystemRole: false, - telemetry: null, - useCmdEnterToSend: false, - }, - preferenceStorage: new AsyncLocalStorage('LOBE_PREFERENCE'), -}; diff --git a/src/store/global/slices/preference/selectors.ts b/src/store/global/slices/preference/selectors.ts deleted file mode 100644 index 41055b797b492..0000000000000 --- a/src/store/global/slices/preference/selectors.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { GlobalStore } from '@/store/global'; -import { SessionDefaultGroup } from '@/types/session'; - -const sessionGroupKeys = (s: GlobalStore): string[] => - s.preference.expandSessionGroupKeys || [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default]; - -const useCmdEnterToSend = (s: GlobalStore): boolean => s.preference.useCmdEnterToSend || false; - -const userAllowTrace = (s: GlobalStore) => s.preference.telemetry; - -const hideSyncAlert = (s: GlobalStore) => s.preference.hideSyncAlert; - -export const preferenceSelectors = { - hideSyncAlert, - sessionGroupKeys, - useCmdEnterToSend, - userAllowTrace, -}; diff --git a/src/store/global/slices/settings/selectors/sync.ts b/src/store/global/slices/settings/selectors/sync.ts deleted file mode 100644 index aae8e1f464a37..0000000000000 --- a/src/store/global/slices/settings/selectors/sync.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { GlobalStore } from '../../../store'; -import { currentSettings } from './settings'; - -const webrtcConfig = (s: GlobalStore) => currentSettings(s).sync.webrtc; -const webrtcChannelName = (s: GlobalStore) => webrtcConfig(s).channelName; -const enableWebRTC = (s: GlobalStore) => webrtcConfig(s).enabled; -const deviceName = (s: GlobalStore) => currentSettings(s).sync.deviceName; - -export const syncSettingsSelectors = { - deviceName, - enableWebRTC, - webrtcChannelName, - webrtcConfig, -}; diff --git a/src/store/global/store.ts b/src/store/global/store.ts index 75382b4296f42..4a616ffda66fc 100644 --- a/src/store/global/store.ts +++ b/src/store/global/store.ts @@ -5,20 +5,16 @@ import { StateCreator } from 'zustand/vanilla'; import { isDev } from '@/utils/env'; +import { type GlobalStoreAction, globalActionSlice } from './action'; import { type GlobalState, initialState } from './initialState'; -import { type CommonAction, createCommonSlice } from './slices/common/action'; -import { type PreferenceAction, createPreferenceSlice } from './slices/preference/action'; -import { type SettingsAction, createSettingsSlice } from './slices/settings/actions'; // =============== 聚合 createStoreFn ============ // -export type GlobalStore = CommonAction & GlobalState & SettingsAction & PreferenceAction; +export type GlobalStore = GlobalState & GlobalStoreAction; const createStore: StateCreator = (...parameters) => ({ ...initialState, - ...createCommonSlice(...parameters), - ...createSettingsSlice(...parameters), - ...createPreferenceSlice(...parameters), + ...globalActionSlice(...parameters), }); // =============== 实装 useStore ============ // diff --git a/src/store/market/action.ts b/src/store/market/action.ts index 3ced8e9947fbd..7cb74136a9d8b 100644 --- a/src/store/market/action.ts +++ b/src/store/market/action.ts @@ -4,7 +4,7 @@ import useSWR, { SWRResponse } from 'swr'; import type { StateCreator } from 'zustand/vanilla'; import { marketService } from '@/services/market'; -import { globalHelpers } from '@/store/global/helpers'; +import { globalHelpers } from '@/store/user/helpers'; import { AgentsMarketItem, LobeChatAgentsMarketIndex } from '@/types/market'; import type { Store } from './store'; diff --git a/src/store/session/slices/session/action.ts b/src/store/session/slices/session/action.ts index 0ff4b6f9636c9..c4ed8f746c57b 100644 --- a/src/store/session/slices/session/action.ts +++ b/src/store/session/slices/session/action.ts @@ -8,9 +8,9 @@ import { message } from '@/components/AntdStaticMethods'; import { DEFAULT_AGENT_LOBE_SESSION, INBOX_SESSION_ID } from '@/const/session'; import { useClientDataSWR } from '@/libs/swr'; import { sessionService } from '@/services/session'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; import { SessionStore } from '@/store/session'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { MetaData } from '@/types/meta'; import { ChatSessionList, @@ -111,7 +111,7 @@ export const createSessionSlice: StateCreator< // merge the defaultAgent in settings const defaultAgent = merge( DEFAULT_AGENT_LOBE_SESSION, - settingsSelectors.defaultAgent(useGlobalStore.getState()), + settingsSelectors.defaultAgent(useUserStore.getState()), ); const newSession: LobeAgentSession = merge(defaultAgent, agent); diff --git a/src/store/global/helpers.ts b/src/store/user/helpers.ts similarity index 71% rename from src/store/global/helpers.ts rename to src/store/user/helpers.ts index f2b8ced737c68..7fa10a27410e4 100644 --- a/src/store/global/helpers.ts +++ b/src/store/user/helpers.ts @@ -1,7 +1,7 @@ import { settingsSelectors } from './slices/settings/selectors'; -import { useGlobalStore } from './store'; +import { useUserStore } from './store'; -const getCurrentLanguage = () => settingsSelectors.currentLanguage(useGlobalStore.getState()); +const getCurrentLanguage = () => settingsSelectors.currentLanguage(useUserStore.getState()); export const globalHelpers = { getCurrentLanguage, diff --git a/src/store/user/index.ts b/src/store/user/index.ts new file mode 100644 index 0000000000000..d4068169bcd1e --- /dev/null +++ b/src/store/user/index.ts @@ -0,0 +1 @@ +export * from './store'; diff --git a/src/store/user/initialState.ts b/src/store/user/initialState.ts new file mode 100644 index 0000000000000..5f8c9f3be99d7 --- /dev/null +++ b/src/store/user/initialState.ts @@ -0,0 +1,11 @@ +import { UserCommonState, initialCommonState } from './slices/common/initialState'; +import { UserPreferenceState, initialPreferenceState } from './slices/preference/initialState'; +import { UserSettingsState, initialSettingsState } from './slices/settings/initialState'; + +export type UserState = UserCommonState & UserSettingsState & UserPreferenceState; + +export const initialState: UserState = { + ...initialCommonState, + ...initialSettingsState, + ...initialPreferenceState, +}; diff --git a/src/store/user/selectors.ts b/src/store/user/selectors.ts new file mode 100644 index 0000000000000..eac9eb7b7cefd --- /dev/null +++ b/src/store/user/selectors.ts @@ -0,0 +1,8 @@ +export { commonSelectors } from './slices/common/selectors'; +export { preferenceSelectors } from './slices/preference/selectors'; +export { + modelConfigSelectors, + modelProviderSelectors, + settingsSelectors, + syncSettingsSelectors, +} from './slices/settings/selectors'; diff --git a/src/store/global/slices/common/action.test.ts b/src/store/user/slices/common/action.test.ts similarity index 70% rename from src/store/global/slices/common/action.test.ts rename to src/store/user/slices/common/action.test.ts index 6d912fc1dc75a..08f0ddd59b4cc 100644 --- a/src/store/global/slices/common/action.test.ts +++ b/src/store/user/slices/common/action.test.ts @@ -6,10 +6,10 @@ import { withSWR } from '~test-utils'; import { globalService } from '@/services/global'; import { messageService } from '@/services/message'; import { userService } from '@/services/user'; -import { useGlobalStore } from '@/store/global'; -import { commonSelectors } from '@/store/global/slices/common/selectors'; -import { preferenceSelectors } from '@/store/global/slices/preference/selectors'; -import { syncSettingsSelectors } from '@/store/global/slices/settings/selectors'; +import { useUserStore } from '@/store/user'; +import { commonSelectors } from '@/store/user/slices/common/selectors'; +import { preferenceSelectors } from '@/store/user/slices/preference/selectors'; +import { syncSettingsSelectors } from '@/store/user/slices/settings/selectors'; import { GlobalServerConfig } from '@/types/serverConfig'; import { switchLang } from '@/utils/client/switchLang'; @@ -32,24 +32,9 @@ afterEach(() => { }); describe('createCommonSlice', () => { - describe('switchBackToChat', () => { - it('should switch back to chat', () => { - const { result } = renderHook(() => useGlobalStore()); - const sessionId = 'session-id'; - const router = { push: vi.fn() } as any; - - act(() => { - useGlobalStore.setState({ router }); - result.current.switchBackToChat(sessionId); - }); - - expect(router.push).toHaveBeenCalledWith('/chat?session=session-id'); - }); - }); - describe('refreshUserConfig', () => { it('should refresh user config', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); await act(async () => { await result.current.refreshUserConfig(); @@ -61,7 +46,7 @@ describe('createCommonSlice', () => { describe('updateAvatar', () => { it('should update avatar', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const avatar = 'new-avatar'; const spyOn = vi.spyOn(result.current, 'refreshUserConfig'); @@ -76,42 +61,6 @@ describe('createCommonSlice', () => { }); }); - describe('useCheckLatestVersion', () => { - it('should set hasNewVersion to false if there is no new version', async () => { - const latestVersion = '0.0.1'; - - vi.spyOn(globalService, 'getLatestVersion').mockResolvedValueOnce(latestVersion); - - const { result } = renderHook(() => useGlobalStore().useCheckLatestVersion(), { - wrapper: withSWR, - }); - - await waitFor(() => { - expect(result.current.data).toBe(latestVersion); - }); - - expect(useGlobalStore.getState().hasNewVersion).toBeUndefined(); - expect(useGlobalStore.getState().latestVersion).toBeUndefined(); - }); - - it('should set hasNewVersion to true if there is a new version', async () => { - const latestVersion = '10000000.0.0'; - - vi.spyOn(globalService, 'getLatestVersion').mockResolvedValueOnce(latestVersion); - - const { result } = renderHook(() => useGlobalStore().useCheckLatestVersion(), { - wrapper: withSWR, - }); - - await waitFor(() => { - expect(result.current.data).toBe(latestVersion); - }); - - expect(useGlobalStore.getState().hasNewVersion).toBe(true); - expect(useGlobalStore.getState().latestVersion).toBe(latestVersion); - }); - }); - describe('useFetchServerConfig', () => { it('should fetch server config correctly', async () => { const mockServerConfig = { @@ -121,7 +70,7 @@ describe('createCommonSlice', () => { } as GlobalServerConfig; vi.spyOn(globalService, 'getGlobalConfig').mockResolvedValueOnce(mockServerConfig); - const { result } = renderHook(() => useGlobalStore().useFetchServerConfig()); + const { result } = renderHook(() => useUserStore().useFetchServerConfig()); await waitFor(() => expect(result.current.data).toEqual(mockServerConfig)); }); @@ -132,7 +81,7 @@ describe('createCommonSlice', () => { const mockUserConfig: any = undefined; // 模拟未初始化服务器的情况 vi.spyOn(userService, 'getUserConfig').mockResolvedValueOnce(mockUserConfig); - const { result } = renderHook(() => useGlobalStore().useFetchUserConfig(false), { + const { result } = renderHook(() => useUserStore().useFetchUserConfig(false), { wrapper: withSWR, }); @@ -151,7 +100,7 @@ describe('createCommonSlice', () => { }; vi.spyOn(userService, 'getUserConfig').mockResolvedValueOnce(mockUserConfig); - const { result } = renderHook(() => useGlobalStore().useFetchUserConfig(true), { + const { result } = renderHook(() => useUserStore().useFetchUserConfig(true), { wrapper: withSWR, }); @@ -159,8 +108,8 @@ describe('createCommonSlice', () => { await waitFor(() => expect(result.current.data).toEqual(mockUserConfig)); // 验证状态是否正确更新 - expect(useGlobalStore.getState().avatar).toBe(mockUserConfig.avatar); - expect(useGlobalStore.getState().settings).toEqual(mockUserConfig.settings); + expect(useUserStore.getState().avatar).toBe(mockUserConfig.avatar); + expect(useUserStore.getState().settings).toEqual(mockUserConfig.settings); // 验证是否正确处理了语言设置 expect(switchLang).not.toHaveBeenCalledWith('auto'); @@ -174,7 +123,7 @@ describe('createCommonSlice', () => { }; vi.spyOn(userService, 'getUserConfig').mockResolvedValueOnce(mockUserConfig); - const { result } = renderHook(() => useGlobalStore().useFetchUserConfig(true), { + const { result } = renderHook(() => useUserStore().useFetchUserConfig(true), { wrapper: withSWR, }); @@ -182,8 +131,8 @@ describe('createCommonSlice', () => { await waitFor(() => expect(result.current.data).toEqual(mockUserConfig)); // 验证状态是否正确更新 - expect(useGlobalStore.getState().avatar).toBe(mockUserConfig.avatar); - expect(useGlobalStore.getState().settings).toEqual(mockUserConfig.settings); + expect(useUserStore.getState().avatar).toBe(mockUserConfig.avatar); + expect(useUserStore.getState().settings).toEqual(mockUserConfig.settings); // 验证是否正确处理了语言设置 expect(switchLang).toHaveBeenCalledWith('auto'); @@ -192,7 +141,7 @@ describe('createCommonSlice', () => { it('should handle the case when user config is null', async () => { vi.spyOn(userService, 'getUserConfig').mockResolvedValueOnce(null as any); - const { result } = renderHook(() => useGlobalStore().useFetchUserConfig(true), { + const { result } = renderHook(() => useUserStore().useFetchUserConfig(true), { wrapper: withSWR, }); @@ -200,14 +149,14 @@ describe('createCommonSlice', () => { await waitFor(() => expect(result.current.data).toBeNull()); // 验证状态未被错误更新 - expect(useGlobalStore.getState().avatar).toBeUndefined(); - expect(useGlobalStore.getState().settings).toEqual({}); + expect(useUserStore.getState().avatar).toBeUndefined(); + expect(useUserStore.getState().settings).toEqual({}); }); }); describe('refreshConnection', () => { it('should not call triggerEnableSync when userId is empty', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const onEvent = vi.fn(); vi.spyOn(commonSelectors, 'userId').mockReturnValueOnce(undefined); @@ -221,7 +170,7 @@ describe('createCommonSlice', () => { }); it('should call triggerEnableSync when userId exists', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const onEvent = vi.fn(); const userId = 'user-id'; @@ -238,7 +187,7 @@ describe('createCommonSlice', () => { describe('triggerEnableSync', () => { it('should return false when sync.channelName is empty', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const userId = 'user-id'; const onEvent = vi.fn(); @@ -270,7 +219,7 @@ describe('createCommonSlice', () => { }); vi.spyOn(syncSettingsSelectors, 'deviceName').mockReturnValueOnce(deviceName); const enabledSyncSpy = vi.spyOn(globalService, 'enabledSync').mockResolvedValueOnce(true); - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const data = await act(async () => { return result.current.triggerEnableSync(userId, onEvent); @@ -290,7 +239,7 @@ describe('createCommonSlice', () => { describe('useCheckTrace', () => { it('should return false when shouldFetch is false', async () => { - const { result } = renderHook(() => useGlobalStore().useCheckTrace(false), { + const { result } = renderHook(() => useUserStore().useCheckTrace(false), { wrapper: withSWR, }); @@ -300,7 +249,7 @@ describe('createCommonSlice', () => { it('should return false when userAllowTrace is already set', async () => { vi.spyOn(preferenceSelectors, 'userAllowTrace').mockReturnValueOnce(true); - const { result } = renderHook(() => useGlobalStore().useCheckTrace(true), { + const { result } = renderHook(() => useUserStore().useCheckTrace(true), { wrapper: withSWR, }); @@ -313,7 +262,7 @@ describe('createCommonSlice', () => { .spyOn(messageService, 'messageCountToCheckTrace') .mockResolvedValueOnce(true); - const { result } = renderHook(() => useGlobalStore().useCheckTrace(true), { + const { result } = renderHook(() => useUserStore().useCheckTrace(true), { wrapper: withSWR, }); @@ -324,10 +273,9 @@ describe('createCommonSlice', () => { describe('useEnabledSync', () => { it('should return false when userId is empty', async () => { - const { result } = renderHook( - () => useGlobalStore().useEnabledSync(true, undefined, vi.fn()), - { wrapper: withSWR }, - ); + const { result } = renderHook(() => useUserStore().useEnabledSync(true, undefined, vi.fn()), { + wrapper: withSWR, + }); await waitFor(() => expect(result.current.data).toBe(false)); }); @@ -336,7 +284,7 @@ describe('createCommonSlice', () => { const disableSyncSpy = vi.spyOn(globalService, 'disableSync').mockResolvedValueOnce(false); const { result } = renderHook( - () => useGlobalStore().useEnabledSync(false, 'user-id', vi.fn()), + () => useUserStore().useEnabledSync(false, 'user-id', vi.fn()), { wrapper: withSWR }, ); @@ -349,7 +297,7 @@ describe('createCommonSlice', () => { const onEvent = vi.fn(); const triggerEnableSyncSpy = vi.fn().mockResolvedValueOnce(true); - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); // replace triggerEnableSync as a mock result.current.triggerEnableSync = triggerEnableSyncSpy; diff --git a/src/store/global/slices/common/action.ts b/src/store/user/slices/common/action.ts similarity index 87% rename from src/store/global/slices/common/action.ts rename to src/store/user/slices/common/action.ts index d973441b68666..2e493246a919f 100644 --- a/src/store/global/slices/common/action.ts +++ b/src/store/user/slices/common/action.ts @@ -1,15 +1,11 @@ -import { gt } from 'semver'; import useSWR, { SWRResponse, mutate } from 'swr'; import { DeepPartial } from 'utility-types'; import type { StateCreator } from 'zustand/vanilla'; -import { INBOX_SESSION_ID } from '@/const/session'; -import { SESSION_CHAT_URL } from '@/const/url'; -import { CURRENT_VERSION } from '@/const/version'; import { globalService } from '@/services/global'; import { messageService } from '@/services/message'; import { UserConfig, userService } from '@/services/user'; -import type { GlobalStore } from '@/store/global'; +import type { UserStore } from '@/store/user'; import type { GlobalServerConfig } from '@/types/serverConfig'; import type { GlobalSettings } from '@/types/settings'; import { OnSyncEvent, PeerSyncStatus } from '@/types/sync'; @@ -30,10 +26,8 @@ const n = setNamespace('common'); export interface CommonAction { refreshConnection: (onEvent: OnSyncEvent) => Promise; refreshUserConfig: () => Promise; - switchBackToChat: (sessionId?: string) => void; triggerEnableSync: (userId: string, onEvent: OnSyncEvent) => Promise; updateAvatar: (avatar: string) => Promise; - useCheckLatestVersion: () => SWRResponse; useCheckTrace: (shouldFetch: boolean) => SWRResponse; useEnabledSync: ( userEnableSync: boolean, @@ -47,7 +41,7 @@ export interface CommonAction { const USER_CONFIG_FETCH_KEY = 'fetchUserConfig'; export const createCommonSlice: StateCreator< - GlobalStore, + UserStore, [['zustand/devtools', never]], [], CommonAction @@ -67,9 +61,6 @@ export const createCommonSlice: StateCreator< get().refreshModelProviderList(); }, - switchBackToChat: (sessionId) => { - get().router?.push(SESSION_CHAT_URL(sessionId || INBOX_SESSION_ID, get().isMobile)); - }, triggerEnableSync: async (userId: string, onEvent: OnSyncEvent) => { // double-check the sync ability // if there is no channelName, don't start sync @@ -106,15 +97,6 @@ export const createCommonSlice: StateCreator< await userService.updateAvatar(avatar); await get().refreshUserConfig(); }, - useCheckLatestVersion: () => - useSWR('checkLatestVersion', globalService.getLatestVersion, { - // check latest version every 30 minutes - focusThrottleInterval: 1000 * 60 * 30, - onSuccess: (data: string) => { - if (gt(data, CURRENT_VERSION)) - set({ hasNewVersion: true, latestVersion: data }, false, n('checkLatestVersion')); - }, - }), useCheckTrace: (shouldFetch) => useSWR( ['checkTrace', shouldFetch], diff --git a/src/store/user/slices/common/initialState.ts b/src/store/user/slices/common/initialState.ts new file mode 100644 index 0000000000000..a88f6c7649ec9 --- /dev/null +++ b/src/store/user/slices/common/initialState.ts @@ -0,0 +1,18 @@ +import { PeerSyncStatus, SyncAwarenessState } from '@/types/sync'; + +export interface Guide { + // Topic 引导 + topic?: boolean; +} + +export interface UserCommonState { + syncAwareness: SyncAwarenessState[]; + syncEnabled: boolean; + syncStatus: PeerSyncStatus; +} + +export const initialCommonState: UserCommonState = { + syncAwareness: [], + syncEnabled: false, + syncStatus: PeerSyncStatus.Disabled, +}; diff --git a/src/store/user/slices/common/selectors.ts b/src/store/user/slices/common/selectors.ts new file mode 100644 index 0000000000000..0947e2719b42d --- /dev/null +++ b/src/store/user/slices/common/selectors.ts @@ -0,0 +1,6 @@ +import { UserStore } from '@/store/user'; + +export const commonSelectors = { + userAvatar: (s: UserStore) => s.avatar || '', + userId: (s: UserStore) => s.userId, +}; diff --git a/src/store/user/slices/preference/action.test.ts b/src/store/user/slices/preference/action.test.ts new file mode 100644 index 0000000000000..f5bc23fd87950 --- /dev/null +++ b/src/store/user/slices/preference/action.test.ts @@ -0,0 +1,41 @@ +import { act, renderHook } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { useUserStore } from '@/store/user'; + +import { type Guide } from './initialState'; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +afterEach(() => { + vi.restoreAllMocks(); +}); + +describe('createPreferenceSlice', () => { + describe('updateGuideState', () => { + it('should update guide state', () => { + const { result } = renderHook(() => useUserStore()); + const guide: Guide = { topic: true }; + + act(() => { + result.current.updateGuideState(guide); + }); + + expect(result.current.preference.guide).toEqual(guide); + }); + }); + + describe('updatePreference', () => { + it('should update preference', () => { + const { result } = renderHook(() => useUserStore()); + + act(() => { + result.current.updatePreference({ hideSyncAlert: true }); + }); + + expect(result.current.preference.hideSyncAlert).toEqual(true); + }); + }); +}); diff --git a/src/store/user/slices/preference/action.ts b/src/store/user/slices/preference/action.ts new file mode 100644 index 0000000000000..84ba81802d9c0 --- /dev/null +++ b/src/store/user/slices/preference/action.ts @@ -0,0 +1,50 @@ +import { SWRResponse } from 'swr'; +import type { StateCreator } from 'zustand/vanilla'; + +import { useClientDataSWR } from '@/libs/swr'; +import type { UserStore } from '@/store/user'; +import { merge } from '@/utils/merge'; +import { setNamespace } from '@/utils/storeDebug'; + +import type { Guide, UserPreference } from './initialState'; + +const n = setNamespace('preference'); + +export interface PreferenceAction { + updateGuideState: (guide: Partial) => void; + updatePreference: (preference: Partial, action?: any) => void; + useInitPreference: () => SWRResponse; +} + +export const createPreferenceSlice: StateCreator< + UserStore, + [['zustand/devtools', never]], + [], + PreferenceAction +> = (set, get) => ({ + updateGuideState: (guide) => { + const { updatePreference } = get(); + const nextGuide = merge(get().preference.guide, guide); + updatePreference({ guide: nextGuide }); + }, + updatePreference: (preference, action) => { + const nextPreference = merge(get().preference, preference); + + set({ preference: nextPreference }, false, action || n('updatePreference')); + + get().preferenceStorage.saveToLocalStorage(nextPreference); + }, + + useInitPreference: () => + useClientDataSWR( + 'initUserPreference', + () => get().preferenceStorage.getFromLocalStorage(), + { + onSuccess: (preference) => { + if (preference) { + set({ preference }, false, n('initPreference')); + } + }, + }, + ), +}); diff --git a/src/store/user/slices/preference/initialState.ts b/src/store/user/slices/preference/initialState.ts new file mode 100644 index 0000000000000..5b5b5281c747f --- /dev/null +++ b/src/store/user/slices/preference/initialState.ts @@ -0,0 +1,33 @@ +import { AsyncLocalStorage } from '@/utils/localStorage'; + +export interface Guide { + // Topic 引导 + topic?: boolean; +} + +export interface UserPreference { + guide?: Guide; + hideSyncAlert?: boolean; + telemetry: boolean | null; + /** + * whether to use cmd + enter to send message + */ + useCmdEnterToSend?: boolean; +} + +export interface UserPreferenceState { + /** + * the user preference, which only store in local storage + */ + preference: UserPreference; + preferenceStorage: AsyncLocalStorage; +} + +export const initialPreferenceState: UserPreferenceState = { + preference: { + guide: {}, + telemetry: null, + useCmdEnterToSend: false, + }, + preferenceStorage: new AsyncLocalStorage('LOBE_PREFERENCE'), +}; diff --git a/src/store/user/slices/preference/selectors.ts b/src/store/user/slices/preference/selectors.ts new file mode 100644 index 0000000000000..0760007529001 --- /dev/null +++ b/src/store/user/slices/preference/selectors.ts @@ -0,0 +1,13 @@ +import { UserStore } from '@/store/user'; + +const useCmdEnterToSend = (s: UserStore): boolean => s.preference.useCmdEnterToSend || false; + +const userAllowTrace = (s: UserStore) => s.preference.telemetry; + +const hideSyncAlert = (s: UserStore) => s.preference.hideSyncAlert; + +export const preferenceSelectors = { + hideSyncAlert, + useCmdEnterToSend, + userAllowTrace, +}; diff --git a/src/store/global/slices/settings/actions/general.test.ts b/src/store/user/slices/settings/actions/general.test.ts similarity index 90% rename from src/store/global/slices/settings/actions/general.test.ts rename to src/store/user/slices/settings/actions/general.test.ts index a0cfd351dde76..fbe739410a7d2 100644 --- a/src/store/global/slices/settings/actions/general.test.ts +++ b/src/store/user/slices/settings/actions/general.test.ts @@ -5,7 +5,7 @@ import { withSWR } from '~test-utils'; import { DEFAULT_AGENT, DEFAULT_SETTINGS } from '@/const/settings'; import { userService } from '@/services/user'; -import { useGlobalStore } from '@/store/global'; +import { useUserStore } from '@/store/user'; import { LobeAgentSettings } from '@/types/session'; import { GlobalSettings } from '@/types/settings'; @@ -20,7 +20,7 @@ vi.mock('@/services/user', () => ({ describe('SettingsAction', () => { describe('importAppSettings', () => { it('should import app settings', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const newSettings: GlobalSettings = { ...DEFAULT_SETTINGS, themeMode: 'dark', @@ -51,7 +51,7 @@ describe('SettingsAction', () => { describe('resetSettings', () => { it('should reset settings to default', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); // Perform the action await act(async () => { @@ -68,7 +68,7 @@ describe('SettingsAction', () => { describe('setSettings', () => { it('should set partial settings', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const partialSettings: Partial = { themeMode: 'dark' }; // Perform the action @@ -83,7 +83,7 @@ describe('SettingsAction', () => { describe('switchThemeMode', () => { it('should switch theme mode', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const themeMode = 'light'; // Perform the action @@ -98,7 +98,7 @@ describe('SettingsAction', () => { describe('updateDefaultAgent', () => { it('should update default agent settings', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const updatedAgent: Partial = { meta: { title: 'docs' }, }; diff --git a/src/store/global/slices/settings/actions/general.ts b/src/store/user/slices/settings/actions/general.ts similarity index 96% rename from src/store/global/slices/settings/actions/general.ts rename to src/store/user/slices/settings/actions/general.ts index fa45a574d08e5..4e2890b411284 100644 --- a/src/store/global/slices/settings/actions/general.ts +++ b/src/store/user/slices/settings/actions/general.ts @@ -4,7 +4,7 @@ import { DeepPartial } from 'utility-types'; import type { StateCreator } from 'zustand/vanilla'; import { userService } from '@/services/user'; -import type { GlobalStore } from '@/store/global'; +import type { UserStore } from '@/store/user'; import { LobeAgentSettings } from '@/types/session'; import { GlobalSettings } from '@/types/settings'; import { difference } from '@/utils/difference'; @@ -19,7 +19,7 @@ export interface GeneralSettingsAction { } export const generalSettingsSlice: StateCreator< - GlobalStore, + UserStore, [['zustand/devtools', never]], [], GeneralSettingsAction diff --git a/src/store/global/slices/settings/actions/index.ts b/src/store/user/slices/settings/actions/index.ts similarity index 87% rename from src/store/global/slices/settings/actions/index.ts rename to src/store/user/slices/settings/actions/index.ts index 880ffb65f2ec3..2200e0ee08a3c 100644 --- a/src/store/global/slices/settings/actions/index.ts +++ b/src/store/user/slices/settings/actions/index.ts @@ -1,6 +1,6 @@ import type { StateCreator } from 'zustand/vanilla'; -import type { GlobalStore } from '@/store/global'; +import type { UserStore } from '@/store/user'; import { GeneralSettingsAction, generalSettingsSlice } from './general'; import { LLMSettingsAction, llmSettingsSlice } from './llm'; @@ -8,7 +8,7 @@ import { LLMSettingsAction, llmSettingsSlice } from './llm'; export interface SettingsAction extends LLMSettingsAction, GeneralSettingsAction {} export const createSettingsSlice: StateCreator< - GlobalStore, + UserStore, [['zustand/devtools', never]], [], SettingsAction diff --git a/src/store/global/slices/settings/actions/llm.test.ts b/src/store/user/slices/settings/actions/llm.test.ts similarity index 86% rename from src/store/global/slices/settings/actions/llm.test.ts rename to src/store/user/slices/settings/actions/llm.test.ts index f59ca0d156182..fa7943588bfc3 100644 --- a/src/store/global/slices/settings/actions/llm.test.ts +++ b/src/store/user/slices/settings/actions/llm.test.ts @@ -2,16 +2,13 @@ import { act, renderHook } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import { userService } from '@/services/user'; -import { GlobalStore, useGlobalStore } from '@/store/global'; -import { - GlobalSettingsState, - initialSettingsState, -} from '@/store/global/slices/settings/initialState'; +import { UserStore, useUserStore } from '@/store/user'; +import { UserSettingsState, initialSettingsState } from '@/store/user/slices/settings/initialState'; import { modelConfigSelectors, modelProviderSelectors, settingsSelectors, -} from '@/store/global/slices/settings/selectors'; +} from '@/store/user/slices/settings/selectors'; import { GeneralModelProviderConfig } from '@/types/settings'; import { merge } from '@/utils/merge'; @@ -28,7 +25,7 @@ vi.mock('@/services/user', () => ({ describe('LLMSettingsSliceAction', () => { describe('setModelProviderConfig', () => { it('should set OpenAI configuration', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const openAIConfig: Partial = { apiKey: 'test-key' }; // Perform the action @@ -47,7 +44,7 @@ describe('LLMSettingsSliceAction', () => { describe('dispatchCustomModelCards', () => { it('should return early when prevState does not exist', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); const provider = 'openai'; const payload: CustomModelCardDispatch = { type: 'add', modelCard: { id: 'test-id' } }; @@ -66,10 +63,10 @@ describe('LLMSettingsSliceAction', () => { describe('refreshDefaultModelProviderList', () => { it('default', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); act(() => { - useGlobalStore.setState({ + useUserStore.setState({ serverConfig: { languageModel: { azure: { serverModelCards: [{ id: 'abc', deploymentName: 'abc' }] }, @@ -91,9 +88,9 @@ describe('LLMSettingsSliceAction', () => { describe('refreshModelProviderList', () => { it('visible', async () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); act(() => { - useGlobalStore.setState({ + useUserStore.setState({ settings: { languageModel: { ollama: { enabledModels: ['llava'] }, @@ -118,10 +115,10 @@ describe('LLMSettingsSliceAction', () => { }); it('modelProviderListForModelSelect should return only enabled providers', () => { - const { result } = renderHook(() => useGlobalStore()); + const { result } = renderHook(() => useUserStore()); act(() => { - useGlobalStore.setState({ + useUserStore.setState({ settings: { languageModel: { perplexity: { enabled: true }, diff --git a/src/store/global/slices/settings/actions/llm.ts b/src/store/user/slices/settings/actions/llm.ts similarity index 99% rename from src/store/global/slices/settings/actions/llm.ts rename to src/store/user/slices/settings/actions/llm.ts index 6a5086a9b4d41..46c9f3ecbb67f 100644 --- a/src/store/global/slices/settings/actions/llm.ts +++ b/src/store/user/slices/settings/actions/llm.ts @@ -17,7 +17,7 @@ import { ZeroOneProviderCard, ZhiPuProviderCard, } from '@/config/modelProviders'; -import { GlobalStore } from '@/store/global'; +import { UserStore } from '@/store/user'; import { ChatModelCard } from '@/types/llm'; import { GlobalLLMConfig, GlobalLLMProviderKey } from '@/types/settings'; import { setNamespace } from '@/utils/storeDebug'; @@ -57,7 +57,7 @@ export interface LLMSettingsAction { } export const llmSettingsSlice: StateCreator< - GlobalStore, + UserStore, [['zustand/devtools', never]], [], LLMSettingsAction diff --git a/src/store/global/slices/settings/initialState.ts b/src/store/user/slices/settings/initialState.ts similarity index 89% rename from src/store/global/slices/settings/initialState.ts rename to src/store/user/slices/settings/initialState.ts index db49008ad70b5..acfe8a0ef3d48 100644 --- a/src/store/global/slices/settings/initialState.ts +++ b/src/store/user/slices/settings/initialState.ts @@ -6,7 +6,7 @@ import { ModelProviderCard } from '@/types/llm'; import { GlobalServerConfig } from '@/types/serverConfig'; import { GlobalSettings } from '@/types/settings'; -export interface GlobalSettingsState { +export interface UserSettingsState { avatar?: string; defaultModelProviderList: ModelProviderCard[]; defaultSettings: GlobalSettings; @@ -17,7 +17,7 @@ export interface GlobalSettingsState { userId?: string; } -export const initialSettingsState: GlobalSettingsState = { +export const initialSettingsState: UserSettingsState = { defaultModelProviderList: DEFAULT_MODEL_PROVIDER_LIST, defaultSettings: DEFAULT_SETTINGS, modelProviderList: DEFAULT_MODEL_PROVIDER_LIST, diff --git a/src/store/global/slices/settings/reducers/customModelCard.test.ts b/src/store/user/slices/settings/reducers/customModelCard.test.ts similarity index 100% rename from src/store/global/slices/settings/reducers/customModelCard.test.ts rename to src/store/user/slices/settings/reducers/customModelCard.test.ts diff --git a/src/store/global/slices/settings/reducers/customModelCard.ts b/src/store/user/slices/settings/reducers/customModelCard.ts similarity index 100% rename from src/store/global/slices/settings/reducers/customModelCard.ts rename to src/store/user/slices/settings/reducers/customModelCard.ts diff --git a/src/store/global/slices/settings/selectors/__snapshots__/selectors.test.ts.snap b/src/store/user/slices/settings/selectors/__snapshots__/selectors.test.ts.snap similarity index 100% rename from src/store/global/slices/settings/selectors/__snapshots__/selectors.test.ts.snap rename to src/store/user/slices/settings/selectors/__snapshots__/selectors.test.ts.snap diff --git a/src/store/global/slices/settings/selectors/index.ts b/src/store/user/slices/settings/selectors/index.ts similarity index 100% rename from src/store/global/slices/settings/selectors/index.ts rename to src/store/user/slices/settings/selectors/index.ts diff --git a/src/store/global/slices/settings/selectors/modelConfig.test.ts b/src/store/user/slices/settings/selectors/modelConfig.test.ts similarity index 87% rename from src/store/global/slices/settings/selectors/modelConfig.test.ts rename to src/store/user/slices/settings/selectors/modelConfig.test.ts index b68510978dd80..94cb8f05ba9db 100644 --- a/src/store/global/slices/settings/selectors/modelConfig.test.ts +++ b/src/store/user/slices/settings/selectors/modelConfig.test.ts @@ -2,8 +2,8 @@ import { describe, expect, it } from 'vitest'; import { merge } from '@/utils/merge'; -import { GlobalStore, useGlobalStore } from '../../../store'; -import { GlobalSettingsState, initialSettingsState } from '../initialState'; +import { UserStore, useUserStore } from '../../../store'; +import { UserSettingsState, initialSettingsState } from '../initialState'; import { modelConfigSelectors } from './modelConfig'; describe('modelConfigSelectors', () => { @@ -15,7 +15,7 @@ describe('modelConfigSelectors', () => { ollama: { enabled: true }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; expect(modelConfigSelectors.isProviderEnabled('ollama')(s)).toBe(true); }); @@ -27,7 +27,7 @@ describe('modelConfigSelectors', () => { perplexity: { enabled: false }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; expect(modelConfigSelectors.isProviderEnabled('perplexity')(s)).toBe(false); }); @@ -46,7 +46,7 @@ describe('modelConfigSelectors', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const customModelCard = modelConfigSelectors.getCustomModelCard({ id: 'custom-model-2', @@ -65,7 +65,7 @@ describe('modelConfigSelectors', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const customModelCard = modelConfigSelectors.getCustomModelCard({ id: 'nonexistent-model', @@ -93,7 +93,7 @@ describe('modelConfigSelectors', () => { id: 'custom-model-2', provider: 'perplexity', }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const currentEditingModelCard = modelConfigSelectors.currentEditingCustomModelCard(s); @@ -112,7 +112,7 @@ describe('modelConfigSelectors', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const currentEditingModelCard = modelConfigSelectors.currentEditingCustomModelCard(s); diff --git a/src/store/global/slices/settings/selectors/modelConfig.ts b/src/store/user/slices/settings/selectors/modelConfig.ts similarity index 70% rename from src/store/global/slices/settings/selectors/modelConfig.ts rename to src/store/user/slices/settings/selectors/modelConfig.ts index a3813fe411f46..fbe03f203a47a 100644 --- a/src/store/global/slices/settings/selectors/modelConfig.ts +++ b/src/store/user/slices/settings/selectors/modelConfig.ts @@ -1,15 +1,15 @@ import { GlobalLLMProviderKey } from '@/types/settings'; -import { GlobalStore } from '../../../store'; +import { UserStore } from '../../../store'; import { currentLLMSettings, getProviderConfigById } from './settings'; -const isProviderEnabled = (provider: GlobalLLMProviderKey) => (s: GlobalStore) => +const isProviderEnabled = (provider: GlobalLLMProviderKey) => (s: UserStore) => getProviderConfigById(provider)(s)?.enabled || false; -const isProviderEndpointNotEmpty = (provider: GlobalLLMProviderKey | string) => (s: GlobalStore) => +const isProviderEndpointNotEmpty = (provider: GlobalLLMProviderKey | string) => (s: UserStore) => !!getProviderConfigById(provider)(s)?.endpoint; -const isProviderFetchOnClient = (provider: GlobalLLMProviderKey | string) => (s: GlobalStore) => { +const isProviderFetchOnClient = (provider: GlobalLLMProviderKey | string) => (s: UserStore) => { const config = getProviderConfigById(provider)(s); if (typeof config?.fetchOnClient !== 'undefined') return config?.fetchOnClient; @@ -18,7 +18,7 @@ const isProviderFetchOnClient = (provider: GlobalLLMProviderKey | string) => (s: const getCustomModelCard = ({ id, provider }: { id?: string; provider?: string }) => - (s: GlobalStore) => { + (s: UserStore) => { if (!provider) return; const config = getProviderConfigById(provider)(s); @@ -26,7 +26,7 @@ const getCustomModelCard = return config?.customModelCards?.find((m) => m.id === id); }; -const currentEditingCustomModelCard = (s: GlobalStore) => { +const currentEditingCustomModelCard = (s: UserStore) => { if (!s.editingCustomCardModel) return; const { id, provider } = s.editingCustomCardModel; @@ -35,16 +35,16 @@ const currentEditingCustomModelCard = (s: GlobalStore) => { const isAutoFetchModelsEnabled = (provider: GlobalLLMProviderKey) => - (s: GlobalStore): boolean => { + (s: UserStore): boolean => { return getProviderConfigById(provider)(s)?.autoFetchModelLists || false; }; -const openAIConfig = (s: GlobalStore) => currentLLMSettings(s).openai; -const bedrockConfig = (s: GlobalStore) => currentLLMSettings(s).bedrock; -const ollamaConfig = (s: GlobalStore) => currentLLMSettings(s).ollama; -const azureConfig = (s: GlobalStore) => currentLLMSettings(s).azure; +const openAIConfig = (s: UserStore) => currentLLMSettings(s).openai; +const bedrockConfig = (s: UserStore) => currentLLMSettings(s).bedrock; +const ollamaConfig = (s: UserStore) => currentLLMSettings(s).ollama; +const azureConfig = (s: UserStore) => currentLLMSettings(s).azure; -const isAzureEnabled = (s: GlobalStore) => currentLLMSettings(s).azure.enabled; +const isAzureEnabled = (s: UserStore) => currentLLMSettings(s).azure.enabled; export const modelConfigSelectors = { azureConfig, diff --git a/src/store/global/slices/settings/selectors/modelProvider.test.ts b/src/store/user/slices/settings/selectors/modelProvider.test.ts similarity index 83% rename from src/store/global/slices/settings/selectors/modelProvider.test.ts rename to src/store/user/slices/settings/selectors/modelProvider.test.ts index 4828346988db4..589b89057f33d 100644 --- a/src/store/global/slices/settings/selectors/modelProvider.test.ts +++ b/src/store/user/slices/settings/selectors/modelProvider.test.ts @@ -2,21 +2,21 @@ import { describe, expect, it } from 'vitest'; import { merge } from '@/utils/merge'; -import { GlobalStore, useGlobalStore } from '../../../store'; -import { GlobalSettingsState, initialSettingsState } from '../initialState'; +import { UserStore, useUserStore } from '../../../store'; +import { UserSettingsState, initialSettingsState } from '../initialState'; import { getDefaultModeProviderById, modelProviderSelectors } from './modelProvider'; describe('modelProviderSelectors', () => { describe('getDefaultModeProviderById', () => { it('should return the correct ModelProviderCard when provider ID matches', () => { - const s = merge(initialSettingsState, {}) as unknown as GlobalStore; + const s = merge(initialSettingsState, {}) as unknown as UserStore; const result = getDefaultModeProviderById('openai')(s); expect(result).not.toBeUndefined(); }); it('should return undefined when provider ID does not exist', () => { - const s = merge(initialSettingsState, {}) as unknown as GlobalStore; + const s = merge(initialSettingsState, {}) as unknown as UserStore; const result = getDefaultModeProviderById('nonExistingProvider')(s); expect(result).toBeUndefined(); }); @@ -32,7 +32,7 @@ describe('modelProviderSelectors', () => { }, }, }, - } as GlobalSettingsState) as unknown as GlobalStore; + } as UserSettingsState) as unknown as UserStore; const modelCards = modelProviderSelectors.getModelCardsById('perplexity')(s); @@ -46,14 +46,14 @@ describe('modelProviderSelectors', () => { describe('defaultEnabledProviderModels', () => { it('should return enabled models for a given provider', () => { - const s = merge(initialSettingsState, {}) as unknown as GlobalStore; + const s = merge(initialSettingsState, {}) as unknown as UserStore; const result = modelProviderSelectors.getDefaultEnabledModelsById('openai')(s); expect(result).toEqual(['gpt-3.5-turbo', 'gpt-4-turbo']); }); it('should return undefined for a non-existing provider', () => { - const s = merge(initialSettingsState, {}) as unknown as GlobalStore; + const s = merge(initialSettingsState, {}) as unknown as UserStore; const result = modelProviderSelectors.getDefaultEnabledModelsById('nonExistingProvider')(s); expect(result).toBeUndefined(); @@ -62,14 +62,14 @@ describe('modelProviderSelectors', () => { describe('modelEnabledVision', () => { it('should return true if the model has vision ability', () => { const hasAbility = modelProviderSelectors.isModelEnabledVision('gpt-4-vision-preview')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(hasAbility).toBeTruthy(); }); it('should return false if the model does not have vision ability', () => { const hasAbility = modelProviderSelectors.isModelEnabledVision('some-other-model')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(hasAbility).toBeFalsy(); @@ -77,7 +77,7 @@ describe('modelProviderSelectors', () => { it('should return false if the model include vision in id', () => { const hasAbility = modelProviderSelectors.isModelEnabledVision('some-other-model-vision')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(hasAbility).toBeTruthy(); @@ -87,14 +87,14 @@ describe('modelProviderSelectors', () => { describe('modelEnabledFiles', () => { it('should return false if the model does not have file ability', () => { const enabledFiles = modelProviderSelectors.isModelEnabledFiles('gpt-4-vision-preview')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(enabledFiles).toBeFalsy(); }); it.skip('should return true if the model has file ability', () => { const enabledFiles = modelProviderSelectors.isModelEnabledFiles('gpt-4-all')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(enabledFiles).toBeTruthy(); }); @@ -103,14 +103,14 @@ describe('modelProviderSelectors', () => { describe('modelHasMaxToken', () => { it('should return true if the model is in the list of models that show tokens', () => { const show = modelProviderSelectors.isModelHasMaxToken('gpt-3.5-turbo')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(show).toBeTruthy(); }); it('should return false if the model is not in the list of models that show tokens', () => { const show = modelProviderSelectors.isModelHasMaxToken('some-other-model')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(show).toBe(false); }); @@ -119,7 +119,7 @@ describe('modelProviderSelectors', () => { describe('modelMaxToken', () => { it('should return the correct token count for a model with specified tokens', () => { const model1Tokens = modelProviderSelectors.modelMaxToken('gpt-3.5-turbo')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(model1Tokens).toEqual(16385); @@ -128,7 +128,7 @@ describe('modelProviderSelectors', () => { it('should return 0 for a model without a specified token count', () => { // 测试未指定tokens属性的模型的tokens值,期望为0 const tokens = modelProviderSelectors.modelMaxToken('chat-bison-001')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(tokens).toEqual(0); }); @@ -136,7 +136,7 @@ describe('modelProviderSelectors', () => { it('should return 0 for a non-existing model', () => { // 测试一个不存在的模型的tokens值,期望为0 const tokens = modelProviderSelectors.modelMaxToken('nonExistingModel')( - useGlobalStore.getState(), + useUserStore.getState(), ); expect(tokens).toEqual(0); diff --git a/src/store/global/slices/settings/selectors/modelProvider.ts b/src/store/user/slices/settings/selectors/modelProvider.ts similarity index 72% rename from src/store/global/slices/settings/selectors/modelProvider.ts rename to src/store/user/slices/settings/selectors/modelProvider.ts index f70a7cd8ae2a9..7f47f77ddf972 100644 --- a/src/store/global/slices/settings/selectors/modelProvider.ts +++ b/src/store/user/slices/settings/selectors/modelProvider.ts @@ -5,7 +5,7 @@ import { ChatModelCard, ModelProviderCard } from '@/types/llm'; import { ServerModelProviderConfig } from '@/types/serverConfig'; import { GlobalLLMProviderKey } from '@/types/settings'; -import { GlobalStore } from '../../../store'; +import { UserStore } from '../../../store'; import { currentSettings, getProviderConfigById } from './settings'; /** @@ -13,7 +13,7 @@ import { currentSettings, getProviderConfigById } from './settings'; */ const serverProviderModelCards = (provider: GlobalLLMProviderKey) => - (s: GlobalStore): ChatModelCard[] | undefined => { + (s: UserStore): ChatModelCard[] | undefined => { const config = s.serverConfig.languageModel?.[provider] as | ServerModelProviderConfig | undefined; @@ -25,7 +25,7 @@ const serverProviderModelCards = const remoteProviderModelCards = (provider: GlobalLLMProviderKey) => - (s: GlobalStore): ChatModelCard[] | undefined => { + (s: UserStore): ChatModelCard[] | undefined => { const cards = currentSettings(s).languageModel?.[provider]?.remoteModelCards as | ChatModelCard[] | undefined; @@ -35,7 +35,7 @@ const remoteProviderModelCards = return cards; }; -const isProviderEnabled = (provider: GlobalLLMProviderKey) => (s: GlobalStore) => +const isProviderEnabled = (provider: GlobalLLMProviderKey) => (s: UserStore) => getProviderConfigById(provider)(s)?.enabled || false; // Default Model Provider List @@ -43,10 +43,9 @@ const isProviderEnabled = (provider: GlobalLLMProviderKey) => (s: GlobalStore) = /** * define all the model list of providers */ -const defaultModelProviderList = (s: GlobalStore): ModelProviderCard[] => - s.defaultModelProviderList; +const defaultModelProviderList = (s: UserStore): ModelProviderCard[] => s.defaultModelProviderList; -export const getDefaultModeProviderById = (provider: string) => (s: GlobalStore) => +export const getDefaultModeProviderById = (provider: string) => (s: UserStore) => defaultModelProviderList(s).find((s) => s.id === provider); /** @@ -54,7 +53,7 @@ export const getDefaultModeProviderById = (provider: string) => (s: GlobalStore) * it's a default enabled model list by Lobe Chat * e.g. openai is ['gpt-3.5-turbo','gpt-4-turbo'] */ -const getDefaultEnabledModelsById = (provider: string) => (s: GlobalStore) => { +const getDefaultEnabledModelsById = (provider: string) => (s: UserStore) => { const modelProvider = getDefaultModeProviderById(provider)(s); if (modelProvider) return filterEnabledModels(modelProvider); @@ -62,7 +61,7 @@ const getDefaultEnabledModelsById = (provider: string) => (s: GlobalStore) => { return undefined; }; -const getDefaultModelCardById = (id: string) => (s: GlobalStore) => { +const getDefaultModelCardById = (id: string) => (s: UserStore) => { const list = defaultModelProviderList(s); return list.flatMap((i) => i.chatModels).find((m) => m.id === id); @@ -72,7 +71,7 @@ const getDefaultModelCardById = (id: string) => (s: GlobalStore) => { const getModelCardsById = (provider: string) => - (s: GlobalStore): ChatModelCard[] => { + (s: UserStore): ChatModelCard[] => { const builtinCards = getDefaultModeProviderById(provider)(s)?.chatModels || []; const userCards = (getProviderConfigById(provider)(s)?.customModelCards || []).map((model) => ({ @@ -83,15 +82,15 @@ const getModelCardsById = return uniqBy([...userCards, ...builtinCards], 'id'); }; -const getEnableModelsById = (provider: string) => (s: GlobalStore) => { +const getEnableModelsById = (provider: string) => (s: UserStore) => { if (!getProviderConfigById(provider)(s)?.enabledModels) return; return getProviderConfigById(provider)(s)?.enabledModels?.filter(Boolean); }; -const modelProviderList = (s: GlobalStore): ModelProviderCard[] => s.modelProviderList; +const modelProviderList = (s: UserStore): ModelProviderCard[] => s.modelProviderList; -const modelProviderListForModelSelect = (s: GlobalStore): ModelProviderCard[] => +const modelProviderListForModelSelect = (s: UserStore): ModelProviderCard[] => modelProviderList(s) .filter((s) => s.enabled) .map((provider) => ({ @@ -99,29 +98,29 @@ const modelProviderListForModelSelect = (s: GlobalStore): ModelProviderCard[] => chatModels: provider.chatModels.filter((model) => model.enabled), })); -const getModelCardById = (id: string) => (s: GlobalStore) => { +const getModelCardById = (id: string) => (s: UserStore) => { const list = modelProviderList(s); return list.flatMap((i) => i.chatModels).find((m) => m.id === id); }; -const isModelEnabledFunctionCall = (id: string) => (s: GlobalStore) => +const isModelEnabledFunctionCall = (id: string) => (s: UserStore) => getModelCardById(id)(s)?.functionCall || false; // vision model white list, these models will change the content from string to array // refs: https://github.com/lobehub/lobe-chat/issues/790 -const isModelEnabledVision = (id: string) => (s: GlobalStore) => +const isModelEnabledVision = (id: string) => (s: UserStore) => getModelCardById(id)(s)?.vision || id.includes('vision'); -const isModelEnabledFiles = (id: string) => (s: GlobalStore) => getModelCardById(id)(s)?.files; +const isModelEnabledFiles = (id: string) => (s: UserStore) => getModelCardById(id)(s)?.files; -const isModelEnabledUpload = (id: string) => (s: GlobalStore) => +const isModelEnabledUpload = (id: string) => (s: UserStore) => isModelEnabledVision(id)(s) || isModelEnabledFiles(id)(s); -const isModelHasMaxToken = (id: string) => (s: GlobalStore) => +const isModelHasMaxToken = (id: string) => (s: UserStore) => typeof getModelCardById(id)(s)?.tokens !== 'undefined'; -const modelMaxToken = (id: string) => (s: GlobalStore) => getModelCardById(id)(s)?.tokens || 0; +const modelMaxToken = (id: string) => (s: UserStore) => getModelCardById(id)(s)?.tokens || 0; export const modelProviderSelectors = { defaultModelProviderList, diff --git a/src/store/global/slices/settings/selectors/selectors.test.ts b/src/store/user/slices/settings/selectors/selectors.test.ts similarity index 93% rename from src/store/global/slices/settings/selectors/selectors.test.ts rename to src/store/user/slices/settings/selectors/selectors.test.ts index 55f33dd45e8e2..99b8bbd935a7d 100644 --- a/src/store/global/slices/settings/selectors/selectors.test.ts +++ b/src/store/user/slices/settings/selectors/selectors.test.ts @@ -1,4 +1,4 @@ -import { GlobalStore } from '../../../store'; +import { UserStore } from '../../../store'; import { settingsSelectors } from './settings'; describe('settingsSelectors', () => { @@ -48,7 +48,7 @@ describe('settingsSelectors', () => { }, }, }, - } as unknown as GlobalStore; + } as unknown as UserStore; const result = settingsSelectors.currentSettings(s); @@ -71,7 +71,7 @@ describe('settingsSelectors', () => { }, }, }, - } as unknown as GlobalStore; + } as unknown as UserStore; const result = settingsSelectors.defaultAgent(s); @@ -90,7 +90,7 @@ describe('settingsSelectors', () => { }, }, }, - } as unknown as GlobalStore; + } as unknown as UserStore; const result = settingsSelectors.defaultAgentMeta(s); @@ -109,7 +109,7 @@ describe('settingsSelectors', () => { }, }, }, - } as unknown as GlobalStore; + } as unknown as UserStore; const result = settingsSelectors.currentTTS(s); @@ -123,7 +123,7 @@ describe('settingsSelectors', () => { settings: { language: 'fr', }, - } as unknown as GlobalStore; + } as unknown as UserStore; const result = settingsSelectors.currentLanguage(s); @@ -142,7 +142,7 @@ describe('settingsSelectors', () => { }, }, }, - } as unknown as GlobalStore; + } as unknown as UserStore; const result = settingsSelectors.dalleConfig(s); @@ -160,7 +160,7 @@ describe('settingsSelectors', () => { }, }, }, - } as unknown as GlobalStore; + } as unknown as UserStore; const result = settingsSelectors.isDalleAutoGenerating(s); diff --git a/src/store/global/slices/settings/selectors/settings.ts b/src/store/user/slices/settings/selectors/settings.ts similarity index 57% rename from src/store/global/slices/settings/selectors/settings.ts rename to src/store/user/slices/settings/selectors/settings.ts index 73260157e9642..62d0ab6cc56a3 100644 --- a/src/store/global/slices/settings/selectors/settings.ts +++ b/src/store/user/slices/settings/selectors/settings.ts @@ -6,33 +6,33 @@ import { GeneralModelProviderConfig, GlobalLLMProviderKey, GlobalSettings } from import { isOnServerSide } from '@/utils/env'; import { merge } from '@/utils/merge'; -import { GlobalStore } from '../../../store'; +import { UserStore } from '../../../store'; -export const currentSettings = (s: GlobalStore): GlobalSettings => +export const currentSettings = (s: UserStore): GlobalSettings => merge(s.defaultSettings, s.settings); -export const currentLLMSettings = (s: GlobalStore) => currentSettings(s).languageModel; +export const currentLLMSettings = (s: UserStore) => currentSettings(s).languageModel; -export const getProviderConfigById = (provider: string) => (s: GlobalStore) => +export const getProviderConfigById = (provider: string) => (s: UserStore) => currentLLMSettings(s)[provider as GlobalLLMProviderKey] as GeneralModelProviderConfig | undefined; -const password = (s: GlobalStore) => currentSettings(s).password; +const password = (s: UserStore) => currentSettings(s).password; -const currentTTS = (s: GlobalStore) => merge(DEFAULT_TTS_CONFIG, currentSettings(s).tts); +const currentTTS = (s: UserStore) => merge(DEFAULT_TTS_CONFIG, currentSettings(s).tts); -const defaultAgent = (s: GlobalStore) => merge(DEFAULT_AGENT, currentSettings(s).defaultAgent); +const defaultAgent = (s: UserStore) => merge(DEFAULT_AGENT, currentSettings(s).defaultAgent); -const defaultAgentMeta = (s: GlobalStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta); +const defaultAgentMeta = (s: UserStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta); // TODO: Maybe we can also export settings difference -const exportSettings = (s: GlobalStore) => { +const exportSettings = (s: UserStore) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { password: _, ...settings } = currentSettings(s); return settings as GlobalSettings; }; -const currentLanguage = (s: GlobalStore) => { +const currentLanguage = (s: UserStore) => { const locale = currentSettings(s).language; if (locale === 'auto') { @@ -44,8 +44,8 @@ const currentLanguage = (s: GlobalStore) => { return locale; }; -const dalleConfig = (s: GlobalStore) => currentSettings(s).tool?.dalle || {}; -const isDalleAutoGenerating = (s: GlobalStore) => currentSettings(s).tool?.dalle?.autoGenerate; +const dalleConfig = (s: UserStore) => currentSettings(s).tool?.dalle || {}; +const isDalleAutoGenerating = (s: UserStore) => currentSettings(s).tool?.dalle?.autoGenerate; export const settingsSelectors = { currentLanguage, diff --git a/src/store/user/slices/settings/selectors/sync.ts b/src/store/user/slices/settings/selectors/sync.ts new file mode 100644 index 0000000000000..b9dd988431f3d --- /dev/null +++ b/src/store/user/slices/settings/selectors/sync.ts @@ -0,0 +1,14 @@ +import { UserStore } from '../../../store'; +import { currentSettings } from './settings'; + +const webrtcConfig = (s: UserStore) => currentSettings(s).sync.webrtc; +const webrtcChannelName = (s: UserStore) => webrtcConfig(s).channelName; +const enableWebRTC = (s: UserStore) => webrtcConfig(s).enabled; +const deviceName = (s: UserStore) => currentSettings(s).sync.deviceName; + +export const syncSettingsSelectors = { + deviceName, + enableWebRTC, + webrtcChannelName, + webrtcConfig, +}; diff --git a/src/store/user/store.ts b/src/store/user/store.ts new file mode 100644 index 0000000000000..0e705c6865678 --- /dev/null +++ b/src/store/user/store.ts @@ -0,0 +1,33 @@ +import { devtools, subscribeWithSelector } from 'zustand/middleware'; +import { shallow } from 'zustand/shallow'; +import { createWithEqualityFn } from 'zustand/traditional'; +import { StateCreator } from 'zustand/vanilla'; + +import { isDev } from '@/utils/env'; + +import { type UserState, initialState } from './initialState'; +import { type CommonAction, createCommonSlice } from './slices/common/action'; +import { type PreferenceAction, createPreferenceSlice } from './slices/preference/action'; +import { type SettingsAction, createSettingsSlice } from './slices/settings/actions'; + +// =============== 聚合 createStoreFn ============ // + +export type UserStore = CommonAction & UserState & SettingsAction & PreferenceAction; + +const createStore: StateCreator = (...parameters) => ({ + ...initialState, + ...createCommonSlice(...parameters), + ...createSettingsSlice(...parameters), + ...createPreferenceSlice(...parameters), +}); + +// =============== 实装 useStore ============ // + +export const useUserStore = createWithEqualityFn()( + subscribeWithSelector( + devtools(createStore, { + name: 'LobeChat_User' + (isDev ? '_DEV' : ''), + }), + ), + shallow, +); diff --git a/src/tools/dalle/Render/ToolBar.tsx b/src/tools/dalle/Render/ToolBar.tsx index f030f46ef6b60..cfea086954037 100644 --- a/src/tools/dalle/Render/ToolBar.tsx +++ b/src/tools/dalle/Render/ToolBar.tsx @@ -5,8 +5,8 @@ import { Flexbox } from 'react-layout-kit'; import { useChatStore } from '@/store/chat'; import { chatToolSelectors } from '@/store/chat/selectors'; -import { useGlobalStore } from '@/store/global'; -import { settingsSelectors } from '@/store/global/selectors'; +import { useUserStore } from '@/store/user'; +import { settingsSelectors } from '@/store/user/selectors'; import { DallEImageItem } from '@/types/tool/dalle'; interface ToolBarProps { @@ -19,7 +19,7 @@ const ToolBar = memo(({ content, messageId }) => { const generateImageFromPrompts = useChatStore((s) => s.generateImageFromPrompts); const isLoading = useChatStore(chatToolSelectors.isGeneratingDallEImage); - const [isAutoGenerate, setSettings] = useGlobalStore((s) => [ + const [isAutoGenerate, setSettings] = useUserStore((s) => [ settingsSelectors.isDalleAutoGenerating(s), s.setSettings, ]); diff --git a/src/utils/localStorage.ts b/src/utils/localStorage.ts index 4408c4fb16a89..fce6f43787f5d 100644 --- a/src/utils/localStorage.ts +++ b/src/utils/localStorage.ts @@ -1,6 +1,8 @@ const PREV_KEY = 'LOBE_GLOBAL'; -type StorageKey = 'LOBE_PREFERENCE'; +// LOBE_PREFERENCE for userStore +// LOBE_GLOBAL_PREFERENCE for globalStore +type StorageKey = 'LOBE_PREFERENCE' | 'LOBE_GLOBAL_PREFERENCE'; export class AsyncLocalStorage { private storageKey: StorageKey;