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:
+92
-1
@@ -102,7 +102,21 @@ export const en = {
|
||||
'profile.timezone': 'Time zone',
|
||||
'profile.hintBalance': 'Hint balance',
|
||||
'profile.guest': 'Guest account',
|
||||
'profile.readonly': 'Editing your profile arrives in a later update.',
|
||||
'profile.edit': 'Edit profile',
|
||||
'profile.displayName': 'Display name',
|
||||
'profile.awayWindow': 'Away window',
|
||||
'profile.awayHint': 'You are not auto-resigned during these hours.',
|
||||
'profile.from': 'From',
|
||||
'profile.to': 'To',
|
||||
'profile.blockChat': 'Disable chat',
|
||||
'profile.blockFriendRequests': 'Disable friend requests',
|
||||
'profile.email': 'Email',
|
||||
'profile.bindEmail': 'Bind email',
|
||||
'profile.emailCode': 'Confirmation code',
|
||||
'profile.emailSent': 'We sent a code to {email}.',
|
||||
'profile.emailBound': 'Email confirmed.',
|
||||
'profile.saved': 'Profile saved.',
|
||||
'profile.guestLocked': 'Sign in with email to manage your profile.',
|
||||
|
||||
'settings.title': 'Settings',
|
||||
'settings.theme': 'Theme',
|
||||
@@ -143,6 +157,83 @@ export const en = {
|
||||
'error.unavailable': 'Connection problem. Retrying…',
|
||||
'error.internal': 'Something went wrong.',
|
||||
'error.generic': 'Something went wrong.',
|
||||
|
||||
'lobby.invitations': 'Invitations',
|
||||
'lobby.friends': 'Friends',
|
||||
|
||||
'friends.title': 'Friends',
|
||||
'friends.yours': 'Your friends',
|
||||
'friends.none': 'No friends yet.',
|
||||
'friends.incoming': 'Friend requests',
|
||||
'friends.accept': 'Accept',
|
||||
'friends.decline': 'Decline',
|
||||
'friends.unfriend': 'Remove',
|
||||
'friends.block': 'Block',
|
||||
'friends.add': 'Add a friend',
|
||||
'friends.addFromGame': 'Add to friends',
|
||||
'friends.requestSent': 'Friend request sent.',
|
||||
'friends.getCode': 'Show my code',
|
||||
'friends.codeHint': 'Give this code to a friend within 12 hours.',
|
||||
'friends.codeExpires': 'Expires at {time}',
|
||||
'friends.enterCode': 'Have a code? Add a friend',
|
||||
'friends.codePlaceholder': '6-digit code',
|
||||
'friends.redeem': 'Add',
|
||||
'friends.added': 'Added {name}.',
|
||||
'friends.blockedList': 'Blocked players',
|
||||
'friends.unblock': 'Unblock',
|
||||
'friends.noneBlocked': 'No blocked players.',
|
||||
|
||||
'invitations.none': 'No invitations.',
|
||||
'invitations.from': 'From {name}',
|
||||
'invitations.with': 'With {names}',
|
||||
'invitations.accept': 'Accept',
|
||||
'invitations.decline': 'Decline',
|
||||
'invitations.cancel': 'Cancel',
|
||||
'invitations.waiting': 'Waiting for replies',
|
||||
|
||||
'new.auto': 'Quick match',
|
||||
'new.withFriends': 'Play with friends',
|
||||
'new.pickFriends': 'Choose who to invite',
|
||||
'new.invite': 'Send invitation',
|
||||
'new.moveTime': 'Move time',
|
||||
'new.hintsPerPlayer': 'Hints per player',
|
||||
'new.invited': 'Invitation sent.',
|
||||
'new.noFriends': 'Add friends first to invite them.',
|
||||
|
||||
'stats.title': 'Statistics',
|
||||
'stats.wins': 'Wins',
|
||||
'stats.losses': 'Losses',
|
||||
'stats.draws': 'Draws',
|
||||
'stats.played': 'Games',
|
||||
'stats.winRate': 'Win rate',
|
||||
'stats.maxGame': 'Best game',
|
||||
'stats.maxWord': 'Best move',
|
||||
'stats.guestHint': 'Sign in to track your statistics.',
|
||||
|
||||
'game.exportGcg': 'Export GCG',
|
||||
'game.gcgActiveOnly': 'Available once the game is finished.',
|
||||
|
||||
'time.minutes': '{n} min',
|
||||
'time.hours': '{n} h',
|
||||
|
||||
'error.self_relation': 'You cannot do that to yourself.',
|
||||
'error.request_exists': 'A request or friendship already exists.',
|
||||
'error.request_blocked': 'This player is not accepting requests.',
|
||||
'error.request_not_found': 'No matching friend request.',
|
||||
'error.no_shared_game': 'You can only add someone you have played with.',
|
||||
'error.request_declined': 'This player declined your request.',
|
||||
'error.friend_code_invalid': 'That friend code is invalid or expired.',
|
||||
'error.invalid_invitation': 'Invalid invitation.',
|
||||
'error.invitation_blocked': 'You cannot invite this player.',
|
||||
'error.invitation_not_found': 'Invitation not found.',
|
||||
'error.invitation_not_pending': 'This invitation is no longer open.',
|
||||
'error.invitation_expired': 'This invitation has expired.',
|
||||
'error.not_invited': 'You were not invited.',
|
||||
'error.already_responded': 'You already responded.',
|
||||
'error.not_inviter': 'Only the inviter can do that.',
|
||||
'error.game_active': 'Available only after the game is finished.',
|
||||
'error.invalid_profile': 'Some profile fields are invalid.',
|
||||
'error.already_confirmed': 'This email is already confirmed.',
|
||||
} as const;
|
||||
|
||||
export type MessageKey = keyof typeof en;
|
||||
|
||||
Reference in New Issue
Block a user