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:
@@ -42,6 +42,31 @@ export function patchLobbyGame(g: GameView): void {
|
||||
snapshot = { ...snapshot, games };
|
||||
}
|
||||
|
||||
/**
|
||||
* patchLobbyInvitation applies a live invitation change to the cached lobby's invitations list, so a
|
||||
* change seen while the lobby is unmounted is reflected on its next render instead of a stale card
|
||||
* lingering until the background refresh. A still-pending invitation is upserted (a new one
|
||||
* prepended newest-first, an updated one replaced in place); an invitation that reached any terminal
|
||||
* status (started, declined, cancelled, expired) is removed — the authoritative list holds only
|
||||
* pending invitations. It is a no-op when there is no snapshot yet, or a terminal invitation is not
|
||||
* in the list. The lobby re-renders from the snapshot, so the in-place upsert needs no re-sort.
|
||||
*/
|
||||
export function patchLobbyInvitation(inv: Invitation): void {
|
||||
if (!snapshot) return;
|
||||
const i = snapshot.invitations.findIndex((x) => x.id === inv.id);
|
||||
let invitations: Invitation[];
|
||||
if (inv.status !== 'pending') {
|
||||
if (i === -1) return; // a terminal invitation already absent — nothing to remove
|
||||
invitations = snapshot.invitations.filter((x) => x.id !== inv.id);
|
||||
} else if (i === -1) {
|
||||
invitations = [inv, ...snapshot.invitations];
|
||||
} else {
|
||||
invitations = snapshot.invitations.slice();
|
||||
invitations[i] = inv;
|
||||
}
|
||||
snapshot = { ...snapshot, invitations };
|
||||
}
|
||||
|
||||
/** clearLobby drops the cached lobby (called on logout). */
|
||||
export function clearLobby(): void {
|
||||
snapshot = null;
|
||||
|
||||
Reference in New Issue
Block a user