From 158bbf74c8080a7352d2a724945465eff24a2c43 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Mon, 11 May 2026 19:27:16 +0200 Subject: [PATCH] Unread badges per category + context menu stays open on action - newMsg() now takes contact_type; room server messages (ADV_TYPE_ROOM) tracked separately in _room_unread; DM badge = msgCount - roomUnread - MODE_SELECT shows unread badges for all 3 rows: DM, Channels, Rooms - Room unread cleared when user opens Room Servers list or msgRead(0) - Channel context menu: ENTER performs action but stays open; only CANCEL (Back button) closes it so user can see the updated state Co-Authored-By: Claude Sonnet 4.6 --- examples/companion_radio/AbstractUITask.h | 2 +- examples/companion_radio/MyMesh.cpp | 4 ++-- examples/companion_radio/ui-new/UITask.cpp | 19 ++++++++++++++----- examples/companion_radio/ui-new/UITask.h | 6 +++++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index 18e1f9cb..ecac65d5 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -40,7 +40,7 @@ public: void enableSerial() { _serial->enable(); } void disableSerial() { _serial->disable(); } virtual void msgRead(int msgcount) = 0; - virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; + virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount, uint8_t contact_type = 0) = 0; virtual void notify(UIEventType t = UIEventType::none) = 0; virtual void addChannelMsg(uint8_t channel_idx, const char* text) {} virtual void loop() = 0; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 7adcaf65..d4157789 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -464,7 +464,7 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe // we only want to show text messages on display, not cli data bool should_display = txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN; if (should_display && _ui) { - _ui->newMsg(path_len, from.name, text, offline_queue_len); + _ui->newMsg(path_len, from.name, text, offline_queue_len, from.type); if (!_serial->isConnected()) { _ui->notify(UIEventType::contactMessage); } @@ -573,7 +573,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe if (getChannel(channel_idx, channel_details)) { channel_name = channel_details.name; } - if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len); + if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len, 0); #endif } diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 6fae0418..5d183087 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -986,7 +986,12 @@ public: display.drawTextCentered(display.width()/2, 0, "MESSAGE"); display.fillRect(0, 10, display.width(), 1); const char* opts[] = { "Direct message", "Channels", "Room Servers" }; - int dm_unread = _task->getMsgCount(); + int badges[3] = { + _task->getMsgCount() - _task->getRoomUnreadCount(), // DM only + _task->getChannelUnreadCount(), + _task->getRoomUnreadCount() + }; + if (badges[0] < 0) badges[0] = 0; for (int i = 0; i < 3; i++) { int y = START_Y + i * ITEM_H; bool sel = (i == _mode_sel); @@ -1001,9 +1006,9 @@ public: display.print(sel ? ">" : " "); display.setCursor(8, y); display.print(opts[i]); - if (i == 0 && dm_unread > 0) { + if (badges[i] > 0) { char badge[5]; - snprintf(badge, sizeof(badge), "%d", dm_unread); + snprintf(badge, sizeof(badge), "%d", badges[i]); int bw = display.getTextWidth(badge) + 2; display.setCursor(display.width() - bw, y); display.print(badge); @@ -1379,6 +1384,7 @@ public: _phase = CHANNEL_PICK; } else { _room_mode = (_mode_sel == 2); + if (_room_mode) _task->clearRoomUnread(); buildContactList(); _contact_sel = _contact_scroll = 0; _phase = CONTACT_PICK; @@ -1421,7 +1427,7 @@ public: uint8_t nstate = chNotifState(ch_idx); setChNotifState(ch_idx, (nstate + 1) % 3); // cycle: default→OFF→ON→default } - _ctx_open = false; + // stay open so user can see result; CANCEL closes return true; } return true; @@ -2002,6 +2008,7 @@ public: display.setTextSize(1); display.drawTextCentered(display.width() / 2, 22, "Messages"); int total_unread = _task->getMsgCount() + _task->getChannelUnreadCount(); + // getMsgCount() already includes room msgs via offline_queue_len; avoid double-count if (total_unread > 0) { char badge[20]; snprintf(badge, sizeof(badge), "%d unread", total_unread); @@ -2360,12 +2367,14 @@ switch(t){ void UITask::msgRead(int msgcount) { _msgcount = msgcount; if (msgcount == 0) { + _room_unread = 0; gotoHomeScreen(); } } -void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) { +void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount, uint8_t contact_type) { _msgcount = msgcount; + if (contact_type == ADV_TYPE_ROOM) _room_unread++; char alert_buf[80]; snprintf(alert_buf, sizeof(alert_buf), "Msg: %.20s", from_name); diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 147f44a3..90ab7431 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -36,6 +36,7 @@ class UITask : public AbstractUITask { char _alert[80]; unsigned long _alert_expiry; int _msgcount; + int _room_unread; int _last_notif_ch_idx; unsigned long ui_started_at, next_batt_chck; uint16_t _batt_mv; // EMA-filtered battery voltage @@ -73,6 +74,7 @@ public: next_batt_chck = _next_refresh = 0; ui_started_at = 0; _batt_mv = 0; + _msgcount = _room_unread = 0; _last_notif_ch_idx = -1; curr = NULL; } @@ -87,6 +89,8 @@ public: void addChannelMsg(uint8_t channel_idx, const char* text) override; int getMsgCount() const { return _msgcount; } int getChannelUnreadCount() const; + int getRoomUnreadCount() const { return _room_unread; } + void clearRoomUnread() { _room_unread = 0; } bool hasDisplay() const { return _display != NULL; } bool isButtonPressed() const; @@ -114,7 +118,7 @@ public: // from AbstractUITask void msgRead(int msgcount) override; - void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; + void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount, uint8_t contact_type = 0) override; void notify(UIEventType t = UIEventType::none) override; void loop() override;