feat(ui): мгновенное открытие партии — предзагрузка идущих партий + кэш черновика без «прыжка» #58

Merged
developer merged 1 commits from feature/instant-game-open into development 2026-06-14 16:08:20 +00:00
Owner

Что и зачем

Две UX-шероховатости при открытии партии из лобби:

  1. Лёгкий «подгруз» при первом за сессию открытии партии (нет в кэше → пустой/загрузочный экран, пока не завершится load()).
  2. Двухшаговый «прыжок» фишек rack → доска на каждом открытии: черновик (незакоммиченная раскладка) хранился на сервере, но не кэшировалсяonMount показывал полный rack, затем load() отдельным запросом draftGet перекладывал фишки на доску.

Общий корень: полное состояние и черновик не доступны синхронно к первому кадру.

Решение

  • Кэшируем черновик рядом с view+moves (CachedGame.draft); applyDraft стал синхронным и принимает уже полученный JSON; draftGet ушёл в тот же Promise.all, что game.state + game.history.
  • setCachedGame сохраняет черновик, когда дельта-путь его не передаёт (ход соперника), и очищает на завершённом ходе (зеркалит сервер).
  • Новый модуль lib/preload.ts прогревает кэш (state+history+draft) для идущих партий лобби с ограниченной конкурентностью (по решению владельца — только идущие; завершённые не греем).

После этого открытие из лобби мгновенное, а сохранённая раскладка нарисована на доске уже с первого кадра — без перекладки.

Тесты

  • gamecache.test.ts (новый) — сохранение/очистка черновика, setCachedDraft.
  • preload.test.ts (новый) — греет идущие незакэшированные, пропускает завершённые/закэшированные.
  • Существующий e2e «a placed tile is saved as a draft and restored on reopening the game» — зелёный.
  • Локально зелёные: pnpm check, pnpm test:unit (195), pnpm build, pnpm test:e2e (118, Chromium + WebKit).

Как посмотреть на контуре

Открыть из лобби несколько идущих партий (в т.ч. с незакоммиченными фишками на доске) — не должно быть ни «подгруза», ни «прыжка» фишек.

## Что и зачем Две UX-шероховатости при открытии партии из лобби: 1. **Лёгкий «подгруз»** при первом за сессию открытии партии (нет в кэше → пустой/загрузочный экран, пока не завершится `load()`). 2. **Двухшаговый «прыжок» фишек rack → доска** на каждом открытии: черновик (незакоммиченная раскладка) хранился на сервере, но **не кэшировался** — `onMount` показывал полный rack, затем `load()` отдельным запросом `draftGet` перекладывал фишки на доску. Общий корень: полное состояние и черновик не доступны синхронно к первому кадру. ## Решение - **Кэшируем черновик** рядом с view+moves (`CachedGame.draft`); `applyDraft` стал синхронным и принимает уже полученный JSON; `draftGet` ушёл в тот же `Promise.all`, что `game.state` + `game.history`. - `setCachedGame` **сохраняет** черновик, когда дельта-путь его не передаёт (ход соперника), и **очищает** на завершённом ходе (зеркалит сервер). - Новый модуль `lib/preload.ts` **прогревает кэш** (state+history+draft) для идущих партий лобби с ограниченной конкурентностью (по решению владельца — только идущие; завершённые не греем). После этого открытие из лобби мгновенное, а сохранённая раскладка нарисована на доске уже с первого кадра — без перекладки. ## Тесты - `gamecache.test.ts` (новый) — сохранение/очистка черновика, `setCachedDraft`. - `preload.test.ts` (новый) — греет идущие незакэшированные, пропускает завершённые/закэшированные. - Существующий e2e «a placed tile is saved as a draft and restored on reopening the game» — зелёный. - Локально зелёные: `pnpm check`, `pnpm test:unit` (195), `pnpm build`, `pnpm test:e2e` (118, Chromium + WebKit). ## Как посмотреть на контуре Открыть из лобби несколько идущих партий (в т.ч. с незакоммиченными фишками на доске) — не должно быть ни «подгруза», ни «прыжка» фишек.
developer added 1 commit 2026-06-14 15:53:30 +00:00
feat(ui): preload ongoing games and cache the draft for an instant, jump-free game open
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 46s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m2s
c9021fc070
Opening a game from the lobby for the first time this session showed a brief
loading flash, and every open showed a two-step rack->board jump: the saved
draft (pending composition) was fetched separately and applied only after the
board had already painted the full rack.

Both stem from the full state and the draft not being available synchronously at
first paint. Cache the draft alongside view+history (CachedGame.draft), make
applyDraft take the already-fetched JSON so it runs synchronously, and fetch the
draft in the same Promise.all as state+history. setCachedGame preserves the
cached draft when the delta path omits it and clears it on a committed move
(mirroring the server). A new preload module warms the per-game cache (state,
history, draft) for the lobby's ongoing games with bounded concurrency, so
opening any of them is instant.

Tests: gamecache (preserve/clear/setCachedDraft) and preload (warm/skip) units;
existing draft-restore e2e still green.
owner approved these changes 2026-06-14 16:08:02 +00:00
developer merged commit 7716353f84 into development 2026-06-14 16:08:20 +00:00
developer deleted branch feature/instant-game-open 2026-06-14 16:08:20 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#58