diff --git a/app/page.tsx b/app/page.tsx index 472f5cbc..a503786f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -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(null); + const [entryConfirmation, setEntryConfirmation] = useState<{ kind: 'create' | 'join'; code: string } | null>(null); const seatClaimUncertain = useRef(false); const [claimUncertain, setClaimUncertain] = useState(false); const pendingEntry = useRef(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 &&
+ setEntryConfirmation(null)} /> +
} {seatConfirmation && !roomUnavailable &&
setSeatConfirmation(null)} />
} diff --git a/components/room-entry-confirmation.tsx b/components/room-entry-confirmation.tsx new file mode 100644 index 00000000..fe21daba --- /dev/null +++ b/components/room-entry-confirmation.tsx @@ -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
+

{kind === 'create' ? 'Room created' : 'Room joined'}

+ + {kind === 'create' + ? <>Room {code} is ready. Share the room code or invitation link from the lobby so others can join. + : <>You have joined room {code}. Your faction and the rules are shown at the table.} + +

Use Protect your saved seat to keep a recovery kit. This message stays until you select Continue.

+ +
; +} diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx index 4a65d1b6..4fbde8c3 100644 --- a/components/ui/toast.tsx +++ b/components/ui/toast.tsx @@ -16,8 +16,9 @@ import { const toast = ToastPrimitive.createToastManager(); -function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) { - return ; +function ToastProvider({ timeout = 0, ...props }: ToastPrimitive.Provider.Props) { + // Text stays readable until the player dismisses it; progress can update in place. + return ; } function ToastPortal({ ...props }: ToastPrimitive.Portal.Props) { @@ -186,7 +187,7 @@ function ToastList() { - + }>Dismiss )); diff --git a/docs/CURRENT_STATUS.md b/docs/CURRENT_STATUS.md index 884e62c4..8152946d 100644 --- a/docs/CURRENT_STATUS.md +++ b/docs/CURRENT_STATUS.md @@ -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. diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md index d1247615..50331d52 100644 --- a/docs/IMPLEMENTATION_STATUS.md +++ b/docs/IMPLEMENTATION_STATUS.md @@ -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 diff --git a/docs/READABLE_NOTICES.md b/docs/READABLE_NOTICES.md index 6ed0349a..fea492d4 100644 --- a/docs/READABLE_NOTICES.md +++ b/docs/READABLE_NOTICES.md @@ -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