feat(social): hide social controls on a deleted opponent's seat
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 29s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Successful in 1m17s
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m49s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 29s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Successful in 1m17s
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m49s
A deleted account keeps its seats in every shared game, so its opponents
still saw the add-friend and block controls on the scoreboard (deletion
drops the friendship, so the 🤝 even reappeared for a former friend) and
a chat composer nobody was behind. A friend request sent that way was
accepted by the server and stayed pending forever.
The per-viewer game views now mark such a seat (SeatView.deleted,
resolved beside the seat display names by a batch accounts.deleted_at
lookup) and the client hides every control aimed at it: add-friend,
block, and the chat composer (message + nudge) once no reachable
opponent is left. Live events carry the game domain's seat standings and
so leave the mark unset, so the delta reducers preserve the cached one.
SendFriendRequest and Block against a tombstone are refused with
social.ErrAccountDeleted (410 account_deleted) — the source of truth for
an older client.
This commit is contained in:
@@ -398,6 +398,16 @@ arrive from a platform rather than completing a mandatory registration).
|
||||
full timeline. (A merge that would otherwise leave the survivor with two identities of one
|
||||
kind — e.g. each account held a confirmed email — keeps the primary's and journals the
|
||||
secondary's with `reason=merge` before dropping it.)
|
||||
A tombstoned account **takes part in no further social exchange**: the per-viewer game
|
||||
views mark its seats (`SeatView.deleted`, resolved by a batch `accounts.deleted_at`
|
||||
lookup beside the seat display names), and the client hides every control aimed at such a
|
||||
seat — add-friend, block, and the chat composer (message + nudge) once no reachable
|
||||
opponent is left. The live events carry the seat standings the game domain holds and so
|
||||
leave the mark unset; the client seeds it from the REST-backed views and the delta
|
||||
reducers preserve it. The server is the source of truth for an older client:
|
||||
`SendFriendRequest` and `Block` against a tombstone return `social.ErrAccountDeleted`
|
||||
(HTTP 410, code `account_deleted`) — otherwise a request would hang pending forever,
|
||||
since deletion already dropped the account's social rows.
|
||||
Step-up is a mailed code (`purpose=delete`, no deeplink — a stray click must not delete;
|
||||
`ConfirmByToken` refuses a delete token) for an email account, else a typed phrase.
|
||||
`last_login_at` / `last_login_ip` are stamped on the cold-load profile fetch (throttled
|
||||
|
||||
+6
-1
@@ -374,7 +374,12 @@ goes **disabled** while a request is pending or was declined and **disappears**
|
||||
friends; both controls **disappear and the opponent's name is struck through** once you have
|
||||
blocked them. They update in place the moment the relationship changes and stay correct across
|
||||
reloads. Applying a block (or friend request) takes effect immediately and is confirmed by a
|
||||
live event; a transport failure rolls the control back to its prior state.
|
||||
live event; a transport failure rolls the control back to its prior state. Once an opponent
|
||||
**deletes their account** there is nobody left to reach, so both controls disappear for that
|
||||
seat for good — in the finished game as much as in one still running — and the chat composer
|
||||
goes too (message and nudge) when no reachable opponent remains; the chat log and the game
|
||||
itself stay readable. Attempting either action anyway (an app version that still shows the
|
||||
controls) is refused with *This player has deleted their account*.
|
||||
|
||||
Block globally — switch off incoming chat and/or friend requests — or block an **individual
|
||||
player**. A per-user block is **one-directional and silent**: you stop receiving everything
|
||||
|
||||
@@ -377,7 +377,12 @@ Telegram/VK** — в отличие от офлайн-режима игры вы
|
||||
**исчезает** после принятия; **оба контрола исчезают, а имя соперника становится зачёркнутым**
|
||||
после блокировки. Они обновляются на месте в момент изменения отношения и остаются верны после
|
||||
перезагрузки. Блокировка (как и заявка в друзья) применяется немедленно и подтверждается живым
|
||||
событием; при сбое транспорта контрол возвращается в прежнее состояние.
|
||||
событием; при сбое транспорта контрол возвращается в прежнее состояние. Если соперник **удалил
|
||||
свою учётную запись**, обращаться больше не к кому: оба контрола на этом месте исчезают
|
||||
навсегда — и в завершённой партии, и в идущей, — а когда доступных соперников не остаётся,
|
||||
пропадает и поле чата (сообщение и nudge); сама переписка и партия остаются доступны для
|
||||
чтения. Попытка выполнить действие всё равно (версия приложения, которая ещё показывает
|
||||
контролы) отклоняется сообщением *Игрок удалил свою учётную запись*.
|
||||
|
||||
Глобальная блокировка — отключить входящие чат и/или заявки — либо блокировка **конкретного
|
||||
игрока**. Пер-юзер блок **односторонний и незаметный**: вы перестаёте получать от этого человека
|
||||
|
||||
Reference in New Issue
Block a user