@lobehub/chat 0.161.9 → 0.161.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +2 -1
  3. package/src/app/(main)/_layout/Desktop.tsx +2 -2
  4. package/src/app/(main)/settings/llm/components/ProviderConfig/index.tsx +5 -2
  5. package/src/app/(main)/settings/llm/components/ProviderModelList/CustomModelOption.tsx +6 -7
  6. package/src/app/(main)/settings/llm/components/ProviderModelList/{ModelConfigModal.tsx → ModelConfigModal/Form.tsx} +19 -63
  7. package/src/app/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/index.tsx +78 -0
  8. package/src/app/(main)/settings/llm/components/ProviderModelList/Option.tsx +35 -11
  9. package/src/app/(main)/settings/llm/components/ProviderModelList/index.tsx +15 -18
  10. package/src/app/layout.tsx +2 -0
  11. package/src/components/ModelProviderIcon/index.tsx +2 -2
  12. package/src/components/ModelSelect/index.tsx +5 -14
  13. package/src/const/layoutTokens.ts +1 -0
  14. package/src/features/PWAInstall/index.tsx +22 -0
  15. package/src/features/User/UserPanel/PanelContent.tsx +1 -1
  16. package/src/hooks/usePWAInstall.test.ts +78 -0
  17. package/src/hooks/usePWAInstall.ts +23 -2
  18. package/src/hooks/usePlatform.test.ts +82 -0
  19. package/src/hooks/usePlatform.ts +19 -2
  20. package/src/hooks/useSyncData.ts +3 -1
  21. package/src/layout/GlobalProvider/StoreInitialization.tsx +17 -9
  22. package/src/layout/GlobalProvider/index.tsx +1 -1
  23. package/src/locales/default/components.ts +1 -0
  24. package/src/services/message/client.test.ts +0 -24
  25. package/src/services/message/client.ts +0 -5
  26. package/src/services/message/type.ts +0 -1
  27. package/src/services/user/client.test.ts +100 -0
  28. package/src/services/user/client.ts +16 -14
  29. package/src/services/user/index.ts +0 -2
  30. package/src/services/user/type.ts +2 -4
  31. package/src/store/user/initialState.ts +10 -1
  32. package/src/store/user/selectors.ts +3 -7
  33. package/src/store/user/slices/auth/action.test.ts +5 -87
  34. package/src/store/user/slices/auth/action.ts +3 -58
  35. package/src/store/user/slices/auth/initialState.ts +2 -1
  36. package/src/store/user/slices/common/action.test.ts +196 -20
  37. package/src/store/user/slices/common/action.ts +55 -26
  38. package/src/store/user/slices/common/initialState.ts +9 -0
  39. package/src/store/user/slices/modelList/action.test.ts +363 -0
  40. package/src/store/user/slices/{settings/actions/llm.ts → modelList/action.ts} +66 -60
  41. package/src/store/user/slices/modelList/initialState.ts +15 -0
  42. package/src/store/user/slices/modelList/selectors/index.ts +2 -0
  43. package/src/store/user/slices/{settings → modelList}/selectors/modelConfig.test.ts +3 -2
  44. package/src/store/user/slices/{settings → modelList}/selectors/modelConfig.ts +1 -1
  45. package/src/store/user/slices/{settings → modelList}/selectors/modelProvider.test.ts +7 -7
  46. package/src/store/user/slices/{settings → modelList}/selectors/modelProvider.ts +2 -4
  47. package/src/store/user/slices/preference/action.test.ts +0 -52
  48. package/src/store/user/slices/preference/action.ts +1 -17
  49. package/src/store/user/slices/preference/initialState.ts +0 -5
  50. package/src/store/user/slices/preference/selectors.test.ts +2 -2
  51. package/src/store/user/slices/preference/selectors.ts +1 -1
  52. package/src/store/user/slices/settings/{actions/general.ts → action.ts} +5 -5
  53. package/src/store/user/slices/settings/initialState.ts +0 -12
  54. package/src/store/user/slices/settings/selectors/index.ts +0 -3
  55. package/src/store/user/slices/sync/action.test.ts +19 -5
  56. package/src/store/user/slices/sync/action.ts +9 -6
  57. package/src/store/user/slices/{settings/selectors/sync.ts → sync/selectors.ts} +2 -2
  58. package/src/store/user/store.ts +5 -2
  59. package/src/types/serverConfig.ts +3 -1
  60. package/src/types/user/index.ts +13 -0
  61. package/src/utils/parseModels.test.ts +121 -1
  62. package/src/utils/parseModels.ts +9 -4
  63. package/src/utils/platform.test.ts +83 -0
  64. package/src/utils/platform.ts +33 -2
  65. package/src/hooks/useIsPWA.ts +0 -13
  66. package/src/store/user/slices/settings/actions/index.ts +0 -18
  67. package/src/store/user/slices/settings/actions/llm.test.ts +0 -136
  68. package/src/utils/matchMedia.ts +0 -10
  69. /package/src/app/(main)/settings/llm/components/ProviderModelList/{MaxTokenSlider.tsx → ModelConfigModal/MaxTokenSlider.tsx} +0 -0
  70. /package/src/store/user/slices/{settings → modelList}/reducers/customModelCard.test.ts +0 -0
  71. /package/src/store/user/slices/{settings → modelList}/reducers/customModelCard.ts +0 -0
  72. /package/src/store/user/slices/settings/{actions/general.test.ts → action.test.ts} +0 -0
  73. /package/src/store/user/slices/settings/selectors/__snapshots__/{selectors.test.ts.snap → settings.test.ts.snap} +0 -0
  74. /package/src/store/user/slices/settings/selectors/{selectors.test.ts → settings.test.ts} +0 -0
