From 645a50353234fd4e8a8254724043ff0b8c5ec044 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Sat, 6 Jun 2026 12:38:04 +0200 Subject: [PATCH] =?UTF-8?q?Stage=2017=20(#4):=20in-memory=20lobby=20cache?= =?UTF-8?q?=20=E2=80=94=20render=20instantly=20on=20the=20back-slide,=20re?= =?UTF-8?q?fresh=20in=20background?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/lib/app.svelte.ts | 2 ++ ui/src/lib/lobbycache.ts | 30 ++++++++++++++++++++++++++++++ ui/src/screens/Lobby.svelte | 14 +++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 ui/src/lib/lobbycache.ts diff --git a/ui/src/lib/app.svelte.ts b/ui/src/lib/app.svelte.ts index 848bbbd..e001ba9 100644 --- a/ui/src/lib/app.svelte.ts +++ b/ui/src/lib/app.svelte.ts @@ -13,6 +13,7 @@ import { insideTelegram, onTelegramPath, telegramColorScheme, telegramLaunch, te import { parseStartParam } from './deeplink'; import { clearSession, loadPrefs, loadSession, saveSession, savePrefs } from './session'; import { clearGameCache } from './gamecache'; +import { clearLobby } from './lobbycache'; import type { BoardLabelMode } from './boardlabels'; export interface Toast { @@ -311,6 +312,7 @@ export async function loginEmail(email: string, code: string): Promise { export async function logout(): Promise { closeStream(); clearGameCache(); + clearLobby(); gateway.setToken(null); await clearSession(); app.session = null; diff --git a/ui/src/lib/lobbycache.ts b/ui/src/lib/lobbycache.ts new file mode 100644 index 0000000..5184bf8 --- /dev/null +++ b/ui/src/lib/lobbycache.ts @@ -0,0 +1,30 @@ +// In-memory lobby snapshot, the lobby counterpart of gamecache.ts. The lobby re-fetches +// its lists on every entry, so without a cache the screen renders blank and "draws in" +// during the back-slide from a game. Caching the last lists lets the lobby render +// instantly (before/under the transition) and refresh in the background. Process-memory +// only; cleared on logout. + +import type { AccountRef, GameView, Invitation } from './model'; + +interface LobbySnapshot { + games: GameView[]; + invitations: Invitation[]; + incoming: AccountRef[]; +} + +let snapshot: LobbySnapshot | null = null; + +/** getLobby returns the last lobby lists, or null before the first load. */ +export function getLobby(): LobbySnapshot | null { + return snapshot; +} + +/** setLobby stores the latest lobby lists. */ +export function setLobby(s: LobbySnapshot): void { + snapshot = s; +} + +/** clearLobby drops the cached lobby (called on logout). */ +export function clearLobby(): void { + snapshot = null; +} diff --git a/ui/src/screens/Lobby.svelte b/ui/src/screens/Lobby.svelte index 685a983..daf61a4 100644 --- a/ui/src/screens/Lobby.svelte +++ b/ui/src/screens/Lobby.svelte @@ -8,6 +8,7 @@ import { navigate } from '../lib/router.svelte'; import { t, type MessageKey } from '../lib/i18n/index.svelte'; import { resultBadge } from '../lib/result'; + import { getLobby, setLobby } from '../lib/lobbycache'; import type { AccountRef, GameView, Invitation } from '../lib/model'; let games = $state([]); @@ -23,12 +24,23 @@ [invitations, incoming] = await Promise.all([gateway.invitationsList(), gateway.friendsIncoming()]); app.notifications = invitations.length + incoming.length; } + setLobby({ games, invitations, incoming }); } catch (e) { handleError(e); } } - onMount(load); + onMount(() => { + // Render instantly from the cached lists (so the screen does not "draw in" during + // the back-slide), then refresh in the background. + const cached = getLobby(); + if (cached) { + games = cached.games; + invitations = cached.invitations; + incoming = cached.incoming; + } + void load(); + }); $effect(() => { if (app.lastEvent) void load(); });