feat(mesh): show sender identity on received channel messages
Received messages snapshot peer_name at receive time, so a Meshtastic text that arrived before its sender's NodeInfo was stuck showing the synthetic "Meshtastic !xxxx" id forever, and channel/group bubbles showed no sender at all. Add a per-bubble sender label for received messages in multi-sender views (mesh + Archipelago channels), resolved LIVE from the peer table so it always shows the current archy identity (e.g. "Arch Optiplex") the moment NodeInfo is learned. Falls back to "Unknown sender" rather than echoing a Channel/synthetic placeholder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a57ae388ec
commit
f392670e2a
@ -768,6 +768,47 @@ function mergedUnreadCount(mp: MergedPeer): number {
|
||||
return total
|
||||
}
|
||||
|
||||
// Live contact_id -> friendly display name, rebuilt from the current peer
|
||||
// table. Received messages snapshot a `peer_name` at receive time, so a text
|
||||
// that arrived before its sender's NodeInfo is stuck showing a synthetic
|
||||
// "Meshtastic !xxxx" id forever. Resolving the name live here means the bubble
|
||||
// always shows the current archy identity (e.g. "Arch Optiplex") the moment it
|
||||
// is learned — in every view, without rewriting stored rows.
|
||||
const nameByContactId = computed<Map<number, string>>(() => {
|
||||
const out = new Map<number, string>()
|
||||
for (const mp of mergedPeers.value) {
|
||||
for (const cid of mp.contact_ids) out.set(cid, mp.display_name)
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
||||
// A generic, non-identifying placeholder the backend stamps before the real
|
||||
// sender is known. We never want these to win over a live archy name.
|
||||
function isPlaceholderName(name: string | null | undefined): boolean {
|
||||
if (!name) return true
|
||||
return /^Meshtastic !?[0-9a-f]{1,8}$/i.test(name)
|
||||
|| /^Channel \d+$/.test(name)
|
||||
|| /^Node #/.test(name)
|
||||
|| name === 'dm-via-channel'
|
||||
|| name === 'Unknown'
|
||||
}
|
||||
|
||||
// Sender label shown above a RECEIVED bubble. Only meaningful in multi-sender
|
||||
// views (mesh channels + the Archipelago channel) where the header alone can't
|
||||
// attribute each message; 1:1 DM threads already name the peer in the header.
|
||||
// Prefers the live archy identity over the snapshotted name.
|
||||
function senderLabelFor(msg: MeshMessage): string | null {
|
||||
if (msg.direction !== 'received') return 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'
|
||||
}
|
||||
|
||||
// Inline contact rename in the chat header. The pencil button toggles an
|
||||
// input bound to renameDraft; commit fires mesh.contacts-save keyed by
|
||||
// DID (or pubkey hex as fallback) so the alias propagates everywhere
|
||||
@ -1689,6 +1730,7 @@ function isImageMime(mime?: string): boolean {
|
||||
:class="msg.direction"
|
||||
>
|
||||
<div class="mesh-chat-bubble" :class="[msg.direction, msg.message_type ? 'typed-' + msg.message_type : '', { 'menu-open': actionMenuForId === msg.id }]">
|
||||
<div v-if="senderLabelFor(msg)" class="mesh-chat-bubble-sender">{{ senderLabelFor(msg) }}</div>
|
||||
<div v-if="replyTargetPreview(msg)" class="mesh-chat-reply-quote">
|
||||
↳ {{ replyTargetPreview(msg) }}
|
||||
</div>
|
||||
|
||||
@ -144,6 +144,7 @@
|
||||
.mesh-chat-bubble { max-width: 75%; padding: 10px 14px; border-radius: 16px; word-break: break-word; }
|
||||
.mesh-chat-bubble.sent { background: rgba(251, 146, 60, 0.15); border: 1px solid rgba(251, 146, 60, 0.2); border-bottom-right-radius: 4px; }
|
||||
.mesh-chat-bubble.received { background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.08); border-bottom-left-radius: 4px; }
|
||||
.mesh-chat-bubble-sender { font-size: 0.7rem; font-weight: 600; color: rgba(251, 146, 60, 0.85); margin-bottom: 3px; }
|
||||
.mesh-chat-bubble-text { color: rgba(255, 255, 255, 0.9); font-size: 0.9rem; line-height: 1.4; }
|
||||
.mesh-chat-bubble-meta { display: flex; align-items: center; gap: 6px; margin-top: 4px; justify-content: flex-end; }
|
||||
.mesh-chat-bubble-time { font-size: 0.65rem; color: rgba(255, 255, 255, 0.3); }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user