diff --git a/src/MessageStore.cpp b/src/MessageStore.cpp index 871e394b9..9de7a09ea 100644 --- a/src/MessageStore.cpp +++ b/src/MessageStore.cpp @@ -65,11 +65,14 @@ void MessageStore::addFromPacket(const meshtastic_MeshPacket &packet) addLiveMessage(sm); - // === Auto-switch thread view on new message === - if (sm.type == MessageType::BROADCAST) { - setThreadMode(ThreadMode::CHANNEL, sm.channelIndex); - } else if (sm.type == MessageType::DM_TO_US) { - setThreadMode(ThreadMode::DIRECT, -1, sm.sender); + // === Auto-switch ONLY if we're currently in ALL-mode === + using graphics::MessageRenderer::getThreadMode; + if (getThreadMode() == ThreadMode::ALL) { + if (sm.type == MessageType::BROADCAST) { + setThreadMode(ThreadMode::CHANNEL, sm.channelIndex); + } else if (sm.type == MessageType::DM_TO_US) { + setThreadMode(ThreadMode::DIRECT, -1, sm.sender); + } } } diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index bc2165047..49664f3cf 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -396,6 +396,13 @@ void menuHandler::messageResponseMenu() bannerOptions.optionsCount = options; bannerOptions.bannerCallback = [](int selected) -> void { LOG_DEBUG("messageResponseMenu: selected %d", selected); + + auto mode = graphics::MessageRenderer::getThreadMode(); + int ch = graphics::MessageRenderer::getThreadChannel(); + uint32_t peer = graphics::MessageRenderer::getThreadPeer(); + + LOG_DEBUG("[ReplyCtx] mode=%d ch=%d peer=0x%08x", (int)mode, ch, (unsigned int)peer); + if (selected == ViewMode) { LOG_DEBUG("Switching to message_viewmode_menu"); menuHandler::menuQueue = menuHandler::message_viewmode_menu; @@ -404,17 +411,33 @@ void menuHandler::messageResponseMenu() messageStore.clearAllMessages(); } else if (selected == DismissOldest) { messageStore.dismissOldestMessage(); - } else if (selected == Preset) { - if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { - cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); + } else if (selected == Preset || selected == Freetext) { + if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) { + LOG_DEBUG("Replying to CHANNEL %d", ch); + if (selected == Preset) + cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, ch); + else + cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, ch); + } else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) { + LOG_DEBUG("Replying to DIRECT peer=0x%08x", peer); + if (selected == Preset) + cannedMessageModule->LaunchWithDestination(peer); + else + cannedMessageModule->LaunchFreetextWithDestination(peer); } else { - cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from); - } - } else if (selected == Freetext) { - if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { - cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); - } else { - cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from); + LOG_DEBUG("Fallback reply using last rx_text_message"); + if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { + if (selected == Preset) + cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); + else + cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, + devicestate.rx_text_message.channel); + } else { + if (selected == Preset) + cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from); + else + cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from); + } } #ifdef HAS_I2S } else if (selected == Aloud) { @@ -429,97 +452,103 @@ void menuHandler::messageResponseMenu() void menuHandler::messageViewModeMenu() { - // Collect menu entries + auto encodeChannelId = [](int ch) -> int { return 100 + ch; }; + auto isChannelSel = [](int id) -> bool { return id >= 100 && id < 200; }; + static std::vector labels; static std::vector ids; + static std::vector idToPeer; // DM lookup labels.clear(); ids.clear(); + idToPeer.clear(); - // Back labels.push_back("Back"); ids.push_back(-1); - - // View All labels.push_back("View All"); ids.push_back(-2); - // Add channels with live messages + // Channels with messages for (int ch = 0; ch < 8; ++ch) { - auto msgs = messageStore.getChannelMessages(ch); + auto msgs = messageStore.getChannelMessages((uint8_t)ch); if (!msgs.empty()) { char buf[40]; const char *cname = channels.getName(ch); - if (cname && cname[0]) { - snprintf(buf, sizeof(buf), "#%s", cname); - } else { - snprintf(buf, sizeof(buf), "#Ch%d", ch); - } + snprintf(buf, sizeof(buf), cname && cname[0] ? "#%s" : "#Ch%d", cname ? cname : "", ch); labels.push_back(buf); - ids.push_back(100 + ch); + ids.push_back(encodeChannelId(ch)); + LOG_DEBUG("messageViewModeMenu: Added live channel %s (id=%d)", buf, encodeChannelId(ch)); } } - // Add channels from registry + // Registry channels for (int ch : graphics::MessageRenderer::getSeenChannels()) { - if (std::find(ids.begin(), ids.end(), 100 + ch) == ids.end()) { + if (ch < 0 || ch >= 8) + continue; + auto msgs = messageStore.getChannelMessages((uint8_t)ch); + if (msgs.empty()) + continue; + int enc = encodeChannelId(ch); + if (std::find(ids.begin(), ids.end(), enc) == ids.end()) { char buf[40]; const char *cname = channels.getName(ch); - if (cname && cname[0]) { - snprintf(buf, sizeof(buf), "#%s", cname); - } else { - snprintf(buf, sizeof(buf), "#Ch%d", ch); - } + snprintf(buf, sizeof(buf), cname && cname[0] ? "#%s" : "#Ch%d", cname ? cname : "", ch); labels.push_back(buf); - ids.push_back(100 + ch); + ids.push_back(enc); + LOG_DEBUG("messageViewModeMenu: Added registry channel %s (id=%d)", buf, enc); } } - // Add DMs from live store + // Gather unique peers auto dms = messageStore.getDirectMessages(); std::vector uniquePeers; for (auto &m : dms) { uint32_t peer = (m.sender == nodeDB->getNodeNum()) ? m.dest : m.sender; - if (peer != nodeDB->getNodeNum() && std::find(uniquePeers.begin(), uniquePeers.end(), peer) == uniquePeers.end()) { + if (peer != nodeDB->getNodeNum() && std::find(uniquePeers.begin(), uniquePeers.end(), peer) == uniquePeers.end()) uniquePeers.push_back(peer); - } } - - // Add DMs from registry for (uint32_t peer : graphics::MessageRenderer::getSeenPeers()) { - if (peer != nodeDB->getNodeNum() && std::find(uniquePeers.begin(), uniquePeers.end(), peer) == uniquePeers.end()) { + if (peer != nodeDB->getNodeNum() && std::find(uniquePeers.begin(), uniquePeers.end(), peer) == uniquePeers.end()) uniquePeers.push_back(peer); - } } - std::sort(uniquePeers.begin(), uniquePeers.end()); - for (auto peer : uniquePeers) { + // Encode peers + for (size_t i = 0; i < uniquePeers.size(); ++i) { + uint32_t peer = uniquePeers[i]; auto node = nodeDB->getMeshNode(peer); std::string name; - if (node && node->has_user) { + if (node && node->has_user) name = sanitizeString(node->user.long_name).substr(0, 15); - } else { + else { char buf[20]; snprintf(buf, sizeof(buf), "Node %08X", peer); name = buf; } labels.push_back("DM: " + name); - ids.push_back(peer); + int encPeer = 1000 + (int)idToPeer.size(); + ids.push_back(encPeer); + idToPeer.push_back(peer); + LOG_DEBUG("messageViewModeMenu: Added DM %s peer=0x%08x id=%d", name.c_str(), (unsigned int)peer, encPeer); } - // Determine active ID + // Active ID int activeId = -2; auto mode = graphics::MessageRenderer::getThreadMode(); - if (mode == graphics::MessageRenderer::ThreadMode::ALL) { - activeId = -2; - } else if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) { - activeId = 100 + graphics::MessageRenderer::getThreadChannel(); - } else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) { - activeId = (int)graphics::MessageRenderer::getThreadPeer(); + if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) + activeId = encodeChannelId(graphics::MessageRenderer::getThreadChannel()); + else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) { + uint32_t cur = graphics::MessageRenderer::getThreadPeer(); + for (size_t i = 0; i < idToPeer.size(); ++i) + if (idToPeer[i] == cur) { + activeId = 1000 + (int)i; + break; + } } - // Prepare arrays for banner + LOG_DEBUG("messageViewModeMenu: Active thread id=%d", activeId); + + // Build banner static std::vector options; static std::vector optionIds; options.clear(); @@ -529,9 +558,8 @@ void menuHandler::messageViewModeMenu() for (size_t i = 0; i < labels.size(); i++) { options.push_back(labels[i].c_str()); optionIds.push_back(ids[i]); - if (ids[i] == activeId) { - initialIndex = i; - } + if (ids[i] == activeId) + initialIndex = (int)i; } BannerOverlayOptions bannerOptions; @@ -541,20 +569,24 @@ void menuHandler::messageViewModeMenu() bannerOptions.optionsCount = options.size(); bannerOptions.InitialSelected = initialIndex; - bannerOptions.bannerCallback = [](int selected) -> void { + bannerOptions.bannerCallback = [=](int selected) -> void { + LOG_DEBUG("messageViewModeMenu: selected=%d", selected); if (selected == -1) { menuHandler::menuQueue = menuHandler::message_response_menu; screen->runNow(); } else if (selected == -2) { graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::ALL); - } else if (selected >= 100) { + } else if (isChannelSel(selected)) { int ch = selected - 100; graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::CHANNEL, ch); - } else { - graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::DIRECT, -1, selected); + } else if (selected >= 1000) { + int idx = selected - 1000; + if (idx >= 0 && (size_t)idx < idToPeer.size()) { + uint32_t peer = idToPeer[idx]; + graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::DIRECT, -1, peer); + } } }; - screen->showOverlayBanner(bannerOptions); } diff --git a/src/graphics/draw/MessageRenderer.cpp b/src/graphics/draw/MessageRenderer.cpp index 9bf10095d..e5db6988c 100644 --- a/src/graphics/draw/MessageRenderer.cpp +++ b/src/graphics/draw/MessageRenderer.cpp @@ -199,9 +199,18 @@ static uint32_t currentPeer = 0; static std::vector seenChannels; static std::vector seenPeers; +// Public helper so menus / store can clear stale registries +void clearThreadRegistries() +{ + LOG_DEBUG("[MessageRenderer] Clearing thread registries (seenChannels/seenPeers)"); + seenChannels.clear(); + seenPeers.clear(); +} + // Setter so other code can switch threads void setThreadMode(ThreadMode mode, int channel /* = -1 */, uint32_t peer /* = 0 */) { + LOG_DEBUG("[MessageRenderer] setThreadMode(mode=%d, ch=%d, peer=0x%08x)", (int)mode, channel, (unsigned int)peer); currentMode = mode; currentChannel = channel; currentPeer = peer; @@ -209,14 +218,18 @@ void setThreadMode(ThreadMode mode, int channel /* = -1 */, uint32_t peer /* = 0 // Track channels we’ve seen if (mode == ThreadMode::CHANNEL && channel >= 0) { - if (std::find(seenChannels.begin(), seenChannels.end(), channel) == seenChannels.end()) + if (std::find(seenChannels.begin(), seenChannels.end(), channel) == seenChannels.end()) { + LOG_DEBUG("[MessageRenderer] Track seen channel: %d", channel); seenChannels.push_back(channel); + } } // Track DMs we’ve seen if (mode == ThreadMode::DIRECT && peer != 0) { - if (std::find(seenPeers.begin(), seenPeers.end(), peer) == seenPeers.end()) + if (std::find(seenPeers.begin(), seenPeers.end(), peer) == seenPeers.end()) { + LOG_DEBUG("[MessageRenderer] Track seen peer: 0x%08x", (unsigned int)peer); seenPeers.push_back(peer); + } } } diff --git a/src/graphics/draw/MessageRenderer.h b/src/graphics/draw/MessageRenderer.h index 63e6bb317..e799c971c 100644 --- a/src/graphics/draw/MessageRenderer.h +++ b/src/graphics/draw/MessageRenderer.h @@ -29,6 +29,8 @@ uint32_t getThreadPeer(); const std::vector &getSeenChannels(); const std::vector &getSeenPeers(); +void clearThreadRegistries(); + // Text and emote rendering void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount); @@ -49,4 +51,4 @@ void renderMessageContent(OLEDDisplay *display, const std::vector & void resetScrollState(); } // namespace MessageRenderer -} // namespace graphics +} // namespace graphics \ No newline at end of file diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 092943271..5225d03e3 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -75,18 +75,16 @@ CannedMessageModule::CannedMessageModule() void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChannel) { - // Use the requested destination, unless it's "broadcast" and we have a previous node/channel - if (newDest == NODENUM_BROADCAST && lastDestSet) { - newDest = lastDest; - newChannel = lastChannel; - } + // 🚫 Do NOT override explicit broadcast replies + // Only reuse lastDest in LaunchRepeatDestination() + dest = newDest; channel = newChannel; + lastDest = dest; lastChannel = channel; lastDestSet = true; - // Rest of function unchanged... // Upon activation, highlight "[Select Destination]" int selectDestination = 0; for (int i = 0; i < messagesCount; ++i) { @@ -103,6 +101,8 @@ void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChan UIFrameEvent e; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; notifyObservers(&e); + + LOG_DEBUG("[CannedMessage] LaunchWithDestination dest=0x%08x ch=%d", dest, channel); } void CannedMessageModule::LaunchRepeatDestination() @@ -116,13 +116,12 @@ void CannedMessageModule::LaunchRepeatDestination() void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t newChannel) { - // Use the requested destination, unless it's "broadcast" and we have a previous node/channel - if (newDest == NODENUM_BROADCAST && lastDestSet) { - newDest = lastDest; - newChannel = lastChannel; - } + // 🚫 Do NOT override explicit broadcast replies + // Only reuse lastDest in LaunchRepeatDestination() + dest = newDest; channel = newChannel; + lastDest = dest; lastChannel = channel; lastDestSet = true; @@ -132,6 +131,8 @@ void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t UIFrameEvent e; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; notifyObservers(&e); + + LOG_DEBUG("[CannedMessage] LaunchFreetextWithDestination dest=0x%08x ch=%d", dest, channel); } static bool returnToCannedList = false;