1
0
forked from GRIN/grim

Build 12: failed username checks no longer read as 'Taken'

check_availability returns Unknown when the Tor request dies, and the
claim panels collapsed everything but Available into 'Taken' — a free
name looked taken whenever a circuit flaked. The full enum now reaches
both panels: Unknown reads 'Couldn't check — connection hiccup. Try
again.' in neutral gray, and Reserved/Invalid/Quarantined get their own
copy. Tor::http_request also backs off up to ~15s across 5 attempts:
fresh circuits fail while arti refreshes its directory consensus over
the bridge, and 3 tries in 4.5s couldn't ride that out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-06-11 10:03:54 -04:00
parent 0c60368280
commit a12f894dff
3 changed files with 38 additions and 27 deletions
+24 -13
View File
@@ -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<bool>, &'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 330 chars: az, 09, _ 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);
});
}
+8 -11
View File
@@ -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,
}),
);
}
+6 -3
View File
@@ -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<String>,
headers: Vec<(String, String)>,
) -> Option<String> {
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;