// Per-user customization of a side panel's group cards: their order, which are collapsed, and // which are hidden — the same "customize" affordance the feed filter rail had, generalized so // every panel (feed / plex / playlists / files) has its own saved layout. Persisted to localStorage and // the server-side `preferences` blob (schemaless), so it follows the account across devices. import { LS, readAccount, writeAccount } from "./storage"; export type PanelId = "feed" | "plex" | "playlists" | "files"; export interface PanelLayout { order: string[]; collapsed: Record; hidden: Record; } // The default group order per panel. Group ids must match the ids each panel feeds to PanelGroups. // Unknown/stale ids are dropped and any missing (e.g. a group added in a later version) are // appended by normalizeLayout, so nothing silently disappears. const PANEL_ORDER: Record = { feed: ["date", "year", "channel", "tags", "language", "topic"], plex: [ "playlists", "watch", "people", "collection", "rating", "genre", "year", "duration", "added", "contentrating", ], playlists: ["manage", "list"], files: ["library", "sources", "own"], }; // localStorage key per panel (feed keeps its original key for backward compatibility). const LS_KEY: Record = { feed: LS.sidebarLayout, plex: LS.plexLayout, playlists: LS.playlistsLayout, files: LS.filesLayout, }; // Key on the server `preferences` blob per panel (feed keeps its original key). export const PREF_KEY: Record< PanelId, "sidebarLayout" | "plexLayout" | "playlistsLayout" | "filesLayout" > = { feed: "sidebarLayout", plex: "plexLayout", playlists: "playlistsLayout", files: "filesLayout", }; export function defaultLayout(panel: PanelId): PanelLayout { return { order: [...PANEL_ORDER[panel]], collapsed: {}, hidden: {} }; } function normalizeLayout(panel: PanelId, raw: unknown): PanelLayout { const all = PANEL_ORDER[panel]; const r = (raw ?? {}) as Partial; const order = Array.isArray(r.order) ? r.order.filter((x) => all.includes(x)) : []; for (const id of all) if (!order.includes(id)) order.push(id); return { order, collapsed: { ...(r.collapsed ?? {}) }, hidden: { ...(r.hidden ?? {}) }, }; } function loadLayout(panel: PanelId): PanelLayout { return normalizeLayout(panel, readAccount(LS_KEY[panel], {})); } // A panel's layout when the app mounts: the account's server copy (the `me` cache's preferences) when // it has one — so a layout changed on another device is right on the first paint — else this browser's. export function seedLayout( panel: PanelId, prefs: Record | undefined, ): PanelLayout { const raw = prefs?.[PREF_KEY[panel]]; return raw ? normalizeLayout(panel, raw) : loadLayout(panel); } export function saveLayoutLocal(panel: PanelId, l: PanelLayout): void { writeAccount(LS_KEY[panel], l); }