feat(ui): app-shell core — single-route dispatcher, route collapse, nav→state
Collapse the game UI to one route (`/`): a screen dispatcher renders login/lobby/lobby-create/game from `appScreen`/`activeView` state instead of URL routes. Move screen components to lib/screens & lib/game; the game shell reads the game id from `appScreen.gameId` and re-inits per-game stores via an $effect; in-game views render from `activeView`. Flip ~23 goto/href nav sites to store mutations; drop the `?sidebar=` URL coupling. Auth gate is now state-based. WIP: browser-history (Back→lobby), restore-validation, the return-to-lobby button, push deep-links, and the test migration are follow-ups on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,21 +7,18 @@ itself is identical. The same component is reused for the mobile
|
||||
|
||||
Lists the seven IA destinations: map, tables (sub-list of six
|
||||
entities), report, battle, mail, ship-class designer, science
|
||||
designer. Closes on Escape, on outside click, and after a
|
||||
navigation. Phase 26 introduces the history-mode entry; Phase 35
|
||||
polishes microcopy.
|
||||
designer. Each entry mutates `activeView` (the single-URL app-shell
|
||||
has no per-view routes) and closes the menu. Closes on Escape, on
|
||||
outside click, and after a selection. Phase 26 introduces the
|
||||
history-mode entry; Phase 35 polishes microcopy.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { activeView, type GameView } from "$lib/app-nav.svelte";
|
||||
import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
|
||||
import { mailStore } from "$lib/mail-store.svelte";
|
||||
import { restoreFocus } from "$lib/a11y/restore-focus";
|
||||
|
||||
type Props = { gameId: string };
|
||||
let { gameId }: Props = $props();
|
||||
|
||||
const mailUnread = $derived(mailStore.unreadCount);
|
||||
|
||||
let open = $state(false);
|
||||
@@ -40,9 +37,12 @@ polishes microcopy.
|
||||
open = !open;
|
||||
}
|
||||
|
||||
function go(path: string): void {
|
||||
function select(
|
||||
view: GameView,
|
||||
params: { tableEntity?: string } = {},
|
||||
): void {
|
||||
open = false;
|
||||
void goto(withBase(path));
|
||||
activeView.select(view, params);
|
||||
}
|
||||
|
||||
function onKeyDown(event: KeyboardEvent): void {
|
||||
@@ -93,7 +93,7 @@ polishes microcopy.
|
||||
type="button"
|
||||
role="menuitem"
|
||||
data-testid="view-menu-item-map"
|
||||
onclick={() => go(`/games/${gameId}/map`)}
|
||||
onclick={() => select("map")}
|
||||
>
|
||||
{i18n.t("game.view.map")}
|
||||
</button>
|
||||
@@ -105,7 +105,7 @@ polishes microcopy.
|
||||
type="button"
|
||||
role="menuitem"
|
||||
data-testid="view-menu-item-table-{entry.slug}"
|
||||
onclick={() => go(`/games/${gameId}/table/${entry.slug}`)}
|
||||
onclick={() => select("table", { tableEntity: entry.slug })}
|
||||
>
|
||||
{i18n.t(entry.key)}
|
||||
</button>
|
||||
@@ -116,7 +116,7 @@ polishes microcopy.
|
||||
type="button"
|
||||
role="menuitem"
|
||||
data-testid="view-menu-item-report"
|
||||
onclick={() => go(`/games/${gameId}/report`)}
|
||||
onclick={() => select("report")}
|
||||
>
|
||||
{i18n.t("game.view.report")}
|
||||
</button>
|
||||
@@ -124,7 +124,7 @@ polishes microcopy.
|
||||
type="button"
|
||||
role="menuitem"
|
||||
data-testid="view-menu-item-battle"
|
||||
onclick={() => go(`/games/${gameId}/battle`)}
|
||||
onclick={() => select("battle")}
|
||||
>
|
||||
{i18n.t("game.view.battle")}
|
||||
</button>
|
||||
@@ -133,7 +133,7 @@ polishes microcopy.
|
||||
role="menuitem"
|
||||
data-testid="view-menu-item-mail"
|
||||
class="with-badge"
|
||||
onclick={() => go(`/games/${gameId}/mail`)}
|
||||
onclick={() => select("mail")}
|
||||
>
|
||||
<span>{i18n.t("game.view.mail")}</span>
|
||||
{#if mailUnread > 0}
|
||||
@@ -146,7 +146,7 @@ polishes microcopy.
|
||||
type="button"
|
||||
role="menuitem"
|
||||
data-testid="view-menu-item-designer-science"
|
||||
onclick={() => go(`/games/${gameId}/designer/science`)}
|
||||
onclick={() => select("designer-science")}
|
||||
>
|
||||
{i18n.t("game.view.designer.science")}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user