Skip to content

fix(meshcore): exclude repeaters from DM peer list; disable send for repeater selection (#3755)#3758

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

fix(meshcore): exclude repeaters from DM peer list; disable send for repeater selection (#3755)#3758
Yeraze wants to merge 1 commit into
mainfrom
claude/great-dijkstra-b9pam4

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #3755 — repeaters (advType=2) cannot receive direct messages, but were showing up in the DM contact sidebar and leaving the send bar enabled. This produced misleading "delivered" indicators for messages that were never actually received.

  • Exclude repeater contacts from the DM peer sidebar list, matching the existing room-server (advType=3) exclusion.
  • Disable the message compose input when the selected peer is a repeater — defence-in-depth for navigation via the nodes-view "›" button, which can reach a repeater even if it isn't in the sidebar list.
  • Two regression tests: repeater absent from sidebar; send input disabled when a repeater is pre-selected via initialSelectedContact.
  • Expanded makeActions() mock in the DM view tests to cover remote-admin actions (getRemoteAdminCapability etc.) so repeater contact-detail panels render cleanly in the JSDOM environment.

Test plan

  • New regression tests pass: repeater excluded from DM list; send input disabled for repeater selection
  • Full Vitest suite: 7508 passed, 3 pre-existing failures in mqttBrokerManager.test.ts (unrelated)
  • Reviewer: confirm a repeater contact does not appear in the DM sidebar, and navigating to it via "›" in the nodes view greyed-out the send bar

🤖 Generated with Claude Code

https://claude.ai/code/session_01TCT8z33322WjjBVSqSXhb5


Generated by Claude Code

…repeater selection (#3755)

Repeaters (advType=2) cannot receive direct messages, but were included
in the DM contact-list sidebar alongside companion nodes. Selecting a
repeater via the nodes-view "›" button also left the send bar enabled,
producing false "delivered" indicators for messages that were never received.

- Exclude advType=2 contacts from the contact-seeded DM peer set, matching
  the existing room-server (advType=3) exclusion.
- Disable the message compose input when the selected DM peer is a repeater,
  as defence-in-depth for navigation from the nodes view.
- Two regression tests: repeater absent from sidebar; send input disabled
  when repeater is selected via initialSelectedContact.
- Expanded makeActions() mock in the DM view test to cover remote-admin
  actions (getRemoteAdminCapability etc.) so repeater contact-detail
  panels render cleanly in the JSDOM test environment.

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

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review

  • Gather context and read changed files
  • Review implementation changes
  • Review test coverage
  • Post final feedback

Summary

This is a small, focused bug fix that correctly handles repeater nodes (advType=2) in the DM view. The implementation is clean and the approach is sound. Below are observations by category.


Code Quality

MeshCoreDirectMessagesView.tsx:187 — The exclusion condition is written correctly and mirrors the existing room-server (advType=3) exclusion pattern on the same line, making the intent clear:

if (c.publicKey && !isChannelPseudoKey(c.publicKey) && c.advType !== 3 && c.advType !== 2) peers.add(c.publicKey);

Minor style note: the line is now quite long. A future reader might appreciate extracting isExcludedFromDmList(c) or ordering the advType checks together (c.advType !== 2 && c.advType !== 3) for symmetry — but this is cosmetic and not a blocker.

MeshCoreDirectMessagesView.tsx:384 — The disabled prop addition for the send bar is correct and follows the existing pattern in MeshCoreMessageStream.tsx (which gates both the <input> and <button> on the same prop):

disabled={!connected || !canSend || contactsByKey.get(selected)?.advType === 2}

No issues here.


Potential Issues

Edge case: repeater selected via message history — The sidebar exclusion in dmPeers prevents a repeater from appearing as a contact-list entry, but the dmPeers computation also sweeps messages to find peers with an existing DM thread. If a repeater key previously appeared in a toPublicKey / fromPublicKey field (e.g. from an old bug or from manual testing), it would still get added to peers via the messages loop (lines 162–181), before the contacts-based exclusion at line 186. The existing send-bar disabled check at line 384 provides defence-in-depth, but the peer would still appear in the sidebar.

You may want to apply the same filter after the messages sweep, e.g.:

// After building peers from messages, drop repeaters and room servers.
for (const key of Array.from(peers)) {
  const c = contactsByKey.get(key);
  if (c && (c.advType === 2 || c.advType === 3)) peers.delete(key);
}

This is a pre-existing gap for advType=3 as well, so it may be intentional (or the assumption is that room-server / repeater keys never appear in DM message history). Worth a comment either way.


Test Coverage

The two new tests are well-structured:

  1. excludes repeater contacts (advType=2) from the DM peer list (line 423) — correctly validates both the absence of the repeater and the presence of a non-repeater in the same render, avoiding false-positives.

  2. disables the send input when the selected contact is a repeater (advType=2) (line 452) — uses initialSelectedContact to simulate the "nodes view › button" navigation path and correctly asserts sendInput.disabled === true.

The expanded makeActions() mock at lines 92–97 (adding getRemoteAdminCapability, forgetRemoteCredential, getRemoteStatus, etc.) is a necessary and correct fix that prevents test failures when the MeshCoreContactDetailPanel tries to render a repeater's remote-admin section.

One minor gap: there is no test asserting that a repeater arriving via message history (not contacts) is also hidden — but as noted above, that scenario may be considered out of scope for this fix.


Security / Performance

No security concerns. No performance concerns — the advType check is O(1) per contact and the useMemo dependency array already includes contacts.


Verdict

Looks good to merge. The fix is minimal, correct, and follows the established pattern. The edge-case around repeaters appearing via message history is worth a follow-up issue but is not a regression introduced here.

@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