From 8e1b3ce847e92d37411f9ee2bccc1e46bb8bd8d1 Mon Sep 17 00:00:00 2001 From: 2ro <17595647+2ro@users.noreply.github.com> Date: Sun, 14 Jun 2026 18:23:00 -0400 Subject: [PATCH] Build 76: split mixnet vs nostr status; tidy the identity chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The profile card drops the duplicate npub line and shows two statuses: the mixnet ("Connected over Nym", ~instant) and under it the nostr relay ("Connected to nostr" / "Connecting to relays…"). The relay connection is the slower step, not the mixnet. - New cheap cached nym::is_ready() flag backs the mixnet status (no per-frame TCP probe), set when the SOCKS5 proxy comes up. - Sidebar identity chip: smaller handle so the npub stays on one line, with a matching Nym/relay status. --- src/gui/views/goblin/mod.rs | 51 +++++++++++++++++++------------------ src/nym/mod.rs | 2 +- src/nym/sidecar.rs | 12 +++++++++ 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/gui/views/goblin/mod.rs b/src/gui/views/goblin/mod.rs index 59376c7..f5367a6 100644 --- a/src/gui/views/goblin/mod.rs +++ b/src/gui/views/goblin/mod.rs @@ -631,18 +631,21 @@ impl GoblinWalletView { w::avatar_any(ui, &handle, &npub_hex, 36.0, hue, tex.as_ref()); ui.add_space(10.0); ui.vertical(|ui| { + // Smaller so the npub/handle stays on one line. ui.label( RichText::new(&handle) - .font(FontId::new(14.0, fonts::semibold())) + .font(FontId::new(12.0, fonts::semibold())) .color(t.surface_text), ); ui.label( RichText::new(if connected { - "synced · Nym" + "Connected over Nym" + } else if crate::nym::is_ready() { + "Nym ready · relays…" } else { - "connecting…" + "Connecting to Nym…" }) - .font(FontId::new(12.0, fonts::regular())) + .font(FontId::new(11.0, fonts::regular())) .color(t.surface_text_mute), ); }); @@ -1859,34 +1862,32 @@ impl GoblinWalletView { .font(FontId::new(17.0, fonts::bold())) .color(t.surface_text), ); - if !npub.is_empty() { - // Full npub when it fits on one line, else head…tail. - let full = ui.painter().layout_no_wrap( - npub.clone(), - FontId::new(11.0, fonts::mono()), - t.surface_text_mute, - ); - let text = if full.size().x <= ui.available_width() { - npub.clone() - } else { - format!("{}…{}", &npub[..12], &npub[npub.len() - 6..]) - }; - ui.label( - RichText::new(text) - .font(FontId::new(11.0, fonts::mono())) - .color(t.surface_text_mute), - ); - } - let status = if connected { + // Mixnet status (fast) in place of the redundant second npub line. + let mixnet = if crate::nym::is_ready() { "Connected over Nym" } else { - "Connecting…" + "Connecting to Nym…" }; ui.label( - RichText::new(status) + RichText::new(mixnet) .font(FontId::new(13.0, fonts::regular())) .color(t.surface_text_dim), ); + // nostr relay status — the slower step (a relay reached over Nym). + let nostr = if connected { + "Connected to nostr" + } else { + "Connecting to relays…" + }; + ui.label( + RichText::new(nostr) + .font(FontId::new(12.0, fonts::regular())) + .color(t.surface_text_mute), + ); + if !crate::nym::is_ready() || !connected { + ui.ctx() + .request_repaint_after(std::time::Duration::from_millis(600)); + } }); }); if avatar_busy { diff --git a/src/nym/mod.rs b/src/nym/mod.rs index 9977f0a..2476c70 100644 --- a/src/nym/mod.rs +++ b/src/nym/mod.rs @@ -25,7 +25,7 @@ pub mod transport; use std::time::Duration; -pub use sidecar::warm_up; +pub use sidecar::{is_ready, warm_up}; pub use transport::NymWebSocketTransport; /// Local SOCKS5 endpoint exposed by the in-process Nym SOCKS5 client. diff --git a/src/nym/sidecar.rs b/src/nym/sidecar.rs index 56d77f0..af36de3 100644 --- a/src/nym/sidecar.rs +++ b/src/nym/sidecar.rs @@ -21,6 +21,7 @@ use std::net::{SocketAddr, TcpStream}; use std::path::PathBuf; +use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::time::{Duration, Instant}; @@ -44,12 +45,22 @@ pub fn warm_up() { thread::spawn(|| { if port_open(Duration::from_millis(300)) { info!("nym: reusing SOCKS5 endpoint already listening on {SOCKS5_HOST}:{SOCKS5_PORT}"); + MIXNET_READY.store(true, Ordering::Relaxed); return; } run_client(); }); } +/// Set once the local mixnet SOCKS5 proxy (:1080) is up and accepting. +static MIXNET_READY: AtomicBool = AtomicBool::new(false); + +/// Whether the mixnet proxy is warm. Cheap and cached — safe to poll from the UI +/// each frame, unlike a fresh TCP probe. Distinct from a relay being connected. +pub fn is_ready() -> bool { + MIXNET_READY.load(Ordering::Relaxed) +} + /// True when something accepts TCP on the SOCKS5 port. fn port_open(timeout: Duration) -> bool { let addr: SocketAddr = match format!("{SOCKS5_HOST}:{SOCKS5_PORT}").parse() { @@ -111,6 +122,7 @@ fn run_client() { started.elapsed().as_millis(), client.nym_address() ); + MIXNET_READY.store(true, Ordering::Relaxed); // Hold the client (and thus the SOCKS5 listener + mixnet tasks) open for // the whole process lifetime; the runtime keeps polling them. std::future::pending::<()>().await;