Skip to content

feat: align MUI message status wording#336

Draft
RCGV1 wants to merge 16 commits into
meshtastic:masterfrom
RCGV1:codex/mui-message-status
Draft

feat: align MUI message status wording#336
RCGV1 wants to merge 16 commits into
meshtastic:masterfrom
RCGV1:codex/mui-message-status

Conversation

@RCGV1

@RCGV1 RCGV1 commented Jul 7, 2026

Copy link
Copy Markdown
Member

Issues

Summary

  • Align MUI/T-Deck inline message status labels with the cross-platform wording in the design issue.
  • Make inline status text smaller and reduce spacing between sent messages and their status labels.
  • Add a tap-to-detail status dialog for failed/warning sent-message states.
  • Show Retry only for retryable statuses, and route retries through the existing send path.
  • Handle additional routing, radio/interface, duty-cycle, rate-limit, app-response, authorization, and admin key/session states.
  • Add bounds checks for oversized payload display and safer status persistence.

UI Evidence

MUI message status states

Validation

  • git diff --check
  • Focused MessageStatus doctests: 6 test cases, 80 assertions.
  • macOS CMake test build and ctest: 1/1 tests passed.
  • t-deck-tft firmware build passed.
  • t-deck-tft app image flashed and verified on the connected T-Deck.
  • GitHub checks pass, including CodeRabbit, build-and-test, Trunk, and PR title validation.

Status

Draft until interactive on-device UI screenshots are captured and attached.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a shared MessageStatus model and threads it through log storage, pending delivery tracking, TFT message rendering, and request-id handling. Also updates log payload bounds, in-place log updates, and build wiring for protobuf and Portduino sources.

Changes

Message status tracking

Layer / File(s) Summary
MessageStatus contract and presentation
include/graphics/common/MessageStatus.h, source/graphics/common/MessageStatus.cpp, tests/test_MessageStatus.cpp
Defines State/Tone, presentation mapping, persisted-state conversion, and unit tests for the status model.
Log storage bounds and in-place update
include/util/LogMessage.h, include/util/LogRotate.h, source/util/LogRotate.cpp, tests/test_LogMessage.cpp
Adds payload-bound helpers, log-entry positions, and in-place update support with boundary tests.
Pending text-message tracking
include/graphics/common/ViewController.h, source/graphics/common/ViewController.cpp
Adds pending-status storage and APIs, records log positions for sent text messages, and persists status updates back to logs.
ResponseHandler requestId tracking
include/graphics/common/ResponseHandler.h, source/graphics/common/ResponseHandler.cpp, tests/test_ResponseHandler.cpp
Stores requestId on pending requests and verifies it survives lookup and removal paths.
TFT message status rendering
include/graphics/view/TFT/TFTView_320x240.h, source/graphics/TFT/TFTView_320x240.cpp
Switches message delivery handling to MessageStatus::State, restores persisted status, and updates the UI presentation logic.
Build wiring
cmake/protobuf.cmake, cmake/Portduino.cmake
Updates the protobuf fetch tag and adds Portduino sources to the target.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Poem

A bunny hopped through logs today,
With statuses in a brighter way.
From pending hops to final hue,
The burrow knows what messages do. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main UI change around message status wording and is concise and specific.

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congratulations for your first pull request

@RCGV1 RCGV1 changed the title Align MUI message status wording feat: align MUI message status wording Jul 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/util/LogRotate.cpp (1)

104-119: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Only publish EntryPosition after the append actually succeeds.

write(entry, &position) currently records an offset and returns true even if _fs.open() or serialize() fails. Downstream pending-status tracking treats that as a persisted log entry, so later status updates can target a non-existent entry.

Proposed fix
-    if (position) {
-        position->logNum = currentLogWrite;
-        position->offset = currentSize;
-    }
-
     // elegant way to let the logentry do its work it knows best and pass just a temporary function for writing
     File file = _fs.open(currentLogName, FILE_APPEND);
