You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
4.1 KiB
TypeScript

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<void>;
updateAvatar: (avatar: string) => Promise<void>;
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<boolean>(
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<UserInitializationState>(
!!isLogin ? GET_USER_STATE_KEY : null,
() => userService.getUserState(),
{
onSuccess: (data) => {
options?.onSuccess?.(data);
if (data) {
// merge settings
const serverSettings: DeepPartial<UserSettings> = {
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');
}
}
},
},
),
});