UI: tab-bar navigation — drop the hamburger
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 39s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 59s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 39s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 59s
Replace Menu.svelte (hamburger) everywhere with tab-bar navigation: - Settings hub (SettingsHub) from the lobby ⚙️ tab: Settings/Profile/ Friends/About as in-place tabs, back → lobby; the lobby ⚙️ badge counts incoming friend requests (invitations keep their own lobby section). - Comms hub (CommsHub) from the move-history 💬: Chat/Dictionary tabs, back → game; Dictionary only while the game is active. - Game menu items relocate into the open history: 🏁 leave / 📤 export in the header, 🤝 add-friend per opponent card, 💬 comms; unread chat is badged on the score bar + the 💬. - TapConfirm (tap → fading ✅ → tap) replaces the Skip/Hint press-and-hold popovers and drives the add-friend confirm. - Fix the move-history "jump": the slid board is inert and the stage can't scroll, so a swipe up genuinely closes the history. Remove Menu.svelte + HoldConfirm.svelte. Docs: UI_DESIGN, FUNCTIONAL(+ru), PRERELEASE. UI check/unit/build/bundle/e2e (Chromium+WebKit) all green.
This commit is contained in:
+11
-7
@@ -68,7 +68,9 @@ account is kept and the guest's games move into it. A merge is blocked only whil
|
||||
two accounts share a game still in progress.
|
||||
|
||||
### Lobby & matchmaking
|
||||
Bottom tab menu: **my games**, **profile**. The **my games** list groups games into three
|
||||
The lobby lists **my games** and offers a bottom tab bar — new game, statistics, and a
|
||||
**⚙️ settings** tab opening the settings hub (settings, profile, friends, about). The
|
||||
**my games** list groups games into three
|
||||
sections — *your turn*, *opponent's turn* and *finished* (empty sections are hidden) — and
|
||||
orders them so the games awaiting your move come first, the longest-waiting on top, while
|
||||
opponent-turn and finished games are most-recent first; it renders as a compact,
|
||||
@@ -128,18 +130,20 @@ digits, valid for twelve hours), or send a **request to someone you have played
|
||||
with** — they accept, ignore it (a request lapses after thirty days and can then be
|
||||
re-sent), or decline (a decline blocks further requests from you until they hand you
|
||||
a code). Cancelling your own pending request withdraws it; unfriending removes the
|
||||
friendship. In a game, an **add to friends** item for each opponent mirrors the live
|
||||
relationship: it reads *request sent* (disabled) while a request is pending or was
|
||||
declined, and *in friends* once accepted — updating in place the moment the opponent
|
||||
answers, and staying correct across reloads. Block globally — switch off incoming chat
|
||||
friendship. In a game, each opponent's score card carries an **add-to-friends 🤝** control (while the
|
||||
move history is open) that mirrors the live relationship: it confirms with a tap on a fading
|
||||
✅ (the card reads *Add friend?* while confirming), goes **disabled** while a request is
|
||||
pending or was declined, and **disappears** once you are friends — updating in place the
|
||||
moment the opponent answers, and staying correct across reloads. Block globally — switch off incoming chat
|
||||
and/or friend requests — and block individual players (a per-user block hides that
|
||||
person's chat and stops requests and game invitations both ways; it also ends any
|
||||
existing friendship). Per-game chat is for quick reactions: messages are short
|
||||
(up to 60 characters) and may not contain links, email addresses or phone numbers,
|
||||
even disguised. Nudge the player whose turn is awaited at most once per hour (the
|
||||
nudge is part of the game chat); the out-of-app push is delivered via the platform.
|
||||
Chat and the word-check tool open as their **own screens** (with a back to the game), and a
|
||||
new chat message raises an **unread badge** on the game's menu until the chat is opened.
|
||||
Chat and the word-check tool share one **comms screen** with **💬 chat** / **🔎 dictionary**
|
||||
tabs, reached from the 💬 in the move-history header (with a back to the game); a new chat
|
||||
message raises an **unread badge** on the game's score bar and the 💬 until the chat is opened.
|
||||
|
||||
### Profile & settings
|
||||
Edit the display name (letters joined by a single space / "." / "_" separator, with an
|
||||
|
||||
+11
-7
@@ -70,7 +70,9 @@ nudge) приходят от бота **этой партии** — по язы
|
||||
запрещено, только пока у аккаунтов есть общая незавершённая игра.
|
||||
|
||||
### Лобби и подбор
|
||||
Нижнее tab-меню: **мои игры**, **профиль**. Список **мои игры** разбит на три секции —
|
||||
В лобби — список **мои игры** и нижний tab-bar (новая игра, статистика и вкладка
|
||||
**⚙️ настройки**, открывающая хаб настроек — настройки, профиль, друзья, о программе).
|
||||
Список **мои игры** разбит на три секции —
|
||||
*твой ход*, *ход соперника* и *завершённые* (пустые секции скрыты) — и упорядочен так,
|
||||
что игры, ждущие твоего хода, идут первыми, дольше всего ждущие сверху, а игры на ходу
|
||||
соперника и завершённые — самые свежие сверху; отображается компактным списком с
|
||||
@@ -132,10 +134,11 @@ nudge) приходят от бота **этой партии** — по язы
|
||||
тому, с кем вы играли** — он принимает, игнорирует (заявка истекает через тридцать
|
||||
дней, после чего её можно отправить снова) или отклоняет (отказ блокирует ваши
|
||||
повторные заявки, пока он сам не передаст вам код). Отмена своей висящей заявки
|
||||
снимает её; удаление расторгает дружбу. В партии пункт меню **в друзья** для каждого
|
||||
соперника отражает живое отношение: он показывает *заявка отправлена* (неактивный),
|
||||
пока заявка висит или была отклонена, и *в друзьях* после принятия — обновляясь на месте
|
||||
в момент ответа соперника и оставаясь верным после перезагрузки. Глобальная блокировка — отключить входящие
|
||||
снимает её; удаление расторгает дружбу. В партии карточка счёта каждого соперника несёт контрол
|
||||
**в друзья 🤝** (при открытой истории ходов), отражающий живое отношение: он подтверждается
|
||||
тапом по затухающей ✅ (карточка показывает *В друзья?* во время подтверждения), становится
|
||||
**неактивным**, пока заявка висит или была отклонена, и **исчезает** после принятия —
|
||||
обновляясь на месте в момент ответа соперника и оставаясь верным после перезагрузки. Глобальная блокировка — отключить входящие
|
||||
чат и/или заявки —
|
||||
и блокировка конкретного игрока (пер-юзер блок скрывает его чат и запрещает заявки
|
||||
и приглашения в игру в обе стороны, а также расторгает уже имеющуюся дружбу). Чат
|
||||
@@ -143,8 +146,9 @@ nudge) приходят от бота **этой партии** — по язы
|
||||
содержать ссылок, email и телефонов, даже завуалированных. Nudge ожидаемого
|
||||
соперника — не чаще раза в час (nudge — часть игрового чата); внеприложенческий
|
||||
push доставляется через платформу.
|
||||
Чат и инструмент проверки слова открываются **отдельными экранами** (с кнопкой «назад» в
|
||||
партию), а новое сообщение в чате рисует **бейдж непрочитанного** на меню партии до открытия чата.
|
||||
Чат и инструмент проверки слова — один **экран связи** со вкладками **💬 чат** / **🔎 словарь**,
|
||||
открываемый по 💬 в шапке истории ходов (с кнопкой «назад» в партию); новое сообщение рисует
|
||||
**бейдж непрочитанного** на строке счёта партии и на 💬 до открытия чата.
|
||||
|
||||
### Профиль и настройки
|
||||
Редактирование отображаемого имени (буквы, разделённые одиночным пробелом / «.» /
|
||||
|
||||
+49
-23
@@ -23,16 +23,24 @@ Login uses `Screen`.
|
||||
|
||||
- **Back**: a thin, compact `<` drawn from two rotated CSS borders (`Header.svelte`
|
||||
`.chev`) — lighter than a glyph.
|
||||
- **Hamburger**: a CSS three-bar (`Menu.svelte`), deliberately larger; opens a dropdown
|
||||
of items (lobby: Friends/Profile/Settings/About; game: History/Chat/Check word, plus
|
||||
*Export GCG* on a finished game and *Add to friends* per opponent, then Drop game). A
|
||||
red count **badge** rides the hamburger (and the lobby *Friends* item) for pending
|
||||
incoming friend requests + invitations; the same dot style serves any future
|
||||
notification count.
|
||||
- **No hamburger**: navigation is entirely bottom tab bars (the former `Menu.svelte`
|
||||
dropdown is gone — it fought the Telegram-fullscreen layout, where it had to be re-centred).
|
||||
Destinations beyond the lobby live behind **hub screens** reached from a tab: a ⚙️
|
||||
**Settings hub** (`screens/SettingsHub.svelte`, the lobby's ⚙️ tab) and an in-game
|
||||
**comms hub** (`game/CommsHub.svelte`, the history's 💬). A hub owns one nav bar + one
|
||||
bottom tab bar whose tabs switch its body **in place** (state, not navigation), so the
|
||||
back control always returns to the hub's parent (Settings → lobby, comms → game). Settings
|
||||
hub tabs: ⚙️ Settings / 👤 Profile / 🤝 Friends / ℹ️ About (Friends hidden for guests);
|
||||
comms hub tabs: 💬 Chat / 🔎 Dictionary (Dictionary only while the game is active). The
|
||||
routes `/settings|/profile|/friends|/about` and `/game/:id/{chat,check}` survive as hub
|
||||
entry points (so a Telegram friend-code deep-link still lands on the Friends tab).
|
||||
- **Tab bar** (`TabBar.svelte`): square, borderless, evenly distributed buttons — a large
|
||||
emoji icon over a tiny truncated label. A press highlights a rounded **square** behind
|
||||
the icon (slightly larger than it) until release; spacing keeps adjacent labels from
|
||||
touching. No text selection on nav / tab-bar / buttons (`user-select: none`).
|
||||
emoji icon over a tiny truncated label (hub tabs are **icon-only**). A press highlights a
|
||||
rounded **square** behind the icon; a hub's **selected** tab stays highlighted (a filled
|
||||
square with an accent underline). A red count **badge** rides the icon's corner — on the
|
||||
lobby ⚙️ tab and the hub's 🤝 Friends tab for pending incoming friend requests (invitations
|
||||
keep their own lobby section), and on the Hint tab for the remaining count. No text
|
||||
selection on nav / tab-bar / buttons (`user-select: none`).
|
||||
- **Screen transitions** (`App.svelte`): navigation slides directionally — a
|
||||
screen entered from the lobby flies in from the right; returning to the lobby reveals it
|
||||
from the left (back). Transitions are local (so they do not play on first load) and
|
||||
@@ -71,8 +79,9 @@ Login uses `Screen`.
|
||||
hovering a dragged tile never jumps the board. On touch the first tile placement auto-zooms
|
||||
in centred on the target, and **holding a dragged tile over a cell ~1 s** auto-zooms there
|
||||
the first time. The swipe-to-open-history gesture stays dropped (it fought native scroll);
|
||||
history opens from the menu or a tap on the players plaque (below). A **hint** auto-zooms
|
||||
centred on the hint's placement, not the top-left.
|
||||
history opens on a **tap of the score bar** and closes on a tap or an **upward swipe** of
|
||||
the then-inert board (below). A **hint** auto-zooms centred on the hint's placement, not
|
||||
the top-left.
|
||||
- **Placing & recall** (`Game.svelte`): a rack tile is placed by tap-then-tap or by
|
||||
dragging it onto a cell; while a dragged tile is carried over the board, the aimed-at empty
|
||||
cell is **highlighted as a drop target** (an accent ring). A pending tile is taken back by a
|
||||
@@ -81,11 +90,21 @@ Login uses `Screen`.
|
||||
recalled tile returns to its original rack slot.
|
||||
- **Players plaque & history** (`Game.svelte`): the seats above the board share
|
||||
the width evenly; the seat whose turn it is is **raised** (a drop shadow on its sides)
|
||||
while the others read **sunk in** (an inset shadow). A tap anywhere on the plaque toggles
|
||||
while the others read **sunk in** (an inset shadow). A tap on the plaque toggles
|
||||
the **move history** — a fixed-height slide-down drawer whose bottom border (and its
|
||||
shadow) pins to the board as the board slides down, instead of tracking the table as
|
||||
moves accumulate; its scrollbar gutter is reserved so the centred word column does not
|
||||
jitter. A move's row lists every word it formed (the main word first).
|
||||
jitter. A move's row lists every word it formed (the main word first). While the history
|
||||
is open the **slid board is inert** and the stage cannot scroll, so the whole board reads
|
||||
as a **tap-or-swipe-up-to-close** surface — closing genuinely clears the open state (it no
|
||||
longer merely scrolls the board out of view, which used to leave a stale-open state that
|
||||
made a follow-up plaque tap "jump" the board). The drawer carries its own **header**: a 🏁
|
||||
**Drop game** (or 📤 **Export GCG** on a finished game) at the left and the comms 💬
|
||||
(badged with unread chat) at the right, icon-only. Each **opponent**'s card also gains a
|
||||
🤝 **add-friend** control (non-guests; hidden once a friend, disabled once requested) that
|
||||
confirms via the fading-✅ tap, swapping the card's score for "Add friend?" while armed
|
||||
(see Controls); the name and score stay centred — the 🤝 is pinned to the card's edge.
|
||||
Unread chat is also badged on the score bar itself, so it shows with the history closed.
|
||||
- **Vertical fit & keyboard**: when the game does not fit the viewport, only the
|
||||
board area scrolls vertically (`Screen` `column` mode; the score bar, status, rack and tab
|
||||
bar stay fixed), while zoom keeps its own scroll. The check-word dialog opens in
|
||||
@@ -107,9 +126,12 @@ Login uses `Screen`.
|
||||
|
||||
## Controls
|
||||
|
||||
- **HoldConfirm** (`components/HoldConfirm.svelte`): the shared press-and-hold control. A
|
||||
short tap opens a small popover above the button; a ~0.7 s hold runs the primary action
|
||||
immediately. Used by the **Skip** and **Hint** tabs (each confirmed by an **Ok ✅** popover).
|
||||
- **TapConfirm** (`components/TapConfirm.svelte`, logic in `lib/tapconfirm.ts`): the shared
|
||||
tap-to-confirm control. A first tap arms a ~2 s window showing a **fading ✅** (no fade
|
||||
under reduce-motion, but the window still holds); a tap on the ✅ within it confirms,
|
||||
otherwise it reverts. Used by the **Skip** and **Hint** tabs (the icon morphs to ✅, no
|
||||
label — replacing the old press-and-hold popover) and the in-game **add-friend 🤝**. The
|
||||
**Drop game** action keeps its `Modal` confirmation (a destructive, less-frequent action).
|
||||
- **MakeMove / Reset**: when ≥1 tile is pending the rack collapses its used slots
|
||||
and shifts left, a **borderless ✅ icon button** (styled like a tab, not a filled accent
|
||||
button) beside the rack commits the move — no popover, and disabled while the pending word
|
||||
@@ -140,7 +162,11 @@ IV 🏅; active games show Your move 🟢 / Opponent's move ⏳; invitations use
|
||||
|
||||
## Social, account & history surfaces
|
||||
|
||||
- **Friends** (`screens/Friends.svelte`, from the lobby menu): an "add a friend" block
|
||||
- **Settings hub** (`screens/SettingsHub.svelte`, the lobby ⚙️ tab): one nav bar + a bottom
|
||||
tab bar over four bodies — ⚙️ Settings, 👤 Profile, 🤝 Friends, ℹ️ About — switched in
|
||||
place; back always returns to the lobby. Guests see all but Friends. The lobby ⚙️ badge and
|
||||
the 🤝 tab badge both show the pending incoming-friend-request count.
|
||||
- **Friends** (`screens/Friends.svelte`, the Settings hub's 🤝 tab): an "add a friend" block
|
||||
pairing a code **input** with a **Show my code** action that reveals a large 6-digit
|
||||
code + its expiry; then the incoming **requests** (Accept / Decline), the **friends**
|
||||
list (Remove / Block), and a **blocked** list (Unblock). Durable accounts only — a
|
||||
@@ -163,12 +189,12 @@ IV 🏅; active games show Your move 🟢 / Opponent's move ⏳; invitations use
|
||||
the icon copies it. Flex text inputs carry `min-width:0` so they shrink instead of
|
||||
overflowing in Safari.
|
||||
- **History / GCG**: the in-game slide-down history gains the running total per move;
|
||||
*Export GCG* shares or downloads the `.gcg` file and appears only once the game is
|
||||
finished.
|
||||
- **Finished game**: the board keeps no last-word highlight and no zoom; the menu drops
|
||||
*Check word* and *Drop game*; and the footer (rack + tab bar) is **drawn but inert**
|
||||
(greyed, non-interactive) rather than hidden, so the layout does not jump. Chat
|
||||
send / nudge are the ⬆️ / 🛎️ icons.
|
||||
*Export GCG* (the 📤 in the history header) shares or downloads the `.gcg` file and
|
||||
appears only once the game is finished.
|
||||
- **Finished game**: the board keeps no last-word highlight and no zoom; the history header
|
||||
offers *Export GCG* (not *Drop game*) and the comms hub hides the 🔎 *Dictionary* tab; and
|
||||
the footer (rack + tab bar) is **drawn but inert** (greyed, non-interactive) rather than
|
||||
hidden, so the layout does not jump. Chat send / nudge are the ⬆️ / 🛎️ icons.
|
||||
|
||||
## Caveat
|
||||
|
||||
|
||||
Reference in New Issue
Block a user