@@ -1,21 +1,11 @@
1
- import useSWR, { SWRResponse, mutate } from 'swr';
2
1
  import { StateCreator } from 'zustand/vanilla';
3
2
 
4
3
  import { enableClerk } from '@/const/auth';
5
- import { UserConfig, userService } from '@/services/user';
6
- import { switchLang } from '@/utils/client/switchLang';
7
- import { setNamespace } from '@/utils/storeDebug';
8
4
 
9
5
  import { UserStore } from '../../store';
10
- import { settingsSelectors } from '../settings/selectors';
11
-
12
- const n = setNamespace('auth');
13
- const USER_CONFIG_FETCH_KEY = 'fetchUserConfig';
14
6
 
15
7
  export interface UserAuthAction {
16
8
  enableAuth: () => boolean;
17
- enabledNextAuth: () => boolean;
18
- getUserConfig: () => void;
19
9
  /**
20
10
  * universal logout method
21
11
  */
@@ -25,9 +15,6 @@ export interface UserAuthAction {
25
15
  */
26
16
  openLogin: () => Promise<void>;
27
17
  openUserProfile: () => Promise<void>;
28
-
29
- refreshUserConfig: () => Promise<void>;
30
- useFetchUserConfig: (initServer: boolean) => SWRResponse<UserConfig | undefined>;
31
18
  }
32
19
 
33
20
  export const createAuthSlice: StateCreator<
@@ -37,13 +24,7 @@ export const createAuthSlice: StateCreator<
37
24
  UserAuthAction
38
25
  > = (set, get) => ({
39
26
  enableAuth: () => {
40
- return enableClerk || get()?.enabledNextAuth();
41
- },
42
- enabledNextAuth: () => {
43
- return !!get()?.serverConfig.enabledOAuthSSO;
44
- },
45
- getUserConfig: () => {
46
- console.log(n('userconfig'));
27
+ return enableClerk || get()?.enabledNextAuth || false;
47
28
  },
48
29
  logout: async () => {
49
30
  if (enableClerk) {
@@ -52,7 +33,7 @@ export const createAuthSlice: StateCreator<
52
33
  return;
53
34
  }
54
35
 
55
- const enableNextAuth = get().enabledNextAuth();
36
+ const enableNextAuth = get().enabledNextAuth;
56
37
  if (enableNextAuth) {
57
38
  const { signOut } = await import('next-auth/react');
58
39
  signOut();
@@ -60,14 +41,12 @@ export const createAuthSlice: StateCreator<
60
41
  },
61
42
  openLogin: async () => {
62
43
  if (enableClerk) {
63
- console.log('fallbackRedirectUrl:', location.toString());
64
-
65
44
  get().clerkSignIn?.({ fallbackRedirectUrl: location.toString() });
66
45
 
67
46
  return;
68
47
  }
69
48
 
70
- const enableNextAuth = get().enabledNextAuth();
49
+ const enableNextAuth = get().enabledNextAuth;
71
50
  if (enableNextAuth) {
72
51
  const { signIn } = await import('next-auth/react');
73
52
  signIn();
@@ -81,38 +60,4 @@ export const createAuthSlice: StateCreator<
81
60
  return;
82
61
  }
83
62
  },
84
- refreshUserConfig: async () => {
85
- await mutate([USER_CONFIG_FETCH_KEY, true]);
86
-
87
- // when get the user config ,refresh the model provider list to the latest
88
- get().refreshModelProviderList();
89
- },
90
- useFetchUserConfig: (initServer) =>
91
- useSWR<UserConfig | undefined>(
92
- [USER_CONFIG_FETCH_KEY, initServer],
93
- async () => {
94
- if (!initServer) return;
95
- return userService.getUserConfig();
96
- },
97
- {
98
- onSuccess: (data) => {
99
- if (!data) return;
100
-
101
- set(
102
- { avatar: data.avatar, settings: data.settings, userId: data.uuid },
103
- false,
104
- n('fetchUserConfig', data),
105
- );
106
-
107
- // when get the user config ,refresh the model provider list to the latest
108
- get().refreshDefaultModelProviderList({ trigger: 'fetchUserConfig' });
109
-
110
- const { language } = settingsSelectors.currentSettings(get());
111
- if (language === 'auto') {
112
- switchLang('auto');
113
- }
114
- },
115
- revalidateOnFocus: false,
116
- },
117
- ),
118
63
  });
@@ -15,12 +15,13 @@ export interface UserAuthState {
15
15
  * @deprecated
16
16
  */
17
17
  avatar?: string;
18
-
19
18
  clerkOpenUserProfile?: (props?: UserProfileProps) => void;
19
+
20
20
  clerkSession?: ActiveSessionResource;
21
21
  clerkSignIn?: (props?: SignInProps) => void;
22
22
  clerkSignOut?: SignOut;
23
23
  clerkUser?: UserResource;
24
+ enabledNextAuth?: boolean;
24
25
 
25
26
  isLoaded?: boolean;
26
27
  isSignedIn?: boolean;
@@ -3,15 +3,20 @@ import { mutate } from 'swr';
3
3
  import { afterEach, describe, expect, it, vi } from 'vitest';
4
4
  import { withSWR } from '~test-utils';
5
5
 
6
- import { globalService } from '@/services/global';
7
- import { messageService } from '@/services/message';
6
+ import { DEFAULT_PREFERENCE } from '@/const/user';
8
7
  import { userService } from '@/services/user';
9
8
  import { useUserStore } from '@/store/user';
10
9
  import { preferenceSelectors } from '@/store/user/selectors';
11
10
  import { GlobalServerConfig } from '@/types/serverConfig';
11
+ import { UserInitializationState, UserPreference } from '@/types/user';
12
+ import { switchLang } from '@/utils/client/switchLang';
12
13
 
13
14
  vi.mock('zustand/traditional');
14
15
 
16
+ vi.mock('@/utils/client/switchLang', () => ({
17
+ switchLang: vi.fn(),
18
+ }));
19
+
15
20
  vi.mock('swr', async (importOriginal) => {
16
21
  const modules = await importOriginal();
17
22
  return {
@@ -30,7 +35,7 @@ describe('createCommonSlice', () => {
30
35
  const { result } = renderHook(() => useUserStore());
31
36
  const avatar = 'new-avatar';
32
37
 
33
- const spyOn = vi.spyOn(result.current, 'refreshUserConfig');
38
+ const spyOn = vi.spyOn(result.current, 'refreshUserState');
34
39
  const updateAvatarSpy = vi.spyOn(userService, 'updateAvatar');
35
40
 
36
41
  await act(async () => {
@@ -42,28 +47,197 @@ describe('createCommonSlice', () => {
42
47
  });
43
48
  });
44
49
 
45
- describe('useFetchServerConfig', () => {
46
- it('should fetch server config correctly', async () => {
47
- const mockServerConfig = {
48
- defaultAgent: 'agent1',
49
- languageModel: 'model1',
50
- telemetry: {},
51
- } as GlobalServerConfig;
52
- vi.spyOn(globalService, 'getGlobalConfig').mockResolvedValueOnce(mockServerConfig);
50
+ describe('useInitUserState', () => {
51
+ const mockServerConfig = {
52
+ defaultAgent: 'agent1',
53
+ languageModel: 'model1',
54
+ telemetry: {},
55
+ } as GlobalServerConfig;
56
+
57
+ it('should not fetch user state if user is not login', async () => {
58
+ const mockUserConfig: any = undefined; // 模拟未初始化服务器的情况
59
+ vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserConfig);
60
+ const successCallback = vi.fn();
61
+
62
+ const { result } = renderHook(
63
+ () =>
64
+ useUserStore().useInitUserState(false, mockServerConfig, {
65
+ onSuccess: successCallback,
66
+ }),
67
+ { wrapper: withSWR },
68
+ );
69
+
70
+ // 因为 initServer 为 false,所以不会触发 getUserState 的调用
71
+ expect(userService.getUserState).not.toHaveBeenCalled();
72
+ // 也不会触发 onSuccess 回调
73
+ expect(successCallback).not.toHaveBeenCalled();
74
+ // 确保状态未改变
75
+ expect(result.current.data).toBeUndefined();
76
+ });
77
+
78
+ it('should fetch user state correctly when user is login', async () => {
79
+ const mockUserState: UserInitializationState = {
80
+ userId: 'user-id',
81
+ isOnboard: true,
82
+ preference: {
83
+ telemetry: true,
84
+ },
85
+ settings: {
86
+ language: 'en-US',
87
+ },
88
+ };
89
+
90
+ vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
91
+ const successCallback = vi.fn();
92
+
93
+ const { result } = renderHook(
94
+ () =>
95
+ useUserStore().useInitUserState(true, mockServerConfig, {
96
+ onSuccess: successCallback,
97
+ }),
98
+ {
99
+ wrapper: withSWR,
100
+ },
101
+ );
102
+
103
+ // 等待 SWR 完成数据获取
104
+ await waitFor(() => expect(result.current.data).toEqual(mockUserState));
105
+
106
+ // 验证状态是否正确更新
107
+ expect(useUserStore.getState().avatar).toBe(mockUserState.avatar);
108
+ expect(useUserStore.getState().settings).toEqual(mockUserState.settings);
109
+ expect(successCallback).toHaveBeenCalledWith(mockUserState);
110
+
111
+ // 验证是否正确处理了语言设置
112
+ expect(switchLang).not.toHaveBeenCalledWith('auto');
113
+ });
114
+
115
+ it('should call switch language when language is auto', async () => {
116
+ const mockUserState: UserInitializationState = {
117
+ userId: 'user-id',
118
+ isOnboard: true,
119
+ preference: {
120
+ telemetry: true,
121
+ },
122
+ settings: {},
123
+ };
124
+
125
+ vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
126
+
127
+ const { result } = renderHook(() => useUserStore().useInitUserState(true, mockServerConfig), {
128
+ wrapper: withSWR,
129
+ });
130
+
131
+ // 等待 SWR 完成数据获取
132
+ await waitFor(() => expect(result.current.data).toEqual(mockUserState));
133
+
134
+ // 验证是否正确处理了语言设置
135
+ expect(switchLang).toHaveBeenCalledWith('auto');
136
+ });
137
+
138
+ it('should fetch use server config correctly', async () => {
139
+ const mockUserState: UserInitializationState = {
140
+ userId: 'user-id',
141
+ isOnboard: true,
142
+ preference: {
143
+ telemetry: true,
144
+ },
145
+ settings: {},
146
+ };
147
+ vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
148
+
149
+ const { result } = renderHook(() => useUserStore().useInitUserState(true, mockServerConfig));
150
+
151
+ await waitFor(() => expect(result.current.data).toEqual(mockUserState));
152
+ });
153
+
154
+ it('should return saved preference when local storage has data', async () => {
155
+ const { result } = renderHook(() => useUserStore());
156
+
157
+ const savedPreference: UserPreference = {
158
+ ...DEFAULT_PREFERENCE,
159
+ hideSyncAlert: true,
160
+ guide: { topic: false, moveSettingsToAvatar: true },
161
+ };
162
+
163
+ const mockUserState: UserInitializationState = {
164
+ userId: 'user-id',
165
+ isOnboard: true,
166
+ preference: savedPreference,
167
+ settings: {
168
+ language: 'en-US',
169
+ },
170
+ };
171
+ vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
172
+
173
+ const { result: preference } = renderHook(
174
+ () => result.current.useInitUserState(true, mockServerConfig),
175
+ { wrapper: withSWR },
176
+ );
177
+
178
+ await waitFor(() => {
179
+ expect(preference.current.data.preference).toEqual(savedPreference);
180
+ expect(result.current.isUserStateInit).toBeTruthy();
181
+ expect(result.current.preference).toEqual(savedPreference);
182
+ });
183
+ });
184
+
185
+ it('should handle the case when user config is null', async () => {
186
+ const { result } = renderHook(() => useUserStore());
187
+ const mockUserState: UserInitializationState = {
188
+ userId: 'user-id',
189
+ isOnboard: true,
190
+ preference: undefined as any,
191
+ settings: null as any,
192
+ };
193
+
194
+ vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
195
+
196
+ renderHook(() => result.current.useInitUserState(true, mockServerConfig), {
197
+ wrapper: withSWR,
198
+ });
199
+
200
+ // 等待 SWR 完成数据获取
201
+ await waitFor(() => {
202
+ expect(result.current.isUserStateInit).toBeTruthy();
203
+ // 验证状态未被错误更新
204
+ expect(result.current.avatar).toBeUndefined();
205
+ expect(result.current.settings).toEqual({});
206
+ });
207
+ });
208
+
209
+ it('should return default preference when local storage is empty', async () => {
210
+ const { result } = renderHook(() => useUserStore());
53
211
 
54
- const { result } = renderHook(() => useUserStore().useFetchServerConfig());
212
+ const mockUserState: UserInitializationState = {
213
+ userId: 'user-id',
214
+ isOnboard: true,
215
+ preference: {} as any,
216
+ settings: {
217
+ language: 'en-US',
218
+ },
219
+ };
55
220
 
56
- await waitFor(() => expect(result.current.data).toEqual(mockServerConfig));
221
+ vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
222
+
223
+ renderHook(() => result.current.useInitUserState(true, mockServerConfig), {
224
+ wrapper: withSWR,
225
+ });
226
+
227
+ await waitFor(() => {
228
+ expect(result.current.isUserStateInit).toBeTruthy();
229
+ expect(result.current.preference).toEqual(DEFAULT_PREFERENCE);
230
+ });
57
231
  });
58
232
  });
59
233
 
60
234
  describe('useCheckTrace', () => {
61
- it('should return false when shouldFetch is false', async () => {
235
+ it('should return undefined when shouldFetch is false', async () => {
62
236
  const { result } = renderHook(() => useUserStore().useCheckTrace(false), {
63
237
  wrapper: withSWR,
64
238
  });
65
239
 
66
- await waitFor(() => expect(result.current.data).toBe(false));
240
+ await waitFor(() => expect(result.current.data).toBeUndefined());
67
241
  });
68
242
 
69
243
  it('should return false when userAllowTrace is already set', async () => {
@@ -78,16 +252,18 @@ describe('createCommonSlice', () => {
78
252
 
79
253
  it('should call messageService.messageCountToCheckTrace when needed', async () => {
80
254
  vi.spyOn(preferenceSelectors, 'userAllowTrace').mockReturnValueOnce(null);
81
- const messageCountToCheckTraceSpy = vi
82
- .spyOn(messageService, 'messageCountToCheckTrace')
83
- .mockResolvedValueOnce(true);
84
255
 
85
- const { result } = renderHook(() => useUserStore().useCheckTrace(true), {
256
+ act(() => {
257
+ useUserStore.setState({
258
+ isUserCanEnableTrace: true,
259
+ });
260
+ });
261
+
262
+ const { result } = renderHook(() => useUserStore.getState().useCheckTrace(true), {
86
263
  wrapper: withSWR,
87
264
  });
88
265
 
89
266
  await waitFor(() => expect(result.current.data).toBe(true));
90
- expect(messageCountToCheckTraceSpy).toHaveBeenCalled();
91
267
  });
92
268
  });
93
269
  });
@@ -1,26 +1,38 @@
1
- import useSWR, { SWRResponse } from 'swr';
1
+ import useSWR, { SWRResponse, mutate } from 'swr';
2
2
  import { DeepPartial } from 'utility-types';
3
3
  import type { StateCreator } from 'zustand/vanilla';
4
4
 
5
- import { messageService } from '@/services/message';
5
+ import { DEFAULT_PREFERENCE } from '@/const/user';
6
6
  import { userService } from '@/services/user';
7
7
  import type { UserStore } from '@/store/user';
8
8
  import type { GlobalServerConfig } from '@/types/serverConfig';
9
9
  import type { GlobalSettings } from '@/types/settings';
10
+ import { UserInitializationState } from '@/types/user';
11
+ import { switchLang } from '@/utils/client/switchLang';
10
12
  import { merge } from '@/utils/merge';
11
13
  import { setNamespace } from '@/utils/storeDebug';
12
14
 
13
15
  import { preferenceSelectors } from '../preference/selectors';
16
+ import { settingsSelectors } from '../settings/selectors';
14
17
 
15
18
  const n = setNamespace('common');
16
19
 
20
+ const GET_USER_STATE_KEY = 'initUserState';
17
21
  /**
18
22
  * 设置操作
19
23
  */
20
24
  export interface CommonAction {
25
+ refreshUserState: () => Promise<void>;
26
+
21
27
  updateAvatar: (avatar: string) => Promise<void>;
22
28
  useCheckTrace: (shouldFetch: boolean) => SWRResponse;
23
- useFetchServerConfig: () => SWRResponse;
29
+ useInitUserState: (
30
+ isLogin: boolean | undefined,
31
+ serverConfig: GlobalServerConfig,
32
+ options?: {
33
+ onSuccess: (data: UserInitializationState) => void;
34
+ },
35
+ ) => SWRResponse;
24
36
  }
25
37
 
26
38
  export const createCommonSlice: StateCreator<
@@ -29,55 +41,72 @@ export const createCommonSlice: StateCreator<
29
41
  [],
30
42
  CommonAction
31
43
  > = (set, get) => ({
44
+ refreshUserState: async () => {
45
+ await mutate(GET_USER_STATE_KEY);
46
+ },
32
47
  updateAvatar: async (avatar) => {
33
48
  await userService.updateAvatar(avatar);
34
- await get().refreshUserConfig();
49
+ await get().refreshUserState();
35
50
  },
36
51
 
37
52
  useCheckTrace: (shouldFetch) =>
38
53
  useSWR<boolean>(
39
- ['checkTrace', shouldFetch],
54
+ shouldFetch ? 'checkTrace' : null,
40
55
  () => {
41
56
  const userAllowTrace = preferenceSelectors.userAllowTrace(get());
42
- // if not init with server side, return false
43
- if (!shouldFetch) return Promise.resolve(false);
44
57
 
45
58
  // if user have set the trace, return false
46
59
  if (typeof userAllowTrace === 'boolean') return Promise.resolve(false);
47
60
 
48
- return messageService.messageCountToCheckTrace();
61
+ return Promise.resolve(get().isUserCanEnableTrace);
49
62
  },
50
63
  {
51
64
  revalidateOnFocus: false,
52
65
  },
53
66
  ),
54
67
 
55
- /**
56
- * TODO: need to be removed in the future
57
- * the serverConfig should be fetched only in the serverConfigStore
58
- * @deprecated
59
- */
60
- useFetchServerConfig: () =>
61
- useSWR<GlobalServerConfig>(
62
- 'fetchGlobalConfig',
63
- async () => {
64
- const { globalService } = await import('@/services/global');
65
-
66
- return globalService.getGlobalConfig();
67
- },
68
+ useInitUserState: (isLogin, serverConfig, options) =>
69
+ useSWR<UserInitializationState>(
70
+ !!isLogin ? GET_USER_STATE_KEY : null,
71
+ () => userService.getUserState(),
68
72
  {
69
73
  onSuccess: (data) => {
74
+ options?.onSuccess?.(data);
75
+
70
76
  if (data) {
77
+ // merge settings
71
78
  const serverSettings: DeepPartial<GlobalSettings> = {
72
- defaultAgent: data.defaultAgent,
73
- languageModel: data.languageModel,
79
+ defaultAgent: serverConfig.defaultAgent,
80
+ languageModel: serverConfig.languageModel,
74
81
  };
75
-
76
82
  const defaultSettings = merge(get().defaultSettings, serverSettings);
77
83
 
78
- set({ defaultSettings, serverConfig: data }, false, n('initGlobalConfig'));
84
+ // merge preference
85
+ const isEmpty = Object.keys(data.preference || {}).length === 0;
86
+ const preference = isEmpty ? DEFAULT_PREFERENCE : data.preference;
87
+
88
+ set(
89
+ {
90
+ defaultSettings,
91
+ enabledNextAuth: serverConfig.enabledOAuthSSO,
92
+ isUserCanEnableTrace: data.canEnableTrace,
93
+ isUserStateInit: true,
94
+ preference,
95
+ serverLanguageModel: serverConfig.languageModel,
96
+ settings: data.settings || {},
97
+ userId: data.userId,
98
+ },
99
+ false,
100
+ n('initUserState'),
101
+ );
102
+
103
+ get().refreshDefaultModelProviderList({ trigger: 'fetchUserState' });
79
104
 
80
- get().refreshDefaultModelProviderList({ trigger: 'fetchServerConfig' });
105
+ // auto switch language
106
+ const { language } = settingsSelectors.currentSettings(get());
107
+ if (language === 'auto') {
108
+ switchLang('auto');
109
+ }
81
110
  }
82
111
  },
83
112
  revalidateOnFocus: false,
@@ -0,0 +1,9 @@
1
+ export interface CommonState {
2
+ isUserCanEnableTrace: boolean;
3
+ isUserStateInit: boolean;
4
+ }
5
+
6
+ export const initialCommonState: CommonState = {
7
+ isUserCanEnableTrace: false,
8
+ isUserStateInit: false,
9
+ };