Stage 8: UI social/account/history surfaces
Wire the deferred Stage 7 surfaces end-to-end (UI -> gateway transcode -> backend REST -> existing domain services): friends (incl. one-time friend codes), per-user blocks, friend-game invitations, profile editing + email binding, the statistics screen, and the in-game history + GCG export. Friends gain two add paths (interview decision, a deliberate plan change): one-time 6-digit codes (friend_codes table, 12h TTL, single-use, rate-limited redeem); and play-gated requests (shared game required) where an explicit decline is permanent, an ignored request lapses after 30 days, and a code bypasses a decline. Migration 00006 widens friendships_status_chk and adds friend_codes. Lobby notification badge is poll + push: a new generic `notify` event drives it live; the client polls on open/focus. Language stays a single Settings control that writes through to the durable account's preferred_language. GCG export is finished-only (game.ErrGameActive) and shares/downloads the .gcg file. Tests: backend unit + inttest (friend gate/decline/code, ListInvitations, GetStats, GCG gate), gateway transcode round-trips + notify constructor, UI vitest (codecs, win-rate, share choice) + Playwright social specs. Docs: PLAN (Stage 8 done + refinements + TODO-5), ARCHITECTURE, FUNCTIONAL(+ru), UI_DESIGN, TESTING, module READMEs.
This commit is contained in:
+28
-13
@@ -269,10 +269,17 @@ requires (there is no DM surface; chat is per-game).
|
||||
robot (§7) and starts the game. On a pairing or substitution the matchmaker
|
||||
emits a **match-found** notification (§10), delivered over the live stream;
|
||||
`Poll` remains as a fallback for a client that is not currently streaming.
|
||||
- **Friends**: a **request → accept** graph (one `friendships` table) — add by
|
||||
friend list or internal ID now, by platform deep-link with Stage 9. Declining or
|
||||
cancelling removes the pending request; blocking someone severs an existing
|
||||
friendship.
|
||||
- **Friends** (Stage 8): two add paths over one `friendships` table. A **one-time
|
||||
code** the to-be-added player issues (a `friend_codes` row: 6-digit numeric,
|
||||
SHA-256-hashed, **12 h** TTL, one live code per issuer, single-use, redeem
|
||||
rate-limited) is redeemed by the other player to become friends immediately.
|
||||
Alternatively a **request → accept** is sent to someone you **share a game with**
|
||||
(active or finished); the recipient may accept, ignore (the pending row lazily
|
||||
expires after **30 days** and may be re-sent), or **decline** — a decline is
|
||||
remembered (`status='declined'`) and blocks further requests from that sender,
|
||||
unless they hand them a code, which overrides it. The requester's own cancel still
|
||||
deletes the row; blocking someone severs an existing friendship. (Discovery by
|
||||
friend list or platform deep-link arrives with Stage 9 / TODO-5.)
|
||||
- **Block**: two independent **global** account toggles (`block_chat`,
|
||||
`block_friend_requests`) **plus** a **per-user block list**. A per-user block is
|
||||
applied mutually: it hides the pair's chat from each other and refuses friend
|
||||
@@ -316,8 +323,9 @@ requires (there is no DM surface; chat is per-game).
|
||||
Stage 4 social/lobby tables `friendships` (the request/accept graph), `blocks`
|
||||
(per-user blocks), `chat_messages` (per-game chat and nudges), `email_confirmations`
|
||||
(pending confirm-codes) and `game_invitations` / `game_invitation_invitees`
|
||||
(friend-game invitations). The matchmaking pool is **in-memory** and persists
|
||||
nothing.
|
||||
(friend-game invitations). Stage 8's migration `00006` widened the `friendships`
|
||||
status to admit `declined` and added `friend_codes` (one-time add-a-friend codes).
|
||||
The matchmaking pool is **in-memory** and persists nothing.
|
||||
- **Active games are event-sourced.** A game is a `games` row (pinned
|
||||
`variant`/`dict_version`, bag `seed`, the per-game settings, and a denormalised
|
||||
turn cursor) plus an append-only, decoded move journal (`game_moves`); the live
|
||||
@@ -352,7 +360,9 @@ the same rows and is likewise self-contained — we ship our own writer (the sol
|
||||
exposes none): the standard Poslfit dialect (UTF-8, `#player`/`#lexicon`
|
||||
pragmas, `8G`/`H8` coordinates, lower-case blanks, `.` pass-throughs, `-TILES`
|
||||
exchanges), plus `#note` lines for resignations and timeouts, which the standard
|
||||
does not cover.
|
||||
does not cover. **GCG export is offered only on a finished game** (`game.ErrGameActive`
|
||||
otherwise, Stage 8), so an in-progress journal is never leaked mid-play; the client
|
||||
shares the `.gcg` file via the Web Share API where available, else downloads it.
|
||||
|
||||
## 10. Notifications
|
||||
|
||||
@@ -365,12 +375,17 @@ services); a single backend→gateway **gRPC server-stream** (`Push.Subscribe`,
|
||||
`user_id` to each client's Connect `Subscribe` stream while the app is open. The
|
||||
catalog is **your-turn** and **opponent-moved** (emitted from the game commit, so
|
||||
robot-driver and timeout-sweeper moves emit too), **chat-message** and **nudge**
|
||||
(from the social service), and **match-found** (from the matchmaker, §8). Event
|
||||
payloads are FlatBuffers-encoded by the backend and forwarded verbatim. A client
|
||||
that is not currently streaming falls back to the matchmaker's `Poll` for
|
||||
match-found. Out-of-app platform push (your-turn, nudge) is wired in Stage 9;
|
||||
session-revocation events and cursor-based stream resume are deferred
|
||||
(single-instance MVP).
|
||||
(from the social service), **match-found** (from the matchmaker, §8), and **notify**
|
||||
(Stage 8 — a lightweight "re-poll" signal carrying a sub-kind: friend-request,
|
||||
friend-added, invitation or game-started; emitted on a friend-request and invitation
|
||||
create and on an invitation's game start). Event payloads are FlatBuffers-encoded by
|
||||
the backend and forwarded verbatim. A client that is not currently streaming falls
|
||||
back to the matchmaker's `Poll` for match-found and, for the lobby **notification
|
||||
badge** (incoming friend requests + open invitations), the client polls on lobby
|
||||
open and on focus as well as re-polling on the `notify` event — covering a push
|
||||
missed while the app was hidden. Out-of-app platform push (your-turn, nudge) is
|
||||
wired in Stage 9; session-revocation events and cursor-based stream resume are
|
||||
deferred (single-instance MVP).
|
||||
|
||||
A separate **announcements channel** feeds the client's one-line banner (UI_DESIGN.md).
|
||||
It is a client-side **mock** rotation today; a server-driven source (operational notices,
|
||||
|
||||
Reference in New Issue
Block a user