From a9b2ebf8216be34950e54dd9a446dbdc0c9c744a Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 6 Aug 2025 12:52:59 +0100 Subject: feat(send): PR fallback to user / custom grasp if use is maintainer, push PR to all repo git servers. if user has a fork, push to all git servers it lists, and repo grasp servers. if user hasn't got a fork but has a user grasp list and pushing push to repo grasp servers fails, create a personal-fork automatically at each user grasp server and push there. fallback to prompting user for either grasp servers or git server with write permission. if user provides grasp servers, suggesting adding to user preference list. --- src/bin/ngit/sub_commands/init.rs | 106 +++----------------------------------- 1 file changed, 8 insertions(+), 98 deletions(-) (limited to 'src/bin/ngit/sub_commands/init.rs') diff --git a/src/bin/ngit/sub_commands/init.rs b/src/bin/ngit/sub_commands/init.rs index eaaf83d..01fcaea 100644 --- a/src/bin/ngit/sub_commands/init.rs +++ b/src/bin/ngit/sub_commands/init.rs @@ -12,12 +12,12 @@ use console::{Style, Term}; use dialoguer::theme::{ColorfulTheme, Theme}; use ngit::{ UrlWithoutSlash, - cli_interactor::{PromptChoiceParms, PromptConfirmParms, PromptMultiChoiceParms}, + cli_interactor::{PromptChoiceParms, PromptConfirmParms, multi_select_with_custom_value}, client::{Params, send_events}, git::nostr_url::{CloneUrl, NostrUrlDecoded}, repo_ref::{ - detect_existing_grasp_servers, extract_npub, extract_pks, normalize_grasp_server_url, - save_repo_config_to_yaml, + detect_existing_grasp_servers, extract_npub, extract_pks, + format_grasp_server_url_as_relay_url, normalize_grasp_server_url, save_repo_config_to_yaml, }, }; use nostr::{ @@ -727,6 +727,11 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { web, relays: relays.clone(), blossoms, + hashtags: if let Some(repo_ref) = repo_ref { + repo_ref.hashtags + } else { + vec![] + }, trusted_maintainer: user_ref.public_key, maintainers_without_annoucnement: None, maintainers: maintainers.clone(), @@ -848,93 +853,6 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { Ok(()) } -fn multi_select_with_custom_value( - prompt: &str, - custom_choice_prompt: &str, - mut choices: Vec, - mut defaults: Vec, - validate_choice: F, -) -> Result> -where - F: Fn(&str) -> Result, -{ - let mut selected_choices = vec![]; - - // Loop to allow users to add more choices - loop { - // Add 'add another' option at the end of the choices - let mut current_choices = choices.clone(); - current_choices.push(if current_choices.is_empty() { - "add".to_string() - } else { - "add another".to_string() - }); - - // Create default selections based on the provided defaults - let mut current_defaults = defaults.clone(); - current_defaults.push(current_choices.len() == 1); // 'add another' should not be selected by default - - // Prompt for selections - let selected_indices: Vec = Interactor::default().multi_choice( - PromptMultiChoiceParms::default() - .with_prompt(prompt) - .dont_report() - .with_choices(current_choices.clone()) - .with_defaults(current_defaults), - )?; - - // Collect selected choices - selected_choices.clear(); // Clear previous selections to update - for &index in &selected_indices { - if index < choices.len() { - // Exclude 'add another' option - selected_choices.push(choices[index].clone()); - } - } - - // Check if 'add another' was selected - if selected_indices.contains(&(choices.len())) { - // Last index is 'add another' - let mut new_choice: String; - loop { - new_choice = Interactor::default().input( - PromptInputParms::default() - .with_prompt(custom_choice_prompt) - .dont_report() - .optional(), - )?; - - if new_choice.is_empty() { - break; - } - // Validate the new choice - match validate_choice(&new_choice) { - Ok(valid_choice) => { - new_choice = valid_choice; // Use the fixed version of the input - break; // Valid choice, exit the loop - } - Err(err) => { - // Inform the user about the validation error - println!("Error: {err}"); - } - } - } - - // Add the new choice to the choices vector - if !new_choice.is_empty() { - choices.push(new_choice.clone()); // Add new choice to the end of the list - selected_choices.push(new_choice); // Automatically select the new choice - defaults.push(true); // Set the new choice as selected by default - } - } else { - // Exit the loop if 'add another' was not selected - break; - } - } - - Ok(selected_choices) -} - fn format_grasp_server_url_as_clone_url( url: &str, public_key: &PublicKey, @@ -953,14 +871,6 @@ fn format_grasp_server_url_as_clone_url( )) } -fn format_grasp_server_url_as_relay_url(url: &str) -> Result { - let grasp_server_url = normalize_grasp_server_url(url)?; - if grasp_server_url.contains("http://") { - return Ok(grasp_server_url.replace("http://", "ws://")); - } - Ok(format!("wss://{grasp_server_url}")) -} - fn format_grasp_server_url_as_blossom_url(url: &str) -> Result { let grasp_server_url = normalize_grasp_server_url(url)?; if grasp_server_url.contains("http://") { -- cgit v1.2.3 From 8c7a7ca2f538fd9240906f6eb746e55d75a6f4fd Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 6 Aug 2025 13:13:57 +0100 Subject: feat(init): use user grasp list for defaults instead of relying on hardcoded grasp server options. couldn't we look up those selected for other repos for the user instead? --- src/bin/ngit/sub_commands/init.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/bin/ngit/sub_commands/init.rs') diff --git a/src/bin/ngit/sub_commands/init.rs b/src/bin/ngit/sub_commands/init.rs index 01fcaea..6f3a357 100644 --- a/src/bin/ngit/sub_commands/init.rs +++ b/src/bin/ngit/sub_commands/init.rs @@ -265,12 +265,24 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { &identifier, ); let mut selections: Vec = vec![true; options.len()]; // Initialize selections based on existing options + let empty = options.is_empty(); + for user_grasp_option in user_ref.grasp_list.urls { + // Check if any option contains the user_grasp_option as a substring + if !options + .iter() + .any(|option| option.contains(user_grasp_option.as_str())) + { + options.push(user_grasp_option.to_string()); // Add if not found + selections.push(empty); // mark as selected if no existing grasp otherwise not + } + } + let empty = options.is_empty(); for fallback in fallback_grasp_servers { // Check if any option contains the fallback as a substring if !options.iter().any(|option| option.contains(fallback)) { options.push(fallback.clone()); // Add fallback if not found - selections.push(empty); // mark as selected if no existing ngit relay otherwise not + selections.push(empty); // mark as selected if no existing selections otherwise not } } let selected = multi_select_with_custom_value( -- cgit v1.2.3 From 896267959bc9e436d7c5d2ee0ff8c8c088fc7274 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Thu, 7 Aug 2025 13:02:08 +0100 Subject: fix(send): PR cli output to keep the user informed of whats happening / happend --- src/bin/ngit/sub_commands/init.rs | 19 ++++--------------- src/bin/ngit/sub_commands/send.rs | 25 ++++++++++++++----------- src/lib/cli_interactor.rs | 18 +++++++++++++++++- 3 files changed, 35 insertions(+), 27 deletions(-) (limited to 'src/bin/ngit/sub_commands/init.rs') diff --git a/src/bin/ngit/sub_commands/init.rs b/src/bin/ngit/sub_commands/init.rs index 6f3a357..98daee4 100644 --- a/src/bin/ngit/sub_commands/init.rs +++ b/src/bin/ngit/sub_commands/init.rs @@ -9,10 +9,12 @@ use std::{ use anyhow::{Context, Result, bail}; use console::{Style, Term}; -use dialoguer::theme::{ColorfulTheme, Theme}; use ngit::{ UrlWithoutSlash, - cli_interactor::{PromptChoiceParms, PromptConfirmParms, multi_select_with_custom_value}, + cli_interactor::{ + PromptChoiceParms, PromptConfirmParms, multi_select_with_custom_value, + show_multi_input_prompt_success, + }, client::{Params, send_events}, git::nostr_url::{CloneUrl, NostrUrlDecoded}, repo_ref::{ @@ -904,19 +906,6 @@ fn parse_relay_url(s: &str) -> Result { .context(format!("failed to parse relay url: {s}")) } -pub fn show_multi_input_prompt_success(label: &str, values: &[String]) { - let values_str: Vec<&str> = values.iter().map(std::string::String::as_str).collect(); - eprintln!("{}", { - let mut s = String::new(); - let _ = ColorfulTheme::default().format_multi_select_prompt_selection( - &mut s, - label, - &values_str, - ); - s - }); -} - fn push_main_or_master_branch(git_repo: &Repo) -> Result<()> { let main_branch_name = { let local_branches = git_repo diff --git a/src/bin/ngit/sub_commands/send.rs b/src/bin/ngit/sub_commands/send.rs index 05054fd..3ae941f 100644 --- a/src/bin/ngit/sub_commands/send.rs +++ b/src/bin/ngit/sub_commands/send.rs @@ -3,7 +3,9 @@ use std::{path::Path, str::FromStr, thread, time::Duration}; use anyhow::{Context, Result, bail}; use console::Style; use ngit::{ - cli_interactor::{PromptChoiceParms, multi_select_with_custom_value}, + cli_interactor::{ + PromptChoiceParms, multi_select_with_custom_value, show_multi_input_prompt_success, + }, client::{Params, send_events}, git::nostr_url::CloneUrl, git_events::{ @@ -343,12 +345,13 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re .collect(); let selections = vec![true; default_choices.len()]; // all selected by default let grasp_servers = multi_select_with_custom_value( - "grasp server(s)", + "alternative grasp server(s)", "grasp server", default_choices, selections, normalize_grasp_server_url, )?; + show_multi_input_prompt_success("alternative grasp server(s)", &grasp_servers); if grasp_servers.is_empty() { // ask again continue; @@ -388,16 +391,16 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re } normalised_grasp_servers } else { - println!( - "{} personal-fork so we can push commits to your prefered grasp servers", - if user_repo_ref.is_some() { - "Updating" - } else { - "Creating a" - }, - ); untried_user_grasp_servers }; + println!( + "{} personal-fork so we can push commits to your prefered grasp servers", + if user_repo_ref.is_some() { + "Updating" + } else { + "Creating a" + }, + ); let grasp_servers_as_personal_clone_url: Vec = grasp_servers .iter() @@ -415,7 +418,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re let updated_user_repo_ref = { if let Some(mut user_repo_ref) = user_repo_ref { for g in &grasp_servers_as_personal_clone_url { - let _ = user_repo_ref.add_grasp_server(g); + user_repo_ref.add_grasp_server(g)?; } user_repo_ref } else { diff --git a/src/lib/cli_interactor.rs b/src/lib/cli_interactor.rs index 8bcda19..e944bf9 100644 --- a/src/lib/cli_interactor.rs +++ b/src/lib/cli_interactor.rs @@ -1,5 +1,8 @@ use anyhow::{Context, Result}; -use dialoguer::{Confirm, Input, Password, theme::ColorfulTheme}; +use dialoguer::{ + Confirm, Input, Password, + theme::{ColorfulTheme, Theme}, +}; use indicatif::TermLike; #[cfg(test)] use mockall::*; @@ -323,6 +326,19 @@ where Ok(selected_choices) } +pub fn show_multi_input_prompt_success(label: &str, values: &[String]) { + let values_str: Vec<&str> = values.iter().map(std::string::String::as_str).collect(); + eprintln!("{}", { + let mut s = String::new(); + let _ = ColorfulTheme::default().format_multi_select_prompt_selection( + &mut s, + label, + &values_str, + ); + s + }); +} + #[derive(Debug, Default)] pub struct Printer { printed_lines: Vec, -- cgit v1.2.3