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:
1 parent
97c2b67650
commit
999edb67e6
6 files changed
+73
-6
No files matched your search
@@ -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>}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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>
|
||||
));
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user