Skip to content

Commit

Permalink
鈾伙笍 refactor: separate common and sync slice
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed May 2, 2024
1 parent 869466f commit 34433a9
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 211 deletions.
7 changes: 4 additions & 3 deletions src/store/user/initialState.ts
@@ -1,12 +1,13 @@
import { UserSyncState, initialSyncState } from '@/store/user/slices/sync/initialState';

import { UserAuthState, initialAuthState } from './slices/auth/initialState';
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 & UserAuthState;
export type UserState = UserSyncState & UserSettingsState & UserPreferenceState & UserAuthState;

export const initialState: UserState = {
...initialCommonState,
...initialSyncState,
...initialSettingsState,
...initialPreferenceState,
...initialAuthState,
Expand Down
128 changes: 0 additions & 128 deletions src/store/user/slices/common/action.test.ts
Expand Up @@ -7,9 +7,7 @@ import { globalService } from '@/services/global';
import { messageService } from '@/services/message';
import { userService } from '@/services/user';
import { useUserStore } from '@/store/user';
import { userProfileSelectors } from '@/store/user/slices/auth/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';

Expand Down Expand Up @@ -154,89 +152,6 @@ describe('createCommonSlice', () => {
});
});

describe('refreshConnection', () => {
it('should not call triggerEnableSync when userId is empty', async () => {
const { result } = renderHook(() => useUserStore());
const onEvent = vi.fn();

vi.spyOn(userProfileSelectors, 'userId').mockReturnValueOnce(undefined as any);
const triggerEnableSyncSpy = vi.spyOn(result.current, 'triggerEnableSync');

await act(async () => {
await result.current.refreshConnection(onEvent);
});

expect(triggerEnableSyncSpy).not.toHaveBeenCalled();
});

it('should call triggerEnableSync when userId exists', async () => {
const { result } = renderHook(() => useUserStore());
const onEvent = vi.fn();
const userId = 'user-id';

vi.spyOn(userProfileSelectors, 'userId').mockReturnValueOnce(userId);
const triggerEnableSyncSpy = vi.spyOn(result.current, 'triggerEnableSync');

await act(async () => {
await result.current.refreshConnection(onEvent);
});

expect(triggerEnableSyncSpy).toHaveBeenCalledWith(userId, onEvent);
});
});

describe('triggerEnableSync', () => {
it('should return false when sync.channelName is empty', async () => {
const { result } = renderHook(() => useUserStore());
const userId = 'user-id';
const onEvent = vi.fn();

vi.spyOn(syncSettingsSelectors, 'webrtcConfig').mockReturnValueOnce({
channelName: '',
enabled: true,
});

const data = await act(async () => {
return result.current.triggerEnableSync(userId, onEvent);
});

expect(data).toBe(false);
});

it('should call globalService.enabledSync when sync.channelName exists', async () => {
const userId = 'user-id';
const onEvent = vi.fn();
const channelName = 'channel-name';
const channelPassword = 'channel-password';
const deviceName = 'device-name';
const signaling = 'signaling';

vi.spyOn(syncSettingsSelectors, 'webrtcConfig').mockReturnValueOnce({
channelName,
channelPassword,
signaling,
enabled: true,
});
vi.spyOn(syncSettingsSelectors, 'deviceName').mockReturnValueOnce(deviceName);
const enabledSyncSpy = vi.spyOn(globalService, 'enabledSync').mockResolvedValueOnce(true);
const { result } = renderHook(() => useUserStore());

const data = await act(async () => {
return result.current.triggerEnableSync(userId, onEvent);
});

expect(enabledSyncSpy).toHaveBeenCalledWith({
channel: { name: channelName, password: channelPassword },
onAwarenessChange: expect.any(Function),
onSyncEvent: onEvent,
onSyncStatusChange: expect.any(Function),
signaling,
user: expect.objectContaining({ id: userId, name: deviceName }),
});
expect(data).toBe(true);
});
});

