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.rs109
1 files changed, 95 insertions, 14 deletions
diff --git a/src/lib/login/fresh.rs b/src/lib/login/fresh.rs
index e81b74a..0b5922b 100644
--- a/src/lib/login/fresh.rs
+++ b/src/lib/login/fresh.rs
@@ -23,7 +23,8 @@ use crate::client::MockConnect;
23use crate::{ 23use crate::{
24 cli_interactor::{ 24 cli_interactor::{
25 Interactor, InteractorPrompt, Printer, PromptChoiceParms, PromptConfirmParms, 25 Interactor, InteractorPrompt, Printer, PromptChoiceParms, PromptConfirmParms,
26 PromptInputParms, PromptPasswordParms, 26 PromptInputParms, PromptPasswordParms, multi_select_with_custom_value,
27 show_multi_input_prompt_success,
27 }, 28 },
28 client::{Connect, nip05_query, save_event_in_global_cache, send_events}, 29 client::{Connect, nip05_query, save_event_in_global_cache, send_events},
29 git::{Repo, RepoActions, remove_git_config_item, save_git_config_item}, 30 git::{Repo, RepoActions, remove_git_config_item, save_git_config_item},
@@ -273,7 +274,46 @@ pub async fn get_fresh_nip46_signer(
273 .dont_report(), 274 .dont_report(),
274 )?; 275 )?;
275 let url = match signer_choice { 276 let url = match signer_choice {
276 0 | 1 => nostr_connect_url, 277 0 | 1 => {
278 // Loop so the user can change relays and see a refreshed QR/URL.
279 let mut current_url = nostr_connect_url;
280 loop {
281 // Display QR or URL with the current relay list.
282 display_nostr_connect(signer_choice, &current_url)?;
283
284 // Offer the option to change relays or proceed.
285 let action = Interactor::default().choice(
286 PromptChoiceParms::default()
287 .with_prompt(format!(
288 "signer relays: {}",
289 current_url
290 .relays()
291 .iter()
292 .map(std::string::ToString::to_string)
293 .collect::<Vec<_>>()
294 .join(", ")
295 ))
296 .with_default(0)
297 .with_choices(vec![
298 "waiting for signer app to connect...".to_string(),
299 "change signer relays".to_string(),
300 ])
301 .dont_report(),
302 )?;
303
304 if action == 0 {
305 break current_url;
306 }
307
308 // User wants to change relays — run the multiselect and rebuild URL.
309 let selected = select_signer_relays(&current_url)?;
310 if !selected.is_empty() {
311 let new_relays: Vec<RelayUrl> =
312 selected.iter().flat_map(|s| RelayUrl::parse(s)).collect();
313 current_url = NostrConnectURI::client(app_key.public_key(), new_relays, "ngit");
314 }
315 }
316 }
277 2 => { 317 2 => {
278 let mut error = None; 318 let mut error = None;
279 loop { 319 loop {
@@ -316,26 +356,16 @@ pub async fn get_fresh_nip46_signer(
316 { 356 {
317 let printer_clone = Arc::clone(&printer); 357 let printer_clone = Arc::clone(&printer);
318 let mut printer_locked = printer_clone.lock().await; 358 let mut printer_locked = printer_clone.lock().await;
359 // For choices 0 and 1 the content was already printed by display_nostr_connect
360 // inside the relay-selection loop above; only the "waiting" hint is added here.
319 match signer_choice { 361 match signer_choice {
320 0 => { 362 0 => {
321 printer_locked
322 .println("login to nostr with remote signer via nostr connect".to_string());
323 printer_locked.println("scan QR code in signer app (eg Amber):".to_string());
324 printer_locked.printlns(generate_qr(&url.to_string())?);
325 printer_locked.println( 363 printer_locked.println(
326 "scan QR code in signer app or use ctrl + c to go back to login menu..." 364 "scan QR code in signer app or use ctrl + c to go back to login menu..."
327 .to_string(), 365 .to_string(),
328 ); 366 );
329 } 367 }
330 1 => { 368 1 => {
331 printer_locked
332 .println("login to nostr with remote signer via nostr connect".to_string());
333 printer_locked.println("".to_string());
334 printer_locked.println_with_custom_formatting(
335 format!("{}", Style::new().bold().apply_to(url.to_string()),),
336 url.to_string(),
337 );
338 printer_locked.println("".to_string());
339 printer_locked.println( 369 printer_locked.println(
340 "paste this url into signer app or use ctrl + c to go back to login menu..." 370 "paste this url into signer app or use ctrl + c to go back to login menu..."
341 .to_string(), 371 .to_string(),
@@ -396,6 +426,57 @@ pub fn generate_nostr_connect_app(
396 Ok((app_key, nostr_connect_url)) 426 Ok((app_key, nostr_connect_url))
397} 427}
398 428
429/// Print the QR code or nostrconnect URL to stderr.
430///
431/// `choice` must be 0 (QR) or 1 (URL). Output goes directly to stderr so it
432/// is visible before the relay-selection choice prompt that follows.
433fn display_nostr_connect(choice: usize, url: &NostrConnectURI) -> Result<()> {
434 eprintln!("login to nostr with remote signer via nostr connect");
435 if choice == 0 {
436 eprintln!("scan QR code in signer app (eg Amber):");
437 for line in generate_qr(&url.to_string())? {
438 eprintln!("{line}");
439 }
440 } else {
441 eprintln!();
442 eprintln!("{}", Style::new().bold().apply_to(url.to_string()));
443 eprintln!();
444 }
445 Ok(())
446}
447
448/// Present the multiselect UI for choosing signer relays.
449///
450/// Returns the selected relay list as strings. An empty return means the user
451/// submitted without selecting anything (caller should keep the existing URL).
452fn select_signer_relays(nostr_connect_url: &NostrConnectURI) -> Result<Vec<String>> {
453 let current_relays: Vec<String> = nostr_connect_url
454 .relays()
455 .iter()
456 .map(std::string::ToString::to_string)
457 .collect();
458
459 let defaults = vec![true; current_relays.len()];
460 let selected = multi_select_with_custom_value(
461 "signer relays",
462 "signer relay",
463 current_relays,
464 defaults,
465 |s| {
466 let url = if s.starts_with("ws://") || s.starts_with("wss://") {
467 s.to_string()
468 } else {
469 format!("wss://{s}")
470 };
471 RelayUrl::parse(&url)
472 .map(|r| r.to_string())
473 .context(format!("invalid relay URL: {s}"))
474 },
475 )?;
476 show_multi_input_prompt_success("signer relays", &selected);
477 Ok(selected)
478}
479
399pub async fn fetch_nip46_uri_from_nip05(nip05: &str) -> Result<NostrConnectURI> { 480pub async fn fetch_nip46_uri_from_nip05(nip05: &str) -> Result<NostrConnectURI> {
400 let term = console::Term::stderr(); 481 let term = console::Term::stderr();
401 term.write_line("contacting login service provider...")?; 482 term.write_line("contacting login service provider...")?;