fix(chat): no chat field or nudge on a finished game
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 59s

A finished game is read-only: gate the nudge behind canNudge (active game,
opponent's turn) so it no longer shows on a finished (or otherwise
non-active) game — where the backend rejects it anyway. The message field
is already hidden off your own turn. Extend the finished-game e2e to assert
neither Send nor Nudge is offered.
This commit is contained in:
Ilia Denisov
2026-06-14 20:29:47 +02:00
parent 02681ae9e0
commit 5f53ec81b9
3 changed files with 17 additions and 8 deletions
+10 -7
View File
@@ -9,6 +9,7 @@
busy,
myTurn = false,
canSend = false,
canNudge = false,
waiting = false,
nudgeOnCooldown = false,
onsend,
@@ -17,16 +18,18 @@
messages: ChatMessage[];
myId: string;
busy: boolean;
// The input area has three turn-driven states: on your own turn the message field +
// send show while you may still post (canSend); once the one allowed message is sent a
// short caption replaces them until the next turn; on the opponent's turn only the
// nudge button shows (nudging your own turn makes no sense — there is no one to hurry).
// While the hourly nudge cooldown is active the nudge is disabled with an "awaiting
// reply" caption.
// The input area is turn-driven, in priority order: while you may still post (canSend)
// the message field + send show; once your one allowed message is sent a short caption
// replaces them until the next turn (myTurn); on the opponent's turn of an active game
// the nudge button shows (canNudge) — disabled with an "awaiting reply" caption while the
// hourly cooldown is active; a finished game offers nothing (it is read-only).
myTurn?: boolean;
// canSend is true while the player may still post this turn (own turn and the per-turn
// limit not yet reached); it gates the message field.
canSend?: boolean;
// canNudge is true on the opponent's turn of an active game; it gates the nudge button
// (nudging your own turn or a finished game makes no sense).
canNudge?: boolean;
// waiting is true while an auto-match game still has no opponent: both send and nudge
// are disabled (there is no one to message or hurry yet).
waiting?: boolean;
@@ -71,7 +74,7 @@
<!-- Own turn, the one allowed message already sent: a caption holds the row until the
next turn (no nudge — there is no one to hurry on your own turn). -->
<span class="cooldown">{t('chat.sentThisTurn')}</span>
{:else}
{:else if canNudge}
<!-- A flex:1 caption keeps the nudge pinned right whether or not the cooldown text shows. -->
<span class="cooldown">{nudgeOnCooldown ? t('chat.awaitingReply') : ''}</span>
<button class="iconbtn" onclick={onnudge} disabled={busy || waiting || nudgeOnCooldown || !connection.online} aria-label={t('chat.nudgeAction')}>🛎️</button>
+4 -1
View File
@@ -26,6 +26,9 @@
// the turn advances. canSend hides the field once the limit is reached (and always on
// the opponent's turn).
const canSend = $derived(canSendChat(isMyTurn, view ? sentThisTurn(messages, myId, view.game.lastActivityUnix) : 0));
// The nudge is offered only on the opponent's turn of an active game (you hurry the player
// who is to move). A finished game is read-only: neither chat nor nudge is offered.
const canNudge = $derived(!!view && view.game.status === 'active' && view.game.toMove !== view.seat);
// While the auto-match game still has no opponent, chat and nudge are both disabled.
const waiting = $derived(!!view && view.game.status === 'open');
const nudgeCooldownSecs = 3600;
@@ -110,4 +113,4 @@
}
</script>
<Chat {messages} {myId} {busy} myTurn={isMyTurn} {canSend} {waiting} {nudgeOnCooldown} onsend={sendChat} onnudge={nudge} />
<Chat {messages} {myId} {busy} myTurn={isMyTurn} {canSend} {canNudge} {waiting} {nudgeOnCooldown} onsend={sendChat} onnudge={nudge} />