refactor(frontend): adopt store/storage helpers; centralize persistence

- errorDialog + hints now use createStore (drop their bespoke listener arrays).
- theme/sidebarLayout/notifications/App filters/Playlists plSort use readMerged/
  readJSON/writeJSON instead of inline try/JSON.parse/catch.
- Stats, SettingsPanel and App's channel filter/view tabs use usePersistedState
  (the 3 sites that reinvented usePersistedTab inline).
- Every siftlode.* key now sourced from the LS registry (no scattered literals).
This commit is contained in:
npeter83 2026-06-26 03:30:19 +02:00
parent 634921b35a
commit 472aba6480
15 changed files with 80 additions and 147 deletions

View file

@ -4,6 +4,7 @@ import { useQueryClient } from "@tanstack/react-query";
import { Bell, Check, Monitor, RotateCcw, Save, Trash2, User } from "lucide-react";
import { SCHEMES, type Scheme, type ThemePrefs } from "../lib/theme";
import { api, type Me } from "../lib/api";
import { LS, usePersistedState } from "../lib/storage";
import Avatar from "./Avatar";
import { notify, type NotifSettings } from "../lib/notifications";
import Tooltip from "./Tooltip";
@ -38,14 +39,6 @@ const TABS: { id: TabId; icon: typeof Monitor }[] = [
{ id: "account", icon: User },
];
// Persist the active tab so a reload (F5) keeps the user where they were instead of
// snapping back to "appearance".
const SETTINGS_TAB_KEY = "siftlode.settingsTab";
function loadSettingsTab(): TabId {
const v = localStorage.getItem(SETTINGS_TAB_KEY);
return TABS.some((tabItem) => tabItem.id === v) ? (v as TabId) : "appearance";
}
// Settings as a page (Design B): rendered in the main content area, not an overlay. It keeps
// the frosted `.glass` surface so it still refracts the ambient backdrop. The page title is
// shown by the Header; the tab rail stays as in-page sub-navigation.
@ -59,11 +52,11 @@ export default function SettingsPanel({
onOpenWizard: () => void;
}) {
const { t } = useTranslation();
const [tab, setTabState] = useState<TabId>(loadSettingsTab);
const setTab = (id: TabId) => {
setTabState(id);
localStorage.setItem(SETTINGS_TAB_KEY, id);
};
const [tabRaw, setTab] = usePersistedState(LS.settingsTab, "appearance");
// Clamp at render so a stale/removed tab id falls back to Appearance.
const tab: TabId = TABS.some((tabItem) => tabItem.id === tabRaw)
? (tabRaw as TabId)
: "appearance";
return (
<div className="p-4 max-w-3xl w-full mx-auto">