diff --git a/src/gui/views/goblin/mod.rs b/src/gui/views/goblin/mod.rs index 3b944a8..b2bbf17 100644 --- a/src/gui/views/goblin/mod.rs +++ b/src/gui/views/goblin/mod.rs @@ -168,11 +168,25 @@ struct ClaimState { } enum ClaimMsg { - Availability(bool), + Availability(crate::nostr::nip05::Availability), Registered(String), Error(String), } +/// Map an availability probe to user-facing state: `None` availability +/// means the check itself failed — never present that as "Taken". +fn availability_feedback(avail: crate::nostr::nip05::Availability) -> (Option, &'static str) { + use crate::nostr::nip05::Availability::*; + match avail { + Available => (Some(true), "Available!"), + Taken => (Some(false), "Taken"), + Reserved => (Some(false), "Reserved"), + Invalid => (Some(false), "Names are 3–30 chars: a–z, 0–9, _ or -"), + Quarantined => (Some(false), "Not available"), + Unknown => (None, "Couldn't check — connection hiccup. Try again."), + } +} + impl Default for ClaimState { fn default() -> Self { Self { @@ -2099,13 +2113,10 @@ impl GoblinWalletView { if let Some(msg) = claim.result.lock().unwrap().take() { claim.checking = false; match msg { - ClaimMsg::Availability(ok) => { - claim.available = Some(ok); - claim.message = Some(if ok { - "Available!".into() - } else { - "Taken".into() - }); + ClaimMsg::Availability(avail) => { + let (available, msg) = availability_feedback(avail); + claim.available = available; + claim.message = Some(msg.to_string()); } ClaimMsg::Registered(nip05) => { claim.message = Some(format!("Registered {}", nip05)); @@ -2156,10 +2167,10 @@ impl GoblinWalletView { ui.label( RichText::new(msg) .font(FontId::new(13.0, fonts::regular())) - .color(if claim.available == Some(false) { - t.neg - } else { - t.pos + .color(match claim.available { + Some(false) => t.neg, + Some(true) => t.pos, + None => t.surface_text_dim, }), ); } @@ -2227,7 +2238,7 @@ fn start_claim_check(claim: &mut ClaimState, name: &str, wallet: &Wallet) { Err(_) => return, }; let avail = rt.block_on(crate::nostr::nip05::check_availability(&server, &name)); - let msg = ClaimMsg::Availability(avail == crate::nostr::nip05::Availability::Available); + let msg = ClaimMsg::Availability(avail); *slot.lock().unwrap() = Some(msg); }); } diff --git a/src/gui/views/goblin/onboarding.rs b/src/gui/views/goblin/onboarding.rs index 094d309..381cd67 100644 --- a/src/gui/views/goblin/onboarding.rs +++ b/src/gui/views/goblin/onboarding.rs @@ -732,13 +732,10 @@ impl OnboardingContent { if let Some(msg) = self.claim.result.lock().unwrap().take() { self.claim.checking = false; match msg { - ClaimMsg::Availability(ok) => { - self.claim.available = Some(ok); - self.claim.message = Some(if ok { - "Available!".into() - } else { - "Taken".into() - }); + ClaimMsg::Availability(avail) => { + let (available, msg) = super::availability_feedback(avail); + self.claim.available = available; + self.claim.message = Some(msg.to_string()); } ClaimMsg::Registered(nip05) => { self.claim.message = @@ -805,10 +802,10 @@ impl OnboardingContent { ui.label( RichText::new(msg) .font(FontId::new(13.0, fonts::regular())) - .color(if self.claim.available == Some(false) { - t.neg - } else { - t.pos + .color(match self.claim.available { + Some(false) => t.neg, + Some(true) => t.pos, + None => t.surface_text_dim, }), ); } diff --git a/src/tor/tor.rs b/src/tor/tor.rs index fee340f..11f7a5b 100644 --- a/src/tor/tor.rs +++ b/src/tor/tor.rs @@ -401,16 +401,19 @@ impl Tor { /// Perform an HTTP request over the embedded Tor client with optional /// headers, returning the response body. Used for NIP-05 and price APIs. /// Retries with a fresh isolated circuit: single-circuit connect failures - /// right after bootstrap are common and transient. + /// right after bootstrap are common and transient, and fresh circuits + /// fail for tens of seconds while arti refreshes its directory consensus + /// over the bridge (existing circuits keep working) — back off long + /// enough to ride a refresh out. pub async fn http_request( method: &str, url: String, body: Option, headers: Vec<(String, String)>, ) -> Option { - for attempt in 0..3 { + for attempt in 0..5 { if attempt > 0 { - tokio::time::sleep(Duration::from_millis(1500)).await; + tokio::time::sleep(Duration::from_secs(1 << (attempt - 1))).await; } let resp = Self::http_request_once(method, url.clone(), body.clone(), headers.clone()).await;