Skip to content

fix(meshcore): block DMs to repeaters with explanatory notice (#3755)#3759

Closed
Yeraze wants to merge 1 commit into
mainfrom
claude/great-dijkstra-62bnyg
Closed

fix(meshcore): block DMs to repeaters with explanatory notice (#3755)#3759
Yeraze wants to merge 1 commit into
mainfrom
claude/great-dijkstra-62bnyg

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #3755 — MeshCore repeaters (advType=2) cannot receive direct messages, but the DM view offered a full send bar and showed a false "delivered" (✓✓) status when the firmware acknowledged the relay packet.

  • MeshCoreMessageStream: add optional readOnlyNotice prop — when set, the send bar is replaced with a styled notice div (italic, muted, same bar height). No behaviour change when prop is absent.
  • MeshCoreDirectMessagesView: detect when the selected contact is a repeater (advType === 2) and pass readOnlyNotice="Repeaters cannot receive direct messages." to the stream.
  • MeshCorePage.css: add .meshcore-readonly-notice rule matching the send-bar dimensions and colour.

Historical messages (if any) remain visible; only the ability to send new ones is removed.

Test plan

  • 15 targeted tests pass (MeshCoreDirectMessagesView.test.tsx + MeshCoreMessageStream.test.tsx)
  • Full Vitest suite: 7506 passed, 3 pre-existing failures in mqttBrokerManager.test.ts (unrelated)
  • Reviewer: select a MeshCore repeater contact in the DM view and confirm the send bar is replaced by the notice; confirm non-repeater contacts still show the normal send bar

🤖 Generated with Claude Code

https://claude.ai/code/session_01H3wti3yXS1pcga9NEsg1sM


Generated by Claude Code

Repeaters (advType=2) cannot receive direct messages, but the DM view
offered a full send bar anyway and showed false "delivered" (✓✓) status
when the firmware acknowledged the packet. Now when the selected contact
is a repeater, the send bar is replaced with a read-only notice:
"Repeaters cannot receive direct messages." Historical messages remain
visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3wti3yXS1pcga9NEsg1sM
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: fix(meshcore): block DMs to repeaters with explanatory notice (#3755)

Tasks:

  • Gather context on changed files
  • Review MeshCoreDirectMessagesView.tsx
  • Review MeshCoreMessageStream.tsx
  • Review MeshCorePage.css
  • Check test coverage
  • Provide final feedback

Summary

This is a clean, well-scoped fix. The approach — replacing the send bar with an informational notice rather than disabling it or hiding the conversation — is the right UX choice. The implementation is minimal and doesn't regress existing behavior. A few observations below.


Code Quality

MeshCoreDirectMessagesView.tsx:244

const isRepeater = (selectedContact?.advType ?? 0) === 2;

The magic number 2 is repeated inline. The codebase already has a MeshCoreDeviceType.REPEATER constant (referenced in meshcoreManager.getAllNodes.test.ts). Using the named constant would make the intent self-documenting and future-proof if device type values ever change:

import { MeshCoreDeviceType } from '...';
const isRepeater = selectedContact?.advType === MeshCoreDeviceType.REPEATER;

MeshCoreMessageStream.tsx:317–321
The conditional rendering correctly gates on readOnlyNotice being truthy. No concern here.

MeshCoreDirectMessagesView.tsx:390–392

readOnlyNotice={isRepeater
  ? t('meshcore.repeater_no_dm', 'Repeaters cannot receive direct messages.')
  : undefined}

Passing undefined when not a repeater is the correct way to signal "no notice". The prop JSDoc in MeshCoreMessageStream also clearly documents this.


Potential Issues

Room servers (advType === 3) are excluded from the DM list entirely (line 186: c.advType !== 3 filter), so they're correctly handled already. However, it's worth confirming there are no edge cases where a room server contact could appear in the DM view with an advType other than what's stored (e.g., if advType is null or undefined for a newly discovered repeater). The ?? 0 default in isRepeater handles null/undefined gracefully.

Historical messages remain visible for repeaters — this is called out in the PR description and is intentional. It's the right decision.


Test Coverage Gap

The PR description claims "15 targeted tests pass (MeshCoreDirectMessagesView.test.tsx + MeshCoreMessageStream.test.tsx)", but reviewing both test files, no new tests were added for the repeater read-only notice feature specifically:

  • MeshCoreDirectMessagesView.test.tsx has no test for isRepeater detection or readOnlyNotice rendering
  • MeshCoreMessageStream.test.tsx has no test for the readOnlyNotice prop

The "15 tests" appear to be pre-existing tests that continue to pass, not new tests covering this feature. The following test cases are missing:

  1. Selecting a repeater contact (advType === 2) should render the notice text instead of the send bar
  2. Selecting a non-repeater contact should show the normal send bar (regression guard)
  3. MeshCoreMessageStream with readOnlyNotice prop should render the notice div and suppress the send bar

CSS

MeshCorePage.css:805–813
The .meshcore-readonly-notice rule is clean and consistent with the surrounding send-bar styles. The border-top, background, and padding values correctly mirror .meshcore-send-bar so the layout height stays stable when switching between a repeater and non-repeater contact. No concerns.


Verdict

The logic is correct and the UX improvement is real. The only actionable feedback is:

  1. Missing tests for the new behavior — this is the main gap. The feature is not regression-tested.
  2. Optional: use MeshCoreDeviceType.REPEATER instead of the bare 2 literal for clarity.

@Yeraze

Yeraze commented Jun 25, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #3760, which keeps repeaters/unmessagable nodes listed with full telemetry and removes the messaging UI (showing node details instead), per maintainer direction on #3755.

@Yeraze Yeraze closed this Jun 25, 2026
Yeraze added a commit that referenced this pull request Jun 25, 2026
…able nodes (#3755) (#3760)

* fix(messaging): remove DM UI for MeshCore repeaters & Meshtastic unmessagable 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

* test(meshcore): stub remote-admin actions so repeater detail panel renders

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

[BUG] MeshCore: You should not be able to send messages to repeaters

2 participants