feat(chat): unread read-receipts — lobby/game dot, history-open ack, admin + metrics #77
Reference in New Issue
Block a user
Delete Branch "feature/chat-read-receipts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
In-game chat read-receipts: a durable unread model replacing the old in-memory client count.
What
00008):chat_messages.unread_seats smallintbitmask — a set bit per recipient seat that has not read the entry; a text message seeds every recipient's bit, a nudge only the awaited seat's. Inverted sounread_seats <> 0= unread (partial index).POST /games/:id/chat/read(sent only when the client holds unread, so a history open is not a constant backend call) — fired when the player opens the move history (even without entering chat) or the chat. A nudge additionally clears when its recipient moves (a wiredgame.NudgeClearer, dependency-inverted).unread_chaton the game view), raised by live chat/nudge events. Opening the history fade-blinks the 💬 twice then acks.chat_read_duration(publish→read latency by kind) + observable gaugechat_unread_messages+ social-domain tracing spans. Grafana dashboard intentionally deferred (owner decision).Owner decisions baked in
Tests
MarkRead,UnreadGames/HasUnread, nudge multiplayer targeting (only the to-move seat), nudge-cleared-by-move (realSubmitPlay), admin filter + per-seat detail. Full suite green.unread_chat+chat.readop; TS codecunreadChat.Docs
docs/ARCHITECTURE.md§8/§9/§11,docs/FUNCTIONAL.md(+_ru),PRERELEASE.md(row CR),gateway/README,backend/README, Go Doc.Notes
Persist per-message read state as a chat_messages.unread_seats bitmask (migration 00008): a text message seeds every recipient seat's bit, a nudge only the awaited seat's. A seat's bit clears when the player opens the move history or chat (POST /games/:id/chat/read, sent only when something is unread), and a nudge additionally clears when its recipient answers by moving (a wired game NudgeClearer, dependency-inverted so game keeps off social). UI shows a per-viewer unread dot in the lobby (next to the opponent) and the game score bar — the unread_chat game-view flag seeds it from authoritative REST views, live chat/nudge events raise it. Opening the move history counts as reading (even without entering chat): the 💬 fade-blinks twice and the client acks. Admin Messages gains an unread-only filter, a read/unread column, and a per-message card with the per-seat read breakdown. Observability: chat_read_duration histogram + chat_unread_messages gauge + social tracing.New commits pushed, approval review dismissed automatically according to repository settings