The keeper can bring back a seat a browser lost: a new token on the ledger, the old one refused and any phrase it minted voided, and a link that carries it in the fragment, minted by waving-hands-reseat.sh through a /keeper door Caddy does not proxy; the join page keeps a linked seat only once the server takes it and never over a seat that still opens, a watcher at a duel in play is told both ways back, the hall asks before forgetting a duel in play, and the server's rooms have tests of their own
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbtUPJ24P6vc6tE1x6RS3m
This commit is contained in:
1 parent
fbe110a7bb
commit
b51934ecd6
13 files changed
+198
-20
No files matched your search
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { goto, replaceState } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import Board from '$lib/components/Board.svelte';
|
||||
import ReportSlip from '$lib/components/ReportSlip.svelte';
|
||||
import TableTalk from '$lib/components/TableTalk.svelte';
|
||||
import { seatFor } from '$lib/game/client';
|
||||
import { rememberSeat, seatFor, seatInLink, trustSeat, type HeldSeat } from '$lib/game/client';
|
||||
import { COMPACT_QUERY, Duel, NAME_MAX, playerName } from '$lib/game/duel.svelte';
|
||||
|
||||
const roomId = $derived((page.params.code ?? '').toUpperCase());
|
||||
@@ -71,23 +71,34 @@
|
||||
/** A watcher may still sit while the table is laid and a seat is free. */
|
||||
const seatFree = $derived(!!room?.remote && !room.remote.started && room.remote.seats.length < room.remote.size);
|
||||
|
||||
// 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 = '';
|
||||
const sent = seatInLink(location.hash);
|
||||
if (sent) replaceState(location.pathname + location.search, {});
|
||||
const held = seatFor(id);
|
||||
if (!held) {
|
||||
name = playerName();
|
||||
joining = true;
|
||||
Duel.watchRoom(id)
|
||||
.then((remote) => (room = new Duel(remote)))
|
||||
.catch((e: Error) => (error = e.message));
|
||||
return;
|
||||
}
|
||||
joining = false;
|
||||
Duel.resumeRoom(id, held)
|
||||
.then((remote) => (room = new Duel(remote)))
|
||||
const take = (s: HeldSeat) =>
|
||||
Duel.resumeRoom(id, s).then((remote) => {
|
||||
rememberSeat(id, s);
|
||||
trustSeat(id);
|
||||
return remote;
|
||||
});
|
||||
const arrive = held
|
||||
? Duel.resumeRoom(id, held).catch((e: Error) => (sent ? take(sent) : Promise.reject(e)))
|
||||
: sent
|
||||
? take(sent).catch(() => Duel.watchRoom(id))
|
||||
: Duel.watchRoom(id);
|
||||
joining = !held && !sent;
|
||||
if (!held) name = playerName();
|
||||
arrive
|
||||
.then((remote) => {
|
||||
room = new Duel(remote);
|
||||
if (remote.spectating) joining = true;
|
||||
})
|
||||
.catch((e: Error) => (error = e.message));
|
||||
});
|
||||
|
||||
@@ -191,6 +202,10 @@
|
||||
<p class="notice muted">On the other device, open the hall and claim this phrase within ten minutes: <b class="phrase">{phrase.phrase}</b></p>
|
||||
{/if}
|
||||
|
||||
{#if watching && room?.remote?.started && !room.state.over}
|
||||
<p class="notice muted">Had a seat at this table on another device? Transfer it from there and claim the phrase in the hall, or <a href="mailto:eric@ericwagoner.com?subject={encodeURIComponent(`My seat in room ${roomId}`)}">write to the keeper</a> for a link that brings it back.</p>
|
||||
{/if}
|
||||
|
||||
{#if room?.remote?.started && room.state.over && !watching}
|
||||
{@const r = room.remote}
|
||||
<p class="notice muted">
|
||||
|
||||
Reference in new issue
Block a user