COndense comments, better prefcache use for theme

This commit is contained in:
binwiederhier
2026-06-26 07:58:28 -04:00
parent 156ad4ae92
commit 7a99bfc717
8 changed files with 82 additions and 96 deletions
+37 -9
View File
@@ -1,15 +1,31 @@
import * as React from "react";
import { createContext, useContext } from "react";
import { createContext, useContext, useEffect } from "react";
import { useLiveQuery } from "dexie-react-hooks";
import prefs, { PREF_DEFAULTS } from "../app/Prefs";
// A render-time CACHE of the user's preferences -- NOT the source of truth. The source of truth is
// the `prefs` table in IndexedDB, always read/written via Prefs.js. This context preloads all prefs
// once (mounted by Layout, above the routed content), so the Settings page renders its controls
// with the right values immediately instead of each one flickering in as its own IndexedDB read
// resolves. By the time you navigate to Settings the query has already resolved.
// A CACHE of the user's prefs -- not the source of truth (that's the `prefs` IndexedDB table, via
// Prefs.js). Preloaded once in Layout so Settings renders instantly, and written through to
// localStorage on every change so (a) Settings is instant even on a cold load and (b) the inline
// splash script in index.html can read the theme synchronously before the bundle loads. The
// "prefcache" key is duplicated in that script -- keep them in sync.
const PREFCACHE_LOCALSTORAGE_KEY = "prefcache";
const PrefCacheContext = createContext(undefined);
// Synchronous fallback before the live query resolves. Merged over PREF_DEFAULTS so a newly-added
// pref still has a value.
const readPersistedCache = () => {
try {
const raw = localStorage.getItem(PREFCACHE_LOCALSTORAGE_KEY);
if (raw) {
return { ...PREF_DEFAULTS, ...JSON.parse(raw) };
}
} catch (e) {
// malformed or unavailable storage -- fall back to defaults
}
return PREF_DEFAULTS;
};
export const PrefCacheProvider = ({ children }) => {
const cache = useLiveQuery(async () => ({
sound: await prefs.sound(),
@@ -18,9 +34,21 @@ export const PrefCacheProvider = ({ children }) => {
theme: await prefs.theme(),
webPushEnabled: await prefs.webPushEnabled(),
}));
// Write through to localStorage on change (prefs change rarely).
useEffect(() => {
if (cache !== undefined) {
try {
localStorage.setItem(PREFCACHE_LOCALSTORAGE_KEY, JSON.stringify(cache));
} catch (e) {
// localStorage may be unavailable (private mode) -- the cache just isn't persisted
}
}
}, [cache]);
return <PrefCacheContext.Provider value={cache}>{children}</PrefCacheContext.Provider>;
};
// While the cache is still loading (only on the very first paint, mostly hidden by the splash) fall
// back to the same defaults the Prefs getters use, so controls never render with no value.
export const usePrefCache = () => useContext(PrefCacheContext) ?? PREF_DEFAULTS;
// Live context once resolved; else the synchronous localStorage snapshot (instant on cold load);
// else defaults.
export const usePrefCache = () => useContext(PrefCacheContext) ?? readPersistedCache();