ui/phase-14: rename planet end-to-end + order read-back
Wires the first end-to-end command through the full pipeline:
inspector rename action → local order draft → user.games.order
submit → optimistic overlay on map / inspector → server hydration
on cache miss via the new user.games.order.get message type.
Backend: GET /api/v1/user/games/{id}/orders forwards to engine
GET /api/v1/order. Gateway parses the engine PUT response into the
extended UserGamesOrderResponse FBS envelope and adds
executeUserGamesOrderGet for the read-back path. Frontend ports
ValidateTypeName to TS, lands the inline rename editor + Submit
button, and exposes a renderedReport context so consumers see the
overlay-applied snapshot.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,11 @@
|
||||
// sets for `LocalPlanet`, `OtherPlanet`, `UninhabitedPlanet`, and
|
||||
// `UnidentifiedPlanet`, and the wrapper preserves that nullability
|
||||
// instead of inventing zero values.
|
||||
//
|
||||
// Phase 14 adds `applyOrderOverlay`: every applied / submitting
|
||||
// rename in the local draft swaps the planet name on the rendered
|
||||
// report so the player sees their intent reflected immediately,
|
||||
// without waiting for the next turn cutoff.
|
||||
|
||||
import { Builder, ByteBuffer } from "flatbuffers";
|
||||
|
||||
@@ -19,6 +24,7 @@ import {
|
||||
GameReportRequest,
|
||||
Report,
|
||||
} from "../proto/galaxy/fbs/report";
|
||||
import type { CommandStatus, OrderCommand } from "../sync/order-types";
|
||||
|
||||
const MESSAGE_TYPE = "user.games.report";
|
||||
|
||||
@@ -205,6 +211,42 @@ export function uuidToHiLo(value: string): [bigint, bigint] {
|
||||
return [hi, lo];
|
||||
}
|
||||
|
||||
/**
|
||||
* applyOrderOverlay returns a copy of `report` with every applied or
|
||||
* still-in-flight (`submitting`) command from `commands` projected on
|
||||
* top. Phase 14 understands `planetRename` only — every other variant
|
||||
* passes through. The function is pure: callers re-derive the
|
||||
* overlay whenever the draft or the report change.
|
||||
*
|
||||
* `statuses` maps command id → status. Entries with `applied` or
|
||||
* `submitting` participate in the overlay; everything else (`draft`,
|
||||
* `valid`, `invalid`, `rejected`) is treated as "not yet committed
|
||||
* by the player" and skipped. This matches the order-composer model:
|
||||
* the player sees their own committed intent, not their unfinished
|
||||
* edits.
|
||||
*/
|
||||
export function applyOrderOverlay(
|
||||
report: GameReport,
|
||||
commands: OrderCommand[],
|
||||
statuses: Record<string, CommandStatus>,
|
||||
): GameReport {
|
||||
if (commands.length === 0) return report;
|
||||
let mutatedPlanets: ReportPlanet[] | null = null;
|
||||
for (const cmd of commands) {
|
||||
const status = statuses[cmd.id];
|
||||
if (status !== "applied" && status !== "submitting") continue;
|
||||
if (cmd.kind !== "planetRename") continue;
|
||||
const idx = report.planets.findIndex((p) => p.number === cmd.planetNumber);
|
||||
if (idx < 0) continue;
|
||||
if (mutatedPlanets === null) {
|
||||
mutatedPlanets = [...report.planets];
|
||||
}
|
||||
mutatedPlanets[idx] = { ...mutatedPlanets[idx]!, name: cmd.name };
|
||||
}
|
||||
if (mutatedPlanets === null) return report;
|
||||
return { ...report, planets: mutatedPlanets };
|
||||
}
|
||||
|
||||
function decodeErrorMessage(payload: Uint8Array): { code: string; message: string } {
|
||||
if (payload.length === 0) {
|
||||
return { code: "internal_error", message: "empty error payload" };
|
||||
|
||||
Reference in New Issue
Block a user