65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
import { getAuthUserKey } from "./auth";
|
|
|
|
export type ThemeModePref = "system" | "light" | "dark";
|
|
export type EffectiveThemeMode = "light" | "dark";
|
|
|
|
export const DEFAULT_THEME_MODE: ThemeModePref = "system";
|
|
export const THEME_MODE_KEY = "jobtracker.themeMode";
|
|
const THEME_KEY_PREFIX = "themeMode:";
|
|
|
|
export function getUserKeyFromToken(): string {
|
|
return getAuthUserKey();
|
|
}
|
|
|
|
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 savedMode = readThemeMode(THEME_MODE_KEY);
|
|
if (savedMode) return savedMode;
|
|
|
|
// Migrate the former account-scoped values once. Theme is a browser UI
|
|
// preference, so changing auth state must never change the visible scheme.
|
|
const userKey = getUserKeyFromToken();
|
|
const savedUserMode = readThemeMode(`${THEME_KEY_PREFIX}${userKey}`);
|
|
const legacyMode = savedUserMode ?? (userKey !== "anon" ? readThemeMode(`${THEME_KEY_PREFIX}anon`) : null);
|
|
if (legacyMode) {
|
|
try {
|
|
window.localStorage.setItem(THEME_MODE_KEY, legacyMode);
|
|
} catch {
|
|
// The migrated value still applies for this render when storage is unavailable.
|
|
}
|
|
return legacyMode;
|
|
}
|
|
return DEFAULT_THEME_MODE;
|
|
}
|
|
|
|
export function setThemeModePref(v: ThemeModePref) {
|
|
try {
|
|
window.localStorage.setItem(THEME_MODE_KEY, 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 === THEME_MODE_KEY) sync();
|
|
};
|
|
window.addEventListener("storage", onStorage);
|
|
return () => {
|
|
window.removeEventListener("storage", onStorage);
|
|
};
|
|
}
|