-    entry.serialize([&file](const uint8_t *buf, size_t size) { return file.write(buf, size); });
+    if (!file) {
+        ILOG_WARN("failed to open %s for append", currentLogName.c_str());
+        return false;
+    }
+
+    const EntryPosition writtenPosition{currentLogWrite, currentSize};
+    const size_t written = entry.serialize([&file](const uint8_t *buf, size_t size) { return file.write(buf, size); });
     file.close();
+    if (written != entry.size()) {
+        ILOG_WARN("failed to append complete log entry to %s", currentLogName.c_str());
+        return false;
+    }
+
+    if (position)
+        *position = writtenPosition;
 
     currentSize += entry.size();
     totalSize += entry.size();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/util/LogRotate.cpp` around lines 104 - 119, `LogRotate::write` is
marking an `EntryPosition` as persisted before the append has actually
completed. Move the `position->logNum` and `position->offset` update so it only
happens after `_fs.open()` and `entry.serialize(...)` succeed, and make the
function return false on any write/open failure instead of always returning
true. Keep the fix localized to `LogRotate::write`, using the existing
`position`, `currentLogWrite`, `currentSize`, and `entry.serialize` flow.
🧹 Nitpick comments (4)
include/graphics/common/MessageStatus.h (1)

50-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider consolidating the two inlineStateForLogStatus overloads.

The two-argument overload simply forwards to the three-argument one with persistedState = 0. A default parameter would remove the duplicate declaration/definition pair.

♻️ Suggested consolidation
-std::optional<State> inlineStateForLogStatus(LogMessage::MsgStatus status, bool livePending);
-std::optional<State> inlineStateForLogStatus(LogMessage::MsgStatus status, uint32_t persistedState, bool livePending);
+std::optional<State> inlineStateForLogStatus(LogMessage::MsgStatus status, uint32_t persistedState, bool livePending, uint32_t defaultPersistedState = 0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/graphics/common/MessageStatus.h` around lines 50 - 51, The two
inlineStateForLogStatus overloads are redundant because the two-argument version
only forwards to the three-argument version with persistedState set to 0.
Consolidate them by keeping a single inlineStateForLogStatus
declaration/definition with a default parameter for persistedState, and update
any callers or declarations in MessageStatus to use that unified signature.
source/graphics/common/MessageStatus.cpp (2)

25-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Low byte capacity for persisted state is implicit and unchecked.

persistedLogStateMask reserves only the low 8 bits (0xff) for the state value (persistedLogStateMagic | (state + 1)), capping the encodable state count at 254. With 18 states today there's plenty of headroom, but nothing guards against silently overflowing into the magic bits if State grows substantially over time.

Also applies to: 121-124

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/graphics/common/MessageStatus.cpp` around lines 25 - 26, The persisted
log state encoding in MessageStatus uses only the low 8 bits for the state
value, but this limit is currently implicit and unchecked. Update the
encoding/decoding logic around persistedLogStateMagic and persistedLogStateMask
(including the matching code later in the file) to explicitly validate that the
State value fits within the available bits before writing it, and reject or
assert on overflow instead of silently corrupting the magic bits. Keep the
mask/magic scheme intact, but add a clear bound check tied to the State enum
size so future state growth cannot break persisted data.

121-136: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Persisted-state bounds check is coupled to enum declaration order.

stateFromPersistedLogState validates the decoded value against static_cast<uint32_t>(State::AdminKeyNotAuthorized) + 1, implicitly assuming AdminKeyNotAuthorized is the last (highest-ordinal) enumerator in State. If a future State value is inserted anywhere but the very end, or the list is reordered, this bound silently becomes wrong (either rejecting valid new states or accepting invalid ones) with no compiler diagnostic.

Consider adding an explicit sentinel (e.g. State::Count as the last enumerator, never used as a real state) and bounding against that instead, so correctness doesn't depend on remembering to update this constant whenever State changes.

♻️ Suggested approach
 enum class State {
     ...
     AdminKeyNotAuthorized,
+    Count, // sentinel — always keep last
 };
-    if (value == 0 || value > static_cast<uint32_t>(State::AdminKeyNotAuthorized) + 1)
+    if (value == 0 || value > static_cast<uint32_t>(State::Count))
         return std::nullopt;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@source/graphics/common/MessageStatus.cpp` around lines 121 - 136,
