feat(ui): flag unread counts understated by a full history ring

DM and channel history each live in a fixed-size ring (32 DM / 48
channel entries). A conversation that outpaces reading can fill its
ring, evicting the oldest entries — including unread ones that were
never opened — so the unread badge silently caps at ring capacity
instead of reflecting how many messages actually arrived.

MessageHistory now tracks a sticky per-channel overflow flag, set at
the exact point an unread channel entry gets evicted (mirroring the
existing unread-decrement logic) and cleared once the count reads back
down to 0. DMUnreadEntry gains the same bit for DM contacts, detected
by comparing the raw per-contact counter against actual ring occupancy
on each new message (the DM ring, unlike the channel ring, doesn't
proactively decrement on eviction).

DisplayDriver::drawUnreadBadge/unreadBadgeWidth/fmtBadgeCount take an
optional overflow flag (default false, existing call sites unchanged)
that appends "+" to the badge. Wired into every place an unread count
is shown: DM/favourites and channel lists, the DM/Channels/Rooms
mode-select screen, favourite tiles, and the aggregate "Msgs" field on
the clock and lock dashboards plus the Messages home-carousel page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-23 13:45:07 +02:00
co-authored by Claude Sonnet 5
parent 26e6bc877a
commit 81cee3846f
7 changed files with 122 additions and 26 deletions
@@ -10,6 +10,8 @@
The Messages screen is split into three modes — **DMs**, **Channels**, and **Rooms** — selectable with UP/DOWN on the mode-select screen. Each mode shows the corresponding list of conversations with unread counters.
DM and channel history are each kept in a fixed-size on-device ring (32 DM / 48 channel entries). A busy conversation that outpaces reading can fill its ring — new messages keep arriving, evicting the oldest ones, including unread ones that were never opened. When that's happened, the unread badge for that conversation (and the DM/Channels row on the mode-select screen, and the clock/lock screen's Msgs field) gets a trailing **+** — e.g. `48+` — meaning the count is honest for what's still on the device but understates how many actually came in; the rest are gone for good. Rooms aren't ring-limited the same way, so they never show a **+**.
---
### Sending messages