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
@@ -103,7 +103,21 @@ export const ru: Record<MessageKey, string> = {
|
||||
'profile.timezone': 'Часовой пояс',
|
||||
'profile.hintBalance': 'Баланс подсказок',
|
||||
'profile.guest': 'Гостевой аккаунт',
|
||||
'profile.readonly': 'Редактирование профиля появится в следующем обновлении.',
|
||||
'profile.edit': 'Редактировать профиль',
|
||||
'profile.displayName': 'Отображаемое имя',
|
||||
'profile.awayWindow': 'Окно отсутствия',
|
||||
'profile.awayHint': 'В эти часы вам не засчитывают автопоражение.',
|
||||
'profile.from': 'С',
|
||||
'profile.to': 'До',
|
||||
'profile.blockChat': 'Отключить чат',
|
||||
'profile.blockFriendRequests': 'Отключить заявки в друзья',
|
||||
'profile.email': 'Эл. почта',
|
||||
'profile.bindEmail': 'Привязать почту',
|
||||
'profile.emailCode': 'Код подтверждения',
|
||||
'profile.emailSent': 'Мы отправили код на {email}.',
|
||||
'profile.emailBound': 'Почта подтверждена.',
|
||||
'profile.saved': 'Профиль сохранён.',
|
||||
'profile.guestLocked': 'Войдите по почте, чтобы управлять профилем.',
|
||||
|
||||
'settings.title': 'Настройки',
|
||||
'settings.theme': 'Тема',
|
||||
@@ -144,4 +158,81 @@ export const ru: Record<MessageKey, string> = {
|
||||
'error.unavailable': 'Проблема соединения. Повторяем…',
|
||||
'error.internal': 'Что-то пошло не так.',
|
||||
'error.generic': 'Что-то пошло не так.',
|
||||
|
||||
'lobby.invitations': 'Приглашения',
|
||||
'lobby.friends': 'Друзья',
|
||||
|
||||
'friends.title': 'Друзья',
|
||||
'friends.yours': 'Ваши друзья',
|
||||
'friends.none': 'Друзей пока нет.',
|
||||
'friends.incoming': 'Заявки в друзья',
|
||||
'friends.accept': 'Принять',
|
||||
'friends.decline': 'Отклонить',
|
||||
'friends.unfriend': 'Удалить',
|
||||
'friends.block': 'Заблокировать',
|
||||
'friends.add': 'Добавить друга',
|
||||
'friends.addFromGame': 'В друзья',
|
||||
'friends.requestSent': 'Заявка в друзья отправлена.',
|
||||
'friends.getCode': 'Показать мой код',
|
||||
'friends.codeHint': 'Передайте этот код другу в течение 12 часов.',
|
||||
'friends.codeExpires': 'Истекает в {time}',
|
||||
'friends.enterCode': 'Есть код? Добавить друга',
|
||||
'friends.codePlaceholder': 'Код из 6 цифр',
|
||||
'friends.redeem': 'Добавить',
|
||||
'friends.added': 'Добавлен(а) {name}.',
|
||||
'friends.blockedList': 'Заблокированные',
|
||||
'friends.unblock': 'Разблокировать',
|
||||
'friends.noneBlocked': 'Заблокированных нет.',
|
||||
|
||||
'invitations.none': 'Приглашений нет.',
|
||||
'invitations.from': 'От {name}',
|
||||
'invitations.with': 'С {names}',
|
||||
'invitations.accept': 'Принять',
|
||||
'invitations.decline': 'Отклонить',
|
||||
'invitations.cancel': 'Отменить',
|
||||
'invitations.waiting': 'Ожидаем ответы',
|
||||
|
||||
'new.auto': 'Быстрая игра',
|
||||
'new.withFriends': 'Игра с друзьями',
|
||||
'new.pickFriends': 'Кого пригласить',
|
||||
'new.invite': 'Отправить приглашение',
|
||||
'new.moveTime': 'Время на ход',
|
||||
'new.hintsPerPlayer': 'Подсказок на игрока',
|
||||
'new.invited': 'Приглашение отправлено.',
|
||||
'new.noFriends': 'Сначала добавьте друзей, чтобы пригласить их.',
|
||||
|
||||
'stats.title': 'Статистика',
|
||||
'stats.wins': 'Победы',
|
||||
'stats.losses': 'Поражения',
|
||||
'stats.draws': 'Ничьи',
|
||||
'stats.played': 'Игр',
|
||||
'stats.winRate': 'Доля побед',
|
||||
'stats.maxGame': 'Лучшая игра',
|
||||
'stats.maxWord': 'Лучший ход',
|
||||
'stats.guestHint': 'Войдите, чтобы вести статистику.',
|
||||
|
||||
'game.exportGcg': 'Экспорт GCG',
|
||||
'game.gcgActiveOnly': 'Доступно после завершения игры.',
|
||||
|
||||
'time.minutes': '{n} мин',
|
||||
'time.hours': '{n} ч',
|
||||
|
||||
'error.self_relation': 'Нельзя сделать это с самим собой.',
|
||||
'error.request_exists': 'Заявка или дружба уже существует.',
|
||||
'error.request_blocked': 'Игрок не принимает заявки.',
|
||||
'error.request_not_found': 'Подходящей заявки нет.',
|
||||
'error.no_shared_game': 'Можно добавить только того, с кем вы играли.',
|
||||
'error.request_declined': 'Игрок отклонил вашу заявку.',
|
||||
'error.friend_code_invalid': 'Код недействителен или истёк.',
|
||||
'error.invalid_invitation': 'Неверное приглашение.',
|
||||
'error.invitation_blocked': 'Нельзя пригласить этого игрока.',
|
||||
'error.invitation_not_found': 'Приглашение не найдено.',
|
||||
'error.invitation_not_pending': 'Приглашение больше не открыто.',
|
||||
'error.invitation_expired': 'Приглашение истекло.',
|
||||
'error.not_invited': 'Вас не приглашали.',
|
||||
'error.already_responded': 'Вы уже ответили.',
|
||||
'error.not_inviter': 'Только пригласивший может это сделать.',
|
||||
'error.game_active': 'Доступно только после завершения игры.',
|
||||
'error.invalid_profile': 'Некоторые поля профиля некорректны.',
|
||||
'error.already_confirmed': 'Эта почта уже подтверждена.',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user