fix(spa): gate mutation controls on stale result sets during refetch (#221) #222

Merged
timothy merged 3 commits from fix/221-stale-refetch into main 2026-07-11 01:47:37 +02:00
Owner

Closes #221 (adversarial-reviewer#18 MUST-FIX gate for #91 phase b).

  • Param-keyed success state in SearchScreen + MediaBrowseScreen: refreshing derived when visible results no longer match current query/kind/page; visible cue (spinner + grid dim), all mutation surfaces disabled (per-card AddToMenu, select mode, selection bar, Add All, Save As Smart Collection); card navigation stays live.
  • addAll completion now discards resolutions whose snapshotted query is no longer current (was: only mount check — could open a bulk-add dialog for a previous query's full result set).
  • 3 new deferred-promise race tests; full web suite 480 green.
  • docs: spa-conventions §3a refreshing/gating pattern + §8 temporal-semantics review checklist (adversarial-reviewer#18 process finding); blazor-route-parity search/media verdict notes.

🤖 Generated with Claude Code

Closes #221 (adversarial-reviewer#18 MUST-FIX gate for #91 phase b). - Param-keyed success state in SearchScreen + MediaBrowseScreen: `refreshing` derived when visible results no longer match current query/kind/page; visible cue (spinner + grid dim), all mutation surfaces disabled (per-card AddToMenu, select mode, selection bar, Add All, Save As Smart Collection); card navigation stays live. - `addAll` completion now discards resolutions whose snapshotted query is no longer current (was: only mount check — could open a bulk-add dialog for a previous query's full result set). - 3 new deferred-promise race tests; full web suite 480 green. - docs: spa-conventions §3a refreshing/gating pattern + §8 temporal-semantics review checklist (adversarial-reviewer#18 process finding); blazor-route-parity search/media verdict notes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
timothy added 1 commit 2026-07-10 23:28:04 +02:00
fix(spa): gate mutation controls + add-all on stale result sets during refetch (fixes #221)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m11s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 10m56s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
81ddd4e5ca
Search and Media browse keep the previous successful result set rendered
during a refetch (query on Search; kind/query/page on Media browse) with no
gating, so per-card Add-to, Select/select-mode, the selection action bar, Add
all, and Save-as-smart-collection stayed live over stale, about-to-be-replaced
items. Worst path: SearchScreen.addAll only checked activeRef, so a late
GET /api/search/all-items could open a bulk-add dialog scoped to the previous
query's entire result set.

Key the success state to the request params that produced it and derive a
`refreshing` flag; while refreshing, keep cards visible but disable every
mutation surface, show a "Refreshing…" cue, and dim the grid. Card navigation
stays live. Bind addAll's completion to its query via lastQueryRef so a stale
all-items result is discarded. Same pattern applied to both screens.

Docs: spa-conventions §3a (refreshing/gating pattern) + §8 (temporal-semantics
review checklist); blazor-route-parity search/media-browse verdicts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
timothy added 1 commit 2026-07-10 23:35:18 +02:00
fix(spa): review fixes for #221 refetch gating — stuck cue, inert select cards, exit-while-refreshing
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m27s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Failing after 2s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 12s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
c038e13a99
Three PR #222 adversarial-review findings fixed:

1. SearchScreen's `refreshing` derivation compared the last success `state.query`
   against the current query even when the query was cleared to empty — `load()`
   early-returns on a blank query, so `state` never updates and the "Refreshing…"
   cue got stuck forever over the empty-query card. Gate on `hasQuery`.
2. `MediaPosterCard` falls back to `onOpen` whenever `onToggleSelect` is
   undefined, so `selectMode && refreshing` (onToggleSelect withheld but onOpen
   still derived from `!canSelect`) made a mid-select click navigate away
   instead of no-op'ing. Both screens now withhold `onOpen` for the whole of
   select mode, not just the "live" part of it.
3. The Select/Done toggle was `disabled={refreshing}`, which also blocked
   *exiting* select mode — but exiting only clears selection, it isn't a
   mutation against the stale result set. Disable only when entering
   (`refreshing && !selectMode`).

Also corrected the "can never get stuck" over-claim in docs/spa-conventions.md
§3a: the param-keyed refreshing derivation is only self-correcting when every
param value actually triggers a fetch; params that suppress fetching (like an
empty search query) must be excluded from the comparison or the whole flag
gated on the same condition.

Tests added: query-cleared-to-empty shows no refreshing cue (both screens'
existing 3 race tests still green); select-mode+refreshing card click neither
selects nor navigates; select toggle disabled only while entering, not exiting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
timothy added 1 commit 2026-07-11 00:31:06 +02:00
Merge main: CI MySQL host-port unpin (collision fix)
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 4s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m8s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 9m37s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
7a28e6ae9c
timothy merged commit 245b1a5889 into main 2026-07-11 01:47:37 +02:00
Sign in to join this conversation.