fix(theme): make preference state deterministic

This commit is contained in:
cesnimda
2026-08-09 18:35:31 +02:00
parent 378807b3b9
commit 11734ee058
8 changed files with 240 additions and 23 deletions
+44 -5
View File
@@ -1,6 +1,10 @@
import { getAuthUserKey } from "./auth";
import { AUTH_USER_CHANGED_EVENT, getAuthUserKey } from "./auth";
export type ThemeModePref = "system" | "light" | "dark";
export type EffectiveThemeMode = "light" | "dark";
export const DEFAULT_THEME_MODE: ThemeModePref = "system";
const THEME_KEY_PREFIX = "themeMode:";
export function getUserKeyFromToken(): string {
return getAuthUserKey();
@@ -10,12 +14,47 @@ function k(base: string) {
return `${base}:${getUserKeyFromToken()}`;
}
function readThemeMode(key: string): ThemeModePref | null {
try {
const raw = window.localStorage.getItem(key);
return raw === "light" || raw === "dark" || raw === "system" ? raw : null;
} catch {
return null;
}
}
export function getThemeModePref(): ThemeModePref {
const raw = window.localStorage.getItem(k("themeMode"));
if (raw === "light" || raw === "dark" || raw === "system") return raw;
return "system";
const userKey = getUserKeyFromToken();
const savedUserMode = readThemeMode(`${THEME_KEY_PREFIX}${userKey}`);
if (savedUserMode) return savedUserMode;
if (userKey !== "anon") {
const anonymousMode = readThemeMode(`${THEME_KEY_PREFIX}anon`);
if (anonymousMode) return anonymousMode;
}
return DEFAULT_THEME_MODE;
}
export function setThemeModePref(v: ThemeModePref) {
window.localStorage.setItem(k("themeMode"), v);
try {
window.localStorage.setItem(k("themeMode"), v);
} catch {
// Keep the in-memory selection even when storage is unavailable.
}
}
export function resolveEffectiveThemeMode(preference: ThemeModePref, systemPrefersDark: boolean): EffectiveThemeMode {
if (preference === "light" || preference === "dark") return preference;
return systemPrefersDark ? "dark" : "light";
}
export function subscribeToThemePreferenceChanges(sync: () => void) {
const onStorage = (event: StorageEvent) => {
if (event.key === "authUserKey" || event.key?.startsWith(THEME_KEY_PREFIX)) sync();
};
window.addEventListener(AUTH_USER_CHANGED_EVENT, sync);
window.addEventListener("storage", onStorage);
return () => {
window.removeEventListener(AUTH_USER_CHANGED_EVENT, sync);
window.removeEventListener("storage", onStorage);
};
}