refactor(state): fix code quality findings for nav panel store and prefs
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { getPrefs, setPrefs } from '../prefs';
|
import { getPrefs, setPrefs } from '../prefs';
|
||||||
|
|
||||||
describe('prefs', () => {
|
describe('prefs', () => {
|
||||||
@@ -14,6 +14,10 @@ describe('prefs', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
it('returns defaults when localStorage empty', () => {
|
it('returns defaults when localStorage empty', () => {
|
||||||
expect(getPrefs()).toEqual({
|
expect(getPrefs()).toEqual({
|
||||||
storyOpenMode: 'auto',
|
storyOpenMode: 'auto',
|
||||||
@@ -27,6 +31,42 @@ describe('prefs', () => {
|
|||||||
setPrefs({ storyOpenMode: 'manual', actionDetailMode: 'hover' });
|
setPrefs({ storyOpenMode: 'manual', actionDetailMode: 'hover' });
|
||||||
expect(getPrefs().storyOpenMode).toBe('manual');
|
expect(getPrefs().storyOpenMode).toBe('manual');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('migrates v1 prefs to v2 if v2 does not exist', () => {
|
||||||
|
const v1Prefs = {
|
||||||
|
storyOpenMode: 'manual',
|
||||||
|
actionDetailMode: 'hover',
|
||||||
|
collapsedActionGroups: { 'some-group': true },
|
||||||
|
};
|
||||||
|
localStorage.setItem('idlegame:prefs:v1', JSON.stringify(v1Prefs));
|
||||||
|
|
||||||
|
const migratedPrefs = getPrefs();
|
||||||
|
|
||||||
|
expect(migratedPrefs).toEqual({
|
||||||
|
storyOpenMode: 'manual',
|
||||||
|
actionDetailMode: 'hover',
|
||||||
|
collapsedActionGroups: { 'some-group': true },
|
||||||
|
showEventLog: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rawV2 = localStorage.getItem('idlegame:prefs:v2');
|
||||||
|
expect(rawV2).not.toBeNull();
|
||||||
|
expect(JSON.parse(rawV2 ?? 'null')).toEqual(migratedPrefs);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns defaults and does not migrate if v1 prefs is invalid JSON', () => {
|
||||||
|
localStorage.setItem('idlegame:prefs:v1', '{invalid-json}');
|
||||||
|
|
||||||
|
const prefs = getPrefs();
|
||||||
|
expect(prefs).toEqual({
|
||||||
|
storyOpenMode: 'auto',
|
||||||
|
actionDetailMode: 'inline',
|
||||||
|
collapsedActionGroups: {},
|
||||||
|
showEventLog: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(localStorage.getItem('idlegame:prefs:v2')).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expanded prefs', () => {
|
describe('expanded prefs', () => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
import { getPrefs } from '../prefs';
|
||||||
import { useGameStore } from '../store';
|
import { useGameStore } from '../store';
|
||||||
|
|
||||||
describe('game store nav and prefs', () => {
|
describe('game store nav and prefs', () => {
|
||||||
@@ -12,6 +13,15 @@ describe('game store nav and prefs', () => {
|
|||||||
store[key] = value;
|
store[key] = value;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({
|
||||||
|
activePanel: 'play',
|
||||||
|
selectedStoryNodeId: null,
|
||||||
|
prefs: getPrefs(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllGlobals();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has default activePanel and selectedStoryNodeId', () => {
|
it('has default activePanel and selectedStoryNodeId', () => {
|
||||||
|
|||||||
+14
-1
@@ -21,7 +21,20 @@ export function getPrefs(): GamePrefs {
|
|||||||
if (typeof localStorage === 'undefined') return { ...DEFAULTS };
|
if (typeof localStorage === 'undefined') return { ...DEFAULTS };
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(PREFS_KEY);
|
const raw = localStorage.getItem(PREFS_KEY);
|
||||||
if (!raw) return { ...DEFAULTS };
|
if (!raw) {
|
||||||
|
const rawV1 = localStorage.getItem('idlegame:prefs:v1');
|
||||||
|
if (rawV1) {
|
||||||
|
try {
|
||||||
|
const parsedV1 = JSON.parse(rawV1);
|
||||||
|
const migrated = { ...DEFAULTS, ...parsedV1 };
|
||||||
|
localStorage.setItem(PREFS_KEY, JSON.stringify(migrated));
|
||||||
|
return migrated;
|
||||||
|
} catch {
|
||||||
|
return { ...DEFAULTS };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { ...DEFAULTS };
|
||||||
|
}
|
||||||
return { ...DEFAULTS, ...JSON.parse(raw) };
|
return { ...DEFAULTS, ...JSON.parse(raw) };
|
||||||
} catch {
|
} catch {
|
||||||
return { ...DEFAULTS };
|
return { ...DEFAULTS };
|
||||||
|
|||||||
+6
-6
@@ -69,13 +69,13 @@ export const useGameStore = create<GameStoreState>((set) => ({
|
|||||||
setSettingsOpen: (open) => set({ settingsOpen: open }),
|
setSettingsOpen: (open) => set({ settingsOpen: open }),
|
||||||
setActivePanel: (panel) => set({ activePanel: panel }),
|
setActivePanel: (panel) => set({ activePanel: panel }),
|
||||||
setSelectedStoryNodeId: (id) => set({ selectedStoryNodeId: id }),
|
setSelectedStoryNodeId: (id) => set({ selectedStoryNodeId: id }),
|
||||||
toggleActionGroupCollapsed: (groupKey) =>
|
toggleActionGroupCollapsed: (groupKey) => {
|
||||||
set((state) => {
|
const currentPrefs = useGameStore.getState().prefs;
|
||||||
const nextCollapsed = {
|
const nextCollapsed = {
|
||||||
...state.prefs.collapsedActionGroups,
|
...currentPrefs.collapsedActionGroups,
|
||||||
[groupKey]: !state.prefs.collapsedActionGroups[groupKey],
|
[groupKey]: !currentPrefs.collapsedActionGroups[groupKey],
|
||||||
};
|
};
|
||||||
const prefs = persistPrefs({ collapsedActionGroups: nextCollapsed });
|
const prefs = persistPrefs({ collapsedActionGroups: nextCollapsed });
|
||||||
return { prefs };
|
set({ prefs });
|
||||||
}),
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user