diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-07 17:52:22 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-08-07 17:52:22 +0100 |
| commit | 92c2362a9bed1bc1f256e7948e087c4102b7c4f9 (patch) | |
| tree | 700bf840ba52a8bd576afcfbe532a669f104dfcb /src/bin/ngit/sub_commands/init.rs | |
| parent | 8724af191f520a822214109f75a1851856c74fd2 (diff) | |
| parent | fa7adf840ac2d78defee398a61b60888f615622a (diff) | |
Merge branch 'add-prs-to-ngit-send'
Diffstat (limited to 'src/bin/ngit/sub_commands/init.rs')
| -rw-r--r-- | src/bin/ngit/sub_commands/init.rs | 137 |
1 files changed, 24 insertions, 113 deletions
diff --git a/src/bin/ngit/sub_commands/init.rs b/src/bin/ngit/sub_commands/init.rs index eaaf83d..98daee4 100644 --- a/src/bin/ngit/sub_commands/init.rs +++ b/src/bin/ngit/sub_commands/init.rs | |||
| @@ -9,15 +9,17 @@ use std::{ | |||
| 9 | 9 | ||
| 10 | use anyhow::{Context, Result, bail}; | 10 | use anyhow::{Context, Result, bail}; |
| 11 | use console::{Style, Term}; | 11 | use console::{Style, Term}; |
| 12 | use dialoguer::theme::{ColorfulTheme, Theme}; | ||
| 13 | use ngit::{ | 12 | use ngit::{ |
| 14 | UrlWithoutSlash, | 13 | UrlWithoutSlash, |
| 15 | cli_interactor::{PromptChoiceParms, PromptConfirmParms, PromptMultiChoiceParms}, | 14 | cli_interactor::{ |
| 15 | PromptChoiceParms, PromptConfirmParms, multi_select_with_custom_value, | ||
| 16 | show_multi_input_prompt_success, | ||
| 17 | }, | ||
| 16 | client::{Params, send_events}, | 18 | client::{Params, send_events}, |
| 17 | git::nostr_url::{CloneUrl, NostrUrlDecoded}, | 19 | git::nostr_url::{CloneUrl, NostrUrlDecoded}, |
| 18 | repo_ref::{ | 20 | repo_ref::{ |
| 19 | detect_existing_grasp_servers, extract_npub, extract_pks, normalize_grasp_server_url, | 21 | detect_existing_grasp_servers, extract_npub, extract_pks, |
| 20 | save_repo_config_to_yaml, | 22 | format_grasp_server_url_as_relay_url, normalize_grasp_server_url, save_repo_config_to_yaml, |
| 21 | }, | 23 | }, |
| 22 | }; | 24 | }; |
| 23 | use nostr::{ | 25 | use nostr::{ |
| @@ -266,11 +268,23 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 266 | ); | 268 | ); |
| 267 | let mut selections: Vec<bool> = vec![true; options.len()]; // Initialize selections based on existing options | 269 | let mut selections: Vec<bool> = vec![true; options.len()]; // Initialize selections based on existing options |
| 268 | let empty = options.is_empty(); | 270 | let empty = options.is_empty(); |
| 271 | for user_grasp_option in user_ref.grasp_list.urls { | ||
| 272 | // Check if any option contains the user_grasp_option as a substring | ||
| 273 | if !options | ||
| 274 | .iter() | ||
| 275 | .any(|option| option.contains(user_grasp_option.as_str())) | ||
| 276 | { | ||
| 277 | options.push(user_grasp_option.to_string()); // Add if not found | ||
| 278 | selections.push(empty); // mark as selected if no existing grasp otherwise not | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | let empty = options.is_empty(); | ||
| 269 | for fallback in fallback_grasp_servers { | 283 | for fallback in fallback_grasp_servers { |
| 270 | // Check if any option contains the fallback as a substring | 284 | // Check if any option contains the fallback as a substring |
| 271 | if !options.iter().any(|option| option.contains(fallback)) { | 285 | if !options.iter().any(|option| option.contains(fallback)) { |
| 272 | options.push(fallback.clone()); // Add fallback if not found | 286 | options.push(fallback.clone()); // Add fallback if not found |
| 273 | selections.push(empty); // mark as selected if no existing ngit relay otherwise not | 287 | selections.push(empty); // mark as selected if no existing selections otherwise not |
| 274 | } | 288 | } |
| 275 | } | 289 | } |
| 276 | let selected = multi_select_with_custom_value( | 290 | let selected = multi_select_with_custom_value( |
| @@ -727,6 +741,11 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 727 | web, | 741 | web, |
| 728 | relays: relays.clone(), | 742 | relays: relays.clone(), |
| 729 | blossoms, | 743 | blossoms, |
| 744 | hashtags: if let Some(repo_ref) = repo_ref { | ||
| 745 | repo_ref.hashtags | ||
| 746 | } else { | ||
| 747 | vec![] | ||
| 748 | }, | ||
| 730 | trusted_maintainer: user_ref.public_key, | 749 | trusted_maintainer: user_ref.public_key, |
| 731 | maintainers_without_annoucnement: None, | 750 | maintainers_without_annoucnement: None, |
| 732 | maintainers: maintainers.clone(), | 751 | maintainers: maintainers.clone(), |
| @@ -848,93 +867,6 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { | |||
| 848 | Ok(()) | 867 | Ok(()) |
| 849 | } | 868 | } |
| 850 | 869 | ||
| 851 | fn multi_select_with_custom_value<F>( | ||
| 852 | prompt: &str, | ||
| 853 | custom_choice_prompt: &str, | ||
| 854 | mut choices: Vec<String>, | ||
| 855 | mut defaults: Vec<bool>, | ||
| 856 | validate_choice: F, | ||
| 857 | ) -> Result<Vec<String>> | ||
| 858 | where | ||
| 859 | F: Fn(&str) -> Result<String>, | ||
| 860 | { | ||
| 861 | let mut selected_choices = vec![]; | ||
| 862 | |||
| 863 | // Loop to allow users to add more choices | ||
| 864 | loop { | ||
| 865 | // Add 'add another' option at the end of the choices | ||
| 866 | let mut current_choices = choices.clone(); | ||
| 867 | current_choices.push(if current_choices.is_empty() { | ||
| 868 | "add".to_string() | ||
| 869 | } else { | ||
| 870 | "add another".to_string() | ||
| 871 | }); | ||
| 872 | |||
| 873 | // Create default selections based on the provided defaults | ||
| 874 | let mut current_defaults = defaults.clone(); | ||
| 875 | current_defaults.push(current_choices.len() == 1); // 'add another' should not be selected by default | ||
| 876 | |||
| 877 | // Prompt for selections | ||
| 878 | let selected_indices: Vec<usize> = Interactor::default().multi_choice( | ||
| 879 | PromptMultiChoiceParms::default() | ||
| 880 | .with_prompt(prompt) | ||
| 881 | .dont_report() | ||
| 882 | .with_choices(current_choices.clone()) | ||
| 883 | .with_defaults(current_defaults), | ||
| 884 | )?; | ||
| 885 | |||
| 886 | // Collect selected choices | ||
| 887 | selected_choices.clear(); // Clear previous selections to update | ||
| 888 | for &index in &selected_indices { | ||
| 889 | if index < choices.len() { | ||
| 890 | // Exclude 'add another' option | ||
| 891 | selected_choices.push(choices[index].clone()); | ||
| 892 | } | ||
| 893 | } | ||
| 894 | |||
| 895 | // Check if 'add another' was selected | ||
| 896 | if selected_indices.contains(&(choices.len())) { | ||
| 897 | // Last index is 'add another' | ||
| 898 | let mut new_choice: String; | ||
| 899 | loop { | ||
| 900 | new_choice = Interactor::default().input( | ||
| 901 | PromptInputParms::default() | ||
| 902 | .with_prompt(custom_choice_prompt) | ||
| 903 | .dont_report() | ||
| 904 | .optional(), | ||
| 905 | )?; | ||
| 906 | |||
| 907 | if new_choice.is_empty() { | ||
| 908 | break; | ||
| 909 | } | ||
| 910 | // Validate the new choice | ||
| 911 | match validate_choice(&new_choice) { | ||
| 912 | Ok(valid_choice) => { | ||
| 913 | new_choice = valid_choice; // Use the fixed version of the input | ||
| 914 | break; // Valid choice, exit the loop | ||
| 915 | } | ||
| 916 | Err(err) => { | ||
| 917 | // Inform the user about the validation error | ||
| 918 | println!("Error: {err}"); | ||
| 919 | } | ||
| 920 | } | ||
| 921 | } | ||
| 922 | |||
| 923 | // Add the new choice to the choices vector | ||
| 924 | if !new_choice.is_empty() { | ||
| 925 | choices.push(new_choice.clone()); // Add new choice to the end of the list | ||
| 926 | selected_choices.push(new_choice); // Automatically select the new choice | ||
| 927 | defaults.push(true); // Set the new choice as selected by default | ||
| 928 | } | ||
| 929 | } else { | ||
| 930 | // Exit the loop if 'add another' was not selected | ||
| 931 | break; | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | Ok(selected_choices) | ||
| 936 | } | ||
| 937 | |||
| 938 | fn format_grasp_server_url_as_clone_url( | 870 | fn format_grasp_server_url_as_clone_url( |
| 939 | url: &str, | 871 | url: &str, |
| 940 | public_key: &PublicKey, | 872 | public_key: &PublicKey, |
| @@ -953,14 +885,6 @@ fn format_grasp_server_url_as_clone_url( | |||
| 953 | )) | 885 | )) |
| 954 | } | 886 | } |
| 955 | 887 | ||
| 956 | fn format_grasp_server_url_as_relay_url(url: &str) -> Result<String> { | ||
| 957 | let grasp_server_url = normalize_grasp_server_url(url)?; | ||
| 958 | if grasp_server_url.contains("http://") { | ||
| 959 | return Ok(grasp_server_url.replace("http://", "ws://")); | ||
| 960 | } | ||
| 961 | Ok(format!("wss://{grasp_server_url}")) | ||
| 962 | } | ||
| 963 | |||
| 964 | fn format_grasp_server_url_as_blossom_url(url: &str) -> Result<String> { | 888 | fn format_grasp_server_url_as_blossom_url(url: &str) -> Result<String> { |
| 965 | let grasp_server_url = normalize_grasp_server_url(url)?; | 889 | let grasp_server_url = normalize_grasp_server_url(url)?; |
| 966 | if grasp_server_url.contains("http://") { | 890 | if grasp_server_url.contains("http://") { |
| @@ -982,19 +906,6 @@ fn parse_relay_url(s: &str) -> Result<RelayUrl> { | |||
| 982 | .context(format!("failed to parse relay url: {s}")) | 906 | .context(format!("failed to parse relay url: {s}")) |
| 983 | } | 907 | } |
| 984 | 908 | ||
| 985 | pub fn show_multi_input_prompt_success(label: &str, values: &[String]) { | ||
| 986 | let values_str: Vec<&str> = values.iter().map(std::string::String::as_str).collect(); | ||
| 987 | eprintln!("{}", { | ||
| 988 | let mut s = String::new(); | ||
| 989 | let _ = ColorfulTheme::default().format_multi_select_prompt_selection( | ||
| 990 | &mut s, | ||
| 991 | label, | ||
| 992 | &values_str, | ||
| 993 | ); | ||
| 994 | s | ||
| 995 | }); | ||
| 996 | } | ||
| 997 | |||
| 998 | fn push_main_or_master_branch(git_repo: &Repo) -> Result<()> { | 909 | fn push_main_or_master_branch(git_repo: &Repo) -> Result<()> { |
| 999 | let main_branch_name = { | 910 | let main_branch_name = { |
| 1000 | let local_branches = git_repo | 911 | let local_branches = git_repo |