feat(mesh-ui): render tx/lightning relay typed messages and skip self-send
Adds renderers for tx_relay, tx_relay_response, tx_confirmation, lightning_relay, and lightning_relay_response message types so these appear as rich cards in the chat stream. sendArchMessage now looks up our own onion via getTorAddress and skips federation peers that match, preventing the duplicate "echoed back to self" message we were seeing on single-node test federations. Empty-federation error message is also clearer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3ed9243c50
commit
36cd3f4e7d
@ -42,6 +42,11 @@ export type MeshMessageTypeLabel =
|
||||
| 'psbt_hash'
|
||||
| 'coordinate'
|
||||
| 'block_header'
|
||||
| 'tx_relay'
|
||||
| 'tx_relay_response'
|
||||
| 'tx_confirmation'
|
||||
| 'lightning_relay'
|
||||
| 'lightning_relay_response'
|
||||
|
||||
export interface MeshMessage {
|
||||
id: number
|
||||
|
||||
@ -97,11 +97,20 @@ async function sendArchMessage() {
|
||||
sendingArch.value = true
|
||||
try {
|
||||
const nodes = await rpcClient.federationListNodes()
|
||||
// Get our own onion address to skip sending to self
|
||||
let selfOnion: string | null = null
|
||||
try {
|
||||
const tor = await rpcClient.getTorAddress()
|
||||
selfOnion = tor.tor_address
|
||||
} catch { /* non-fatal */ }
|
||||
const msg = messageText.value.trim()
|
||||
let sent = 0
|
||||
for (const node of nodes.nodes) {
|
||||
const nodeOnion = node.onion || node.did
|
||||
// Skip sending to ourselves (would create duplicate received message)
|
||||
if (selfOnion && (nodeOnion === selfOnion || nodeOnion === selfOnion.replace('.onion', '') || selfOnion === nodeOnion + '.onion')) continue
|
||||
try {
|
||||
await rpcClient.sendMessageToPeer(node.onion || node.did, msg)
|
||||
await rpcClient.sendMessageToPeer(nodeOnion, msg)
|
||||
sent++
|
||||
} catch { /* some peers may be offline */ }
|
||||
}
|
||||
@ -109,7 +118,7 @@ async function sendArchMessage() {
|
||||
await rpcClient.call({ method: 'node-store-sent', params: { message: msg } })
|
||||
} catch { /* non-fatal */ }
|
||||
messageText.value = ''
|
||||
if (sent === 0) sendError.value = 'No peers reachable — message may arrive when they come online'
|
||||
if (sent === 0 && nodes.nodes.length <= 1) sendError.value = 'No other peers in federation — add nodes first'
|
||||
await loadArchMessages()
|
||||
} catch (e) {
|
||||
sendError.value = e instanceof Error ? e.message : 'Send failed'
|
||||
@ -217,7 +226,7 @@ const chatMessages = computed(() => {
|
||||
} else if (m.from_name) {
|
||||
peerName = m.from_name
|
||||
} else if (fedNodeNames.value[m.from_pubkey]) {
|
||||
peerName = fedNodeNames.value[m.from_pubkey]
|
||||
peerName = fedNodeNames.value[m.from_pubkey]!
|
||||
} else {
|
||||
peerName = m.from_pubkey.slice(0, 12) + '...'
|
||||
}
|
||||
@ -642,6 +651,46 @@ function truncatePubkey(hex: string | null): string {
|
||||
<span class="mesh-typed-icon">⛓</span>
|
||||
<span class="mesh-typed-label">{{ msg.typed_payload.message || msg.plaintext }}</span>
|
||||
</div>
|
||||
<!-- TX relay request -->
|
||||
<div v-else-if="msg.message_type === 'tx_relay' && msg.typed_payload" class="mesh-typed-block">
|
||||
<span class="mesh-typed-icon">↪</span>
|
||||
<span class="mesh-typed-label">TX relay #{{ msg.typed_payload.request_id }} ({{ (msg.typed_payload.tx_hex || '').length }} hex chars)</span>
|
||||
</div>
|
||||
<!-- TX relay response -->
|
||||
<div v-else-if="msg.message_type === 'tx_relay_response' && msg.typed_payload" class="mesh-typed-block">
|
||||
<span class="mesh-typed-icon">{{ msg.typed_payload.txid ? '✅' : '❌' }}</span>
|
||||
<span class="mesh-typed-label">
|
||||
<template v-if="msg.typed_payload.txid">Broadcast #{{ msg.typed_payload.request_id }}: {{ String(msg.typed_payload.txid).substring(0, 12) }}…</template>
|
||||
<template v-else>TX relay failed #{{ msg.typed_payload.request_id }}: {{ msg.typed_payload.error || 'unknown' }}</template>
|
||||
</span>
|
||||
</div>
|
||||
<!-- TX confirmation -->
|
||||
<div v-else-if="msg.message_type === 'tx_confirmation' && msg.typed_payload" class="mesh-typed-block">
|
||||
<span class="mesh-typed-icon">⛓</span>
|
||||
<span class="mesh-typed-label">{{ msg.typed_payload.confirmations }} conf @ block {{ msg.typed_payload.block_height }} — {{ String(msg.typed_payload.txid || '').substring(0, 12) }}…</span>
|
||||
</div>
|
||||
<!-- Lightning relay request -->
|
||||
<div v-else-if="msg.message_type === 'lightning_relay' && msg.typed_payload" class="mesh-typed-invoice">
|
||||
<div class="mesh-typed-invoice-header">
|
||||
<span class="mesh-typed-icon">⚡</span>
|
||||
<span class="mesh-typed-label">Lightning Relay Request</span>
|
||||
</div>
|
||||
<div class="mesh-typed-invoice-amount">{{ (msg.typed_payload.amount_sats || 0).toLocaleString() }} sats</div>
|
||||
<div class="mesh-typed-invoice-bolt11">{{ (msg.typed_payload.bolt11 || '').substring(0, 40) }}…</div>
|
||||
</div>
|
||||
<!-- Lightning relay response -->
|
||||
<div v-else-if="msg.message_type === 'lightning_relay_response' && msg.typed_payload" class="mesh-typed-invoice">
|
||||
<div class="mesh-typed-invoice-header">
|
||||
<span class="mesh-typed-icon">{{ msg.typed_payload.preimage ? '✅' : '❌' }}</span>
|
||||
<span class="mesh-typed-label">
|
||||
<template v-if="msg.typed_payload.preimage">Lightning Paid</template>
|
||||
<template v-else>Lightning Failed</template>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="msg.typed_payload.payment_hash" class="mesh-typed-invoice-bolt11">hash: {{ String(msg.typed_payload.payment_hash).substring(0, 20) }}…</div>
|
||||
<div v-if="msg.typed_payload.preimage" class="mesh-typed-invoice-bolt11">preimage: {{ String(msg.typed_payload.preimage).substring(0, 20) }}…</div>
|
||||
<div v-if="msg.typed_payload.error" class="mesh-typed-invoice-memo">{{ msg.typed_payload.error }}</div>
|
||||
</div>
|
||||
<!-- Default: plain text -->
|
||||
<div v-else class="mesh-chat-bubble-text">{{ msg.plaintext }}</div>
|
||||
<div class="mesh-chat-bubble-meta">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user