describe('useCheckTrace', () => {
it('should return false when shouldFetch is false', async () => {
const { result } = renderHook(() => useUserStore().useCheckTrace(false), {
Expand Down Expand Up @@ -270,47 +185,4 @@ describe('createCommonSlice', () => {
expect(messageCountToCheckTraceSpy).toHaveBeenCalled();
});
});

describe('useEnabledSync', () => {
it('should return false when userId is empty', async () => {
const { result } = renderHook(() => useUserStore().useEnabledSync(true, undefined, vi.fn()), {
wrapper: withSWR,
});

await waitFor(() => expect(result.current.data).toBe(false));
});

it('should call globalService.disableSync when userEnableSync is false', async () => {
const disableSyncSpy = vi.spyOn(globalService, 'disableSync').mockResolvedValueOnce(false);

const { result } = renderHook(
() => useUserStore().useEnabledSync(false, 'user-id', vi.fn()),
{ wrapper: withSWR },
);

await waitFor(() => expect(result.current.data).toBeUndefined());
expect(disableSyncSpy).toHaveBeenCalled();
});

it('should call triggerEnableSync when userEnableSync and userId exist', async () => {
const userId = 'user-id';
const onEvent = vi.fn();
const triggerEnableSyncSpy = vi.fn().mockResolvedValueOnce(true);

const { result } = renderHook(() => useUserStore());

// replace triggerEnableSync as a mock
result.current.triggerEnableSync = triggerEnableSyncSpy;

const { result: swrResult } = renderHook(
() => result.current.useEnabledSync(true, userId, onEvent),
{
wrapper: withSWR,
},
);

await waitFor(() => expect(swrResult.current.data).toBe(true));
expect(triggerEnableSyncSpy).toHaveBeenCalledWith(userId, onEvent);
});
});
});
71 changes: 1 addition & 70 deletions src/store/user/slices/common/action.ts
Expand Up @@ -8,32 +8,22 @@ import { UserConfig, userService } from '@/services/user';
import type { UserStore } from '@/store/user';
import type { GlobalServerConfig } from '@/types/serverConfig';
import type { GlobalSettings } from '@/types/settings';
import { OnSyncEvent, PeerSyncStatus } from '@/types/sync';
import { switchLang } from '@/utils/client/switchLang';
import { merge } from '@/utils/merge';
import { browserInfo } from '@/utils/platform';
import { setNamespace } from '@/utils/storeDebug';

import { userProfileSelectors } from '../auth/selectors';
import { preferenceSelectors } from '../preference/selectors';
import { settingsSelectors, syncSettingsSelectors } from '../settings/selectors';
import { settingsSelectors } from '../settings/selectors';

const n = setNamespace('common');

/**
* 璁剧疆鎿嶄綔
*/
export interface CommonAction {
refreshConnection: (onEvent: OnSyncEvent) => Promise<void>;
refreshUserConfig: () => Promise<void>;
triggerEnableSync: (userId: string, onEvent: OnSyncEvent) => Promise<boolean>;
updateAvatar: (avatar: string) => Promise<void>;
useCheckTrace: (shouldFetch: boolean) => SWRResponse;
useEnabledSync: (
userEnableSync: boolean,
userId: string | undefined,
onEvent: OnSyncEvent,
) => SWRResponse;
useFetchServerConfig: () => SWRResponse;
useFetchUserConfig: (initServer: boolean) => SWRResponse<UserConfig | undefined>;
}
Expand All @@ -46,53 +36,13 @@ export const createCommonSlice: StateCreator<
[],
CommonAction
> = (set, get) => ({
refreshConnection: async (onEvent) => {
const userId = userProfileSelectors.userId(get());

if (!userId) return;

await get().triggerEnableSync(userId, onEvent);
},

refreshUserConfig: async () => {
await mutate([USER_CONFIG_FETCH_KEY, true]);

// when get the user config ,refresh the model provider list to the latest
get().refreshModelProviderList();
},

triggerEnableSync: async (userId: string, onEvent: OnSyncEvent) => {
// double-check the sync ability
// if there is no channelName, don't start sync
const sync = syncSettingsSelectors.webrtcConfig(get());
if (!sync.channelName) return false;

const name = syncSettingsSelectors.deviceName(get());

const defaultUserName = `My ${browserInfo.browser} (${browserInfo.os})`;

set({ syncStatus: PeerSyncStatus.Connecting });
return globalService.enabledSync({
channel: {
name: sync.channelName,
password: sync.channelPassword,
},
onAwarenessChange(state) {
set({ syncAwareness: state });
},
onSyncEvent: onEvent,
onSyncStatusChange: (status) => {
set({ syncStatus: status });
},
signaling: sync.signaling,
user: {
id: userId,
// if user don't set the name, use default name
name: name || defaultUserName,
...browserInfo,
},
});
},
updateAvatar: async (avatar) => {
await userService.updateAvatar(avatar);
await get().refreshUserConfig();
Expand All @@ -115,25 +65,6 @@ export const createCommonSlice: StateCreator<
},
),

useEnabledSync: (userEnableSync, userId, onEvent) =>
useSWR<boolean>(
['enableSync', userEnableSync, userId],
async () => {
// if user don't enable sync or no userId ,don't start sync
if (!userId) return false;

// if user don't enable sync, stop sync
if (!userEnableSync) return globalService.disableSync();

return get().triggerEnableSync(userId, onEvent);
},
{
onSuccess: (syncEnabled) => {
set({ syncEnabled }, false, n('useEnabledSync'));
},
revalidateOnFocus: false,
},
),
useFetchServerConfig: () =>
useSWR<GlobalServerConfig>('fetchGlobalConfig', globalService.getGlobalConfig, {
onSuccess: (data) => {
Expand Down

0 comments on commit 34433a9

Please sign in to comment.