fix(meshcore): MQTT unread dot + readability + byte-accurate message counter#3790
Conversation
…/hop text readability (#3787) Two fixes for issue #3787: 1. MQTT notification dot: The unread-count badge was counting MQTT-relayed messages even when showMqttMessages=false, causing a phantom dot. Added excludeMqtt query param to /api/messages/unread-counts, threaded the showMqttMessages toggle from UIContext through MessagingContext and useUnreadCounts hook. Provider order in App.tsx fixed so UIProvider wraps MessagingProvider (UIContext must be available inside Messaging). 2. Text readability: Timestamp and hop-count metadata opacity bumped from 0.7 to 0.85; received-message timestamps use --ctp-subtext1 instead of the doubly-muted --ctp-subtext0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TpTiMaXqnhkW6TqNxWGMg5
…ts (#3782) Adds a live byte counter below the MeshCore message input that counts UTF-8 encoded bytes (not characters) to accurately reflect what the firmware enforces. Limits are context-aware: - Channel message: 130 bytes - Channel message with scope: 120 bytes (scope header consumes ~10 bytes) - Direct message: 150 bytes Counter appears only when the draft is non-empty. Color shifts to yellow at 90% of the limit and red when over. Send button is disabled when the limit is exceeded. Backend validation also uses Buffer.byteLength(text, 'utf8') with the same per-context limits instead of the previous character-count heuristic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TpTiMaXqnhkW6TqNxWGMg5
|
Claude finished @Yeraze's task —— View job PR Review — fix(meshcore): MQTT unread dot + readability + byte-accurate message counterOverall: Well-structured PR with two clear problem/solution pairs and good multi-database awareness. A few points worth attention: Bug #3787 — MQTT unread dot✅ What's good
|
| Area | Status |
|---|---|
| Provider ordering fix | ✅ Correct |
excludeMqtt backend filter |
✅ Correct for Postgres/MySQL; verify SQLite routing |
| DM unread counts | |
| Byte counter — frontend | ✅ Correct; minor allocation note |
| Byte counter — backend | ✅ Correct |
| Scope limit edge case | '' |
| Magic numbers in consumer | |
| Test coverage | excludeMqtt param or provider ordering |
The core logic is sound. The main asks before merging are: (1) confirm the SQLite path actually reaches the Drizzle code, and (2) add at least one test for the excludeMqtt query parameter.
|
Reviewed while untangling the overlapping #3787 PRs (closed the redundant #3789; #3774 stays on #3786). This PR is the keeper for #3787 + #3782 — the approach is right and CI is green. One concrete gap worth addressing before merge: Unread filtering covers the sidebar dot but not the per-channel badge. There are two unread-count paths:
Net effect: with "Show MQTT/Bridge Messages" unchecked, the sidebar dot correctly stays dark for MQTT-only traffic, but a channel's numeric badge can still count those hidden MQTT messages — an inconsistent dot-vs-badge state. Suggest threading the same Other than that: provider reorder is safe (UIContext doesn't consume MessagingContext), readability bumps look good, and the #3782 byte-counter is consistent client+server with the test updated. Minor process nit: this bundles two issues (#3787 + #3782) in one PR. |
…le (#3787) The dedicated unread query already applies excludeMqtt, fixing the sidebar dot. But the per-channel unread badges in ChannelsTab are fed by the /poll aggregate, which is not filtered — so with MQTT hidden the dot went dark while a channel badge could still count MQTT-only traffic. Source the badge counts from the already-filtered unread query (unreadCountsData) instead of the raw poll payload, bridged into the memoized poll callback via a ref. Both unread surfaces now agree. Adds a NotificationsRepository test asserting excludeMqtt drops viaMqtt rows from the channel unread count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
|
Claude finished @Yeraze's task —— View job PR Review — fix(meshcore): MQTT unread dot + readability + byte-accurate message counterTasks:
Overall AssessmentThe core logic is sound and well-structured. Several issues raised in earlier reviews have been addressed — the ref-based bridge for the poll path ( A few items still worth attention before merge: Bug #3787 — MQTT unread dot / per-channel badge✅ Resolved
|
| Area | Status |
|---|---|
SQLite excludeMqtt routing |
✅ Confirmed working |
| Provider ordering fix | ✅ Correct |
| Per-channel badge poll fallback | |
| DM unread counts | |
Backend excludeMqtt filter |
✅ Correct (all backends) |
Repository test for excludeMqtt |
✅ Added |
HTTP route test for excludeMqtt param |
|
| Byte counter client/server agreement | ✅ Correct |
| Magic numbers in consumer components | |
Scope edge case ('' override) |
|
| Readability CSS/token bumps | ✅ Clean |
Items 1 and 3 are the only ones that affect observable behavior. The rest are maintainability/documentation gaps. The implementation is solid enough to merge with those acknowledged.
|
Addressed the gap I flagged above in The per-channel unread badge now also respects "Show MQTT/Bridge Messages": instead of reading the unfiltered Added a Monitoring CI; will merge once green. |
…oser (#3782) The MeshCore composer counter reimplemented UTF-8 byte counting, the 90%/100% warning thresholds, the N/M display format, and the warning/over colors inline — all of which already exist in src/utils/text.ts (getUtf8ByteLength + formatByteCount) and the shared .byte-counter* CSS used by the Meshtastic channel and DM composers. Reuse those helpers and the shared color classes. Per-context limits stay consumer-specific (Meshtastic 200 default; MeshCore channel 130 / scoped 120 / DM 150, passed via maxBytes). Only the layout is overridden — MeshCore shows the counter as a block under the input+button row rather than the absolute overlay the Meshtastic textarea uses. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
|
Claude finished @Yeraze's task —— View job PR Review — fix(meshcore): MQTT unread dot + readability + byte-accurate message counterTasks:
Overall AssessmentThis is a solid PR with two well-scoped fixes. Previous automated reviews were thorough and Yeraze's follow-up commit ( Bug #3787 — MQTT Unread Filtering✅ What's working
|
| Area | Status |
|---|---|
SQLite excludeMqtt routing |
✅ Confirmed working via sync path |
Provider ordering (UIProvider → MessagingProvider) |
✅ Correct |
| Per-channel badge filtering | ✅ Fixed via unreadCountsDataRef |
| Sidebar dot filtering | ✅ Fixed |
| Backend filter (all 3 DBs) | ✅ Correct |
Repository test for excludeMqtt |
✅ Added and well-scoped |
| Initial-load badge flash (MQTT-inflated) | |
| DM unread counts unfiltered | |
HTTP route test for excludeMqtt param |
|
| Byte counter (client/server agreement) | ✅ Correct |
TextEncoder re-instantiation |
|
| Magic numbers in view components | |
Scope edge-case ('' override) |
|
| Readability CSS/token changes | ✅ Clean |
The implementation is correct and ready to merge. The remaining items are either cosmetic (initial-load flash), documentation (DM exclusion comment), or maintainability concerns that can be addressed in follow-up issues rather than blocking this PR.
Fixes #3787 and closes #3782.
Summary
Bug #3787 — MQTT notification dot persists when MQTT messages are hidden
Root cause:
/api/messages/unread-countscounted all unread messages regardless of theshowMqttMessagestoggle, so even when MQTT messages were hidden from the UI, they still contributed to the unread badge.Fix:
excludeMqttquery parameter to/api/messages/unread-countsMessagingContextnow readsshowMqttMessagesfromUIContextand passesexcludeMqtt: !showMqttMessagestouseUnreadCountsOR (viaMqtt IS NULL OR viaMqtt = false)— includes older records where the column didn't exist yetApp.tsx:UIProvidernow wrapsMessagingProvidersoUIContextis available insideMessagingContextAlso in #3787 — timestamp/hop-count text readability:
0.7→0.85--ctp-subtext0(doubly muted) →--ctp-subtext10.7→0.85Feature #3782 — Byte-accurate MeshCore message counter
Adds a live counter below the MeshCore send bar that shows
<bytes used> / <limit>, tracking UTF-8 encoded bytes to match what the firmware actually enforces (multi-byte emoji/CJK characters count more than 1).Per-context limits:
Counter appears only when the draft is non-empty. Color is neutral → yellow at 90% → red when over limit. Send button disabled when over limit. Backend updated to use
Buffer.byteLength(text, 'utf8')with the same per-context limits.Test plan
viaMqttcolumn, NULL values) show in unread count normally🤖 Generated with Claude Code
https://claude.ai/code/session_01TpTiMaXqnhkW6TqNxWGMg5
Generated by Claude Code