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

@ -5,10 +5,10 @@ import { History, Pause, Play, RefreshCw } from "lucide-react";
import { api, type AdminQuotaRow, type Me } from "../lib/api";
import { formatEta, quotaActionLabel } from "../lib/format";
import { notify } from "../lib/notifications";
import { LS, usePersistedState } from "../lib/storage";
import Tooltip from "./Tooltip";
const RANGES = [7, 30, 90] as const;
const STATS_TAB_KEY = "siftlode.statsTab";
type StatsTab = "overview" | "system";
// Usage & stats page. "Overview" is per-user (sync status + your own API usage + actions);
@ -17,14 +17,9 @@ type StatsTab = "overview" | "system";
export default function Stats({ me }: { me: Me }) {
const { t } = useTranslation();
const isAdmin = me.role === "admin";
const [tab, setTabState] = useState<StatsTab>(() => {
const v = localStorage.getItem(STATS_TAB_KEY);
return v === "system" && isAdmin ? "system" : "overview";
});
const setTab = (id: StatsTab) => {
setTabState(id);
localStorage.setItem(STATS_TAB_KEY, id);
};
const [tabRaw, setTab] = usePersistedState(LS.statsTab, "overview");
// Clamp at render: only admins may land on the System tab (covers a stale stored value).
const tab: StatsTab = tabRaw === "system" && isAdmin ? "system" : "overview";
return (
<div className="p-4 max-w-4xl mx-auto">