Skip to content

Commit 98f82ff

Browse files
committed
chore: Refactor user settings slice
1 parent c93a4fc commit 98f82ff

File tree

4 files changed

+39
-43
lines changed

4 files changed

+39
-43
lines changed

src/components/nav/NavSettings.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
changeGameMode,
4242
getExpertMode,
4343
getGameMode,
44+
useSaveUserSettingsOnChange,
4445
} from '../../state/slices/userSettings';
4546
import LanguageSelection from '../baseComponents/LanguageSelection';
4647
import Settings from '../baseComponents/Settings';
@@ -52,8 +53,6 @@ const useStyles = makeStyles()((theme) => ({
5253
},
5354
}));
5455

55-
const SETTINGS_STORAGE_KEY = 'globalSettings';
56-
5756
export const GAME_MODES = (t: TFunction) => [
5857
{ value: 'fractals', label: t('Fractals') },
5958
{ value: 'raids', label: t('Raids/Strikes') },
@@ -65,9 +64,6 @@ export default function NavSettings() {
6564
const dispatch = useAppDispatch();
6665
const { classes } = useStyles();
6766

68-
const { i18n } = useTranslation();
69-
const { language } = i18n;
70-
7167
const expertMode = useSelector(getExpertMode);
7268
const gameMode = useSelector(getGameMode);
7369
const defaultStatInfusions = useSelector(getDefaultStatInfusions);
@@ -84,13 +80,7 @@ export default function NavSettings() {
8480
setOpen(false);
8581
};
8682

87-
// save user settings to localStorage
88-
React.useEffect(() => {
89-
const settings = JSON.stringify({ expertMode, gameMode, language });
90-
console.log(`saving... ${settings}`);
91-
92-
localStorage.setItem(SETTINGS_STORAGE_KEY, settings);
93-
}, [expertMode, gameMode, language]);
83+
useSaveUserSettingsOnChange();
9484

9585
const { error: hwThreadsError } = parseHwThreads(hwThreadsString);
9686

src/state/slices/localUserSettings.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,18 @@ import type { PayloadAction } from '@reduxjs/toolkit';
22
import { createSlice } from '@reduxjs/toolkit';
33
import { reduxSideEffect } from '../redux-hooks';
44
import type { RootState } from '../store';
5+
import { getLocalStorageObject } from '../../utils/usefulFunctions';
56

67
// this slice is for settings which should not be loaded from URLs;
78
// it has no changeAll reducer case
89

910
const UNSHARED_SETTINGS_STORAGE_KEY = 'localUserSettings';
1011

11-
function getLocalStorageState(): object {
12-
try {
13-
const stored = localStorage.getItem(UNSHARED_SETTINGS_STORAGE_KEY);
14-
if (stored) {
15-
return JSON.parse(stored) as object;
16-
}
17-
} catch {
18-
return {};
19-
}
20-
return {};
21-
}
22-
2312
const defaultState = { defaultStatInfusions: '' };
2413

2514
export const loadedLocalUserSettings = {
2615
...defaultState,
27-
...getLocalStorageState(), // override default state with potentially saved localStorage variables
16+
...getLocalStorageObject(UNSHARED_SETTINGS_STORAGE_KEY), // override default state with potentially saved localStorage variables
2817
};
2918

3019
const getSliceState = (state: RootState) => state.optimizer.localUserSettings;

src/state/slices/userSettings.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
11
import type { PayloadAction } from '@reduxjs/toolkit';
22
import { createSlice } from '@reduxjs/toolkit';
3+
import React from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
import { useSelector } from 'react-redux';
36
import { PARAMS, setQueryParm, useQueryParam } from '../../utils/queryParam';
7+
import { getLocalStorageObject } from '../../utils/usefulFunctions';
48
import type { RootState } from '../store';
59
import { changeAll } from './controlsSlice';
610

711
export type GameMode = 'fractals' | 'raids' | 'wvw';
812

9-
function getLocalStorageState(): object {
10-
try {
11-
const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
12-
if (stored) {
13-
return JSON.parse(stored) as object;
14-
}
15-
} catch {
16-
return {};
17-
}
18-
return {};
19-
}
20-
2113
const SETTINGS_STORAGE_KEY = 'globalSettings';
2214

2315
const defaultState = { expertMode: true, gameMode: 'fractals', language: 'en' };
2416
// eslint-disable-next-line react-hooks/rules-of-hooks
2517
const gameModeParam = useQueryParam({ key: PARAMS.GAMEMODE });
2618

19+
// language is included here for historical reasons; src/utils/i18n.js imports this
2720
export const loadedSettings = {
2821
...defaultState,
29-
...getLocalStorageState(), // override default state with potentially saved localStorage variables
22+
...getLocalStorageObject(SETTINGS_STORAGE_KEY), // override default state with potentially saved localStorage variables
3023
...(gameModeParam && { gameMode: gameModeParam }), // gameMode from query param takes priority
3124
} as {
3225
expertMode: boolean;
@@ -37,18 +30,31 @@ export const loadedSettings = {
3730
// append the gamemode to the query param if no query param is present
3831
if (!gameModeParam) setQueryParm({ key: PARAMS.GAMEMODE, value: loadedSettings.gameMode });
3932

33+
// save user settings to localStorage (as a react hook, to access react-i18next language)
34+
const getSliceState = (state: RootState) => state.optimizer.userSettings;
35+
export const useSaveUserSettingsOnChange = () => {
36+
const { i18n } = useTranslation();
37+
const { language } = i18n;
38+
const sliceState = useSelector(getSliceState);
39+
40+
React.useEffect(() => {
41+
const settings = JSON.stringify({ ...sliceState, language });
42+
console.log(`saving... ${settings}`);
43+
44+
localStorage.setItem(SETTINGS_STORAGE_KEY, settings);
45+
}, [sliceState, language]);
46+
};
47+
4048
export const userSettingsSlice = createSlice({
4149
name: 'userSettings',
4250

4351
initialState: {
4452
expertMode: loadedSettings.expertMode,
4553
gameMode: loadedSettings.gameMode,
54+
// language is excluded from this redux slice; react-i18next has its own mutable state
4655
},
4756

4857
reducers: {
49-
changeAllUserSettings: (state, action) => {
50-
return { ...state, ...action.payload?.userSettings };
51-
},
5258
changeExpertMode: (state, action: PayloadAction<boolean>) => {
5359
state.expertMode = action.payload;
5460
},
@@ -74,7 +80,6 @@ export const userSettingsSlice = createSlice({
7480
export const getExpertMode = (state: RootState) => state.optimizer.userSettings.expertMode;
7581
export const getGameMode = (state: RootState) => state.optimizer.userSettings.gameMode;
7682

77-
export const { changeAllUserSettings, changeExpertMode, changeGameMode } =
78-
userSettingsSlice.actions;
83+
export const { changeExpertMode, changeGameMode } = userSettingsSlice.actions;
7984

8085
export default userSettingsSlice.reducer;

src/utils/usefulFunctions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ export function firstUppercase(text: string | undefined | null): string {
99
return text.split(' ').map(toUpper).join(' ').trim();
1010
}
1111

12+
export function getLocalStorageObject(key: string): object {
13+
try {
14+
const stored = localStorage.getItem(key);
15+
if (stored) {
16+
return JSON.parse(stored) as object;
17+
}
18+
} catch {
19+
return {};
20+
}
21+
return {};
22+
}
23+
1224
/*
1325
* Parses a string to a number, treating non-parsable strings like empty inputs but indicating an
1426
* error so text boxes can display the error validation state

0 commit comments

Comments
 (0)