fix(plex): resolve deep-review findings on the filter-bar overhaul

Self-review of the branch diff (feature/plex-filter-bar-overhaul) against main:

- Surprise rail query key omitted `lang` while the backend /surprise now filters
  against the language-specific genre column → a HU/EN switch under an active genre
  filter left the rail stale. Key it by scope+lang+filters, matching plexDiscover.
- Chip-removing a collection left its entry in `collectionTitles` (hashed into the
  plex query keys), splitting the cache from the pristine no-collection state and
  growing the titles map unboundedly. Prune the title on remove, like CollectionPicker.
- The saved-views default-mirror effect gained `cfg` in its deps (re-memoized on every
  live filter edit) → a synchronous localStorage write per keystroke. Depend on the
  module-stable mirror fn instead.
- Collections-browse search input had no accessible name (placeholder only). Add
  aria-label + type="search".
- Extract the tile hue-hash + gradient duplicated between PlexDiscover and
  plexBrowseLists into a shared lib/hueTile so the two surfaces can't drift.
- Document migration 0073 downgrade() as lossy and de-dup non-feed rows before
  recreating the narrow (user_id, name) constraint (verified by the drift test).
- Add unit tests for coercePlexFilters (legacy single-collection migration) and
  plexFiltersSignature (order-independence, title-ignoring).

Gate: tsc/lint/knip/prettier clean, 410 vitest; backend ruff + migration drift +
saved-views tests green.
This commit is contained in:
2026-08-23 01:39:29 +02:00
parent a20e27800c
commit b7a4eaff8d
8 changed files with 110 additions and 29 deletions
+2 -1
View File
@@ -99,7 +99,8 @@ export const qk = {
// both for a prefix invalidation after a watched-toggle. Surprise is its own key so the ↻ reshuffle
// refetches it alone.
plexDiscover: (...rest: unknown[]) => ["plex-discover", ...rest],
// Variadic like plexDiscover so it can key by scope + the active filters; `plexSurprise()` (no args)
// Variadic like plexDiscover so it can key by scope + lang + the active filters (lang because a genre
// filter narrows against the language-specific genre column, like the grid); `plexSurprise()` (no args)
// stays a prefix for the reshuffle/watched-toggle invalidations.
plexSurprise: (...rest: unknown[]) => ["plex-surprise", ...rest],
plexSuggest: (...rest: unknown[]) => ["plex-suggest", ...rest],