From 5a80696fe8d2092b7f0e48d0e7fd26912be2c874 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Tue, 23 Jun 2026 00:09:39 +0200 Subject: [PATCH] feat(ui): friends list as lobby-style rows with kebab + confirm modals Settings -> Friends previously rendered each friend as a bordered card with two always-visible text buttons (Remove / Block) that fired immediately. Rework the whole screen to the lobby's visual language: one-line rows split by hairline separators across all three sections (friends, incoming requests, blocked). Each friend row gains a right-hand kebab that slides the row open to reveal two icon actions split by a vertical divider -- block (no-entry) and remove (cross) -- mirroring the lobby's slide-to-reveal. Both actions now require a confirmation modal; since the slide moves a short name off-screen, the modal keeps a generic title and shows the friend's name in the body, above the buttons, so a long name cannot stretch the sheet. Incoming keeps its accept/decline buttons and blocked keeps unblock, inline on their rows. Add the friends.actions / friends.blockConfirm / friends.unfriendConfirm keys to both i18n catalogs and document the flow in FUNCTIONAL (+_ru). --- docs/FUNCTIONAL.md | 3 + docs/FUNCTIONAL_ru.md | 3 + ui/src/lib/i18n/en.ts | 3 + ui/src/lib/i18n/ru.ts | 3 + ui/src/screens/Friends.svelte | 218 +++++++++++++++++++++++++++------- 5 files changed, 188 insertions(+), 42 deletions(-) diff --git a/docs/FUNCTIONAL.md b/docs/FUNCTIONAL.md index 38260fe..11e580e 100644 --- a/docs/FUNCTIONAL.md +++ b/docs/FUNCTIONAL.md @@ -213,6 +213,9 @@ block **overrides but does not delete** an existing friendship (so you may block they keep seeing you as one); active games are never interrupted — you can finish them, with the blocked opponent's chat composer hidden (only the log remains). Blocking from a game card mirrors the block in **Settings → Friends**; **unblock** and **unfriend** live there only. +On Settings → Friends each friend is a one-line row whose right-hand kebab (⋮) slides open +**block 🚫** and **remove ✖️** icon actions, and each action is gated by a confirmation +that names the friend (*Block this player?* / *Remove from friends?*). Blocking an **auto-match opponent who is secretly a robot** behaves the same in that game (struck name, hidden composer) and lists the blocked opponent under the name you saw, but is recorded only against that game — the disguise holds, the shared robot is never globally diff --git a/docs/FUNCTIONAL_ru.md b/docs/FUNCTIONAL_ru.md index fa1dcd6..3fc7c05 100644 --- a/docs/FUNCTIONAL_ru.md +++ b/docs/FUNCTIONAL_ru.md @@ -218,6 +218,9 @@ _Вход сейчас только через провайдера, поэто заблокированного соперника «подвал» чата скрыт (остаётся только лог). Блокировка с карточки в партии повторяет блокировку в **Настройках → Друзья**; **разблокировка** и **удаление из друзей** есть только там. +В **Настройках → Друзья** каждый друг — однострочник, чей правый кебаб (⋮) выдвигает +иконки-действия **заблокировать 🚫** и **удалить ✖️**, и каждое действие подтверждается +диалогом с именем друга (*Заблокировать?* / *Удалить из друзей?*). Блокировка **авто-матч соперника, который втайне робот**, в этой партии ведёт себя так же (зачёркнутое имя, скрытый «подвал») и в списке заблокированных показывается под тем именем, которое ты видел, но записывается только для этой партии — маскировка сохраняется, общий diff --git a/ui/src/lib/i18n/en.ts b/ui/src/lib/i18n/en.ts index db0cad9..6f7cb9e 100644 --- a/ui/src/lib/i18n/en.ts +++ b/ui/src/lib/i18n/en.ts @@ -227,6 +227,9 @@ export const en = { 'friends.decline': 'Decline', 'friends.unfriend': 'Remove', 'friends.block': 'Block', + 'friends.actions': 'Actions', + 'friends.blockConfirm': 'Block this player?', + 'friends.unfriendConfirm': 'Remove from friends?', 'friends.add': 'Add a friend', 'friends.addFromGame': 'Add to friends', 'friends.blockFromGame': 'Block player', diff --git a/ui/src/lib/i18n/ru.ts b/ui/src/lib/i18n/ru.ts index 4321efd..bd5b5a6 100644 --- a/ui/src/lib/i18n/ru.ts +++ b/ui/src/lib/i18n/ru.ts @@ -228,6 +228,9 @@ export const ru: Record = { 'friends.decline': 'Отклонить', 'friends.unfriend': 'Удалить', 'friends.block': 'Заблокировать', + 'friends.actions': 'Действия', + 'friends.blockConfirm': 'Заблокировать?', + 'friends.unfriendConfirm': 'Удалить из друзей?', 'friends.add': 'Добавить друга', 'friends.addFromGame': 'В друзья', 'friends.blockFromGame': 'Заблокировать', diff --git a/ui/src/screens/Friends.svelte b/ui/src/screens/Friends.svelte index 4471ef4..831f3d2 100644 --- a/ui/src/screens/Friends.svelte +++ b/ui/src/screens/Friends.svelte @@ -1,5 +1,6 @@