fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
import { LS, readAccountMerged, writeAccount } from "./storage";
|
2026-06-26 03:30:19 +02:00
|
|
|
|
2026-07-11 04:47:08 +02:00
|
|
|
type Mode = "dark" | "light";
|
2026-06-11 02:19:47 +02:00
|
|
|
export type Scheme = "midnight" | "forest" | "slate" | "youtube";
|
|
|
|
|
|
|
|
|
|
export const SCHEMES: { id: Scheme; name: string; swatch: string }[] = [
|
|
|
|
|
{ id: "midnight", name: "Midnight", swatch: "#6d8cff" },
|
|
|
|
|
{ id: "forest", name: "Forest", swatch: "#2dd4bf" },
|
|
|
|
|
{ id: "slate", name: "Slate", swatch: "#e8913c" },
|
|
|
|
|
{ id: "youtube", name: "YouTube", swatch: "#ff3b46" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export interface ThemePrefs {
|
|
|
|
|
mode: Mode;
|
|
|
|
|
scheme: Scheme;
|
|
|
|
|
fontScale: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const DEFAULT_THEME: ThemePrefs = {
|
|
|
|
|
mode: "dark",
|
|
|
|
|
scheme: "midnight",
|
|
|
|
|
fontScale: 1.06,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function applyTheme(t: ThemePrefs): void {
|
|
|
|
|
const el = document.documentElement;
|
|
|
|
|
el.dataset.theme = t.mode;
|
|
|
|
|
el.dataset.scheme = t.scheme;
|
|
|
|
|
el.style.setProperty("--font-scale", String(t.fontScale));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function loadLocalTheme(): ThemePrefs {
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
return readAccountMerged(LS.theme, DEFAULT_THEME);
|
2026-06-11 02:19:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function saveLocalTheme(t: ThemePrefs): void {
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
writeAccount(LS.theme, t);
|
2026-06-11 02:19:47 +02:00
|
|
|
}
|