feat(ui): app-shell behaviour — restore validation, return-to-lobby, push

- A restored game that no longer exists (cancelled/removed/revoked) drops to
  the lobby with a toast instead of the in-game error state: game-state
  exposes a `notFound` flag and the shell redirects via appScreen.go("lobby").
- Add a visible "return to lobby" control to the in-game header.
- Push/toast deep-links use activeView.select(...) (no URL); fix a latent
  visibility-listener double-install on in-place game switches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-23 20:11:54 +02:00
parent be7f06e163
commit 80545e9f9d
5 changed files with 73 additions and 0 deletions
@@ -515,6 +515,25 @@ return to the lobby still disposes the stores via `onDestroy`.
orderDraft.init({ cache, gameId: activeGameId }),
mailStore.init({ client, cache, gameId: activeGameId }),
]);
// A restored or stale game id may point at a game that is
// no longer in the player's list (cancelled, removed, or
// access revoked). `init` flags that distinct case via
// `gameState.notFound` (a transient network error keeps it
// false and surfaces the in-game error state instead). Drop
// to the lobby with a toast rather than stranding the user
// on the in-game "not in your list" error. Leaving the
// `game` screen unmounts the shell, so the stores are
// disposed via `onDestroy`; the rest of the bootstrap
// (client bind, server hydration) is skipped for the dead
// game.
if (gameState.notFound) {
appScreen.go("lobby");
toast.show({
messageKey: "game.events.unavailable.message",
durationMs: 8000,
});
return;
}
galaxyClient.set(client);
orderDraft.bindClient(client, {
getCurrentTurn: () => gameState.currentTurn,