diff --git a/src/reducers/index.ts b/src/reducers/index.ts index c88fb12e..61bd0139 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -15,7 +15,7 @@ import shortUrlDetailReducer from '../short-urls/reducers/shortUrlDetail'; import tagsListReducer from '../tags/reducers/tagsList'; import tagDeleteReducer from '../tags/reducers/tagDelete'; import tagEditReducer from '../tags/reducers/tagEdit'; -import settingsReducer from '../settings/reducers/settings'; +import { settingsReducer } from '../settings/reducers/settings'; import visitsOverviewReducer from '../visits/reducers/visitsOverview'; import { appUpdatesReducer } from '../app/reducers/appUpdates'; import { sidebarReducer } from '../common/reducers/sidebar'; diff --git a/src/settings/reducers/settings.ts b/src/settings/reducers/settings.ts index 8a106682..bfc4e2aa 100644 --- a/src/settings/reducers/settings.ts +++ b/src/settings/reducers/settings.ts @@ -1,14 +1,10 @@ -import { Action } from 'redux'; -import { dissoc, mergeDeepRight } from 'ramda'; -import { buildReducer } from '../../utils/helpers/redux'; -import { RecursivePartial } from '../../utils/utils'; +import { createSlice, PayloadAction, PrepareAction } from '@reduxjs/toolkit'; +import { mergeDeepRight } from 'ramda'; import { Theme } from '../../utils/theme'; import { DateInterval } from '../../utils/dates/types'; import { TagsOrder } from '../../tags/data/TagsListChildrenProps'; import { ShortUrlsOrder } from '../../short-urls/data'; -export const SET_SETTINGS = 'shlink/realTimeUpdates/SET_SETTINGS'; - export const DEFAULT_SHORT_URLS_ORDERING: ShortUrlsOrder = { field: 'dateCreated', dir: 'DESC', @@ -78,45 +74,37 @@ const initialState: Settings = { }, }; -type SettingsAction = Action & Settings; +type SettingsAction = PayloadAction; +type SettingsPrepareAction = PrepareAction; -type PartialSettingsAction = Action & RecursivePartial; +const commonReducer = (state: Settings, { payload }: SettingsAction) => mergeDeepRight(state, payload); +const toReducer = (prepare: SettingsPrepareAction) => ({ reducer: commonReducer, prepare }); +const toPreparedAction: SettingsPrepareAction = (payload: Settings) => ({ payload }); -export default buildReducer({ - [SET_SETTINGS]: (state, action) => mergeDeepRight(state, dissoc('type', action)), -}, initialState); - -export const toggleRealTimeUpdates = (enabled: boolean): PartialSettingsAction => ({ - type: SET_SETTINGS, - realTimeUpdates: { enabled }, +const { reducer, actions } = createSlice({ + name: 'settingsReducer', + initialState, + reducers: { + toggleRealTimeUpdates: toReducer((enabled: boolean) => toPreparedAction({ realTimeUpdates: { enabled } })), + setRealTimeUpdatesInterval: toReducer((interval: number) => toPreparedAction({ realTimeUpdates: { interval } })), + setShortUrlCreationSettings: toReducer( + (shortUrlCreation: ShortUrlCreationSettings) => toPreparedAction({ shortUrlCreation }), + ), + setShortUrlsListSettings: toReducer((shortUrlsList: ShortUrlsListSettings) => toPreparedAction({ shortUrlsList })), + setUiSettings: toReducer((ui: UiSettings) => toPreparedAction({ ui })), + setVisitsSettings: toReducer((visits: VisitsSettings) => toPreparedAction({ visits })), + setTagsSettings: toReducer((tags: TagsSettings) => toPreparedAction({ tags })), + }, }); -export const setRealTimeUpdatesInterval = (interval: number): PartialSettingsAction => ({ - type: SET_SETTINGS, - realTimeUpdates: { interval }, -}); +export const { + toggleRealTimeUpdates, + setRealTimeUpdatesInterval, + setShortUrlCreationSettings, + setShortUrlsListSettings, + setUiSettings, + setVisitsSettings, + setTagsSettings, +} = actions; -export const setShortUrlCreationSettings = (settings: ShortUrlCreationSettings): PartialSettingsAction => ({ - type: SET_SETTINGS, - shortUrlCreation: settings, -}); - -export const setShortUrlsListSettings = (settings: ShortUrlsListSettings): PartialSettingsAction => ({ - type: SET_SETTINGS, - shortUrlsList: settings, -}); - -export const setUiSettings = (settings: UiSettings): PartialSettingsAction => ({ - type: SET_SETTINGS, - ui: settings, -}); - -export const setVisitsSettings = (settings: VisitsSettings): PartialSettingsAction => ({ - type: SET_SETTINGS, - visits: settings, -}); - -export const setTagsSettings = (settings: TagsSettings): PartialSettingsAction => ({ - type: SET_SETTINGS, - tags: settings, -}); +export const settingsReducer = reducer; diff --git a/test/settings/reducers/settings.test.ts b/test/settings/reducers/settings.test.ts index 427eec21..50d31ac1 100644 --- a/test/settings/reducers/settings.test.ts +++ b/test/settings/reducers/settings.test.ts @@ -1,6 +1,6 @@ -import reducer, { - SET_SETTINGS, +import { DEFAULT_SHORT_URLS_ORDERING, + settingsReducer, toggleRealTimeUpdates, setRealTimeUpdatesInterval, setShortUrlCreationSettings, @@ -20,7 +20,9 @@ describe('settingsReducer', () => { describe('reducer', () => { it('returns realTimeUpdates when action is SET_SETTINGS', () => { - expect(reducer(undefined, { type: SET_SETTINGS, realTimeUpdates })).toEqual(settings); + expect( + settingsReducer(undefined, { type: toggleRealTimeUpdates.toString(), payload: { realTimeUpdates } }), + ).toEqual(settings); }); }); @@ -28,7 +30,10 @@ describe('settingsReducer', () => { it.each([[true], [false]])('updates settings with provided value and then loads updates again', (enabled) => { const result = toggleRealTimeUpdates(enabled); - expect(result).toEqual({ type: SET_SETTINGS, realTimeUpdates: { enabled } }); + expect(result).toEqual({ + type: toggleRealTimeUpdates.toString(), + payload: { realTimeUpdates: { enabled } }, + }); }); }); @@ -36,7 +41,10 @@ describe('settingsReducer', () => { it.each([[0], [1], [2], [10]])('updates settings with provided value and then loads updates again', (interval) => { const result = setRealTimeUpdatesInterval(interval); - expect(result).toEqual({ type: SET_SETTINGS, realTimeUpdates: { interval } }); + expect(result).toEqual({ + type: setRealTimeUpdatesInterval.toString(), + payload: { realTimeUpdates: { interval } }, + }); }); }); @@ -44,7 +52,10 @@ describe('settingsReducer', () => { it('creates action to set shortUrlCreation settings', () => { const result = setShortUrlCreationSettings({ validateUrls: true }); - expect(result).toEqual({ type: SET_SETTINGS, shortUrlCreation: { validateUrls: true } }); + expect(result).toEqual({ + type: setShortUrlCreationSettings.toString(), + payload: { shortUrlCreation: { validateUrls: true } }, + }); }); }); @@ -52,7 +63,10 @@ describe('settingsReducer', () => { it('creates action to set ui settings', () => { const result = setUiSettings({ theme: 'dark' }); - expect(result).toEqual({ type: SET_SETTINGS, ui: { theme: 'dark' } }); + expect(result).toEqual({ + type: setUiSettings.toString(), + payload: { ui: { theme: 'dark' } }, + }); }); }); @@ -60,7 +74,10 @@ describe('settingsReducer', () => { it('creates action to set visits settings', () => { const result = setVisitsSettings({ defaultInterval: 'last180Days' }); - expect(result).toEqual({ type: SET_SETTINGS, visits: { defaultInterval: 'last180Days' } }); + expect(result).toEqual({ + type: setVisitsSettings.toString(), + payload: { visits: { defaultInterval: 'last180Days' } }, + }); }); }); @@ -68,7 +85,10 @@ describe('settingsReducer', () => { it('creates action to set tags settings', () => { const result = setTagsSettings({ defaultMode: 'list' }); - expect(result).toEqual({ type: SET_SETTINGS, tags: { defaultMode: 'list' } }); + expect(result).toEqual({ + type: setTagsSettings.toString(), + payload: { tags: { defaultMode: 'list' } }, + }); }); }); @@ -76,7 +96,10 @@ describe('settingsReducer', () => { it('creates action to set short URLs list settings', () => { const result = setShortUrlsListSettings({ defaultOrdering: DEFAULT_SHORT_URLS_ORDERING }); - expect(result).toEqual({ type: SET_SETTINGS, shortUrlsList: { defaultOrdering: DEFAULT_SHORT_URLS_ORDERING } }); + expect(result).toEqual({ + type: setShortUrlsListSettings.toString(), + payload: { shortUrlsList: { defaultOrdering: DEFAULT_SHORT_URLS_ORDERING } }, + }); }); }); });