From 2e958821f928d049f3242e1d3996fab6a8f8346b Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 9 Aug 2026 19:30:37 -0700 Subject: [PATCH] fix(BaseUI): suppress new-message pop-up while a menu is on screen (#11373) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Menus share the single global banner slot with notification pop-ups (Screen::showOverlayBanner overwrites message, options, and callback unconditionally), so a "New Message" banner arriving mid-menu destroyed the open menu and stole its input. Add NotificationRenderer::isMenuShowing() — true when the active overlay is interactive (a menu with options, or any picker/keyboard/pairing-PIN type) rather than a plain text banner — and skip the new-message banner in handleNewMessage() while such an overlay is up. Screen wake and hasUnreadMessage behavior are unchanged, and a new message can still replace an earlier plain banner. Claude-Session: https://claude.ai/code/session_01PSAouemtAihV5P87AgCadu Co-authored-by: Claude Co-authored-by: Jason P --- src/graphics/draw/MessageRenderer.cpp | 6 +++++- src/graphics/draw/NotificationRenderer.cpp | 8 ++++++++ src/graphics/draw/NotificationRenderer.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/graphics/draw/MessageRenderer.cpp b/src/graphics/draw/MessageRenderer.cpp index a6ae217e7..05a283b8f 100644 --- a/src/graphics/draw/MessageRenderer.cpp +++ b/src/graphics/draw/MessageRenderer.cpp @@ -14,6 +14,7 @@ #include "graphics/TFTColorRegions.h" #include "graphics/TFTPalette.h" #include "graphics/TimeFormatters.h" +#include "graphics/draw/NotificationRenderer.h" #include "graphics/emotes.h" #include "main.h" #include "meshUtils.h" @@ -1128,6 +1129,9 @@ void handleNewMessage(OLEDDisplay *display, const StoredMessage &sm, const mesht if (packet.from != 0) { hasUnreadMessage = true; const bool suppressBanner = cannedMessageModule && cannedMessageModule->isFreeTextActive(); + // Don't let the pop-up clobber a menu/picker the user is interacting with; the wake below + // still happens so a message can light the screen back up. + const bool menuShowing = NotificationRenderer::isMenuShowing(); // Determine if message belongs to a muted channel bool isChannelMuted = false; @@ -1222,7 +1226,7 @@ void handleNewMessage(OLEDDisplay *display, const StoredMessage &sm, const mesht screen->setOn(true); } - if (!suppressBanner) { + if (!suppressBanner && !menuShowing) { screen->showSimpleBanner(banner, inThread ? 1000 : 3000); } } diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 33d36535b..9aa70fd81 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -1216,5 +1216,13 @@ bool NotificationRenderer::isOverlayBannerShowing() return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil); } +bool NotificationRenderer::isMenuShowing() +{ + // A menu, picker, keyboard, or pairing-PIN overlay - anything interactive, as opposed to a plain + // informational text banner (which has no options and type text_banner). Menus don't set a + // notificationType of their own, so options are the only thing distinguishing them. + return isOverlayBannerShowing() && (alertBannerOptions > 0 || current_notification_type != notificationTypeEnum::text_banner); +} + } // namespace graphics #endif diff --git a/src/graphics/draw/NotificationRenderer.h b/src/graphics/draw/NotificationRenderer.h index 360bfac3c..4febe07fc 100644 --- a/src/graphics/draw/NotificationRenderer.h +++ b/src/graphics/draw/NotificationRenderer.h @@ -53,6 +53,7 @@ class NotificationRenderer static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static bool isOverlayBannerShowing(); + static bool isMenuShowing(); static graphics::notificationTypeEnum current_notification_type; };