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:
@@ -9,6 +9,7 @@
|
||||
import { navigate } from '../lib/router.svelte';
|
||||
import { t, type MessageKey } from '../lib/i18n/index.svelte';
|
||||
import { resultBadge } from '../lib/result';
|
||||
import { badgeKind } from '../lib/unread';
|
||||
import { getLobby, setLobby } from '../lib/lobbycache';
|
||||
import { preloadGames } from '../lib/preload';
|
||||
import { gamePhase, groupGames, shouldBlink, type LobbyPhase } from '../lib/lobbysort';
|
||||
@@ -35,7 +36,7 @@
|
||||
games = list.games;
|
||||
// Seed the per-game unread badges from the authoritative list. The live stream only raises
|
||||
// unread (it never seeds it from a GameView), so a persisted unread survives a reload here.
|
||||
for (const g of games) seedChatUnread(g.id, g.unreadChat);
|
||||
for (const g of games) seedChatUnread(g.id, g.unreadChat, g.unreadMessages);
|
||||
atGameLimit = list.atGameLimit;
|
||||
if (!guest) {
|
||||
[invitations, incoming] = await Promise.all([gateway.invitationsList(), gateway.friendsIncoming()]);
|
||||
@@ -75,7 +76,7 @@
|
||||
});
|
||||
|
||||
const myId = $derived(app.session?.userId ?? '');
|
||||
const groups = $derived(groupGames(games, myId));
|
||||
const groups = $derived(groupGames(games, myId, app.chatUnread));
|
||||
|
||||
// Per-card "look here" blink. When a game changes lobby bucket into "your turn" or "finished"
|
||||
// while the lobby is on screen, its status emoji blinks twice (an opponent's-turn change is
|
||||
@@ -253,6 +254,7 @@
|
||||
<h2>{t(group.h as 'lobby.yourTurn')}</h2>
|
||||
<div class="list">
|
||||
{#each group.list as g (g.id)}
|
||||
{@const badge = badgeKind(app.chatUnread[g.id] ?? false, app.messageUnread[g.id] ?? false)}
|
||||
<div class="rowwrap" class:revealed={group.finished && revealedId === g.id}>
|
||||
{#if group.finished}
|
||||
<button class="del" onclick={() => hide(g.id)} disabled={!connection.online} aria-label={t('lobby.hideGame')}>❌</button>
|
||||
@@ -267,7 +269,7 @@
|
||||
<span class="info">
|
||||
<span class="who">
|
||||
<span class="who-name">{opponents(g) || '—'}</span>
|
||||
{#if app.chatUnread[g.id]}<span class="unread-dot"></span>{/if}
|
||||
{#if badge}<span class="unread-dot" class:nudge={badge === 'nudge'}></span>{/if}
|
||||
</span>
|
||||
<span class="sub">{scoreline(g)}</span>
|
||||
</span>
|
||||
@@ -458,7 +460,8 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* A small red dot beside the opponent name: this game has an unread chat entry. */
|
||||
/* A small dot beside the opponent name: this game has an unread chat entry. Red for an unread
|
||||
message, a softer amber when only nudges are unread. */
|
||||
.unread-dot {
|
||||
flex: 0 0 auto;
|
||||
width: 8px;
|
||||
@@ -466,6 +469,9 @@
|
||||
border-radius: 50%;
|
||||
background: var(--danger);
|
||||
}
|
||||
.unread-dot.nudge {
|
||||
background: var(--warn);
|
||||
}
|
||||
.sub {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
|
||||
Reference in New Issue
Block a user