feat(mesh): RSSI/SNR dBm tooltip on the existing signal-bars indicator

The bars UI (signalBars/.mesh-signal-bars) was already built and wired to
mp.primary_rssi -- it just needed real backend data, which the previous
commit provides. Adds primary_snr alongside primary_rssi in MergedPeer and a
hover tooltip showing exact dBm/SNR values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-06-30 22:54:51 -04:00
parent 02b6b52a8c
commit 4a309a3ee4

View File

@ -533,6 +533,7 @@ interface MergedPeer {
primary_contact_id: number
primary_pubkey_hex: string | null
primary_rssi: number | null
primary_snr: number | null
is_archy: boolean
reachable: boolean
// The original active-chat marker uses contact_id equality, so keep a
@ -669,6 +670,7 @@ const mergedPeers = computed<MergedPeer[]>(() => {
primary_contact_id: peer.contact_id,
primary_pubkey_hex: peer.pubkey_hex,
primary_rssi: peer.rssi,
primary_snr: peer.snr,
is_archy: isArchyNode(peer) || !!matchedFed,
reachable: peer.reachable ?? true,
primary: peer,
@ -719,6 +721,7 @@ const mergedPeers = computed<MergedPeer[]>(() => {
primary_contact_id: synthCid,
primary_pubkey_hex: fed.pubkey,
primary_rssi: null,
primary_snr: null,
is_archy: true,
reachable: true,
primary: placeholder,
@ -1017,6 +1020,14 @@ function signalBars(rssi: number | null): number {
return 1
}
function signalTitle(rssi: number | null, snr: number | null): string {
if (rssi === null && snr === null) return 'No signal data yet'
const parts: string[] = []
if (rssi !== null) parts.push(`${rssi} dBm`)
if (snr !== null) parts.push(`SNR ${snr.toFixed(1)} dB`)
return parts.join(' · ')
}
function timeAgo(iso: string): string {
const diff = Date.now() - new Date(iso).getTime()
const secs = Math.floor(diff / 1000)
@ -1823,7 +1834,7 @@ function isImageMime(mime?: string): boolean {
<span v-if="mergedUnreadCount(mp)" class="mesh-unread-badge">
{{ mergedUnreadCount(mp) }}
</span>
<div class="mesh-peer-signal">
<div class="mesh-peer-signal" :title="signalTitle(mp.primary_rssi, mp.primary_snr)">
<div class="mesh-signal-bars">
<div v-for="i in 4" :key="i" class="mesh-signal-bar" :class="{ active: i <= signalBars(mp.primary_rssi) }" />
</div>