diff --git a/src/common/ShlinkWebComponentContainer.tsx b/src/common/ShlinkWebComponentContainer.tsx index ab82c7d0..5d87bf0a 100644 --- a/src/common/ShlinkWebComponentContainer.tsx +++ b/src/common/ShlinkWebComponentContainer.tsx @@ -5,13 +5,11 @@ import { ShlinkWebComponent, } from '@shlinkio/shlink-web-component'; import type { Settings } from '@shlinkio/shlink-web-component/settings'; -import type { FC } from 'react'; import { memo } from 'react'; -import type { ShlinkApiClientBuilder } from '../api/services/ShlinkApiClientBuilder'; import type { FCWithDeps } from '../container/utils'; import { componentFactory, useDependencies } from '../container/utils'; import { isReachableServer } from '../servers/data'; -import type { WithSelectedServerProps } from '../servers/helpers/withSelectedServer'; +import type { WithSelectedServerProps, WithSelectedServerPropsDeps } from '../servers/helpers/withSelectedServer'; import { withSelectedServer } from '../servers/helpers/withSelectedServer'; import { NotFound } from './NotFound'; @@ -19,10 +17,8 @@ type ShlinkWebComponentContainerProps = WithSelectedServerProps & { settings: Settings; }; -type ShlinkWebComponentContainerDeps = { - buildShlinkApiClient: ShlinkApiClientBuilder, +type ShlinkWebComponentContainerDeps = WithSelectedServerPropsDeps & { TagColorsStorage: TagColorsStorage, - ServerError: FC, }; const ShlinkWebComponentContainer: FCWithDeps< diff --git a/src/servers/EditServer.tsx b/src/servers/EditServer.tsx index 0c959964..485f18f7 100644 --- a/src/servers/EditServer.tsx +++ b/src/servers/EditServer.tsx @@ -1,26 +1,22 @@ -import { Button,useParsedQuery } from '@shlinkio/shlink-frontend-kit'; -import type { FC } from 'react'; +import { Button, useParsedQuery } from '@shlinkio/shlink-frontend-kit'; import { NoMenuLayout } from '../common/NoMenuLayout'; import type { FCWithDeps } from '../container/utils'; -import { componentFactory } from '../container/utils'; +import { componentFactory, useDependencies } from '../container/utils'; import { useGoBack } from '../utils/helpers/hooks'; import type { ServerData } from './data'; import { isServerWithId } from './data'; import { ServerForm } from './helpers/ServerForm'; -import type { WithSelectedServerProps } from './helpers/withSelectedServer'; +import type { WithSelectedServerProps, WithSelectedServerPropsDeps } from './helpers/withSelectedServer'; import { withSelectedServer } from './helpers/withSelectedServer'; type EditServerProps = WithSelectedServerProps & { editServer: (serverId: string, serverData: ServerData) => void; }; -type EditServerDeps = { - ServerError: FC; -}; - -const EditServer: FCWithDeps = withSelectedServer(( +const EditServer: FCWithDeps = withSelectedServer(( { editServer, selectedServer, selectServer }, ) => { + const { buildShlinkApiClient } = useDependencies(EditServer); const goBack = useGoBack(); const { reconnect } = useParsedQuery<{ reconnect?: 'true' }>(); @@ -31,7 +27,7 @@ const EditServer: FCWithDeps = withSelectedServ const handleSubmit = (serverData: ServerData) => { editServer(selectedServer.id, serverData); if (reconnect === 'true') { - selectServer(selectedServer.id); + selectServer({ serverId: selectedServer.id, buildShlinkApiClient }); } goBack(); }; diff --git a/src/servers/helpers/withSelectedServer.tsx b/src/servers/helpers/withSelectedServer.tsx index e1b41ba1..6bd9c43e 100644 --- a/src/servers/helpers/withSelectedServer.tsx +++ b/src/servers/helpers/withSelectedServer.tsx @@ -2,34 +2,37 @@ import { Message } from '@shlinkio/shlink-frontend-kit'; import type { FC } from 'react'; import { useEffect } from 'react'; import { useParams } from 'react-router'; +import type { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { NoMenuLayout } from '../../common/NoMenuLayout'; import type { FCWithDeps } from '../../container/utils'; import { useDependencies } from '../../container/utils'; import type { SelectedServer } from '../data'; import { isNotFoundServer } from '../data'; +import type { SelectServerOptions } from '../reducers/selectedServer'; export type WithSelectedServerProps = { - selectServer: (serverId: string) => void; + selectServer: (options: SelectServerOptions) => void; selectedServer: SelectedServer; }; -type WithSelectedServerPropsDeps = { +export type WithSelectedServerPropsDeps = { ServerError: FC; + buildShlinkApiClient: ShlinkApiClientBuilder; }; export function withSelectedServer( WrappedComponent: FCWithDeps, ) { const ComponentWrapper: FCWithDeps = (props) => { - const { ServerError } = useDependencies(ComponentWrapper); + const { ServerError, buildShlinkApiClient } = useDependencies(ComponentWrapper); const params = useParams<{ serverId: string }>(); const { selectServer, selectedServer } = props; useEffect(() => { if (params.serverId) { - selectServer(params.serverId); + selectServer({ serverId: params.serverId, buildShlinkApiClient }); } - }, [params.serverId, selectServer]); + }, [buildShlinkApiClient, params.serverId, selectServer]); if (!selectedServer) { return ( diff --git a/src/servers/reducers/selectedServer.ts b/src/servers/reducers/selectedServer.ts index b5d94dbb..9e274fa5 100644 --- a/src/servers/reducers/selectedServer.ts +++ b/src/servers/reducers/selectedServer.ts @@ -29,9 +29,14 @@ const initialState: SelectedServer = null; export const resetSelectedServer = createAction(`${REDUCER_PREFIX}/resetSelectedServer`); -export const selectServer = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( +export type SelectServerOptions = { + serverId: string; + buildShlinkApiClient: ShlinkApiClientBuilder; +}; + +export const selectServer = createAsyncThunk( `${REDUCER_PREFIX}/selectServer`, - async (serverId: string, { dispatch, getState }): Promise => { + async ({ serverId, buildShlinkApiClient }: SelectServerOptions, { dispatch, getState }): Promise => { dispatch(resetSelectedServer()); const { servers } = getState(); @@ -58,14 +63,11 @@ export const selectServer = (buildShlinkApiClient: ShlinkApiClientBuilder) => cr const { reducer } = createSlice({ name: REDUCER_PREFIX, - initialState, + initialState: initialState as SelectedServer, reducers: {}, extraReducers: (builder) => { builder.addCase(resetSelectedServer, () => initialState); - builder.addCase( - `${REDUCER_PREFIX}/selectServer/fulfilled`, - (_, { payload }: { payload: SelectedServer }) => payload, - ); + builder.addCase(selectServer.fulfilled, (_, { payload }) => payload); }, }); diff --git a/src/servers/services/provideServices.ts b/src/servers/services/provideServices.ts index 2e33a649..71ed54c9 100644 --- a/src/servers/services/provideServices.ts +++ b/src/servers/services/provideServices.ts @@ -54,7 +54,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.service('ServersExporter', ServersExporter, 'Storage', 'window', 'jsonToCsv'); // Actions - bottle.serviceFactory('selectServer', selectServer, 'buildShlinkApiClient', 'loadMercureInfo'); + bottle.serviceFactory('selectServer', () => selectServer, 'buildShlinkApiClient', 'loadMercureInfo'); bottle.serviceFactory('createServers', () => createServers); bottle.serviceFactory('deleteServer', () => deleteServer); bottle.serviceFactory('editServer', () => editServer); diff --git a/test/servers/reducers/selectedServer.test.ts b/test/servers/reducers/selectedServer.test.ts index 36720fe4..87ffc8e9 100644 --- a/test/servers/reducers/selectedServer.test.ts +++ b/test/servers/reducers/selectedServer.test.ts @@ -7,14 +7,13 @@ import { MIN_FALLBACK_VERSION, resetSelectedServer, selectedServerReducer as reducer, - selectServer as selectServerCreator, + selectServer, } from '../../../src/servers/reducers/selectedServer'; describe('selectedServerReducer', () => { const dispatch = vi.fn(); const health = vi.fn(); - const buildApiClient = vi.fn().mockReturnValue(fromPartial({ health })); - const selectServer = selectServerCreator(buildApiClient); + const buildShlinkApiClient = vi.fn().mockReturnValue(fromPartial({ health })); describe('reducer', () => { it('returns default when action is RESET_SELECTED_SERVER', () => @@ -22,7 +21,7 @@ describe('selectedServerReducer', () => { it('returns selected server when action is SELECT_SERVER', () => { const payload = fromPartial({ id: 'abc123' }); - expect(reducer(null, selectServer.fulfilled(payload, '', ''))).toEqual(payload); + expect(reducer(null, selectServer.fulfilled(payload, '', { serverId: '', buildShlinkApiClient }))).toEqual(payload); }); }); @@ -49,10 +48,10 @@ describe('selectedServerReducer', () => { health.mockResolvedValue({ version: serverVersion }); - await selectServer(id)(dispatch, getState, {}); + await selectServer({ serverId: id, buildShlinkApiClient })(dispatch, getState, {}); expect(getState).toHaveBeenCalledTimes(1); - expect(buildApiClient).toHaveBeenCalledTimes(1); + expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(3); // "Pending", "reset" and "fulfilled" expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: expectedSelectedServer })); }); @@ -64,7 +63,7 @@ describe('selectedServerReducer', () => { health.mockRejectedValue({}); - await selectServer(id)(dispatch, getState, {}); + await selectServer({ serverId: id, buildShlinkApiClient })(dispatch, getState, {}); expect(health).toHaveBeenCalled(); expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: expectedSelectedServer })); @@ -75,7 +74,7 @@ describe('selectedServerReducer', () => { const getState = vi.fn(() => fromPartial({ servers: {} })); const expectedSelectedServer: NotFoundServer = { serverNotFound: true }; - await selectServer(id)(dispatch, getState, {}); + await selectServer({ serverId: id, buildShlinkApiClient })(dispatch, getState, {}); expect(getState).toHaveBeenCalled(); expect(health).not.toHaveBeenCalled();