import useSWR, { SWRResponse, mutate } from 'swr'; import { DeepPartial } from 'utility-types'; import type { StateCreator } from 'zustand/vanilla'; import { DEFAULT_PREFERENCE } from '@/const/user'; import { useOnlyFetchOnceSWR } from '@/libs/swr'; import { userService } from '@/services/user'; import { ClientService } from '@/services/user/client'; import type { UserStore } from '@/store/user'; import type { GlobalServerConfig } from '@/types/serverConfig'; import { UserInitializationState } from '@/types/user'; import type { UserSettings } from '@/types/user/settings'; import { switchLang } from '@/utils/client/switchLang'; import { merge } from '@/utils/merge'; import { setNamespace } from '@/utils/storeDebug'; import { preferenceSelectors } from '../preference/selectors'; import { userGeneralSettingsSelectors } from '../settings/selectors'; const n = setNamespace('common'); const GET_USER_STATE_KEY = 'initUserState'; /** * 设置操作 */ export interface CommonAction { refreshUserState: () => Promise; updateAvatar: (avatar: string) => Promise; useCheckTrace: (shouldFetch: boolean) => SWRResponse; useInitUserState: ( isLogin: boolean | undefined, serverConfig: GlobalServerConfig, options?: { onSuccess: (data: UserInitializationState) => void; }, ) => SWRResponse; } export const createCommonSlice: StateCreator< UserStore, [['zustand/devtools', never]], [], CommonAction > = (set, get) => ({ refreshUserState: async () => { await mutate(GET_USER_STATE_KEY); }, updateAvatar: async (avatar) => { const clientService = new ClientService(); await clientService.updateAvatar(avatar); await get().refreshUserState(); }, useCheckTrace: (shouldFetch) => useSWR( shouldFetch ? 'checkTrace' : null, () => { const userAllowTrace = preferenceSelectors.userAllowTrace(get()); // if user have set the trace, return false if (typeof userAllowTrace === 'boolean') return Promise.resolve(false); return Promise.resolve(get().isUserCanEnableTrace); }, { revalidateOnFocus: false, }, ), useInitUserState: (isLogin, serverConfig, options) => useOnlyFetchOnceSWR( !!isLogin ? GET_USER_STATE_KEY : null, () => userService.getUserState(), { onSuccess: (data) => { options?.onSuccess?.(data); if (data) { // merge settings const serverSettings: DeepPartial = { defaultAgent: serverConfig.defaultAgent, languageModel: serverConfig.languageModel, systemAgent: serverConfig.systemAgent, }; const defaultSettings = merge(get().defaultSettings, serverSettings); // merge preference const isEmpty = Object.keys(data.preference || {}).length === 0; const preference = isEmpty ? DEFAULT_PREFERENCE : data.preference; // if there is avatar or userId (from client DB), update it into user const user = data.avatar || data.userId ? merge(get().user, { avatar: data.avatar, id: data.userId }) : get().user; set( { defaultSettings, enabledNextAuth: serverConfig.enabledOAuthSSO, isOnboard: data.isOnboard, isShowPWAGuide: data.canEnablePWAGuide, isUserCanEnableTrace: data.canEnableTrace, isUserHasConversation: data.hasConversation, isUserStateInit: true, preference, serverLanguageModel: serverConfig.languageModel, settings: data.settings || {}, user, }, false, n('initUserState'), ); get().refreshDefaultModelProviderList({ trigger: 'fetchUserState' }); // auto switch language const language = userGeneralSettingsSelectors.config(get()).language; if (language === 'auto') { switchLang('auto'); } } }, }, ), });