feat: sparser robot nudges, typed unread badge, lobby unread bump
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m26s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m26s
Three owner-requested polish changes: - robot: replace the lengthening 60-90 min -> 6 h proactive-nudge ramp with a flat uniform 9-12 h wait before every nudge; the existing sleep-window gate still skips and defers a nudge that would land in the robot's night. - ui: colour the lobby/in-game unread dot by type -- the regular danger colour when a chat message is unread, a softer amber (--warn) when only nudges are. Adds a per-viewer unread_messages flag (chat_messages.kind='message') across the backend DTO, FlatBuffers wire, gateway transcode and the UI store. - ui: float games with any unread notification to the top of the lobby's your-turn and opponent-turn sections (finished keeps its order), reusing the existing unread_chat flag. Docs (ARCHITECTURE 7, FUNCTIONAL + _ru) updated. No DB migration; the new wire field is backward-compatible.
This commit is contained in:
+13
-5
@@ -446,9 +446,10 @@ disguised robot stays indistinguishable from a person.
|
||||
**sleeps 00:00–07:00** anchored to the **opponent's** profile timezone with a
|
||||
per-game drift of **±3 h** (fallback UTC), so its night overlaps the human's
|
||||
rather than running anti-phase; on a daytime nudge it replies near the move's lower
|
||||
band; it proactively nudges the idle human on a **lengthening, randomized schedule** — the
|
||||
first ~60-90 min into the turn, each later reminder spaced further out toward 1-6 h — so a long
|
||||
wait gets a handful of increasingly-spaced nudges rather than an hourly stream.
|
||||
band; it proactively nudges the idle human on a **sparse, randomized schedule** — every nudge
|
||||
waits a uniform random **9-12 h** (the first measured from the turn start, each later one from
|
||||
the previous nudge), so a neglected turn gets only a handful of widely-spaced reminders. A nudge
|
||||
that would fall inside the sleep window is skipped and fires at the first scan after wake.
|
||||
- **Dead-endgame timing**: once the **two most recent moves are both passes**, the board and the
|
||||
robot's rack are frozen and it is bound to pass again, so the robot drops the long late-game
|
||||
think time and answers on a **shortened schedule scaled to the human's own last (pass) think
|
||||
@@ -570,8 +571,15 @@ in either direction (the enqueue excludes the caller's `BlockedWith` set);
|
||||
recipient answers by moving** (the move path calls a wired `NudgeClearer`). The mask is
|
||||
inverted so "anything unread" is a plain `unread_seats <> 0`, which the per-viewer
|
||||
`unread_chat` game-view flag (seeding the lobby and in-game unread **dot**), the admin
|
||||
unread filter and the unread gauge all use. On each clear the publish-to-read latency is
|
||||
recorded; the read time itself is not retained.
|
||||
unread filter and the unread gauge all use. A second per-viewer flag, **`unread_messages`**
|
||||
(`unread_seats <> 0 AND kind = 'message'`), reports whether any unread entry is a real
|
||||
message rather than only a nudge, so the **dot is coloured**: the regular danger colour
|
||||
when a message is unread, a softer amber when only nudges are. Both flags share the
|
||||
REST-seed-then-event-bump lifecycle (the live-event game-view leaves them false; a nudge
|
||||
event raises only `unread_chat`, a message event raises both). The lobby additionally
|
||||
**floats games with any unread entry to the top** of the your-turn and opponent-turn
|
||||
sections (the finished section keeps its activity order). On each clear the publish-to-read
|
||||
latency is recorded; the read time itself is not retained.
|
||||
- **Profile**: `preferred_language` (en/ru, edited in Settings), display name, email
|
||||
(confirm-code binding, see §4), **timezone**, the daily **away window** and the
|
||||
block toggles — all editable through `account.UpdateProfile`, which validates them:
|
||||
|
||||
+8
-5
@@ -74,8 +74,9 @@ The lobby lists **my games** and offers a bottom tab bar — new game, statistic
|
||||
**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,
|
||||
line-separated list. While the lobby is open and a listed game **becomes your turn or
|
||||
opponent-turn and finished games are most-recent first; within the your-turn and
|
||||
opponent-turn sections a game with any unread notification floats to the top (the finished
|
||||
section keeps its order). It renders as a compact, line-separated list. While the lobby is open and a listed game **becomes your turn or
|
||||
finishes**, its status icon **blinks twice** to draw the eye (an opponent's-turn change is
|
||||
silent, applied in place). You can **remove a finished game from your own list**:
|
||||
swipe a finished row left (or, on desktop, tap its **⋮**) to reveal a **❌**, then tap it.
|
||||
@@ -161,7 +162,8 @@ times for most moves, the occasional long one, and a night-time pause that track
|
||||
player's own day; once a game is clearly decided and both sides are only passing, it stops
|
||||
dragging it out, answering its forced passes at roughly the player's own pace rather than
|
||||
after a long deliberation. It answers a nudge
|
||||
within a few minutes and nudges back when the player has been away a long time. It
|
||||
within a few minutes and, when the player keeps it waiting, nudges back on a sparse
|
||||
schedule — roughly every 9-12 hours, and never during its night-time pause. It
|
||||
carries a human-like, language-appropriate name — a fresh one each game, drawn from a wide
|
||||
international pool of real names and handles, so the arena feels populated by many different
|
||||
players (a Russian game shows mostly Russian names, never East-Asian scripts; an international
|
||||
@@ -216,8 +218,9 @@ Chat and the word-check tool share one **comms screen** with **💬 chat** / **
|
||||
tabs, reached from the 💬 in the move-history header (with a back to the game). An AI game has no
|
||||
chat, so its comms screen is the word-check alone — and once an AI game is finished (no chat, the
|
||||
dictionary closed) the 💬 entry disappears entirely. An unread chat
|
||||
entry — a message **or a nudge** from an opponent — raises a small **red dot** next to the game
|
||||
in the **lobby** and on the game's **score bar**. Opening the **move history** counts as reading
|
||||
entry — a message **or a nudge** from an opponent — raises a small **dot** next to the game
|
||||
in the **lobby** and on the game's **score bar**, **red when an unread message is waiting and a
|
||||
softer amber when only nudges are**. Opening the **move history** counts as reading
|
||||
the chat, even without entering it: the dot clears and the 💬 icon **fade-blinks twice**. A nudge
|
||||
also clears the moment its recipient **takes their move**.
|
||||
|
||||
|
||||
@@ -75,8 +75,9 @@ nudge) приходят от бота **этой партии** — по язы
|
||||
Список **мои игры** разбит на три секции —
|
||||
*твой ход*, *ход соперника* и *завершённые* (пустые секции скрыты) — и упорядочен так,
|
||||
что игры, ждущие твоего хода, идут первыми, дольше всего ждущие сверху, а игры на ходу
|
||||
соперника и завершённые — самые свежие сверху; отображается компактным списком с
|
||||
линиями-разделителями. Пока лобби открыто и партия из списка **переходит на твой ход или
|
||||
соперника и завершённые — самые свежие сверху; внутри секций «твой ход» и «ход соперника»
|
||||
игра с любым непрочитанным уведомлением всплывает наверх (секция «завершённые» сохраняет
|
||||
свой порядок). Отображается компактным списком с линиями-разделителями. Пока лобби открыто и партия из списка **переходит на твой ход или
|
||||
завершается**, её значок статуса **дважды моргает**, привлекая внимание (переход на ход
|
||||
соперника — без моргания, применяется на месте). Завершённую партию можно **убрать из своего списка**:
|
||||
проведи по строке завершённой партии влево (или, на десктопе, нажми её **⋮**), чтобы открыть
|
||||
@@ -166,8 +167,9 @@ nudge) приходят от бота **этой партии** — по язы
|
||||
человеческим темпом — чаще короткие раздумья, изредка долгие, и ночная пауза,
|
||||
подстроенная под день игрока; когда партия уже явно решена и обе стороны только пасуют,
|
||||
он перестаёт её затягивать — отвечает на вынужденные пасы примерно в темпе самого игрока,
|
||||
а не после долгого раздумья. На nudge отвечает за несколько минут и
|
||||
сам шлёт nudge, когда игрок надолго пропал. Носит человекоподобное имя, подходящее
|
||||
а не после долгого раздумья. На nudge отвечает за несколько минут и, когда игрок
|
||||
держит его в ожидании, сам шлёт nudge редко — примерно раз в 9-12 часов и никогда во время
|
||||
своей ночной паузы. Носит человекоподобное имя, подходящее
|
||||
языку партии — каждую партию новое, из широкого международного пула реальных имён и
|
||||
никнеймов, так что арена кажется полной разных игроков (в русской партии — в основном
|
||||
русские имена, без восточноазиатских письменностей; в международной — весь спектр); не общается в чате и
|
||||
@@ -222,8 +224,9 @@ nudge) приходят от бота **этой партии** — по язы
|
||||
открываемый по 💬 в шапке истории ходов (с кнопкой «назад» в партию). В ИИ-партии чата нет, поэтому её
|
||||
экран связи — только проверка слова; а после завершения ИИ-партии (чата нет, словарь закрыт) сам вход
|
||||
💬 исчезает. Непрочитанная запись чата —
|
||||
сообщение **или nudge** от соперника — зажигает маленькую **красную точку** рядом с партией в
|
||||
**лобби** и на **строке счёта** партии. Открытие **истории ходов** считается прочтением чата, даже
|
||||
сообщение **или nudge** от соперника — зажигает маленькую **точку** рядом с партией в
|
||||
**лобби** и на **строке счёта** партии, **красную при непрочитанном сообщении и мягкую жёлтую,
|
||||
когда непрочитаны только nudge'и**. Открытие **истории ходов** считается прочтением чата, даже
|
||||
без захода в него: точка гаснет, а значок 💬 **дважды мигает**. Nudge также гаснет в момент, когда
|
||||
его получатель **делает ход**.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user