fix(ui): keep lobby/game caches fresh across screens (no stale-status flash)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 48s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 58s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 48s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 58s
The per-screen in-memory caches (lobbycache, gamecache) were refreshed only by the screen that owns them while it was mounted, so a state change that crossed screens left the other screen's cache stale and it visibly redrew on the next navigation: - game -> lobby: the player's own move advanced the game cache but not the lobby snapshot, and an own move carries no self-directed push event, so returning to the lobby painted the pre-move status until the background refresh corrected it. - lobby -> game (and from any other screen): an opponent's move / game-over refreshed the lobby (while mounted) but never the per-game cache, so opening that game flashed the pre-move board. Make cache freshness cross-screen, owned by the single global stream handler that runs for every live event regardless of the mounted screen: - patchLobbyGame upserts the affected game's GameView into the lobby snapshot; the global handler calls it on opponent_moved / game_over / opponent_joined and on a match_found / game_started seed (so a game started elsewhere is present too). The game board still mirrors the player's own move and its own load() — the two updates no live event carries. - advanceCached (a pure wrapper over the existing delta reducers) advances a not-currently-viewed game's cache from opponent_moved / game_over; the game in view is skipped so its mounted board stays the sole owner (no double apply). End-state behaviour is unchanged (the background refresh always reconciled); this removes the transient stale frame. Unit-tested patchLobbyGame and advanceCached; docs/UI_DESIGN.md updated.
This commit is contained in:
+18
-1
@@ -4,7 +4,7 @@
|
||||
// `refetch` — which keeps the gap / own-move / idempotency logic unit-testable in isolation.
|
||||
|
||||
import type { CachedGame } from './gamecache';
|
||||
import type { GameView, MoveRecord, StateView } from './model';
|
||||
import type { GameView, MoveRecord, PushEvent, StateView } from './model';
|
||||
|
||||
/** The fields an opponent_moved delta carries that advance a cached game. */
|
||||
export interface MoveDelta {
|
||||
@@ -67,3 +67,20 @@ export function applyGameOver(cached: CachedGame | undefined, game: GameView | u
|
||||
const view: StateView = { ...cached.view, game };
|
||||
return { cache: { view, moves: cached.moves }, refetch: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* advanceCached applies a live game event to a cached game off-screen, for the global stream handler
|
||||
* to keep a game the player is not currently viewing warm: opening it from the lobby then renders the
|
||||
* opponent's move (or the final result) with no stale-board flash. It returns the advanced cache, or
|
||||
* undefined when nothing changes — no cache, an idempotent re-delivery, an unrelated event kind, or a
|
||||
* gap / missing payload that the next open cold-loads (off-screen there is no game screen to honour a
|
||||
* refetch, so a fall-back simply leaves the cache for load() to repair). The mounted game screen owns
|
||||
* its own cache, so the caller skips the game currently in view to avoid applying a delta twice.
|
||||
*/
|
||||
export function advanceCached(cached: CachedGame | undefined, e: PushEvent): CachedGame | undefined {
|
||||
if (e.kind === 'opponent_moved') {
|
||||
return applyMoveDelta(cached, { move: e.move, game: e.game, bagLen: e.bagLen }).cache;
|
||||
}
|
||||
if (e.kind === 'game_over') return applyGameOver(cached, e.game).cache;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user