feat(lobby): keep lobby/game caches fresh from any screen + invitation delta channel
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 48s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m7s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 48s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m7s
Builds on the cross-screen cache work: the global stream handler now keeps both caches current no matter which screen is mounted, and invitations become a live delta channel so the lobby's invitations list is fresh from any screen too. Client (boundary already started): - advanceCached now also folds opponent_joined into a not-currently-viewed game's cache via a new pure reducer applyOpponentJoined (extracted and reused by the mounted game board), so opening an open game that filled while you were elsewhere is flash-free. - patchLobbyInvitation upserts a still-pending invitation and removes a terminal one (started/declined/cancelled/expired); the global notify handler calls it on the invitation / invitation_update sub-kinds. Invitations delta channel (no wire/gateway/connector change — the notification already carries the full invitation with id/status/game_id end to end): - notify: a new in-app-only NotifyInvitationUpdate sub + NotificationInvitationUpdate constructor (shares encoding with NotificationInvitation). The Telegram connector renders no message for it, so a decline/cancel never becomes an out-of-app push. - lobby: emit the changed invitation to every participant on respond (accept/decline), on the final accept's game start, and on cancel — so each participant's lobby patches its list in place. The authoritative list holds only pending invitations, so the client's pending-vs-terminal rule matches it exactly. Tests: applyOpponentJoined + patchLobbyInvitation unit tests (TDD), the NotificationInvitationUpdate encoding unit test, and integration assertions that decline/cancel/accept publish invitation_update to every participant. Full local suite green (backend unit+integration, UI check/unit/build/bundle/e2e). Docs: ARCHITECTURE §10 (notify catalog + in-app-only note) and UI_DESIGN updated.
This commit is contained in:
+10
-5
@@ -549,10 +549,11 @@ including the mover**, so the mover's own other devices and their lobby refresh
|
||||
in-app only, so the actor gets no out-of-app push for their own move), **chat-message** and **nudge**
|
||||
(from the social service), **opponent-joined** (from the matchmaker, §8), and **notify**
|
||||
(a lightweight "re-poll" signal carrying a sub-kind: friend-request,
|
||||
friend-added, friend-declined, invitation or game-started; emitted on a friend-request,
|
||||
friend-added, friend-declined, invitation, invitation-update or game-started; emitted on a friend-request,
|
||||
on answering one (accept → friend-added, decline → friend-declined — to the original
|
||||
requester, so a game screen watching that opponent re-derives its "add to friends" state),
|
||||
and on an invitation create or its game start). **game-over** is emitted to every
|
||||
and on an invitation create (**invitation**) or any later change to it (**invitation-update**: an
|
||||
updated response, a decline, a cancel, or its game start — to every participant)). **game-over** is emitted to every
|
||||
seat from the same game commit when a game finishes — any path: a closing play, all-pass,
|
||||
resign or timeout — and **your-turn** is enriched so the out-of-app push reads in full: it
|
||||
also carries the mover's display name, their last action and the main word of a scoring play,
|
||||
@@ -566,7 +567,10 @@ refetch); **your-turn** carries that move count as a consistency check; the **ga
|
||||
notify carries the recipient's full **initial `StateView`** so opening a freshly started game is
|
||||
instant, and **opponent-joined** carries the waiting starter's refreshed `StateView` so the
|
||||
opponent card and the resign/chat controls update **in place**; **game-over** carries the final summary; the lobby **notify** sub-kinds
|
||||
carry the changed account / invitation. The move-commit **response** (`submit_play` / `pass` /
|
||||
carry the changed account / invitation, so the client patches its lobby lists in place: **invitation**
|
||||
and **invitation-update** carry the full invitation, and the client upserts a still-pending one and
|
||||
drops a terminal one (started, declined, cancelled, expired) — the invitations list is a delta channel
|
||||
too, fresh from any screen without a refetch. The move-commit **response** (`submit_play` / `pass` /
|
||||
`exchange` / `resign`) likewise returns the actor's own refilled rack and bag size, so the mover
|
||||
renders the next turn without a self-refetch. The `notify` package owns the FlatBuffers encoding
|
||||
(fed wire-agnostic input structs by the domain services) and the gateway forwards every payload
|
||||
@@ -589,8 +593,9 @@ not the recipient's latest-login bot. It then asks the **Telegram connector** to
|
||||
localized message with a Mini App deep-link button — only when the recipient has a Telegram
|
||||
identity and has not confined notifications to the app, so the two channels never duplicate. The
|
||||
connector routes by that language to the matching bot and renders the message in it. The out-of-app set is
|
||||
your-turn, game-over, nudge and the invitation / friend-request notify sub-kinds;
|
||||
the connector renders the message and skips the rest. Operator broadcasts
|
||||
your-turn, game-over, nudge and the **invitation** (a new invitation) / friend-request notify sub-kinds;
|
||||
the connector renders the message and skips the rest — so the in-app-only **invitation-update** (a
|
||||
response/withdrawal lobby sync) never becomes a platform push. Operator broadcasts
|
||||
(`SendToUser` / `SendToGameChannel`, §10 admin) instead pick the bot by an
|
||||
**operator-chosen** language in the console, unrelated to the recipient's login. Session-revocation events and
|
||||
cursor-based stream resume stay deferred (single-instance MVP).
|
||||
|
||||
Reference in New Issue
Block a user