upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/lib/login
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/login')
-rw-r--r--src/lib/login/fresh.rs154
1 files changed, 117 insertions, 37 deletions
diff --git a/src/lib/login/fresh.rs b/src/lib/login/fresh.rs
index 8d3a806..057b41a 100644
--- a/src/lib/login/fresh.rs
+++ b/src/lib/login/fresh.rs
@@ -3,6 +3,7 @@ use std::{str::FromStr, sync::Arc, time::Duration};
3use anyhow::{Context, Result, bail}; 3use anyhow::{Context, Result, bail};
4use console::Style; 4use console::Style;
5use dialoguer::theme::{ColorfulTheme, Theme}; 5use dialoguer::theme::{ColorfulTheme, Theme};
6use indicatif::{ProgressBar, ProgressStyle};
6use nostr::nips::nip46::NostrConnectURI; 7use nostr::nips::nip46::NostrConnectURI;
7use nostr_connect::client::NostrConnect; 8use nostr_connect::client::NostrConnect;
8use nostr_sdk::{EventBuilder, Keys, Metadata, NostrSigner, PublicKey, RelayUrl, ToBech32}; 9use nostr_sdk::{EventBuilder, Keys, Metadata, NostrSigner, PublicKey, RelayUrl, ToBech32};
@@ -337,7 +338,72 @@ pub async fn get_fresh_nip46_signer(
337 // Display QR or URL with the current relay list. 338 // Display QR or URL with the current relay list.
338 display_nostr_connect(signer_choice, &current_url)?; 339 display_nostr_connect(signer_choice, &current_url)?;
339 340
340 // Offer the option to change relays or proceed. 341 // Start listening for the signer immediately after displaying
342 // the QR/URL — don't wait for the user to press anything.
343 let nostr_connect = Arc::new(NostrConnect::new(
344 current_url.clone(),
345 app_key.clone(),
346 Duration::from_secs(10 * 60),
347 None,
348 )?);
349 let signer_arc: Arc<dyn NostrSigner> = nostr_connect.clone();
350 let pubkey_handle = tokio::spawn(async move { signer_arc.get_public_key().await });
351
352 // Show a spinner while waiting; Ctrl+C lets the user change
353 // relays or go back instead of waiting indefinitely.
354 eprintln!();
355 // TODO: remove the dim delay note once the rust-nostr
356 // NostrConnect handshake delay bug is fixed.
357 let spinner_msg = format!(
358 "{} {}",
359 console::style("waiting for signer app to connect...").bold(),
360 console::style("(may take 10s+ to connect once added)").color256(247),
361 );
362 let spinner = ProgressBar::new_spinner()
363 .with_style(
364 ProgressStyle::with_template("{spinner} {msg}")
365 .unwrap()
366 .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "),
367 )
368 .with_message(spinner_msg);
369 spinner.enable_steady_tick(Duration::from_millis(100));
370
371 let res = tokio::select! {
372 result = pubkey_handle => {
373 spinner.finish_and_clear();
374 match result {
375 Ok(Ok(pk)) => Some(pk),
376 _ => None,
377 }
378 },
379 _ = signal::ctrl_c() => {
380 spinner.finish_and_clear();
381 None
382 }
383 };
384
385 if let Some(public_key) = res {
386 // Connection succeeded — retrieve the canonical bunker URI
387 // and return.
388 let bunker_url = nostr_connect
389 .bunker_uri()
390 .await
391 .context("failed to get bunker URI from NostrConnect client")?;
392 let signer_info = SignerInfo::Bunker {
393 bunker_uri: bunker_url.to_string(),
394 bunker_app_key: app_key.secret_key().to_secret_hex(),
395 npub: Some(public_key.to_bech32()?),
396 };
397 return Ok(Some((
398 nostr_connect as Arc<dyn NostrSigner>,
399 public_key,
400 signer_info,
401 SignerInfoSource::GitGlobal,
402 )));
403 }
404
405 // Ctrl+C was pressed — offer the user a chance to change
406 // relays or go back to the top-level login menu.
341 let action = Interactor::default().choice( 407 let action = Interactor::default().choice(
342 PromptChoiceParms::default() 408 PromptChoiceParms::default()
343 .with_prompt(format!( 409 .with_prompt(format!(
@@ -351,22 +417,29 @@ pub async fn get_fresh_nip46_signer(
351 )) 417 ))
352 .with_default(0) 418 .with_default(0)
353 .with_choices(vec![ 419 .with_choices(vec![
354 "waiting for signer app to connect...".to_string(), 420 "try again".to_string(),
355 "change signer relays".to_string(), 421 "change signer relays".to_string(),
422 "back".to_string(),
356 ]) 423 ])
357 .dont_report(), 424 .dont_report(),
358 )?; 425 )?;
359 426
360 if action == 0 { 427 match action {
361 break current_url; 428 0 => {
362 } 429 // Redisplay QR/URL and start listening again.
363 430 continue;
364 // User wants to change relays — run the multiselect and rebuild URL. 431 }
365 let selected = select_signer_relays(&current_url)?; 432 1 => {
366 if !selected.is_empty() { 433 // Change relays and rebuild URL.
367 let new_relays: Vec<RelayUrl> = 434 let selected = select_signer_relays(&current_url)?;
368 selected.iter().flat_map(|s| RelayUrl::parse(s)).collect(); 435 if !selected.is_empty() {
369 current_url = NostrConnectURI::client(app_key.public_key(), new_relays, "ngit"); 436 let new_relays: Vec<RelayUrl> =
437 selected.iter().flat_map(|s| RelayUrl::parse(s)).collect();
438 current_url =
439 NostrConnectURI::client(app_key.public_key(), new_relays, "ngit");
440 }
441 }
442 _ => return Ok(None),
370 } 443 }
371 } 444 }
372 } 445 }
@@ -412,28 +485,9 @@ pub async fn get_fresh_nip46_signer(
412 { 485 {
413 let printer_clone = Arc::clone(&printer); 486 let printer_clone = Arc::clone(&printer);
414 let mut printer_locked = printer_clone.lock().await; 487 let mut printer_locked = printer_clone.lock().await;
415 // For choices 0 and 1 the content was already printed by display_nostr_connect 488 printer_locked.println(
416 // inside the relay-selection loop above; only the "waiting" hint is added here. 489 "add / approve in your signer or use ctrl + c to go back to login menu...".to_string(),
417 match signer_choice { 490 );
418 0 => {
419 printer_locked.println(
420 "scan QR code in signer app or use ctrl + c to go back to login menu..."
421 .to_string(),
422 );
423 }
424 1 => {
425 printer_locked.println(
426 "paste this url into signer app or use ctrl + c to go back to login menu..."
427 .to_string(),
428 );
429 }
430 _ => {
431 printer_locked.println(
432 "add / approve in your signer or use ctrl + c to go back to login menu..."
433 .to_string(),
434 );
435 }
436 }
437 } 491 }
438 492
439 let (signer, user_public_key, bunker_url) = 493 let (signer, user_public_key, bunker_url) =
@@ -487,17 +541,43 @@ pub fn generate_nostr_connect_app(
487/// `choice` must be 0 (QR) or 1 (URL). Output goes directly to stderr so it 541/// `choice` must be 0 (QR) or 1 (URL). Output goes directly to stderr so it
488/// is visible before the relay-selection choice prompt that follows. 542/// is visible before the relay-selection choice prompt that follows.
489fn display_nostr_connect(choice: usize, url: &NostrConnectURI) -> Result<()> { 543fn display_nostr_connect(choice: usize, url: &NostrConnectURI) -> Result<()> {
490 eprintln!("login to nostr with remote signer via nostr connect"); 544 let dim = Style::new().for_stderr().color256(247);
545 let hint = dim.apply_to("(ctrl+c to change)");
546 eprintln!(
547 "{}",
548 Style::new().for_stderr().bold().apply_to("nostr connect")
549 );
491 if choice == 0 { 550 if choice == 0 {
492 eprintln!("scan QR code in signer app (eg Amber):"); 551 eprintln!(
552 "{}",
553 dim.apply_to("scan QR code in signer app (eg. Amber):")
554 );
493 for line in generate_qr(&url.to_string())? { 555 for line in generate_qr(&url.to_string())? {
494 eprintln!("{line}"); 556 eprintln!("{line}");
495 } 557 }
496 } else { 558 } else {
497 eprintln!(); 559 eprintln!();
498 eprintln!("{}", Style::new().bold().apply_to(url.to_string())); 560 eprintln!(
561 "{}",
562 Style::new()
563 .for_stderr()
564 .bold()
565 .cyan()
566 .apply_to(url.to_string())
567 );
499 eprintln!(); 568 eprintln!();
500 } 569 }
570 let relays = url
571 .relays()
572 .iter()
573 .map(std::string::ToString::to_string)
574 .collect::<Vec<_>>()
575 .join(", ");
576 eprintln!(
577 "{} {}",
578 dim.apply_to(format!("signer relays: {relays}")),
579 hint
580 );
501 Ok(()) 581 Ok(())
502} 582}
503 583