mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-28 07:48:13 +00:00
fix(ui): cap channel unread badge to what the message ring still holds
The per-channel unread counter was independent of the ring's actual contents, so the two drifted apart: - Opening a channel whose entries had been evicted left the badge claiming messages the list could no longer show. The viewing-session bookkeeping computes the count from an _unread_at_entry snapshot, and with an empty list _hist_visible is 0, so entering only knocked the count down by one instead of clearing it (badge "7", empty list, then "6"). - Eviction from a full ring decremented the counter for any dropped entry, including already-read ones, undercounting the newer unread messages the counter actually refers to. chUnread()/getTotalChannelUnread() now clamp to the channel's ring occupancy, so the badge can never promise more than the history holds whatever the raw counter says, and eviction only decrements when the entry being dropped was itself unread. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,9 +77,14 @@ public:
|
||||
} else {
|
||||
pos = _hist_head;
|
||||
// Evicting the oldest entry — drop its share of the unread counter so
|
||||
// the badge can't claim a message the ring no longer holds.
|
||||
// the badge can't claim a message the ring no longer holds. The counter
|
||||
// refers to the channel's NEWEST unread messages, so losing the oldest
|
||||
// entry only costs an unread one when everything the ring still holds
|
||||
// for that channel is unread; otherwise the evicted entry was already
|
||||
// read and decrementing would undercount the newer unread ones.
|
||||
uint8_t evicted = _hist[pos].ch_idx;
|
||||
if (evicted < MAX_GROUP_CHANNELS && _ch_unread[evicted] > 0) {
|
||||
if (evicted < MAX_GROUP_CHANNELS && _ch_unread[evicted] > 0 &&
|
||||
_ch_unread[evicted] >= histCountForChannel(evicted)) {
|
||||
_ch_unread[evicted]--;
|
||||
}
|
||||
_hist_head = (_hist_head + 1) % CH_HIST_MAX;
|
||||
@@ -142,16 +147,34 @@ public:
|
||||
const ChHistEntry& chAtPos(int pos) const { return _hist[pos]; }
|
||||
|
||||
// ── Per-channel unread counters ─────────────────────────────────────────────
|
||||
// Always clamped to what the ring still holds for that channel. The raw
|
||||
// counter and the ring can drift apart — MessagesScreen's viewing-session
|
||||
// bookkeeping re-applies the unread snapshot taken when the channel was
|
||||
// opened, so entries evicted mid-session would otherwise leave the badge
|
||||
// promising messages the history list can no longer show (badge says 7,
|
||||
// opening the channel shows an empty list). Capping on read keeps the badge
|
||||
// honest whatever the raw counter says.
|
||||
uint8_t chUnread(int ch) const {
|
||||
return (ch >= 0 && ch < MAX_GROUP_CHANNELS) ? _ch_unread[ch] : 0;
|
||||
if (ch < 0 || ch >= MAX_GROUP_CHANNELS) return 0;
|
||||
int held = histCountForChannel(ch);
|
||||
return _ch_unread[ch] < held ? _ch_unread[ch] : (uint8_t)held;
|
||||
}
|
||||
void setChUnread(int ch, uint8_t v) {
|
||||
if (ch >= 0 && ch < MAX_GROUP_CHANNELS) _ch_unread[ch] = v;
|
||||
}
|
||||
void clearAllChannelUnread() { memset(_ch_unread, 0, sizeof(_ch_unread)); }
|
||||
int getTotalChannelUnread() const {
|
||||
// Same clamp as chUnread(), but counting ring occupancy for every channel
|
||||
// in one pass instead of re-walking the ring once per channel.
|
||||
uint8_t held[MAX_GROUP_CHANNELS];
|
||||
memset(held, 0, sizeof(held));
|
||||
for (int i = 0; i < _hist_count; i++) {
|
||||
uint8_t ch = _hist[(_hist_head + i) % CH_HIST_MAX].ch_idx;
|
||||
if (ch < MAX_GROUP_CHANNELS && held[ch] < 255) held[ch]++;
|
||||
}
|
||||
int total = 0;
|
||||
for (int i = 0; i < MAX_GROUP_CHANNELS; i++) total += _ch_unread[i];
|
||||
for (int i = 0; i < MAX_GROUP_CHANNELS; i++)
|
||||
total += (_ch_unread[i] < held[i]) ? _ch_unread[i] : held[i];
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
- **DM/room replies sometimes didn't show who they were addressed to, and room messages could wrap incorrectly.** The reply-prefix (`@[nick] `) was being stripped at the wrong point for room posts and DMs feeding the fullscreen message view, so a DM reply's "To:" header could go missing while a room reply showed the raw `@[nick] ` marker as literal message text; a related mismatch meant the history list's scrollbar/box-height sizing pass wrapped room messages with their sender name still attached, disagreeing with the actual rendered text.
|
||||
- **Remote Bot Actions (`!buzz`/`!gps`/`!advert`/`!gpio1`..`!gpio4`) ran immediately on every matching message, before quiet hours/cooldown/per-contact throttle were checked** — those gates only suppressed the reply text, not the actual buzz/GPS toggle/advert/pin write, so e.g. `!buzz` still audibly buzzed during configured quiet hours, and repeating a command wasn't rate-limited at all. Actions now only run once those checks have passed.
|
||||
- **A channel's unread badge could claim messages the history no longer held** — e.g. showing "7 unread" on a channel that opens to an empty list, and opening it only knocked the count down by one instead of clearing it. The badge is now capped at what the message ring actually still holds, and evicting an old message from a full ring only decrements the counter when the message being dropped was itself unread (previously it decremented regardless, undercounting newer unread messages).
|
||||
- **CardKB's Fn+`<letter>` accent-popup shortcut could open the accent popup while the device was locked**, unlike every other key on the keyboard (all of which are correctly discarded while locked) — it went straight to the keyboard widget instead of through the normal locked-input gate. Now respects the lock like everything else. Since a locked device now correctly ignores CardKB entirely, **Fn+Esc** (single press) is added as CardKB's own lock/unlock gesture — otherwise a CardKB-only setup had no way to unlock at all.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user