`stateFromPersistedLogState` currently validates persisted values against
`State::AdminKeyNotAuthorized`, which ties the check to the enum’s current
ordering. Add an explicit terminal sentinel such as `State::Count` in the
`State` enum and use it as the upper bound in the persisted-state range check,
so `persistedLogState` and `stateFromPersistedLogState` stay correct even if
`State` is extended or reordered.
include/util/LogRotate.h (1)

38-40: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Consider collapsing write(const ILogEntry&, EntryPosition*) into a single overload with a defaulted position parameter. This would trim the API without changing behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@include/util/LogRotate.h` around lines 38 - 40, The LogRotate API exposes two
write-related overloads where one can be simplified: collapse write(const
ILogEntry&, EntryPosition*) into a single write overload with a defaulted
position parameter, and update any callers or declarations that reference the
separate pointer-based form. Keep the existing behavior intact in LogRotate by
preserving the optional position output while removing the redundant overload;
use the write and update declarations in ILogEntry/LogRotate as the anchor
points for the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@source/graphics/common/ViewController.cpp`:
- Around line 477-484: Replace the compile-time-only payload check in the text
message send path with a runtime guard in the send/log flow around send() and
LogMessageEnv. In ViewController::sendTextMessage (or the nearby message
handling code), reject oversized text before calling send and before
constructing the log entry, and skip logging or truncate as appropriate so
persisted LogMessageEnv data never exceeds the deserialization limit. Keep the
existing fitsLogMessagePayload check as a debug aid if desired, but add a real
conditional that enforces the size at runtime.

In `@source/graphics/TFT/TFTView_320x240.cpp`:
- Around line 4499-4501: In the oversized-payload branch of TFTView_320x240.cpp,
the code still passes the full msg into addMessage even though
fitsLogMessagePayload(msgLen) has already failed. Update this path so addMessage
receives a bounded/truncated display string, or avoid calling addMessage at all
and only trigger handleTextMessageResponse for the MessageTooLarge case. Use the
existing addMessage and handleTextMessageResponse flow to keep the fix
localized.

---

Outside diff comments:
In `@source/util/LogRotate.cpp`:
- Around line 104-119: `LogRotate::write` is marking an `EntryPosition` as
persisted before the append has actually completed. Move the `position->logNum`
and `position->offset` update so it only happens after `_fs.open()` and
`entry.serialize(...)` succeed, and make the function return false on any
write/open failure instead of always returning true. Keep the fix localized to
`LogRotate::write`, using the existing `position`, `currentLogWrite`,
`currentSize`, and `entry.serialize` flow.

---

Nitpick comments:
In `@include/graphics/common/MessageStatus.h`:
- Around line 50-51: The two inlineStateForLogStatus overloads are redundant
because the two-argument version only forwards to the three-argument version
with persistedState set to 0. Consolidate them by keeping a single
inlineStateForLogStatus declaration/definition with a default parameter for
persistedState, and update any callers or declarations in MessageStatus to use
that unified signature.

In `@include/util/LogRotate.h`:
- Around line 38-40: The LogRotate API exposes two write-related overloads where
one can be simplified: collapse write(const ILogEntry&, EntryPosition*) into a
single write overload with a defaulted position parameter, and update any
callers or declarations that reference the separate pointer-based form. Keep the
existing behavior intact in LogRotate by preserving the optional position output
while removing the redundant overload; use the write and update declarations in
ILogEntry/LogRotate as the anchor points for the change.

