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:
+148
-1
@@ -103,7 +103,8 @@ table Ack {
|
||||
|
||||
// --- profile (authenticated) ---
|
||||
|
||||
// Profile is the authenticated account's own profile view.
|
||||
// Profile is the authenticated account's own profile view. away_start/away_end are
|
||||
// the "HH:MM" daily away-window bounds (added trailing — backward-compatible).
|
||||
table Profile {
|
||||
user_id:string;
|
||||
display_name:string;
|
||||
@@ -113,6 +114,8 @@ table Profile {
|
||||
block_chat:bool;
|
||||
block_friend_requests:bool;
|
||||
is_guest:bool;
|
||||
away_start:string;
|
||||
away_end:string;
|
||||
}
|
||||
|
||||
// --- game (authenticated) ---
|
||||
@@ -242,6 +245,142 @@ table ChatList {
|
||||
messages:[ChatMessage];
|
||||
}
|
||||
|
||||
// --- Stage 8: account, statistics, friends, blocks, invitations, history ---
|
||||
|
||||
// AccountRef is a referenced account with its display name resolved — the shared
|
||||
// shape for friends, blocked users and invitation participants.
|
||||
table AccountRef {
|
||||
account_id:string;
|
||||
display_name:string;
|
||||
}
|
||||
|
||||
// UpdateProfileRequest overwrites the full editable profile (the client sends the
|
||||
// complete desired profile). away_start/away_end are "HH:MM" bounds.
|
||||
table UpdateProfileRequest {
|
||||
display_name:string;
|
||||
preferred_language:string;
|
||||
time_zone:string;
|
||||
away_start:string;
|
||||
away_end:string;
|
||||
block_chat:bool;
|
||||
block_friend_requests:bool;
|
||||
}
|
||||
|
||||
// EmailBindRequest asks the backend to send a confirm-code binding email to the
|
||||
// caller's account.
|
||||
table EmailBindRequest {
|
||||
email:string;
|
||||
}
|
||||
|
||||
// EmailConfirmRequest verifies the code and binds the email (returns Profile).
|
||||
table EmailConfirmRequest {
|
||||
email:string;
|
||||
code:string;
|
||||
}
|
||||
|
||||
// StatsView is a durable account's lifetime statistics (games-played and win-rate
|
||||
// are derived client-side).
|
||||
table StatsView {
|
||||
wins:int;
|
||||
losses:int;
|
||||
draws:int;
|
||||
max_game_points:int;
|
||||
max_word_points:int;
|
||||
}
|
||||
|
||||
// TargetRequest names a single counterpart account (friend request/cancel/unfriend,
|
||||
// block/unblock).
|
||||
table TargetRequest {
|
||||
account_id:string;
|
||||
}
|
||||
|
||||
// FriendRespondRequest accepts or declines a pending request from a requester.
|
||||
table FriendRespondRequest {
|
||||
requester_id:string;
|
||||
accept:bool;
|
||||
}
|
||||
|
||||
// FriendList is the caller's accepted friends.
|
||||
table FriendList {
|
||||
friends:[AccountRef];
|
||||
}
|
||||
|
||||
// IncomingRequestList is the friend requests awaiting the caller's response.
|
||||
table IncomingRequestList {
|
||||
requests:[AccountRef];
|
||||
}
|
||||
|
||||
// FriendCode is a freshly issued one-time add-a-friend code (returned once).
|
||||
table FriendCode {
|
||||
code:string;
|
||||
expires_at_unix:long;
|
||||
}
|
||||
|
||||
// RedeemCodeRequest redeems a friend code, befriending its issuer.
|
||||
table RedeemCodeRequest {
|
||||
code:string;
|
||||
}
|
||||
|
||||
// RedeemResult reports the new friend gained by redeeming a code.
|
||||
table RedeemResult {
|
||||
friend:AccountRef;
|
||||
}
|
||||
|
||||
// BlockList is the accounts the caller has blocked.
|
||||
table BlockList {
|
||||
blocked:[AccountRef];
|
||||
}
|
||||
|
||||
// InvitationInvitee is one invitee's seat and response, name resolved.
|
||||
table InvitationInvitee {
|
||||
account_id:string;
|
||||
display_name:string;
|
||||
seat:int;
|
||||
response:string;
|
||||
}
|
||||
|
||||
// Invitation is a friend-game invitation with its settings and invitees.
|
||||
table Invitation {
|
||||
id:string;
|
||||
inviter:AccountRef;
|
||||
invitees:[InvitationInvitee];
|
||||
variant:string;
|
||||
turn_timeout_secs:int;
|
||||
hints_allowed:bool;
|
||||
hints_per_player:int;
|
||||
dropout_tiles:string;
|
||||
status:string;
|
||||
game_id:string;
|
||||
expires_at_unix:long;
|
||||
}
|
||||
|
||||
// CreateInvitationRequest proposes a 2-4 player friend game to the named invitees.
|
||||
table CreateInvitationRequest {
|
||||
invitee_ids:[string];
|
||||
variant:string;
|
||||
turn_timeout_secs:int;
|
||||
hints_allowed:bool;
|
||||
hints_per_player:int;
|
||||
dropout_tiles:string;
|
||||
}
|
||||
|
||||
// InvitationActionRequest accepts / declines / cancels an invitation by id.
|
||||
table InvitationActionRequest {
|
||||
invitation_id:string;
|
||||
}
|
||||
|
||||
// InvitationList is the caller's open invitations.
|
||||
table InvitationList {
|
||||
invitations:[Invitation];
|
||||
}
|
||||
|
||||
// GcgExport is a finished game's GCG transcript: a suggested filename and the text.
|
||||
table GcgExport {
|
||||
game_id:string;
|
||||
filename:string;
|
||||
content:string;
|
||||
}
|
||||
|
||||
// --- push event payloads ---
|
||||
|
||||
// YourTurnEvent signals that it is now the recipient's turn.
|
||||
@@ -271,3 +410,11 @@ table NudgeEvent {
|
||||
table MatchFoundEvent {
|
||||
game_id:string;
|
||||
}
|
||||
|
||||
// NotificationEvent is a lightweight "something changed, re-poll" signal that
|
||||
// drives the lobby badge (incoming friend requests, invitations). kind is a sub-
|
||||
// discriminator ("friend_request", "friend_added", "invitation", "game_started");
|
||||
// the client re-fetches its lobby counters on any of them.
|
||||
table NotificationEvent {
|
||||
kind:string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user