A module's definition names its store (feed: filters, view, saved views; Plex: browse state, saved views; channels, files, playlists their provider), and ModuleStores mounts them at the root in registry order instead of a hand-nested tree in main.tsx. A contract test holds that every store's read side is in reach and that no store's static imports leave its own module, so the order cannot matter. Saved views were one provider that switched between the feed and Plex by the active page and reset on each switch. The logic is now one hook (useSavedViewsStore) with an instance per module; the shared chip, popover and modal read the views their page or rail hands down. A feed view applied and edited now survives a visit to Plex. Plex's views are still asked for only on its page, and a module's manage modal still closes when its page is left. The feed's URL-override check applies only on the feed page, where its filter params live. The Channels store's tab type moves from the page into the store's context.
35 lines
1.7 KiB
TypeScript
35 lines
1.7 KiB
TypeScript
// Pure selection helpers for saved ("personal") views, split out of useSavedViewsStore so the
|
|
// tie-break logic is unit-testable without mounting a store (its React Query / context deps).
|
|
|
|
/** Among the views whose filters match the live filters, pick the one to treat as active: prefer the
|
|
* explicitly-applied ("sticky") view when several capture identical filters, so apply(B) isn't
|
|
* silently re-attributed to an earlier duplicate A; else the starred default, which is what the
|
|
* first-load seed applies (so the seed needs no sticky write of its own); else the first match.
|
|
* Null if none match. */
|
|
export function preferSticky<T extends { id: number; is_default?: boolean }>(
|
|
matches: T[],
|
|
stickyId: number | null,
|
|
): T | null {
|
|
return (
|
|
matches.find((v) => v.id === stickyId) ??
|
|
matches.find((v) => v.is_default) ??
|
|
matches[0] ??
|
|
null
|
|
);
|
|
}
|
|
|
|
/** The starred default view to seed a module's filters with on first load, or null to leave them.
|
|
* Seeds ONLY when the module is a genuinely fresh start (`canSeed`: no stored filters for this
|
|
* account and — feed only — no URL filter override); otherwise the last-session/URL/manual filters
|
|
* win, exactly as they do after any edit. First `is_default` wins if somehow several are flagged, so
|
|
* the choice is deterministic. This is the single default-application path for BOTH the feed and Plex
|
|
* (useSavedViewsStore drives it on the views query's first resolve) — it replaced the feed-only
|
|
* localStorage mirror that raced the login effect and left Plex with no default at all. */
|
|
export function pickDefaultToSeed<T extends { is_default: boolean }>(
|
|
views: T[],
|
|
canSeed: boolean,
|
|
): T | null {
|
|
if (!canSeed) return null;
|
|
return views.find((v) => v.is_default) ?? null;
|
|
}
|