import { create } from 'zustand' import { immer } from 'zustand/middleware/immer' import { DEFAULT_USER_SETTINGS, mergeSettings, type UserSettings, } from '@/lib/user-settings' import { updateUserSettingsAction } from '@/actions/user-settings' type SettingsPath = readonly (string | number)[] interface PendingMutation { id: number prev: UserSettings } interface UserSettingsState { entities: { settings: UserSettings } context: { hydrated: boolean isDemo: boolean } pendingMutations: Record } interface UserSettingsActions { hydrate: (initial: UserSettings, isDemo: boolean) => void setPref: (path: SettingsPath, value: unknown) => Promise applyServerPatch: (patch: Partial) => void } let nextMutationId = 1 function patchFromPath(path: SettingsPath, value: unknown): Partial { if (path.length === 0) { if (value && typeof value === 'object' && !Array.isArray(value)) { return value as Partial } return {} } const out: Record = {} let cursor: Record = out for (let i = 0; i < path.length - 1; i++) { const key = String(path[i]) cursor[key] = {} cursor = cursor[key] as Record } cursor[String(path[path.length - 1])] = value return out as Partial } export const useUserSettingsStore = create()( immer((set, get) => ({ entities: { settings: DEFAULT_USER_SETTINGS }, context: { hydrated: false, isDemo: false }, pendingMutations: {}, hydrate: (initial, isDemo) => { set((draft) => { draft.entities.settings = initial as UserSettings draft.context.hydrated = true draft.context.isDemo = isDemo }) }, applyServerPatch: (patch) => { set((draft) => { draft.entities.settings = mergeSettings( draft.entities.settings as UserSettings, patch, ) as UserSettings }) }, setPref: async (path, value) => { const patch = patchFromPath(path, value) // Demo: lokale merge zonder server-call. if (get().context.isDemo) { set((draft) => { draft.entities.settings = mergeSettings( draft.entities.settings as UserSettings, patch, ) as UserSettings }) return } const mutationId = nextMutationId++ const prev = get().entities.settings as UserSettings // Optimistic. set((draft) => { draft.entities.settings = mergeSettings( draft.entities.settings as UserSettings, patch, ) as UserSettings draft.pendingMutations[mutationId] = { id: mutationId, prev } }) const result = await updateUserSettingsAction(patch) set((draft) => { delete draft.pendingMutations[mutationId] if ('error' in result) { // Rollback alleen als geen latere mutatie de waarde alweer heeft overschreven. draft.entities.settings = prev as UserSettings } else { // Settle: server-merge is autoritatief. draft.entities.settings = result.settings as UserSettings } }) }, })), )