From aa803260a117a85d02e3491de2d01b2be7315213 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Wed, 15 Jul 2026 02:17:13 +0200 Subject: [PATCH] fix(game): recognise the device-local guest as "me" in the game header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The game header identified the viewer's seat only by app.session.userId, but a device-local game (vs_ai / hotseat) created offline is seated under the local guest id (the lobby already matches both). After a merge switched the active account to a durable id, the human seat matched neither, so a vs_ai game showed BOTH seats as robots (the turn still worked — it is seat-index based). seatName now matches app.session.userId OR localGuestId, like the lobby. Completes the v1.22.0 repointLocalGameSeats fix, which only covered games seated under the retired session id. --- ui/src/game/Game.svelte | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/src/game/Game.svelte b/ui/src/game/Game.svelte index b5a7472..2e21ee4 100644 --- a/ui/src/game/Game.svelte +++ b/ui/src/game/Game.svelte @@ -10,6 +10,7 @@ import Rack from './Rack.svelte'; import { gateway } from '../lib/gateway'; import { gameSource, localSource, isLocalGameId } from '../lib/gamesource'; + import { localGuestId } from '../lib/localguest'; import { notePreviewLocal, notePreviewNetwork } from '../lib/localeval-metrics'; import { navigate } from '../lib/router.svelte'; import { app, handleError, showToast, markChatRead, seedChatUnread } from '../lib/app.svelte'; @@ -1442,9 +1443,18 @@ // seatName renders a seat's name: "you" for the viewer, the localized "searching for // opponent" placeholder for an open game's still-empty seat (no account), otherwise the // display name. + // isMe reports whether a seat belongs to the viewer — matched against the server account id OR + // the device-local guest id, the same rule the lobby uses for "my games". A device-local game + // (vs_ai / hotseat) is seated under the local guest id when created offline, and its human seat + // must still read "You" after a merge switches the active account to a durable id; without the + // local-guest arm the seat matched neither and a vs_ai game showed BOTH seats as robots. + function isMe(accountId: string | undefined): boolean { + return !!accountId && (accountId === app.session?.userId || accountId === localGuestId()); + } + function seatName(s: { accountId: string; displayName: string } | undefined): string { if (!s) return ''; - if (s.accountId === app.session?.userId) return t('common.you'); + if (isMe(s.accountId)) return t('common.you'); // An honest-AI game shows the robot opponent as 🤖, never its (pooled human-like) name. if (view?.game.vsAi) return '🤖'; if (!s.accountId) return t('game.searchingForOpponent');