Keep room confirmations visible until acknowledged

Show confirmed create/join guidance until local Continue without polling or ordinary game actions clearing unread text. Default shared toasts to no timeout with visible Dismiss controls.

Validation: 23 focused entry/action/seat tests, types/lint/build, and original local room creation with 43-second retention through polls and a circle change, explicit dismissal and no replay. No console errors. QA room preserved. Deployed acceptance remains open. Runtime fingerprint d57fa6a34bb1738017c2690ca5ad9f04b0b99458078a60c7ddd51e2c29483301; documentation overlay a8baa23802ac1c79321d67913c27512a199699839c1932947dfa3af95434cc37.
This commit is contained in:
Quintine committed 2026-10-18 10:27:41 +11:00
1 parent 97c2b67650
commit 999edb67e6
6 files changed
+73 -6

No files matched your search

+8
View File
@@ -4,6 +4,7 @@ import { TableTalk } from '@/components/table-talk';
import { RemovedRoomNotice } from '@/components/removed-room-notice';
import { GameTable } from '@/components/game-table';
import { SeatConfirmation, type SeatConfirmationKind } from '@/components/seat-confirmation';
import { RoomEntryConfirmation } from '@/components/room-entry-confirmation';
import { RulesetControls } from '@/components/ruleset-controls';
import {
SeatHandoverSetup,
@@ -81,6 +82,7 @@ export default function Home() {
const [code, setCode] = useState('');
const [notice, setNotice] = useState('');
const [seatConfirmation, setSeatConfirmation] = useState<SeatConfirmationKind | null>(null);
const [entryConfirmation, setEntryConfirmation] = useState<{ kind: 'create' | 'join'; code: string } | null>(null);
const seatClaimUncertain = useRef(false);
const [claimUncertain, setClaimUncertain] = useState(false);
const pendingEntry = useRef<RoomEntryAttempt | null>(null);
@@ -406,6 +408,7 @@ export default function Home() {
setNeedsReconcile(false);
setShowEntryRecovery(false);
setAbandonAcknowledged(false);
setEntryConfirmation({ kind: attempt.kind, code: data.code });
setGame(data);
window.history.replaceState(null, '', `/?room=${data.code}`);
} catch (error) {
@@ -498,6 +501,7 @@ export default function Home() {
setConnection('');
setGame(null);
setSeatConfirmation(null);
setEntryConfirmation(null);
setRemovedRoom(null);
setRestoreFailed(false);
setNotice('');
@@ -639,6 +643,7 @@ export default function Home() {
)
return;
if (claim) {
setEntryConfirmation(null);
setSeatConfirmation(confirmation ?? null);
setHandoverPending(true);
setAiPermissionPending(true);
@@ -714,6 +719,9 @@ export default function Home() {
if (game)
return (
<>
{entryConfirmation?.code === game.code && !roomUnavailable && <div className="table-shell">
<RoomEntryConfirmation {...entryConfirmation} onContinue={() => setEntryConfirmation(null)} />
</div>}
{seatConfirmation && !roomUnavailable && <div className="table-shell">
<SeatConfirmation kind={seatConfirmation} onContinue={() => setSeatConfirmation(null)} />
</div>}
+21
View File
@@ -0,0 +1,21 @@
'use client';
import { Button } from './ui/button';
/** A local acknowledgement: polling and game actions do not dismiss this text. */
export function RoomEntryConfirmation({ kind, code, onContinue }: {
kind: 'create' | 'join';
code: string;
onContinue: () => void;
}) {
return <section className="notice min-w-0 space-y-3 break-words" style={{ fontSize: '1rem', lineHeight: 1.5 }} aria-label="Room confirmation">
<h2>{kind === 'create' ? 'Room created' : 'Room joined'}</h2>
<output className="block" aria-live="polite">
{kind === 'create'
? <>Room <strong>{code}</strong> is ready. Share the room code or invitation link from the lobby so others can join.</>
: <>You have joined room <strong>{code}</strong>. Your faction and the rules are shown at the table.</>}
</output>
<p>Use Protect your saved seat to keep a recovery kit. This message stays until you select Continue.</p>
<Button type="button" variant="outline" className="min-h-11" style={{ fontSize: '1rem' }} onClick={onContinue}>Continue</Button>
</section>;
}
+4 -3
View File
@@ -16,8 +16,9 @@ import {
const toast = ToastPrimitive.createToastManager();
function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {
return <ToastPrimitive.Provider {...props} />;
function ToastProvider({ timeout = 0, ...props }: ToastPrimitive.Provider.Props) {
// Text stays readable until the player dismisses it; progress can update in place.
return <ToastPrimitive.Provider timeout={timeout} {...props} />;
}
function ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {
@@ -186,7 +187,7 @@ function ToastList() {
<ToastDescription />
</div>
<ToastAction />
<ToastClose />
<ToastClose aria-label="Dismiss message" render={<Button type="button" variant="outline" className="min-h-11" />}>Dismiss</ToastClose>
</ToastContent>
</Toast>
));
+9
View File
@@ -16,6 +16,15 @@ open mode gates.
## Current checkpoint and work
**Readable room confirmations — 18 October.** The user-requested create/join
confirmation stays above the table until local Continue, independently of
polling and game actions. Shared toasts default to no timeout and a visible
Dismiss control. Local QA room NAZLA8P4 retained the message for43 seconds,
including a circle change; Continue removed it without leaving the room and it
did not return on later polls. No browser console errors. Focused23/23,
types/lint/build pass; the QA room is preserved. Deployed verification remains
open. [Behavior and boundaries](READABLE_NOTICES.md).
**Classic independent World/Nexus — 18 October. Prototyped / Partial.**
Fresh classic2–6 independent36 + Homeworlds + Nexus12 now retains optional all14,
Tech3+ and Advanced Strongholds2+ through original module selection/setup.
+12
View File
@@ -1,5 +1,17 @@
# Dune implementation status
## 18 October 2026 — Persistent room entry confirmations
Successful create/join now shows the room code and concise invitation/seat
protection guidance above the table until Continue. Polls and ordinary actions
cannot replace this local acknowledgement. The shared toast provider defaults
to no automatic dismissal and its standard list has a visible Dismiss button.
Existing persistent recovery/action controls and concise first-request progress
remain. Focused23/23, types/lint/build and a real local room creation pass:
43-second retention, circle change, Continue and later polling, no browser
console errors. QA room preserved; deployed acceptance remains open.
See [readable notices](READABLE_NOTICES.md).
## 18 October 2026 — Classic independent World/Nexus
Fresh classic independent36/World/Nexus now retains optional14 and original
+19 -3
View File
@@ -7,8 +7,13 @@ then removed it immediately, making actionable recovery instructions appear to
flash on screen even when nothing had gone wrong.
First submissions now show only **Creating your room…** or **Joining the room…**
and a short progress message. Successful responses still open the confirmed
table. A lost/uncertain response, saved request after refresh, or storage problem
and a short progress message. Successful responses open the confirmed
table with a **Room created** or **Room joined** confirmation above it. The
confirmation shows the room code and invitation/seat-protection guidance, and
stays until **Continue**. Polling, reconnecting and ordinary game actions do not
dismiss it. Continue acknowledges only local text, without submitting a game
action. Leaving, reloading or claiming a different seat clears the local notice.
A lost/uncertain response, saved request after refresh, or storage problem
shows the existing persistent recovery panel. Its exact retry, deliberate
abandonment and saved-kit recovery controls remain available until the player
resolves the request. The private retry proof is still saved before submission;
@@ -37,11 +42,22 @@ it. Continue dismisses only the message. Leaving or reloading the page clears th
local confirmation, without changing the recovered seat.
Errors and recovery prompts have explicit controls; component inspectors have
close buttons. Network progress, polling deadlines and permission-expiry
close buttons. The shared toast provider also defaults to no automatic timeout;
its standard notice list provides a visible **Dismiss** button. Network
progress, polling deadlines and permission-expiry
countdowns keep their existing behavior.
## Verification
18 October follow-up: local QA room NAZLA8P4 kept its unread confirmation for
43 seconds through background polling and a normal player-circle change.
Continue removed only the notice; the same room stayed open and subsequent
polls did not restore the notice. The screenshot and browser record are kept
outside Git. No console errors; focused23/23 existing entry/notice/seat tests,
types/lint and production build pass. The dedicated QA room is preserved.
This is local evidence; deployed verification remains open.
Browser QA uses dedicated rooms and a temporary local transport that delays the
first response and can lose one successful create response. This verifies actual
rendered progress, durable recovery instructions, and explicit same-request retry