diff --git a/template/src/routes/join/[code]/+page.svelte b/template/src/routes/join/[code]/+page.svelte index 4929f5d..dfc480c 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 } from '$lib/net/client'; + import { rememberSeat, seatFor, seatInLink, 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()); @@ -25,23 +25,31 @@ const link = $derived(`${location.origin}/join/${roomId}`); const watching = $derived(room?.spectating ?? false); - // Return to a held seat, or take a place in the gallery and offer one. + // Return to a held seat, or take a place in the gallery and offer one. A link from the keeper carries a seat: + // it leaves the address bar at once, is kept only once the server takes it, and never replaces a seat that + // still opens, so a link someone else sends cannot take a working seat away. $effect(() => { const id = roomId; room = null; error = ''; - // A link from the keeper carries a seat; it is kept, and the token taken out of the address bar. const sent = seatInLink(location.hash); - if (sent) { - rememberSeat(id, sent); - trustSeat(id); - replaceState(location.pathname + location.search, {}); - } + if (sent) replaceState(location.pathname + location.search, {}); const held = seatFor(id); - const arrive = held ? Room.resume(id, held) : Room.watch(id); - joining = !held; + const take = (s: HeldSeat) => + Room.resume(id, s).then((r) => { + rememberSeat(id, s); + 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); + joining = !held && !sent; if (!held) name = playerName(); - arrive.then((r) => (room = r)).catch((e: Error) => (error = e.message)); + arrive + .then((r) => { + room = r; + if (r.spectating) joining = true; + }) + .catch((e: Error) => (error = e.message)); }); // Keep the view fresh for as long as the room is on screen.