feat: persist theme prefs in localStorage for reliable same-device persistence
- Add src/lib/local-theme-prefs.ts: read/write color theme, radius, sidebar config to localStorage key 'isletmem-theme-prefs' - ThemeCustomizer: init state from localStorage (falls back to Appwrite prefs), save to localStorage on every color/radius/sidebar change in addition to Appwrite - PrefsInitializer: merge localStorage (wins) with server Appwrite prefs on mount Appwrite updatePrefs had a silent try/catch so failures were invisible; dark/light was already in localStorage via ThemeProvider but color theme was not.
This commit is contained in:
@@ -7,6 +7,7 @@ import { useTheme } from "@/hooks/use-theme";
|
||||
import { useThemeManager } from "@/hooks/use-theme-manager";
|
||||
import { tweakcnThemes } from "@/config/theme-data";
|
||||
import type { ThemePrefs } from "@/lib/appwrite/theme-prefs-actions";
|
||||
import { getLocalThemePrefs } from "@/lib/local-theme-prefs";
|
||||
|
||||
export function PrefsInitializer({ prefs }: { prefs: ThemePrefs }) {
|
||||
const { setTheme } = useTheme();
|
||||
@@ -18,30 +19,40 @@ export function PrefsInitializer({ prefs }: { prefs: ThemePrefs }) {
|
||||
if (applied.current) return;
|
||||
applied.current = true;
|
||||
|
||||
if (prefs.theme) setTheme(prefs.theme);
|
||||
// localStorage wins (most recent change on this device); Appwrite prefs are fallback
|
||||
const local = getLocalThemePrefs();
|
||||
const effectiveTheme = prefs.theme;
|
||||
const effectiveColorTheme = local.colorTheme ?? prefs.colorTheme;
|
||||
const effectiveTweakcnTheme = local.tweakcnTheme ?? prefs.tweakcnTheme;
|
||||
const effectiveRadius = local.radius ?? prefs.radius;
|
||||
const effectiveSidebarVariant = (local.sidebarVariant as ThemePrefs["sidebarVariant"]) ?? prefs.sidebarVariant;
|
||||
const effectiveSidebarCollapsible = (local.sidebarCollapsible as ThemePrefs["sidebarCollapsible"]) ?? prefs.sidebarCollapsible;
|
||||
const effectiveSidebarSide = (local.sidebarSide as ThemePrefs["sidebarSide"]) ?? prefs.sidebarSide;
|
||||
|
||||
if (effectiveTheme) setTheme(effectiveTheme);
|
||||
|
||||
const isDark =
|
||||
prefs.theme === "dark" ||
|
||||
(prefs.theme !== "light" &&
|
||||
effectiveTheme === "dark" ||
|
||||
(effectiveTheme !== "light" &&
|
||||
typeof window !== "undefined" &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
|
||||
if (prefs.radius) applyRadius(prefs.radius);
|
||||
if (effectiveRadius) applyRadius(effectiveRadius);
|
||||
|
||||
if (prefs.colorTheme) {
|
||||
applyTheme(prefs.colorTheme, isDark);
|
||||
if (effectiveColorTheme) {
|
||||
applyTheme(effectiveColorTheme, isDark);
|
||||
}
|
||||
|
||||
if (prefs.tweakcnTheme) {
|
||||
const preset = tweakcnThemes.find((t) => t.value === prefs.tweakcnTheme)?.preset;
|
||||
if (effectiveTweakcnTheme) {
|
||||
const preset = tweakcnThemes.find((t) => t.value === effectiveTweakcnTheme)?.preset;
|
||||
if (preset) applyTweakcnTheme(preset, isDark);
|
||||
}
|
||||
|
||||
if (prefs.sidebarVariant || prefs.sidebarCollapsible || prefs.sidebarSide) {
|
||||
if (effectiveSidebarVariant || effectiveSidebarCollapsible || effectiveSidebarSide) {
|
||||
updateConfig({
|
||||
...(prefs.sidebarVariant && { variant: prefs.sidebarVariant }),
|
||||
...(prefs.sidebarCollapsible && { collapsible: prefs.sidebarCollapsible }),
|
||||
...(prefs.sidebarSide && { side: prefs.sidebarSide }),
|
||||
...(effectiveSidebarVariant && { variant: effectiveSidebarVariant }),
|
||||
...(effectiveSidebarCollapsible && { collapsible: effectiveSidebarCollapsible }),
|
||||
...(effectiveSidebarSide && { side: effectiveSidebarSide }),
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
Reference in New Issue
Block a user