Why this matters now
The notification system is listed as shipped in the current roadmap. mergeNotificationDefaults exists precisely to ensure that when a new notification type (e.g., PROOF_TIMEOUT) is added, existing users who upgrade get that type enabled rather than silently missing notifications. But the onRehydrateStorage callback that calls mergeNotificationDefaults is wired incorrectly for Zustand v4's API and never executes.
Problem / What
src/store/prefsStore.ts:
onRehydrateStorage: () => (persisted, error) => {
if (error != null || !persisted) { return; }
const merged = mergeNotificationDefaults(persisted.notificationPrefs);
usePrefsStore.setState({ notificationPrefs: merged });
},
Zustand v4's persist middleware calls onRehydrateStorage with (state) => void, not with () => (state, error) => void. The outer () => returns a function, but Zustand v4 calls the return value with only state, not (state, error). The inner function is therefore invoked as (persisted) where persisted is the state and error is always undefined — but the outer arrow function is the one actually registered, and its body (return (persisted, error) => {...}) is simply returning a function that Zustand ignores. The setState call never runs.
The fix is to use the Zustand v4 signature directly:
onRehydrateStorage: (state) => {
if (!state) { return; }
usePrefsStore.setState({ notificationPrefs: mergeNotificationDefaults(state.notificationPrefs) });
},
Key Challenges
- The fix is a Zustand API subtlety; the test in
prefsStore.test.ts must verify the actual post-rehydration store state, not just that the function was called.
- Confirm the
onRehydrateStorage signature against the installed Zustand version (package.json) to use the correct form.
- After the fix, run the existing
prefsStore.test.ts — it likely passes vacuously because the dead code never ran before.
Acceptance Criteria
Relevant files / functions
src/store/prefsStore.ts — onRehydrateStorage, mergeNotificationDefaults
src/__tests__/prefsStore.test.ts — extend with rehydration tests
Out of scope
- Adding new notification types.
- Changes to
quietHours or scheduledNotificationIds persistence.
Why this matters now
The notification system is listed as shipped in the current roadmap.
mergeNotificationDefaultsexists precisely to ensure that when a new notification type (e.g.,PROOF_TIMEOUT) is added, existing users who upgrade get that type enabled rather than silently missing notifications. But theonRehydrateStoragecallback that callsmergeNotificationDefaultsis wired incorrectly for Zustand v4's API and never executes.Problem / What
src/store/prefsStore.ts:Zustand v4's
persistmiddleware callsonRehydrateStoragewith(state) => void, not with() => (state, error) => void. The outer() =>returns a function, but Zustand v4 calls the return value with onlystate, not(state, error). The inner function is therefore invoked as(persisted)wherepersistedis the state anderroris alwaysundefined— but the outer arrow function is the one actually registered, and its body (return (persisted, error) => {...}) is simply returning a function that Zustand ignores. ThesetStatecall never runs.The fix is to use the Zustand v4 signature directly:
Key Challenges
prefsStore.test.tsmust verify the actual post-rehydration store state, not just that the function was called.onRehydrateStoragesignature against the installed Zustand version (package.json) to use the correct form.prefsStore.test.ts— it likely passes vacuously because the dead code never ran before.Acceptance Criteria
notificationPrefsthat is missing thePROOF_TIMEOUTkey, the store containsPROOF_TIMEOUT: true.notificationPrefsthat hasREWARD_CONFIRMED: false, that preference is preserved (not overwritten by defaults).prefsStore.test.tshas explicit assertions for both cases above.onRehydrateStorageside effects are introduced.Relevant files / functions
src/store/prefsStore.ts—onRehydrateStorage,mergeNotificationDefaultssrc/__tests__/prefsStore.test.ts— extend with rehydration testsOut of scope
quietHoursorscheduledNotificationIdspersistence.