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/lib/cli_interactor.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'src/lib/cli_interactor.rs') diff --git a/src/lib/cli_interactor.rs b/src/lib/cli_interactor.rs index 8fca81d..8bcda19 100644 --- a/src/lib/cli_interactor.rs +++ b/src/lib/cli_interactor.rs @@ -236,6 +236,93 @@ impl PromptMultiChoiceParms { } } +pub 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) +} + #[derive(Debug, Default)] pub struct Printer { printed_lines: Vec, -- 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/lib/cli_interactor.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