Fix functional bugs found in UI audit

Send flow:
- "Sent" success now gates on the real dispatch result (NostrService send
  phase Working/Sent/Failed) instead of send_creating, which cleared before
  the DM left. Added a Failed stage with Try again / Close so a failed send
  is no longer shown as success.
- NIP-05 handle resolution moved off the UI thread (was .join()-blocking the
  render thread for the Tor timeout); now a worker thread writes into a polled
  slot with an inline "Looking up…" spinner.
- Desktop amount entry no longer steals keystrokes from the Note field (guards
  on the note TextEdit focus).
- Review screen now shows a network-fee line.

Requests / identity / archive:
- Approve is now double-tap safe (per-session approving set + disabled button),
  closing a double-pay window.
- "Backup nostr key" copied the public npub; split into "Copy npub (public)"
  and "Back up secret key (nsec)" which copies the real secret.
- Added inline username claim (availability check + NIP-98 register over Tor,
  off-thread) for anonymous users; persists nip05 + republishes identity.
- Added archive Export / Wipe history controls.
- Receive "Slatepack" button now copies the grin1 address; "Scan" tab relabeled
  "Receive" (no scanner was wired).
- Goblin view resets on wallet switch so a half-filled send can't leak across.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-06-10 02:26:54 -04:00
parent ce1c071f3c
commit aa9847bb41
5 changed files with 434 additions and 41 deletions
+7
View File
@@ -2123,6 +2123,7 @@ async fn handle_task(w: &Wallet, t: WalletTask) {
return;
};
w.send_creating.store(true, Ordering::Relaxed);
service.set_send_phase(crate::nostr::send_phase::WORKING);
match w.send(*a, None) {
Ok(s) => {
sync_wallet_data(&w, false);
@@ -2161,6 +2162,7 @@ async fn handle_task(w: &Wallet, t: WalletTask) {
contact.unknown = false;
service.store.save_contact(&contact);
}
service.set_send_phase(crate::nostr::send_phase::SENT);
}
Err(e) => {
error!("nostr send dispatch failed: {e}");
@@ -2176,14 +2178,19 @@ async fn handle_task(w: &Wallet, t: WalletTask) {
))),
);
}
service.set_send_phase(crate::nostr::send_phase::FAILED);
}
}
} else {
// No slatepack text produced — treat as failure.
service.set_send_phase(crate::nostr::send_phase::FAILED);
}
w.on_task_result(tx, &t);
}
Err(e) => {
error!("nostr send error: {:?}", e);
w.send_creating.store(false, Ordering::Relaxed);
service.set_send_phase(crate::nostr::send_phase::FAILED);
}
}
}