feat(ui): mesh chat polish — transport pills in image modal, hop-route modal, reaction dropdown, real read-tracking
Demo images / Build & push demo images (push) Failing after 3m29s
Demo images / Build & push demo images (push) Failing after 3m29s
- Image quality modal: 'Send via' pills (LoRa / FIPS / Tor) when the peer is federation-reachable — mesh.transport-advice now returns has_fips + last_transport alongside has_tor. Picking FIPS/Tor routes the image over the content-ref path instead of the radio. - Attachment modals (transport chooser, image quality, new hop modal) Teleport to body so the backdrop dims the FULL viewport — rendered in-place they sat inside a transformed glass panel that trapped position:fixed to the right chat panel. - Click a message's transport pill → route modal: radio hops + live SNR/RSSI quality for LoRa transports, overlay/circuit shape for FIPS/Tor, delivery + E2E state. - Reactions move behind a compact 'React ▾' dropdown with a larger 12-emoji palette. - Unread badges now clear like a normal chat app: opening a contact clears ALL twins of the merged conversation (badge sums every contact_id — clearing just the clicked one left it stuck), and only once the chat has scrolled to the latest messages; scrolled up into history, new arrivals accumulate until you scroll back down. - Refresh button shows only the spinner while refreshing (text+spinner overflowed the fixed button width). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e62f911810
commit
0aa3941c40
@@ -575,14 +575,17 @@ impl RpcHandler {
|
||||
let nodes = crate::federation::load_nodes(&self.config.data_dir)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let has_tor = peer_pubkey_hex
|
||||
.as_ref()
|
||||
.map(|pk| nodes.iter().any(|n| &n.pubkey == pk))
|
||||
.unwrap_or(false)
|
||||
|| peer_did
|
||||
.as_ref()
|
||||
.map(|d| nodes.iter().any(|n| &n.did == d))
|
||||
.unwrap_or(false);
|
||||
let fed_node = nodes.iter().find(|n| {
|
||||
peer_pubkey_hex.as_deref() == Some(n.pubkey.as_str())
|
||||
|| peer_did.as_deref() == Some(n.did.as_str())
|
||||
});
|
||||
let has_tor = fed_node.is_some();
|
||||
// Distinct FIPS capability so the frontend can offer FIPS as its own
|
||||
// pill (not just a generic "Tor") — the dial layer still picks
|
||||
// FIPS-first with Tor fallback on the actual send; these are honest
|
||||
// capability labels, with `last_transport` saying what worked last.
|
||||
let has_fips = fed_node.is_some_and(|n| n.fips_npub.is_some());
|
||||
let last_transport = fed_node.and_then(|n| n.last_transport.clone());
|
||||
|
||||
let est_seconds = (size.saturating_add(lora_bytes_per_sec - 1) / lora_bytes_per_sec).max(1);
|
||||
|
||||
@@ -618,6 +621,8 @@ impl RpcHandler {
|
||||
"tier": tier,
|
||||
"est_seconds": est_seconds,
|
||||
"has_tor": has_tor,
|
||||
"has_fips": has_fips,
|
||||
"last_transport": last_transport,
|
||||
"reason": reason,
|
||||
"size": size,
|
||||
"mesh_auto_max": MESH_AUTO_MAX,
|
||||
|
||||
Reference in New Issue
Block a user