fix: surface a failed collections fetch instead of a false empty state (#6019) - #6131
Merged
Conversation
#6019) On /media/collections a rejected listMediaCollections() was swallowed into `[]`. That made a transient 500 or offline blip render the "No collections yet" onboarding copy and hand every image and video in the library to the synthetic "Unsorted" bucket — reading as "all my collections were deleted", with no error banner and no way to recover once the toast faded. Collections now use the repo's sentinel convention: `null` = never fetched or fetch failed, `[]` = the server really has none. A failed read sets an error state and leaves the list alone (the sentinel on first load, the last good list on a later refresh), so the grid never claims the library is unfiled. The page renders a persistent error banner naming the failure with a Retry button, and the onboarding empty state only appears on a genuine zero-collection response. The fetch now passes `silent: true` since the page owns its failure UI. Claude-Session: https://claude.ai/code/session_01RA3pD5YM2dukQwbZ3pC6WA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On
/media/collections,refresh()swallowed a rejectedlistMediaCollections()into[]. A transient 500, network timeout, or DB blip therefore rendered the "No collections yet" onboarding empty state and handed every image and video in the library to the synthetic "Unsorted" bucket — reading as "all my collections were deleted." There was no error banner and no retry path; once therequest()toast faded, nothing on screen said a read had failed.The fix applies the repo's sentinel convention (AGENTS.md, "Sentinel + validate to distinguish 'not set / failed' from 'present-but-empty / valid'"):
collectionsis nownull= never fetched / fetch failed,[]= the server genuinely has none.Promise.allresolves to an array on success and an{ error }envelope on failure, so the failure survives the join as data instead of flattening into an indistinguishable[].collectionsalone — the sentinel on a first-load failure, the last good list on a later refresh — so the grid never blanks and the synthetic "Unsorted" bucket is skipped entirely rather than claiming the whole library is unfiled.Banner tone="error"names the failure, states that nothing was deleted, and offers a Retry that re-invokesrefresh()(disabled while a load is in flight).EmptyStatenow renders only on a genuine zero-collection response.{ silent: true }, per the client convention that a caller owning its own error UI suppresses the sharedrequest()toast.handleCreate/handleDeleteno longer assume an array: they extend or filter only a list that actually loaded, so a mutation can't clear the sentinel and resurrect the "everything is unsorted" state.Test plan
Four new tests in
client/src/pages/MediaCollections.test.jsx, each verified to fail against the pre-fix component (4 failed / 20 passed) and pass after:{ silent: true }so the shared toast doesn't double-signalwithin()rather than indexing a shared-label match listFull client suite: 833 files / 10238 tests passed, 1 skipped. (One unrelated
LoomProductionPanelflake appeared on the first run; it passes in isolation on a clean tree and on a full-suite rerun.)Closes #6019
https://claude.ai/code/session_01RA3pD5YM2dukQwbZ3pC6WA