fix(mesh): route Meshtastic public-channel text to the channel thread, not DMs

Inbound Meshtastic text addressed to BROADCAST_NUM (the default public
LongFast channel, or any channel slot) was filed into a per-sender 1:1 DM
thread, so public-channel messages polluted individual people's DM chats
and appeared as if sent directly to the user.

packet_to_inbound_frame now detects `to == BROADCAST_NUM` and emits a new
synthetic RESP_MESHTASTIC_CHANNEL_TEXT frame
([channel_idx][sender_prefix(6)][text]) that the listener files under the
channel thread (contact_id = u32::MAX - idx) while still attributing the
message to its real sender. Directed text (to == our node) still routes to
the DM thread — a regression test locks that split in.

send_channel_text now sets MeshPacket.channel (field 3) so archy actually
transmits on channel 0 (public) instead of ignoring the slot. Mesh.vue keeps
the synthetic "Meshtastic !xxxx" sender id when that is the best identity
available for a stock public-channel device.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-06-30 14:33:30 -04:00
co-authored by Claude Opus 4.8
parent f392670e2a
commit 12e7990b10
5 changed files with 171 additions and 24 deletions
+8 -5
View File
@@ -802,11 +802,14 @@ function senderLabelFor(msg: MeshMessage): string | null {
if (!activeChatChannel.value && !archChannelActive.value) return null
const live = nameByContactId.value.get(msg.peer_contact_id)
if (live && !isPlaceholderName(live)) return live
if (!isPlaceholderName(msg.peer_name)) return msg.peer_name ?? null
// Sender genuinely unknown (e.g. a meshcore channel broadcast, which drops
// the sender, or a text seen before its NodeInfo) — stay honest rather than
// echoing a "Channel N" / synthetic id as if it were a person.
return 'Unknown sender'
// The sender name snapshotted on the row. For a stock public-channel device a
// synthetic "Meshtastic !xxxx" id IS the best identity we have, so keep it —
// only suppress the genuinely contentless group/placeholder labels.
const snap = msg.peer_name
if (snap && !/^Channel \d+$/.test(snap) && snap !== 'dm-via-channel' && snap !== 'Unknown') {
return snap
}
return live || 'Unknown sender'
}
// Inline contact rename in the chat header. The pencil button toggles an