feat(ui): room list loading/migrating state (#397)#401
Draft
sanity wants to merge 1 commit into
Draft
Conversation
During startup load — and especially during a delegate/room-contract migration — the room list rendered blank with no indicator, which reads to users as "all my rooms are gone" (Ivvor's report after the stdlib-0.8 republish). The `ROOMS` map populates one room at a time and, on a re-keyed delegate, only after legacy migration, so an empty map does not mean "no rooms." Give the room list a tri-state: - loading / migrating -> a quiet inline spinner + "Loading your rooms…" / "Restoring your rooms…" (the latter driven by the existing `is_legacy_migration_in_progress()` flag); - load resolved AND genuinely empty -> the "No rooms yet" create/join CTA; - populated -> the list. The genuinely-empty CTA now only shows once the initial load has actually resolved, tracked by a new `INITIAL_ROOMS_LOADED` signal set at the terminal points of `load_rooms_per_room` (with a bounded settle after the fire-and-forget legacy probe so a migrating user never flashes the empty state). UI-only — no contract/delegate WASM change, no re-key. The tri-state decision is a pure `room_list_display()` fn with unit tests. A dev-only `?room_list_state=` URL override (example-data builds only) lets each state be screenshotted without a live node. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CD8VBzb8qsDvdcPdF6TNQG
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.
Problem
During startup load — and especially during a delegate / room-contract migration — the River room list rendered blank with no loading or "migrating" indicator, which reads to users as "all my rooms are gone." Reported by Ivvor after the stdlib-0.8 River republish (delegate + room-contract re-key).
The
ROOMSmap populates one room at a time as each per-room slot GET returns, and on a re-keyed (empty) current delegate the rooms only arrive later via legacy migration. So an empty map does not mean "no rooms" — it usually means "still loading." Today the empty map fell straight through to a blank list (or, worse, could read as the empty state), with no way to tell "loading" from "you have no rooms."Closes #397.
Approach
Give the room list a tri-state (
ui/src/components/room_list.rs):Loading your rooms…(initial load) orRestoring your rooms…(migration, driven by the existingis_legacy_migration_in_progress()localStorage flag);No rooms yetcreate/join call-to-action;The genuinely-empty CTA now only shows once the initial load has actually resolved, tracked by a new
INITIAL_ROOMS_LOADEDsignal (ui/src/components/app.rs) set at the terminal points ofload_rooms_per_room(response_handler.rs). Because the legacy-migration probe is fire-and-forget (a fresh user with no legacy delegate gets no response at all), the empty-delegate branch waits a bounded settle (LEGACY_PROBE_SETTLE_MS = 3s, legacy delegates are node-local) before declaring "loaded & empty" — so a migrating user never flashes the empty state during the probe gap, while a fresh user gets the CTA after a short, one-time wait.The signal only ever transitions
false → true, so a WebSocket reconnect never flashes the list back to "loading."The tri-state decision lives in a pure
room_list_display(has_rooms, migrating, initial_loaded)fn (unit-tested), consumed inline inRoomList().UI-only — no contract/delegate WASM change, no re-key. No republish in this PR; publishing is a deliberate follow-up after the look is approved.
Aesthetics — please review the look
Restrained, near-monochrome, theme-aware; the spinner reuses the existing per-room
border-text-mutedstyle (no accent, no banner). Screenshots (light + dark) of the migrating and empty states:(Screenshots attached in the PR thread / provided to the reviewer. The red WebSocket pill visible at the bottom of the dev screenshots is the pre-existing
ConnectionStatusIndicator— this is a no-sync dev build with no node; it is unrelated to this change.)Testing
room_list_display) pin the crux: an empty map during migration or before load resolves is never the empty CTA; only a resolved-and-empty map is (cargo test -p river-ui --bins, 393 passed).cargo clippyclean (wasm,no-syncandexample-data,no-sync);cargo fmtclean.?room_list_state=migrating|loading|empty|populatedURL override (compiled out of production —example-data/wasm-gated), screenshotted in light + dark.[AI-assisted - Claude]
🤖 Generated with Claude Code
https://claude.ai/code/session_01CD8VBzb8qsDvdcPdF6TNQG