a8f78c7880
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m14s
CI / conformance (pull_request) Successful in 12s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Failing after 22s
Land the offline-model redesign (ANDROID_PLAN.md O1-O7): replace the explicit offline toggle with a single detected net-state machine, unify the lobby, and add a two-tier client-version gate. Contour-safe: both version vars empty => dormant, and the wire change is additive, so web/pwa/vk/tg behaviour is unchanged unless the gate is deliberately configured. O1 pure net-state reducer (test-first). O2 reactive store + event wiring (connection/offline become thin shims; +@capacitor/network). O3 remove the offline toggle + migrate the pref. O4 two-tier gate: hard update_required degrades to an offline Update/Play-offline notice (not terminal); soft GATEWAY_RECOMMENDED_CLIENT_VERSION -> X-Update-Recommended nudge. O5 unified lobby (device-local + greyed-from-cache server games; self-set identity; closes G-step-0). O6 create flows (with-friends online/offline segment + offline dict guard). O7 docs (ARCHITECTURE, FUNCTIONAL +_ru, TESTING, deploy/README). Also disable the manual android-build CI workflow for now (rename to .disabled); the Android APK release comes later. Tests: gateway go green, svelte-check 0/0, vitest 617, e2e 122/122 (chromium + webkit).
76 lines
3.3 KiB
TypeScript
76 lines
3.3 KiB
TypeScript
// 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. The unified lobby caches one
|
|
* snapshot (device-local + server games merged); offline reuses its server games, greyed, and drops
|
|
* the local ones for a fresh device read. */
|
|
export function getLobby(): LobbySnapshot | null {
|
|
return snapshot;
|
|
}
|
|
|
|
/** setLobby stores the latest lobby lists. */
|
|
export function setLobby(s: LobbySnapshot): void {
|
|
snapshot = s;
|
|
}
|
|
|
|
/**
|
|
* patchLobbyGame upserts the given game into the cached lobby — replacing the matching entry, or
|
|
* prepending it when absent — so a game-state change seen while the lobby is unmounted (the player's
|
|
* own move, or any live game event the global stream handler applies from another screen) is already
|
|
* reflected the next time the lobby renders from the snapshot, instead of showing the stale status
|
|
* until the background refresh lands. Prepending a freshly started game likewise lets it appear at
|
|
* once. It is a no-op only when there is no snapshot yet (the lobby cold-loads on its first mount).
|
|
* The lobby re-groups and re-sorts on render, so the insert position carries no meaning.
|
|
*/
|
|
export function patchLobbyGame(g: GameView): void {
|
|
if (!snapshot) return;
|
|
const i = snapshot.games.findIndex((x) => x.id === g.id);
|
|
const games = snapshot.games.slice();
|
|
if (i === -1) games.unshift(g);
|
|
else games[i] = g;
|
|
snapshot = { ...snapshot, games };
|
|
}
|
|
|
|
/**
|
|
* patchLobbyInvitation applies a live invitation change to the cached lobby's invitations list, so a
|
|
* change seen while the lobby is unmounted is reflected on its next render instead of a stale card
|
|
* lingering until the background refresh. A still-pending invitation is upserted (a new one
|
|
* prepended newest-first, an updated one replaced in place); an invitation that reached any terminal
|
|
* status (started, declined, cancelled, expired) is removed — the authoritative list holds only
|
|
* pending invitations. It is a no-op when there is no snapshot yet, or a terminal invitation is not
|
|
* in the list. The lobby re-renders from the snapshot, so the in-place upsert needs no re-sort.
|
|
*/
|
|
export function patchLobbyInvitation(inv: Invitation): void {
|
|
if (!snapshot) return;
|
|
const i = snapshot.invitations.findIndex((x) => x.id === inv.id);
|
|
let invitations: Invitation[];
|
|
if (inv.status !== 'pending') {
|
|
if (i === -1) return; // a terminal invitation already absent — nothing to remove
|
|
invitations = snapshot.invitations.filter((x) => x.id !== inv.id);
|
|
} else if (i === -1) {
|
|
invitations = [inv, ...snapshot.invitations];
|
|
} else {
|
|
invitations = snapshot.invitations.slice();
|
|
invitations[i] = inv;
|
|
}
|
|
snapshot = { ...snapshot, invitations };
|
|
}
|
|
|
|
/** clearLobby drops the cached lobby (called on logout). */
|
|
export function clearLobby(): void {
|
|
snapshot = null;
|
|
}
|