feat(ui): tile-crossword loading splash for cold lobby open
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 57s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m17s

On a cold app open the lobby's game list arrives over the network; on a
slow link the empty "no games yet" line flashed before the games loaded.
Add a full-screen tile splash that lays a Scrabble crossword of ЭРУДИТ /
ЗАГРУЗКА / ОЖИДАНИЕ (Эрудит point values, hardcoded since the alphabet
table is not cached at boot) until the lobby's first load settles, then
removes itself to reveal the populated list.

- lib/splash.ts: pure layout + reveal schedule (unit-tested).
- components/Splash.svelte: App-level overlay; per-tile drop-in; loops
  ЗАГРУЗКА → ОЖИДАНИЕ until ready, dismisses on a word boundary. Static
  ЭРУДИТ under reduced motion / the mock build.
- app state: lobbyReady (set by Lobby on first settle) + splashDone,
  reset on logout.
- App.svelte: overlay while routeIsLobby && !splashDone; the plain text
  splash now only covers non-lobby deep-links during bootstrap.
- docs: UI_DESIGN + FUNCTIONAL (+ _ru).
This commit is contained in:
Ilia Denisov
2026-06-21 10:29:39 +02:00
parent e79c1ea891
commit ba6ee90278
9 changed files with 460 additions and 1 deletions
+11
View File
@@ -41,6 +41,12 @@ export interface Toast {
export const app = $state<{
ready: boolean;
/** Whether the lobby's first cold load has settled (success or error). The loading splash
* (components/Splash.svelte) watches it to know when to dismiss; set by screens/Lobby. */
lobbyReady: boolean;
/** Whether the loading splash has finished and removed itself. Gates the App-level overlay
* so a warm intra-session return to the lobby shows no splash again. */
splashDone: boolean;
/** Whether the live-event stream is connected. The event hub is best-effort and never replays a
* missed event, so an open game waiting for an opponent uses this to recover: it polls the game
* state while the stream is down and refetches once on reconnect (see game/Game.svelte). */
@@ -84,6 +90,8 @@ export const app = $state<{
resync: number;
}>({
ready: false,
lobbyReady: false,
splashDone: false,
streamAlive: false,
session: null,
profile: null,
@@ -658,6 +666,9 @@ export async function logout(): Promise<void> {
resetConnection();
clearGameCache();
clearLobby();
// Replay the loading splash on the next cold lobby load after a re-login.
app.lobbyReady = false;
app.splashDone = false;
gateway.setToken(null);
await clearSession();
app.session = null;