From c439c15e36a95c65ad0d1badb6ea064309c3b42f Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Tue, 13 Oct 2026 18:31:36 -0400 Subject: [PATCH] A seat link replaces a held seat only when the server refuses the held token, not when the connection drops, so a link cannot take a working seat away on a bad connection Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MbtUPJ24P6vc6tE1x6RS3m --- template/src/routes/join/[code]/+page.svelte | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/template/src/routes/join/[code]/+page.svelte b/template/src/routes/join/[code]/+page.svelte index 38b2188..7f4b976 100644 --- a/template/src/routes/join/[code]/+page.svelte +++ b/template/src/routes/join/[code]/+page.svelte @@ -5,7 +5,7 @@ import Lobby from '$lib/components/Lobby.svelte'; import ReportSlip from '$lib/components/ReportSlip.svelte'; import Preferences from '$lib/components/Preferences.svelte'; - import { rememberSeat, seatFor, seatInLink, trustSeat, type HeldSeat } from '$lib/net/client'; + import { rememberSeat, seatFor, seatInLink, ServerError, trustSeat, type HeldSeat } from '$lib/net/client'; import { NAME_MAX, playerName, rememberName, Room } from '$lib/net/room.svelte'; const roomId = $derived((page.params.code ?? '').toUpperCase()); @@ -42,7 +42,9 @@ trustSeat(id); return r; }); - const arrive = held ? Room.resume(id, held).catch((e: Error) => (sent ? take(sent) : Promise.reject(e))) : sent ? take(sent).catch(() => Room.watch(id)) : Room.watch(id); + // Only the server's refusal of the held token lets a link in: a dropped connection is not a lost seat. + const refused = (e: unknown) => e instanceof ServerError && e.status === 403; + const arrive = held ? Room.resume(id, held).catch((e: Error) => (sent && refused(e) ? take(sent) : Promise.reject(e))) : sent ? take(sent).catch(() => Room.watch(id)) : Room.watch(id); joining = !held && !sent; if (!held) name = playerName(); arrive