Phase 28: diplomatic mail UI (work in progress) #11
Reference in New Issue
Block a user
Delete Branch "feat/ui-stage-28"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 28 (diplomatic mail view) — work in progress.
This PR will be built up commit-by-commit on the feature branch as the stage progresses through the per-step plan in
ui/PLAN.md§28.Step 1 (this commit): backend support for race-name addressed mail. Adds
diplomail_messages.sender_race_namesnapshot (player senders) and an alternativerecipient_race_namefield on the user-mail send endpoints. The in-game UI consumes these to group personal mail into per-race threads without an extra membership-listing RPC.Follow-up steps (FBS schemas, gateway translators, UI store + view, push handler, badge, i18n, docs, tests, CI gate) will land as additional commits on this branch.
Adds the gateway-side translation layer that maps the eight new ConnectRPC mail commands onto backend's `/api/v1/user/games/{game_id}/mail/*` REST endpoints. - `gateway/internal/backendclient/mail_commands.go` defines `ExecuteMailCommand` and one helper per command (inbox, sent, message.get, send, broadcast, admin, read, delete). Each helper decodes the FlatBuffers request envelope, issues the REST call via the existing `*RESTClient.do`, decodes the JSON body, and re-encodes a typed FlatBuffers response. Recipient identifiers travel through unchanged so the new `recipient_race_name` shortcut introduced in Step 1 reaches backend untouched. - `routes.go` exposes a `MailRoutes` constructor and a matching `mailCommandClient` implementing `downstream.Client`. - `cmd/gateway/main.go` registers the new routes alongside the existing user / lobby / game-engine routes. - `mail_commands_test.go` covers the inbox, send-by-race-name, and read-state paths end-to-end against an `httptest.Server`, asserting request shapes (path, body, X-User-ID) and the decoded FlatBuffers response. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Adds `src/lib/mail-store.svelte.ts` — the reactive store that coordinates the in-game mail view. Responsibilities: - holds the inbox and sent listings for the current game and fires the initial parallel fetch (`fetchInbox` + `fetchSent`) on `setGame`; - exposes a `entries` derived rune that builds the unified list pane: per-race threads merged from incoming + outgoing personal messages, plus stand-alone items for system / admin / own paid-tier broadcasts. Thread messages are sorted oldest → newest for chat-style rendering; the list itself sorts newest-first by the most-recent entry timestamp; - derives `unreadCount` from `readAt === null` rows for the header view-menu badge; - imperative `markRead` / `softDelete` actions with optimistic state flips and roll-back on RPC failure; - compose actions for personal / paid-tier broadcast / owner-admin sends; - `applyPushEvent(gameId)` hook called by the layout when a `diplomail.message.received` push frame arrives; refetches the inbox without trusting the preview payload; - persists the most recent message id under `cache.diplomail/${gameId}/last-seen` so a returning session can pre-paint the badge without a network round-trip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Step 7 — header view-menu badge. `view-menu.svelte` reads `mailStore.unreadCount` and renders an inline pill next to the "diplomatic mail" entry whenever the counter is non-zero. The badge styling matches the per-row dot in `thread-list.svelte` so the two surfaces feel consistent. Step 8 — push event handler + MailStore init in the in-game layout. `routes/games/[id]/+layout.svelte`: - registers a `diplomail.message.received` handler alongside the existing `game.turn.ready` / `game.paused` ones, parses the signed payload, calls `mailStore.applyPushEvent` to refresh the inbox for the matching game, and raises a toast with a "view" deep-link that navigates to `/games/:id/mail`; - adds `mailStore.init({ client, cache, gameId })` to the boot `Promise.all` so the inbox + sent lists are warm by the time the view mounts, and the badge counter is populated before any user interaction; - disposes the new subscription in the `onDestroy` block so a game switch does not leak handlers across navigations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.