Files
mnote/wolai-frontend/src/store/app-preferences.ts
T

113 lines
3.1 KiB
TypeScript
Raw Normal View History

2026-02-01 08:47:40 +08:00
"use client";
import { create } from "zustand";
export type ThemeMode = "system" | "light" | "dark";
type PersistedPreferences = {
theme: ThemeMode;
showStructure: boolean;
spellCheck: boolean;
flightMode: boolean;
};
const STORAGE_KEY = "mnote:preferences:v1";
const loadPersisted = (): PersistedPreferences | null => {
if (typeof window === "undefined") return null;
try {
const raw = window.localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw) as Partial<PersistedPreferences>;
const theme = parsed.theme === "light" || parsed.theme === "dark" || parsed.theme === "system" ? parsed.theme : "system";
return {
theme,
showStructure: typeof parsed.showStructure === "boolean" ? parsed.showStructure : false,
spellCheck: typeof parsed.spellCheck === "boolean" ? parsed.spellCheck : false,
flightMode: typeof parsed.flightMode === "boolean" ? parsed.flightMode : false,
};
} catch {
return null;
}
};
const persist = (value: PersistedPreferences) => {
if (typeof window === "undefined") return;
try {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
} catch {
// ignore
}
};
interface AppPreferencesState extends PersistedPreferences {
hydrated: boolean;
hydrate: () => void;
setTheme: (theme: ThemeMode) => void;
setShowStructure: (showStructure: boolean) => void;
setSpellCheck: (spellCheck: boolean) => void;
setFlightMode: (flightMode: boolean) => void;
}
export const useAppPreferencesStore = create<AppPreferencesState>((set, get) => ({
hydrated: false,
theme: "system",
showStructure: false,
spellCheck: false,
flightMode: false,
hydrate: () => {
const current = get();
if (current.hydrated) return;
const persisted = loadPersisted();
if (persisted) {
set({ ...persisted, hydrated: true });
} else {
set({ hydrated: true });
}
},
setTheme: (theme) =>
set((state) => {
const next = { ...state, theme };
persist({
theme: next.theme,
showStructure: next.showStructure,
spellCheck: next.spellCheck,
flightMode: next.flightMode,
});
return next;
}),
setShowStructure: (showStructure) =>
set((state) => {
const next = { ...state, showStructure };
persist({
theme: next.theme,
showStructure: next.showStructure,
spellCheck: next.spellCheck,
flightMode: next.flightMode,
});
return next;
}),
setSpellCheck: (spellCheck) =>
set((state) => {
const next = { ...state, spellCheck };
persist({
theme: next.theme,
showStructure: next.showStructure,
spellCheck: next.spellCheck,
flightMode: next.flightMode,
});
return next;
}),
setFlightMode: (flightMode) =>
set((state) => {
const next = { ...state, flightMode };
persist({
theme: next.theme,
showStructure: next.showStructure,
spellCheck: next.spellCheck,
flightMode: next.flightMode,
});
return next;
}),
}));