In `@source/graphics/common/MessageStatus.cpp`:
- Around line 25-26: The persisted log state encoding in MessageStatus uses only
the low 8 bits for the state value, but this limit is currently implicit and
unchecked. Update the encoding/decoding logic around persistedLogStateMagic and
persistedLogStateMask (including the matching code later in the file) to
explicitly validate that the State value fits within the available bits before
writing it, and reject or assert on overflow instead of silently corrupting the
magic bits. Keep the mask/magic scheme intact, but add a clear bound check tied
to the State enum size so future state growth cannot break persisted data.
- Around line 121-136: `stateFromPersistedLogState` currently validates
persisted values against `State::AdminKeyNotAuthorized`, which ties the check to
the enum’s current ordering. Add an explicit terminal sentinel such as
`State::Count` in the `State` enum and use it as the upper bound in the
persisted-state range check, so `persistedLogState` and
`stateFromPersistedLogState` stay correct even if `State` is extended or
reordered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1b84f703-c83f-449a-9b44-a11515ede6e5

📥 Commits

Reviewing files that changed from the base of the PR and between 1c45ebc and 7ec815b.

⛔ Files ignored due to path filters (19)
  • artifacts/message-status-evidence/admin-key-not-authorized.png is excluded by !**/*.png
  • artifacts/message-status-evidence/admin-session-expired.png is excluded by !**/*.png
  • artifacts/message-status-evidence/all-message-status-states.png is excluded by !**/*.png
  • artifacts/message-status-evidence/channel-implicit-ack.png is excluded by !**/*.png
  • artifacts/message-status-evidence/dm-implicit-ack.png is excluded by !**/*.png
  • artifacts/message-status-evidence/duty-cycle-limit.png is excluded by !**/*.png
  • artifacts/message-status-evidence/explicit-ack.png is excluded by !**/*.png
  • artifacts/message-status-evidence/generic-encrypted-send-failure.png is excluded by !**/*.png
  • artifacts/message-status-evidence/invalid-request.png is excluded by !**/*.png
  • artifacts/message-status-evidence/message-too-large.png is excluded by !**/*.png
  • artifacts/message-status-evidence/no-ack.png is excluded by !**/*.png
  • artifacts/message-status-evidence/no-app-response.png is excluded by !**/*.png
  • artifacts/message-status-evidence/no-channel.png is excluded by !**/*.png
  • artifacts/message-status-evidence/no-radio-interface.png is excluded by !**/*.png
  • artifacts/message-status-evidence/not-authorized.png is excluded by !**/*.png
  • artifacts/message-status-evidence/rate-limited.png is excluded by !**/*.png
  • artifacts/message-status-evidence/recipient-key-unavailable.png is excluded by !**/*.png
  • artifacts/message-status-evidence/recipient-needs-your-key.png is excluded by !**/*.png
  • artifacts/message-status-evidence/sending.png is excluded by !**/*.png
📒 Files selected for processing (15)
  • cmake/protobuf.cmake
  • include/graphics/common/MessageStatus.h
  • include/graphics/common/ResponseHandler.h
  • include/graphics/common/ViewController.h
  • include/graphics/view/TFT/TFTView_320x240.h
  • include/util/LogMessage.h
  • include/util/LogRotate.h
  • source/graphics/TFT/TFTView_320x240.cpp
  • source/graphics/common/MessageStatus.cpp
  • source/graphics/common/ResponseHandler.cpp
  • source/graphics/common/ViewController.cpp
  • source/util/LogRotate.cpp
  • tests/test_LogMessage.cpp
  • tests/test_MessageStatus.cpp
  • tests/test_ResponseHandler.cpp

Comment thread source/graphics/common/ViewController.cpp
Comment thread source/graphics/TFT/TFTView_320x240.cpp Outdated
@RCGV1 RCGV1 marked this pull request as draft July 7, 2026 15:45
@RCGV1

RCGV1 commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant