fix(android/resilience): auto-recover failed loads on reconnect #106
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes the "fails once, stays broken forever" behavior (issue #1245): when a screen failed to load (dead zone, server restart), nothing ever retried — and the cache-first screens masked the failure as an empty account.
What changed
Screen-level recovery (the missing wire)
connectivity/Recovery.kt:NetworkStatusController.recoveries()— per-collector flow of down→Healthy transitions (StateFlow replay dropped, so an already-healthy subscribe never double-loads). Screen-level half of the idiom SyncController / MutationReplayer / DiagnosticsUploader already use.Failure no longer masquerades as emptiness
refreshErrorStateFlow folded into their state combine; Library reads newSyncController.lastSyncError. Empty cache + failed load → "Couldn't load…" with Retry instead of "Welcome to Minstrel" / "No playlists yet". Failed refresh over a populated cache stays silent (stale content beats a full-screen error; the connection banner tells the story).Stuck corners unstuck
UiState.Error(was structurally unable to escape it — predicate required a Success state). Also covers non-transport failures (e.g. a 500) that health monitoring never sees.retry()bypasses thedistinctUntilChangedquery pipeline so re-running the same query after a transient failure works; error state now has a Retry button.Retry affordance normalized (rule 24)
ErrorRetrygains optional title + LazyColumn wrapper so pull-to-refresh works on error states (same rationale as EmptyState); adopted on every error branch. PlaylistDetail's private one-shotErrorBlockdeleted in its favor.Verification
7628330f(ktlint, detekt, 99 unit tests, assemble).LibraryViewModelTestupdated:lastSyncErrorstubbed with a realMutableStateFlow(a relaxed-mock flow never emits, which kept the state combine from firing).🤖 Generated with Claude Code
https://claude.ai/code/session_01TsF3cNoKrqCYsU78cXC8U6