fix(messaging): no DM UI for MeshCore repeaters & Meshtastic unmessagable nodes (#3755)#3760
Conversation
…ssagable nodes (#3755) Repeaters (MeshCore advType=2) and Meshtastic nodes with is_unmessagable set cannot receive direct messages — the firmware acks the relay/packet, which surfaced as a misleading "delivered" (✓✓). Rather than send messages that never arrive, remove the messaging UI for these nodes while keeping them first-class everywhere else: they stay listed in node/contact sidebars and still show full telemetry and other detail. MeshCore (MeshCoreDirectMessagesView): - When the selected contact is a repeater, render the contact detail panel + telemetry as before but drop the MeshCoreMessageStream entirely, replaced by a short "repeaters cannot receive direct messages" notice. - Repeaters remain in the contact sidebar (NOT filtered out). The Nodes view "›" button is already labelled "More details" and now lands on the detail-only pane, so it needs no change. - 4 new render tests: repeater stays in sidebar; composer hidden + notice shown on repeater select; composer still shown for companions; telemetry still mounts for a selected repeater. Meshtastic: - NodesTab: hide the 💬 DM button for is_unmessagable nodes (no DM entry point) — the node stays in the list with all indicators/details. - MessagesTab: reuse the existing read-only mode — effectiveReadOnly = mqttReadOnly || selectedNode.isUnmessagable — so selecting an unmessagable node hides the message log + send composer + mesh-transmit actions and shows only the per-node telemetry, while the node stays selectable in the DM list. Supersedes #3758 (which removed repeaters from the sidebar) and #3759 (which only greyed the send bar with a notice). 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 ReviewTasks
SummaryThis is a solid, well-scoped fix. The overall approach is correct: keep nodes visible in sidebars, replace the messaging UI with a details-only view when the node can't receive DMs. The implementation correctly reuses the existing Code Quality
const isSelectedRepeater = selected ? contactsByKey.get(selected)?.advType === 2 : false;The magic number
const effectiveReadOnly = mqttReadOnly || selectedNode?.isUnmessagable === true;The
{hasPermission('messages', 'read') && !node.isUnmessagable && (Checking Potential IssuesUnmessagable node still appears in Messages DM sidebar
{isSelectedRepeater ? (
<div className="meshcore-dm-no-messaging" role="note">
{t('meshcore.repeater_no_messaging', 'Repeaters cannot receive...')}
</div>
) : (
<MeshCoreMessageStream ... />
)}
<div className="meshcore-detail-pane">
<MeshCoreContactDetailPanel ... />
...
</div>The notice renders above the detail pane, which is the right visual order. The i18n / Localization
The None of the other locale files ( Test CoverageMeshCore tests (4 new in
The test for composer presence uses Meshtastic ( PerformanceNo concerns. SecurityNo concerns. No new API surface, no new data paths. The change is purely UI-gating. Minor Nits
VerdictApprove with minor suggestions. The core logic is correct, the approach is clean, and the MeshCore test coverage is solid. The main follow-up worth addressing before or after merge:
|
…nders Selecting a repeater mounts the contact-detail panel's remote-admin console, which calls getRemoteAdminCapability() on mount. The DM-view test mock omitted the contact/remote-admin actions (companion contacts never mounted the console), so the new repeater tests threw in CI with "getRemoteAdminCapability is not a function". Add safe vi.fn() stubs for the full contact + remote-admin action set. 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 ReviewTasks
SummaryThis is a well-scoped fix with clean implementation. The approach is correct: keep nodes visible in sidebars, replace the messaging UI with a details-only view when the node can't receive DMs. The core implementation is solid and the MeshCore test coverage is thorough. Code Quality
const isSelectedRepeater = selected ? contactsByKey.get(selected)?.advType === 2 : false;The value
const effectiveReadOnly = mqttReadOnly || selectedNode?.isUnmessagable === true;Good defensive use of
{hasPermission('messages', 'read') && !node.isUnmessagable && (The permission check precedes the node-level gate, which is correct ordering: cheap first, specific second. ✓ Potential IssuesNo notice for unmessagable nodes in The MeshCore path shows a This is a consistency gap between the two paths. Consider adding a brief notice similar to the MeshCore one — something parallel to the existing MQTT-bridge notice pattern at line 1340. Unmessagable nodes still appear in the DM sidebar Per the PR description this is intentional ("keep the node selectable in the DM list"), but it means a user could reach an unmessagable node conversation without ever touching the Nodes tab 💬 button. The i18n / Localization
t('meshcore.repeater_no_messaging', 'Repeaters cannot receive direct messages — showing node details only.')The Test CoverageMeshCore (4 new tests in
Note: the composer presence test (line 466) uses Meshtastic ( PerformanceNo concerns. SecurityNo concerns. No new API surface, no new data flows. Purely UI-gating over pre-existing fields. Minor Nits
VerdictApprove with two minor follow-ups:
Neither is blocking — the core logic is correct and the feature is immediately safe to merge. |
* chore(release): bump version to 4.12.0 Finalize 4.12.0 (from 4.12.0-rc2): package.json, package-lock.json, desktop/package.json, desktop/src-tauri/tauri.conf.json, helm/Chart.yaml. Promote CHANGELOG [Unreleased] → [4.12.0]. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4 * docs(4.12.0): document Automation Engine, MeshCore regions/scopes & UI additions Release-audit of every PR since v4.11.5 surfaced documentation gaps. Brings the docs site current with the 4.12.0 feature set: - New page docs/features/automation-engine.md (+ sidebar nav) for the node/ builder-based Automation Engine (#3653 et al). v-pre-wrapped so the {{ }} token syntax renders literally without breaking the VitePress build. - meshcore.md: new Regions/Scopes section (#3667 et al), channel unread indicators, heard-repeaters badge, per-message route line, byte counter, on-demand telemetry polling, Define Path editor, repeater DM-hidden behavior. - automation.md: corrected stale auto-favorite eligibility table (#3786/#3774). - maps.md: traceroute default-keyed channel routing + channel dropdowns (#3723). - map-analysis.md: rich multi-source node popups (#3692). - device.md: unmessagable-node DM-hidden behavior (#3760). Verified: vitepress build passes; tokens render literally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4 * fix(migration): make migration 104 PostgreSQL channel_database rebuild atomic Release-audit (PR #3753) flagged the tombstone-exhaustion rebuild path as a data-availability risk: it DROPs the live channel_database and RENAMEs a fresh copy into place, but the migration runner wraps each migration's statements in no transaction, so each DDL auto-commits. A crash/connection drop in the DROP→RENAME window would leave the database with no channel_database — and the migration's own idempotency check (`'channel_database'::regclass`) would then throw "relation does not exist" on the next startup, blocking recovery. Wrap the rebuild in BEGIN/COMMIT with ROLLBACK on error. PostgreSQL DDL is transactional, so any failure now rolls back to the original table intact. (Channel keys/PSKs were already copied before the drop, so this hardens the narrow availability window rather than fixing data loss. The path only fires at ≥1500 attnum tombstones — 1500+ restarts on the old migration 021 bug — so it's rare, but the blast radius warranted the fix.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Repeaters (MeshCore
advType=2) and Meshtastic nodes withis_unmessagableset cannot receive direct messages — the firmware acks the relay/packet, which surfaced as a misleading "delivered" (✓✓) for messages that never arrived. This removes the messaging UI for those nodes while keeping them first-class everywhere else: they stay listed in the node/contact sidebars and still show full telemetry and other detail.This supersedes the two earlier attempts:
The chosen behavior (confirmed with the maintainer): keep the node in the list; when selected, show its node details / telemetry in place of the conversation.
Changes
MeshCore (
MeshCoreDirectMessagesView)advType===2), drop theMeshCoreMessageStream(log + composer) and show a short notice; theMeshCoreContactDetailPanel+ telemetry/graphs still render below as before.MeshCorePage.css:.meshcore-dm-no-messagingnotice style.Meshtastic
NodesTab: hide the 💬 DM button foris_unmessagablenodes (no DM entry point); the node stays in the list with all indicators/details.MessagesTab: reuse the existing read-only mode viaeffectiveReadOnly = mqttReadOnly || selectedNode.isUnmessagable. Selecting an unmessagable node hides the message log + send composer + mesh-transmit actions and shows only the per-node telemetry, while the node stays selectable in the DM list.Issues Resolved
Fixes #3755. Supersedes #3758 and #3759.
Documentation Updates
No documentation changes needed (behavioral UI change; no documented feature/config/API surface affected).
Testing
MeshCoreDirectMessagesView.test.tsx): repeater stays in the sidebar; composer hidden + notice shown on repeater select; composer still shown for companions; telemetry still mounts for a selected repeater. File 28/28.success: true, 7562 passed, 0 failed, 0 suite failures (sibling.claude/worktrees/checkout excluded).tscclean on all changed files.is_unmessagablenode has no 💬 in the Nodes list and, when selected in Messages, shows telemetry only (no log/composer) while remaining selectable.🤖 Generated with Claude Code