···66 * re-renders when subscriptions push updates.
77 */
8899-import { useMemo } from 'react';
99+import { useEffect, useMemo, useRef } from 'react';
1010import { useSpacetimeDB, useTable, useReducer } from 'spacetimedb/react';
1111import { tables, reducers } from '../module_bindings';
1212···116116117117 const isQueued = queuedTimeControlSecs !== null;
118118119119+ // Track game IDs that were active at some point during this browser session.
120120+ // This lets us show the outcome overlay when a game ends mid-session, without
121121+ // resurrecting stale ended games on a fresh page load / refresh.
122122+ const witnessedActiveIds = useRef(new Set<bigint>());
123123+124124+ useEffect(() => {
125125+ if (!identityHex) return;
126126+ for (const g of allGames) {
127127+ if (g.status !== 'active') continue;
128128+ const isPlayer =
129129+ g.whiteIdentity.toHexString() === identityHex ||
130130+ g.blackIdentity.toHexString() === identityHex;
131131+ if (isPlayer) {
132132+ witnessedActiveIds.current.add(g.id);
133133+ }
134134+ }
135135+ }, [allGames, identityHex]);
136136+119137 const activeGame = useMemo(() => {
120138 if (!identityHex) return null;
121139122122- // Find the most recent game this player is in. Prefer 'active' games,
123123- // but also return recently-ended games so the GameStatus overlay can
124124- // render (checkmate/resign/draw/timeout screen). Without this, ending a
125125- // game would instantly route back to the lobby with no outcome shown.
140140+ // Find the most recent game this player is in. Active games always win.
141141+ // Ended games are only returned if we witnessed them as active during this
142142+ // session — this is what lets the outcome overlay render after a game ends
143143+ // without showing a stale "game ended" screen on every page refresh.
126144 let best: (typeof allGames)[number] | null = null;
127145 for (const g of allGames) {
128146 const isPlayer =
···135153 best = g;
136154 break;
137155 }
156156+ // Only consider ended games we witnessed as active in this session
157157+ if (!witnessedActiveIds.current.has(g.id)) continue;
138158 // Keep the most recently ended game (highest id = newest)
139159 if (!best || g.id > best.id) {
140160 best = g;