docs(ui): sync docs to the app-shell; fix stale nav comments
Tests · UI / test (push) Failing after 9m28s
Tests · UI / test (push) Failing after 9m28s
Rewrite ui/docs (navigation, order-composer, auth-flow, pwa-strategy, game-state + secondary topic docs) and ui/README for the single-URL app-shell (in-memory screens/views, Back→lobby via shallow routing, sessionStorage restore + validation, return-to-lobby). ui/PLAN.md gets a Phase-10 supersede note (implemented; standalone-compatible). Fix stale code comments (session-store auth gate, report-sections spec contract). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+32
-12
@@ -6,13 +6,15 @@ inspector tabs, the order composer, and the calculator.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
`routes/games/[id]/+layout.svelte` instantiates one `GameStateStore`
|
||||
per game (the layout remounts when the user navigates to a different
|
||||
game id, so each game gets a fresh store). The layout exposes the
|
||||
instance through Svelte context under `GAME_STATE_CONTEXT_KEY`;
|
||||
descendants read it via `getContext(GAME_STATE_CONTEXT_KEY)`.
|
||||
The in-game shell (`lib/game/game-shell.svelte`) instantiates one
|
||||
`GameStateStore` per game. The shell is mounted by the single-route
|
||||
dispatcher only while `appScreen.screen === "game"`, and remounts when
|
||||
`appScreen.gameId` changes, so each game gets a fresh store. The shell
|
||||
exposes the instance through Svelte context under
|
||||
`GAME_STATE_CONTEXT_KEY`; descendants read it via
|
||||
`getContext(GAME_STATE_CONTEXT_KEY)`.
|
||||
|
||||
The layout's `onMount` builds the `GalaxyClient`, loads `Cache`
|
||||
The shell's boot effect builds the `GalaxyClient`, loads `Cache`
|
||||
through `loadStore()`, then calls `gameState.init({ client, cache,
|
||||
gameId })`. `init`:
|
||||
|
||||
@@ -21,9 +23,10 @@ gameId })`. `init`:
|
||||
2. calls `setGame(gameId)`, which:
|
||||
- reads the per-game wrap-mode preference from `Cache`
|
||||
(`game-prefs / <gameId>/wrap-mode`, default `torus`);
|
||||
- calls `lobby.my.games.list` and finds the game record
|
||||
(`GameSummary` carries `current_turn`); if the user is not a
|
||||
member, the store flips to `error`;
|
||||
- calls `lobby.my.games.list` (`findGame`) and finds the game
|
||||
record (`GameSummary` carries `current_turn`); if the game is not
|
||||
in the player's list, the store sets the `notFound` flag (see
|
||||
below);
|
||||
- calls `user.games.report` for the discovered turn and decodes
|
||||
the FlatBuffers response into a TS-friendly `GameReport` shape.
|
||||
|
||||
@@ -40,6 +43,23 @@ The store exposes:
|
||||
| `pendingTurn` | `number \| null` | latest server turn the user has not yet opened |
|
||||
| `wrapMode` | `torus / no-wrap` | per-game preference, persisted via `Cache` |
|
||||
| `error` | `string \| null` | localised error message when `status === "error"` |
|
||||
| `notFound` | `boolean` | true when the game is not in the player's list (cancelled / removed / access revoked); the shell drops to the lobby |
|
||||
|
||||
## Missing or inaccessible game
|
||||
|
||||
A restored or stale game id (a `sessionStorage` snapshot pointing at a
|
||||
game that was cancelled, removed, or whose access was revoked) is a
|
||||
distinct case from a transient failure. When `findGame` returns no
|
||||
matching record, `setGame` sets the boolean `notFound` flag rather
|
||||
than synthesising an error message. After `init` resolves, the in-game
|
||||
shell reads `gameState.notFound` and, when true, calls
|
||||
`appScreen.go("lobby")` and shows a `game.events.unavailable` toast —
|
||||
the player lands back in the lobby instead of on an in-game error
|
||||
screen. A transient network failure takes the catch path instead,
|
||||
leaving `notFound` false and flipping `status` to `error` so the
|
||||
in-game error state offers a retry. `notFound` resets to false at the
|
||||
start of every `setGame` / `advanceToPending`. See
|
||||
[`navigation.md`](navigation.md) for the restore-and-validate flow.
|
||||
|
||||
## Store extensions
|
||||
|
||||
@@ -48,7 +68,7 @@ wire lands (ships, fleets, sciences, routes, battles, mail).
|
||||
`currentTurn` is split from `viewedTurn`, and `viewTurn(turn)` /
|
||||
`returnToCurrent()` handle history navigation. The derived
|
||||
`historyMode` rune flips automatically when `viewedTurn <
|
||||
currentTurn`; the layout passes it to the sidebar / bottom-tabs
|
||||
currentTurn`; the shell passes it to the sidebar / bottom-tabs
|
||||
wiring (which hides the order tab) and to
|
||||
`OrderDraftStore.bindClient` (which gates `add` / `remove` / `move`).
|
||||
See "History mode" below for the cache and refresh rules.
|
||||
@@ -161,9 +181,9 @@ without losing the live snapshot. The store keeps two turn runes:
|
||||
The derived `historyMode` rune (`status === "ready" && viewedTurn
|
||||
< currentTurn`) drives every history-aware consumer:
|
||||
|
||||
- the layout passes it to `Sidebar` / `BottomTabs` so the order
|
||||
- the shell passes it to `Sidebar` / `BottomTabs` so the order
|
||||
tab vanishes;
|
||||
- the layout passes a `getHistoryMode` getter to
|
||||
- the shell passes a `getHistoryMode` getter to
|
||||
`OrderDraftStore.bindClient` so `add` / `remove` / `move` are
|
||||
no-ops while the user is looking at a past turn;
|
||||
- `RenderedReportSource` returns the raw report (no order overlay)
|
||||
|
||||
Reference in New Issue
Block a user