E32 S4 — the burn-down from 29 skipped functions to 1: - try/finally (26 sites): the cleanup runs after a try/catch; steps that return early moved into their own function (push, the setup wizard's next, the share page's unlock). - A conditional, loop or optional call inside a try skips the function too, and react-hooks/todo does not see it: the value is computed before the try, or the try lives in a module-level helper (config writes, clip queueing, clipboard, frame counts, seekable end, YouTube player times). - The two exhaustive-deps suppressions are gone. useYtProgress takes its per-item snapshot through one effect event, read once when the player is built; PlexPlayer's unmount relies on the leaving-the-item cleanup for the generation bump it duplicated. - What the skips hid: PlexPlayer's 17 render-phase ref writes (useLatest), its tracks popover alias, the loadSession self-reference (declared early, set after commit); PlayerModal's session-position read during render (now where the player starts), navRef/modeRef writes, and a description helper that ran a ref-carrying callback during render (now PlayerDescription); SavedViewsProvider's latest-cfg ref, and its setState in the seed effect (preferSticky now prefers the starred default among same-filter views, so the seed needs no sticky write — test seen red on the old helper). - A store's hook was called `use()`, which the compiler does not know as a hook: inside the newly compiled usePlaylistActions it was memoized, ran on the first render alone, and the playlist rail threw "rendered fewer hooks" on its second render. Renamed to `useValue()` everywhere; the old contract test claimed the call was safe inside any `use…` function, which was false — replaced by a render test that is red under the old name. - PlexPlayer's palette ref was written and never read since the Backspace guard moved to the layer stack: removed. react-hooks/refs is back at error with no exception. The ratchet lists only VirtualGrid (IncompatibleLibrary) and pins both players as compiled. No size or complexity ceiling was raised.
35 lines
1.7 KiB
TypeScript
35 lines
1.7 KiB
TypeScript
// Pure selection helpers for saved ("personal") views, split out of SavedViewsProvider so the
|
|
// tie-break logic is unit-testable without mounting the provider (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
|
|
* (SavedViewsProvider 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;
|
|
}
|