fix(ui): poll/refetch fallback for a missed opponent_joined in the open-game wait
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
PR #51 moved the auto-match "wait for an opponent" from the lobby matchmaking screen into the open game but did not carry over that screen's poll fallback. The notify hub is best-effort and never replays, the live-stream resubscribe sends no cursor, and the game screen refreshed only from push events — so an opponent_joined dropped while the stream was down (e.g. a mobile suspend) left the starter stuck on "searching for opponent" until they re-entered the game. Unlike opponent_moved/game_over, opponent_joined has no follow-up event to trigger the existing move-count gap refetch. Recover it in Game.svelte: (A) refetch once on stream reconnect (covers the common suspend/resume case and rescues a missed move/game_over too), and (B) poll game.state every 2.5s while still waiting with the stream down (mirrors the old matchmaking startPoll). Add a mock-mode __stream e2e seam and two specs isolating each path, fix the now-stale streamAlive comment, and document the fallback in ARCHITECTURE §10.
This commit is contained in:
@@ -36,7 +36,9 @@ export interface Toast {
|
||||
|
||||
export const app = $state<{
|
||||
ready: boolean;
|
||||
/** Whether the live-event stream is connected; drives the matchmaking poll fallback. */
|
||||
/** Whether the live-event stream is connected. The event hub is best-effort and never replays a
|
||||
* missed event, so an open game waiting for an opponent uses this to recover: it polls the game
|
||||
* state while the stream is down and refetches once on reconnect (see game/Game.svelte). */
|
||||
streamAlive: boolean;
|
||||
session: Session | null;
|
||||
profile: Profile | null;
|
||||
@@ -500,3 +502,14 @@ if (typeof window !== 'undefined') {
|
||||
}
|
||||
telegramOnEvent('activated', goForeground);
|
||||
telegramOnEvent('deactivated', goBackground);
|
||||
|
||||
// Mock-mode e2e seam (tree-shaken from a production build): drive the live-stream lifecycle so the
|
||||
// e2e can exercise the open-game fallbacks for a missed opponent_joined. `drop` tears the stream
|
||||
// down without scheduling a reconnect — so only the in-game poll can then recover — and `restore`
|
||||
// reopens it (the reconnect catch-up path). Never wired outside mock mode.
|
||||
if (import.meta.env.MODE === 'mock' && typeof window !== 'undefined') {
|
||||
(window as unknown as { __stream?: { drop(): void; restore(): void } }).__stream = {
|
||||
drop: closeStream,
|
||||
restore: openStream,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user