fix(battle-viewer): unblock synthetic-game battle load
The Phase 28 ConnectRPC migration of the battle viewer added a guard in `lib/active-view/battle.svelte` that waits for the surrounding layout to publish a `GalaxyClient` before issuing the fetch. The in-game shell layout deliberately skips `galaxyClient.set(...)` on the synthetic branch (gateway is not reachable in synthetic mode), so for any battle opened from a synthetic-report game the viewer sat on "loading battle…" forever — `fetchBattle` was never called, so the synthetic-fixture short-circuit it carries was unreachable. Let the guard skip synthetic ids: `fetchBattle` already resolves those through `lookupSyntheticBattle` and never touches the client, so its signature widens to `GalaxyClient | null` and the synthetic path passes `null`. The live path still waits for the handle as before; a `null` client on the live path now fails fast with a transport-level `BattleFetchError` instead of silently sitting on `loading`. Tests: - Existing "loading placeholder" smoke now uses a non-synthetic game id so it keeps asserting the live-path wait. - Two new cases pin the synthetic behaviour: missing fixture → `battle-not-found`; registered fixture → `BattleViewer` mounts. Docs: - `docs/FUNCTIONAL.md` §6.5 still described the pre-Phase-28 raw REST path. Updated to the signed ConnectRPC command and noted the synthetic short-circuit. Russian mirror updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -73,13 +73,16 @@ const RESULT_CODE_OK = "ok";
|
||||
/**
|
||||
* fetchBattle returns the `BattleReport` for the supplied game, turn,
|
||||
* and battle id. In synthetic-report mode (DEV / e2e) the lookup is
|
||||
* served from `synthetic-battle.ts`; otherwise the function calls the
|
||||
* `user.games.battle` ConnectRPC command through the supplied
|
||||
* `GalaxyClient`. Throws `BattleFetchError` with the upstream HTTP
|
||||
* status (or `0` for transport-level failures) on error.
|
||||
* served from `synthetic-battle.ts` and the `client` argument is
|
||||
* ignored; the in-game shell's synthetic branch deliberately never
|
||||
* publishes a `GalaxyClient`, so callers pass `null` on that path.
|
||||
* Otherwise the function calls the `user.games.battle` ConnectRPC
|
||||
* command through the supplied `GalaxyClient` and throws
|
||||
* `BattleFetchError` with the upstream HTTP status (or `0` for
|
||||
* transport-level failures) on error.
|
||||
*/
|
||||
export async function fetchBattle(
|
||||
client: GalaxyClient,
|
||||
client: GalaxyClient | null,
|
||||
gameId: string,
|
||||
turn: number,
|
||||
battleId: string,
|
||||
@@ -92,6 +95,10 @@ export async function fetchBattle(
|
||||
return fixture;
|
||||
}
|
||||
|
||||
if (client === null) {
|
||||
throw new BattleFetchError(0, "GalaxyClient is unavailable");
|
||||
}
|
||||
|
||||
const payload = encodeRequest(gameId, turn, battleId);
|
||||
const result = await client.executeCommand(MESSAGE_TYPE, payload);
|
||||
if (result.resultCode !== RESULT_CODE_OK) {
|
||||
|
||||
@@ -20,6 +20,7 @@ viewer keeps its prop-driven contract.
|
||||
fetchBattle,
|
||||
type BattleReport,
|
||||
} from "../../api/battle-fetch";
|
||||
import { isSyntheticGameId } from "../../api/synthetic-report";
|
||||
import { i18n } from "$lib/i18n/index.svelte";
|
||||
import {
|
||||
RENDERED_REPORT_CONTEXT_KEY,
|
||||
@@ -93,7 +94,14 @@ viewer keeps its prop-driven contract.
|
||||
return;
|
||||
}
|
||||
const client = galaxyClient?.client ?? null;
|
||||
if (!client) {
|
||||
// Synthetic games never publish a GalaxyClient because the
|
||||
// surrounding layout deliberately skips `galaxyClient.set(...)`
|
||||
// on that branch (gateway is not reachable in synthetic mode).
|
||||
// `fetchBattle` short-circuits synthetic ids through the
|
||||
// in-memory fixture map and ignores the client argument, so we
|
||||
// must not wait for the client handle on this path — otherwise
|
||||
// the viewer would sit on `loading` forever.
|
||||
if (!client && !isSyntheticGameId(gameId)) {
|
||||
// Layout populates the client after the boot Promise.all
|
||||
// resolves; stay in `loading` so the effect re-runs once
|
||||
// the handle becomes non-null.
|
||||
|
||||
Reference in New Issue
Block a user