fix(online): leave focus alone while a player is typing in chat

A turn arriving pulled focus into the word field even mid-sentence in the
chat, dropping the rest of the message into the game. Focus is now only
claimed when no other text field holds it; syllable seeding is unchanged.
This commit is contained in:
tiennm99 committed 2026-09-08 13:07:38 +07:00
1 parent e19b083dc0
commit 0f0cc3a357
2 files changed
+38 -1

No files matched your search

+18
View File
@@ -313,6 +313,24 @@ test.describe('playing a stranger', () => {
await close();
});
test('a turn arriving does not take the chat field away', async ({ browser }) => {
const { host, guest, close } = await playingPair(browser);
// The guest starts typing while the host is still on turn. The word
// field wants focus the moment a turn lands, and taking it here would
// drop the rest of the sentence into the game.
await chat(guest).input.click();
await chat(guest).input.fill('đang gõ dở');
await playLegalMove(host, new Set(await chainWords(host)));
await waitForMyTurn(guest);
await expect(chat(guest).input).toBeFocused();
await expect(chat(guest).input).toHaveValue('đang gõ dở');
await close();
});
test('a message is rendered as text, never as markup', async ({ browser }) => {
const { host, guest, close } = await twoPlayers(browser);
+20 -1
View File
@@ -25,6 +25,23 @@
/** @type {unknown} */
let seededRejection = null;
/**
* Whether the player is typing somewhere else — the chat, in practice,
* which now sits beside the board rather than folded away under it.
*
* Taking focus off a field somebody is mid-sentence in would drop the rest
* of that sentence into the word field, so a turn arriving is allowed to
* ask for focus only when nothing else holds it.
*/
function typingElsewhere() {
const active = document.activeElement;
if (!active || active === field) return false;
return (
active instanceof HTMLElement &&
(active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable)
);
}
// Focus when the turn arrives, so a player on a phone can type without
// reaching for the field, and seed it with the syllable the word has to
// start with — that part of the answer is already decided, and typing it
@@ -37,7 +54,9 @@
const syllable = game.state.currentSyllable;
const rejection = game.state.rejection;
field?.focus();
// The seeding below happens either way: it writes into the field
// without disturbing wherever the player actually is.
if (!typingElsewhere()) field?.focus();
if (!field || (turn === seededTurn && rejection === seededRejection)) return;
seededTurn = turn;
seededRejection = rejection;