diff --git a/Cargo.toml b/Cargo.toml index a88de84..bbc4ce0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "managarr" -version = "0.0.14" +version = "0.0.15" authors = ["Alex Clarke "] description = "A TUI for managing *arr servers" keywords = ["managarr", "tui-rs", "dashboard", "servarr"] diff --git a/README.md b/README.md index 93001d7..8f9d82b 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ tautulli: - [x] Manually search for movies - [x] Edit your movies and collections - [x] Manage your tags +- [x] Manage your root folders - [ ] Manage your quality profiles - [ ] Manage your quality definitions - [ ] Manage your indexers diff --git a/src/app/radarr.rs b/src/app/radarr.rs index b68b8e1..bbb28de 100644 --- a/src/app/radarr.rs +++ b/src/app/radarr.rs @@ -23,6 +23,7 @@ pub struct RadarrData { pub monitor_list: StatefulList, pub minimum_availability_list: StatefulList, pub quality_profile_list: StatefulList, + pub root_folder_list: StatefulList, pub selected_block: ActiveRadarrBlock, pub downloads: StatefulTable, pub quality_profile_map: BiMap, @@ -101,6 +102,7 @@ impl RadarrData { self.monitor_list = StatefulList::default(); self.minimum_availability_list = StatefulList::default(); self.quality_profile_list = StatefulList::default(); + self.root_folder_list = StatefulList::default(); } pub fn populate_preferences_lists(&mut self) { @@ -112,6 +114,9 @@ impl RadarrData { self.quality_profile_map.right_values().cloned().collect(); quality_profile_names.sort(); self.quality_profile_list.set_items(quality_profile_names); + self + .root_folder_list + .set_items(self.root_folders.items.to_vec()); } pub fn populate_edit_movie_fields(&mut self) { @@ -226,7 +231,8 @@ impl Default for RadarrData { monitor_list: StatefulList::default(), minimum_availability_list: StatefulList::default(), quality_profile_list: StatefulList::default(), - selected_block: ActiveRadarrBlock::AddMovieSelectMonitor, + root_folder_list: StatefulList::default(), + selected_block: ActiveRadarrBlock::AddMovieSelectRootFolder, filtered_movies: StatefulTable::default(), downloads: StatefulTable::default(), quality_profile_map: BiMap::default(), @@ -333,6 +339,7 @@ pub enum ActiveRadarrBlock { AddMovieSelectMinimumAvailability, AddMovieSelectQualityProfile, AddMovieSelectMonitor, + AddMovieSelectRootFolder, AddMovieConfirmPrompt, AddMovieTagsInput, AddMovieEmptySearchResults, @@ -379,7 +386,7 @@ pub enum ActiveRadarrBlock { ViewMovieOverview, } -pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 9] = [ +pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 10] = [ ActiveRadarrBlock::AddMovieSearchInput, ActiveRadarrBlock::AddMovieSearchResults, ActiveRadarrBlock::AddMovieEmptySearchResults, @@ -387,6 +394,7 @@ pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 9] = [ ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectQualityProfile, + ActiveRadarrBlock::AddMovieSelectRootFolder, ActiveRadarrBlock::AddMovieAlreadyInLibrary, ActiveRadarrBlock::AddMovieTagsInput, ]; @@ -436,6 +444,7 @@ pub const FILTER_BLOCKS: [ActiveRadarrBlock; 2] = [ impl ActiveRadarrBlock { pub fn next_add_movie_prompt_block(&self) -> Self { match self { + ActiveRadarrBlock::AddMovieSelectRootFolder => ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMonitor => { ActiveRadarrBlock::AddMovieSelectMinimumAvailability } @@ -444,7 +453,7 @@ impl ActiveRadarrBlock { } ActiveRadarrBlock::AddMovieSelectQualityProfile => ActiveRadarrBlock::AddMovieTagsInput, ActiveRadarrBlock::AddMovieTagsInput => ActiveRadarrBlock::AddMovieConfirmPrompt, - _ => ActiveRadarrBlock::AddMovieSelectMonitor, + _ => ActiveRadarrBlock::AddMovieSelectRootFolder, } } @@ -486,7 +495,8 @@ impl ActiveRadarrBlock { pub fn previous_add_movie_prompt_block(&self) -> Self { match self { - ActiveRadarrBlock::AddMovieSelectMonitor => ActiveRadarrBlock::AddMovieConfirmPrompt, + ActiveRadarrBlock::AddMovieSelectRootFolder => ActiveRadarrBlock::AddMovieConfirmPrompt, + ActiveRadarrBlock::AddMovieSelectMonitor => ActiveRadarrBlock::AddMovieSelectRootFolder, ActiveRadarrBlock::AddMovieSelectMinimumAvailability => { ActiveRadarrBlock::AddMovieSelectMonitor } @@ -495,7 +505,7 @@ impl ActiveRadarrBlock { } ActiveRadarrBlock::AddMovieTagsInput => ActiveRadarrBlock::AddMovieSelectQualityProfile, ActiveRadarrBlock::AddMovieConfirmPrompt => ActiveRadarrBlock::AddMovieTagsInput, - _ => ActiveRadarrBlock::AddMovieSelectMonitor, + _ => ActiveRadarrBlock::AddMovieSelectRootFolder, } } @@ -712,7 +722,7 @@ pub mod radarr_test_utils { use crate::app::radarr::RadarrData; use crate::models::radarr_models::{ AddMovieSearchResult, Collection, CollectionMovie, Credit, MinimumAvailability, Monitor, Movie, - MovieHistoryItem, Release, ReleaseField, + MovieHistoryItem, Release, ReleaseField, RootFolder, }; use crate::models::ScrollableText; @@ -748,6 +758,9 @@ pub mod radarr_test_utils { radarr_data .quality_profile_list .set_items(vec![String::default()]); + radarr_data + .root_folder_list + .set_items(vec![RootFolder::default()]); radarr_data .movie_releases_sort .set_items(vec![ReleaseField::default()]); @@ -830,6 +843,7 @@ pub mod radarr_test_utils { assert!($radarr_data.monitor_list.items.is_empty()); assert!($radarr_data.minimum_availability_list.items.is_empty()); assert!($radarr_data.quality_profile_list.items.is_empty()); + assert!($radarr_data.root_folder_list.items.is_empty()); }; } } @@ -846,7 +860,9 @@ mod tests { use crate::app::radarr::radarr_test_utils::create_test_radarr_data; use crate::app::radarr::{ActiveRadarrBlock, RadarrData}; - use crate::models::radarr_models::{Collection, MinimumAvailability, Monitor, Movie}; + use crate::models::radarr_models::{ + Collection, MinimumAvailability, Monitor, Movie, RootFolder, + }; use crate::models::HorizontallyScrollableText; use crate::models::Route; use crate::models::StatefulTable; @@ -927,6 +943,13 @@ mod tests { #[test] fn test_populate_preferences_lists() { + let root_folder = RootFolder { + id: Number::from(1), + path: "/nfs".to_owned(), + accessible: true, + free_space: Number::from(219902325555200u64), + unmapped_folders: None, + }; let mut radarr_data = RadarrData { quality_profile_map: BiMap::from_iter([ (2222, "HD - 1080p".to_owned()), @@ -934,6 +957,9 @@ mod tests { ]), ..RadarrData::default() }; + radarr_data + .root_folders + .set_items(vec![root_folder.clone()]); radarr_data.populate_preferences_lists(); @@ -949,6 +975,7 @@ mod tests { radarr_data.quality_profile_list.items, vec!["Any".to_owned(), "HD - 1080p".to_owned()] ); + assert_eq!(radarr_data.root_folder_list.items, vec![root_folder]); } #[rstest] @@ -1069,9 +1096,10 @@ mod tests { assert!(radarr_data.monitor_list.items.is_empty()); assert!(radarr_data.minimum_availability_list.items.is_empty()); assert!(radarr_data.quality_profile_list.items.is_empty()); + assert!(radarr_data.root_folder_list.items.is_empty()); assert_eq!( radarr_data.selected_block, - ActiveRadarrBlock::AddMovieSelectMonitor + ActiveRadarrBlock::AddMovieSelectRootFolder ); assert!(radarr_data.filtered_movies.items.is_empty()); assert!(radarr_data.downloads.items.is_empty()); @@ -1233,7 +1261,11 @@ mod tests { #[test] fn test_next_add_movie_prompt_block() { - let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.next_add_movie_prompt_block(); + let active_block = ActiveRadarrBlock::AddMovieSelectRootFolder.next_add_movie_prompt_block(); + + assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectMonitor); + + let active_block = active_block.next_add_movie_prompt_block(); assert_eq!( active_block, @@ -1257,7 +1289,7 @@ mod tests { let active_block = active_block.next_add_movie_prompt_block(); - assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectMonitor); + assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectRootFolder); } #[test] @@ -1338,7 +1370,8 @@ mod tests { #[test] fn test_previous_add_movie_prompt_block() { - let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.previous_add_movie_prompt_block(); + let active_block = + ActiveRadarrBlock::AddMovieSelectRootFolder.previous_add_movie_prompt_block(); assert_eq!(active_block, ActiveRadarrBlock::AddMovieConfirmPrompt); @@ -1363,6 +1396,10 @@ mod tests { let active_block = active_block.previous_add_movie_prompt_block(); assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectMonitor); + + let active_block = active_block.previous_add_movie_prompt_block(); + + assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectRootFolder); } #[test] diff --git a/src/handlers/radarr_handlers/add_movie_handler.rs b/src/handlers/radarr_handlers/add_movie_handler.rs index 2acd85e..7b9be02 100644 --- a/src/handlers/radarr_handlers/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/add_movie_handler.rs @@ -48,6 +48,9 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { ActiveRadarrBlock::AddMovieSelectQualityProfile => { self.app.data.radarr_data.quality_profile_list.scroll_up() } + ActiveRadarrBlock::AddMovieSelectRootFolder => { + self.app.data.radarr_data.root_folder_list.scroll_up() + } ActiveRadarrBlock::AddMoviePrompt => { self.app.data.radarr_data.selected_block = self .app @@ -78,6 +81,9 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { ActiveRadarrBlock::AddMovieSelectQualityProfile => { self.app.data.radarr_data.quality_profile_list.scroll_down() } + ActiveRadarrBlock::AddMovieSelectRootFolder => { + self.app.data.radarr_data.root_folder_list.scroll_down() + } ActiveRadarrBlock::AddMoviePrompt => { self.app.data.radarr_data.selected_block = self .app @@ -113,6 +119,9 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { .radarr_data .quality_profile_list .scroll_to_top(), + ActiveRadarrBlock::AddMovieSelectRootFolder => { + self.app.data.radarr_data.root_folder_list.scroll_to_top() + } ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.scroll_home(), ActiveRadarrBlock::AddMovieTagsInput => self.app.data.radarr_data.edit_tags.scroll_home(), _ => (), @@ -142,6 +151,12 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { .radarr_data .quality_profile_list .scroll_to_bottom(), + ActiveRadarrBlock::AddMovieSelectRootFolder => self + .app + .data + .radarr_data + .root_folder_list + .scroll_to_bottom(), ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.reset_offset(), ActiveRadarrBlock::AddMovieTagsInput => self.app.data.radarr_data.edit_tags.reset_offset(), _ => (), @@ -206,7 +221,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { .app .push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); self.app.data.radarr_data.populate_preferences_lists(); - self.app.data.radarr_data.selected_block = ActiveRadarrBlock::AddMovieSelectMonitor; + self.app.data.radarr_data.selected_block = ActiveRadarrBlock::AddMovieSelectRootFolder; } } ActiveRadarrBlock::AddMoviePrompt => match self.app.data.radarr_data.selected_block { @@ -220,7 +235,8 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { } ActiveRadarrBlock::AddMovieSelectMonitor | ActiveRadarrBlock::AddMovieSelectMinimumAvailability - | ActiveRadarrBlock::AddMovieSelectQualityProfile => self + | ActiveRadarrBlock::AddMovieSelectQualityProfile + | ActiveRadarrBlock::AddMovieSelectRootFolder => self .app .push_navigation_stack((self.app.data.radarr_data.selected_block, *self.context).into()), ActiveRadarrBlock::AddMovieTagsInput => { @@ -233,7 +249,8 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { }, ActiveRadarrBlock::AddMovieSelectMonitor | ActiveRadarrBlock::AddMovieSelectMinimumAvailability - | ActiveRadarrBlock::AddMovieSelectQualityProfile => self.app.pop_navigation_stack(), + | ActiveRadarrBlock::AddMovieSelectQualityProfile + | ActiveRadarrBlock::AddMovieSelectRootFolder => self.app.pop_navigation_stack(), ActiveRadarrBlock::AddMovieTagsInput => { self.app.pop_navigation_stack(); self.app.should_ignore_quit_key = false; @@ -262,7 +279,8 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { ActiveRadarrBlock::AddMovieSelectMonitor | ActiveRadarrBlock::AddMovieSelectMinimumAvailability | ActiveRadarrBlock::AddMovieSelectQualityProfile - | ActiveRadarrBlock::AddMovieAlreadyInLibrary => self.app.pop_navigation_stack(), + | ActiveRadarrBlock::AddMovieAlreadyInLibrary + | ActiveRadarrBlock::AddMovieSelectRootFolder => self.app.pop_navigation_stack(), ActiveRadarrBlock::AddMovieTagsInput => { self.app.pop_navigation_stack(); self.app.should_ignore_quit_key = false; @@ -295,7 +313,9 @@ mod tests { use crate::event::Key; use crate::handlers::radarr_handlers::add_movie_handler::AddMovieHandler; use crate::handlers::KeyEventHandler; - use crate::models::radarr_models::{AddMovieSearchResult, MinimumAvailability, Monitor}; + use crate::models::radarr_models::{ + AddMovieSearchResult, MinimumAvailability, Monitor, RootFolder, + }; use crate::models::HorizontallyScrollableText; mod test_handle_scroll_up_and_down { @@ -344,6 +364,16 @@ mod tests { None ); + test_iterable_scroll!( + test_add_movie_select_root_folder_scroll, + AddMovieHandler, + root_folder_list, + simple_stateful_iterable_vec!(RootFolder, String, path), + ActiveRadarrBlock::AddMovieSelectRootFolder, + None, + path + ); + #[rstest] fn test_add_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) { let mut app = App::default(); @@ -405,13 +435,23 @@ mod tests { ); test_iterable_home_and_end!( - test_add_movie_select_quality_profile_scroll, + test_add_movie_select_quality_profile_home_end, AddMovieHandler, quality_profile_list, ActiveRadarrBlock::AddMovieSelectQualityProfile, None ); + test_iterable_home_and_end!( + test_add_movie_select_root_folder_home_end, + AddMovieHandler, + root_folder_list, + extended_stateful_iterable_vec!(RootFolder, String, path), + ActiveRadarrBlock::AddMovieSelectRootFolder, + None, + path + ); + #[test] fn test_add_movie_search_input_home_end_keys() { test_text_box_home_end_keys!( @@ -528,7 +568,7 @@ mod tests { ); assert_eq!( app.data.radarr_data.selected_block, - ActiveRadarrBlock::AddMovieSelectMonitor + ActiveRadarrBlock::AddMovieSelectRootFolder ); assert!(!app.data.radarr_data.monitor_list.items.is_empty()); assert!(!app @@ -642,6 +682,7 @@ mod tests { ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ActiveRadarrBlock::AddMovieSelectQualityProfile, + ActiveRadarrBlock::AddMovieSelectRootFolder, ActiveRadarrBlock::AddMovieTagsInput )] selected_block: ActiveRadarrBlock, @@ -681,6 +722,7 @@ mod tests { ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ActiveRadarrBlock::AddMovieSelectQualityProfile, + ActiveRadarrBlock::AddMovieSelectRootFolder, ActiveRadarrBlock::AddMovieTagsInput )] active_radarr_block: ActiveRadarrBlock, @@ -868,7 +910,8 @@ mod tests { #[values( ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMinimumAvailability, - ActiveRadarrBlock::AddMovieSelectQualityProfile + ActiveRadarrBlock::AddMovieSelectQualityProfile, + ActiveRadarrBlock::AddMovieSelectRootFolder )] active_radarr_block: ActiveRadarrBlock, ) { diff --git a/src/handlers/radarr_handlers/mod.rs b/src/handlers/radarr_handlers/mod.rs index 105bbf6..c170ab9 100644 --- a/src/handlers/radarr_handlers/mod.rs +++ b/src/handlers/radarr_handlers/mod.rs @@ -11,7 +11,7 @@ use crate::handlers::radarr_handlers::movie_details_handler::MovieDetailsHandler use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler}; use crate::models::{HorizontallyScrollableText, Scrollable}; use crate::network::radarr_network::RadarrEvent; -use crate::utils::strip_non_alphanumeric_characters; +use crate::utils::strip_non_search_characters; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, App, Key}; mod add_movie_handler; @@ -572,7 +572,7 @@ impl RadarrHandler<'_> { { let search_string = self.app.data.radarr_data.search.drain().to_lowercase(); let search_index = rows.iter().position(|item| { - strip_non_alphanumeric_characters(field_selection_fn(item)).contains(&search_string) + strip_non_search_characters(field_selection_fn(item)).contains(&search_string) }); self.app.data.radarr_data.is_searching = false; @@ -590,10 +590,10 @@ impl RadarrHandler<'_> { F: Fn(&T) -> &str, T: Clone, { - let filter = strip_non_alphanumeric_characters(&self.app.data.radarr_data.filter.drain()); + let filter = strip_non_search_characters(&self.app.data.radarr_data.filter.drain()); let filter_matches: Vec = rows .iter() - .filter(|&item| strip_non_alphanumeric_characters(field_selection_fn(item)).contains(&filter)) + .filter(|&item| strip_non_search_characters(field_selection_fn(item)).contains(&filter)) .cloned() .collect(); @@ -1951,6 +1951,7 @@ mod tests { ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ActiveRadarrBlock::AddMovieSelectQualityProfile, + ActiveRadarrBlock::AddMovieSelectRootFolder, ActiveRadarrBlock::AddMovieAlreadyInLibrary, ActiveRadarrBlock::AddMovieTagsInput )] diff --git a/src/ui/radarr_ui/add_movie_ui.rs b/src/ui/radarr_ui/add_movie_ui.rs index 3f34ee5..6a169da 100644 --- a/src/ui/radarr_ui/add_movie_ui.rs +++ b/src/ui/radarr_ui/add_movie_ui.rs @@ -10,6 +10,7 @@ use crate::models::Route; use crate::ui::radarr_ui::collection_details_ui::draw_collection_details; use crate::ui::radarr_ui::{ draw_select_minimum_availability_popup, draw_select_quality_profile_popup, + draw_select_root_folder_popup, }; use crate::ui::utils::{ borderless_block, get_width_from_percentage, horizontal_chunks, layout_block, @@ -40,6 +41,7 @@ pub(super) fn draw_add_movie_search_popup( | ActiveRadarrBlock::AddMovieSelectMonitor | ActiveRadarrBlock::AddMovieSelectMinimumAvailability | ActiveRadarrBlock::AddMovieSelectQualityProfile + | ActiveRadarrBlock::AddMovieSelectRootFolder | ActiveRadarrBlock::AddMovieTagsInput => { if context_option.is_some() { draw_medium_popup_over( @@ -119,6 +121,7 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App, area: | ActiveRadarrBlock::AddMovieSelectMonitor | ActiveRadarrBlock::AddMovieSelectMinimumAvailability | ActiveRadarrBlock::AddMovieSelectQualityProfile + | ActiveRadarrBlock::AddMovieSelectRootFolder | ActiveRadarrBlock::AddMovieAlreadyInLibrary | ActiveRadarrBlock::AddMovieTagsInput => { let mut help_text = Text::from(" details | edit search"); @@ -260,6 +263,15 @@ fn draw_confirmation_popup(f: &mut Frame<'_, B>, app: &mut App, prom draw_select_quality_profile_popup, ); } + ActiveRadarrBlock::AddMovieSelectRootFolder => { + draw_drop_down_popup( + f, + app, + prompt_area, + draw_confirmation_prompt, + draw_select_root_folder_popup, + ); + } ActiveRadarrBlock::AddMoviePrompt | ActiveRadarrBlock::AddMovieTagsInput => { draw_confirmation_prompt(f, app, prompt_area) } @@ -278,7 +290,6 @@ fn draw_select_monitor_popup(f: &mut Frame<'_, B>, app: &mut App, po } fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, prompt_area: Rect) { - let title = "Add Movie"; let (movie_title, movie_overview) = if let Route::Radarr(_, Some(_)) = app.get_current_route() { ( &app @@ -314,7 +325,8 @@ fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, pro .clone(), ) }; - let prompt = format!("{}:\n\n{}", movie_title, movie_overview); + let title = format!("Add Movie - {}", movie_title); + let prompt = movie_overview; let yes_no_value = &app.data.radarr_data.prompt_confirm; let selected_block = &app.data.radarr_data.selected_block; let highlight_yes_no = *selected_block == ActiveRadarrBlock::AddMovieConfirmPrompt; @@ -330,17 +342,19 @@ fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, pro .radarr_data .quality_profile_list .current_selection(); + let selected_root_folder = app.data.radarr_data.root_folder_list.current_selection(); - f.render_widget(title_block_centered(title), prompt_area); + f.render_widget(title_block_centered(&title), prompt_area); let chunks = vertical_chunks_with_margin( vec![ - Constraint::Percentage(30), + Constraint::Length(6), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Min(3), + Constraint::Length(3), + Constraint::Min(0), Constraint::Length(3), ], prompt_area, @@ -352,12 +366,20 @@ fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, pro let horizontal_chunks = horizontal_chunks( vec![Constraint::Percentage(50), Constraint::Percentage(50)], - chunks[6], + chunks[7], ); draw_drop_down_menu_button( f, chunks[1], + "Root Folder", + &selected_root_folder.path, + *selected_block == ActiveRadarrBlock::AddMovieSelectRootFolder, + ); + + draw_drop_down_menu_button( + f, + chunks[2], "Monitor", selected_monitor.to_display_str(), *selected_block == ActiveRadarrBlock::AddMovieSelectMonitor, @@ -365,14 +387,14 @@ fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, pro draw_drop_down_menu_button( f, - chunks[2], + chunks[3], "Minimum Availability", selected_minimum_availability.to_display_str(), *selected_block == ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ); draw_drop_down_menu_button( f, - chunks[3], + chunks[4], "Quality Profile", selected_quality_profile, *selected_block == ActiveRadarrBlock::AddMovieSelectQualityProfile, @@ -381,7 +403,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, pro if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() { draw_text_box_with_label( f, - chunks[4], + chunks[5], "Tags", &app.data.radarr_data.edit_tags.text, *app.data.radarr_data.edit_tags.offset.borrow(), diff --git a/src/ui/radarr_ui/edit_collection_ui.rs b/src/ui/radarr_ui/edit_collection_ui.rs index bef1895..09032c2 100644 --- a/src/ui/radarr_ui/edit_collection_ui.rs +++ b/src/ui/radarr_ui/edit_collection_ui.rs @@ -115,7 +115,7 @@ fn draw_edit_collection_confirmation_prompt( let chunks = vertical_chunks_with_margin( vec![ - Constraint::Percentage(25), + Constraint::Length(6), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), diff --git a/src/ui/radarr_ui/edit_movie_ui.rs b/src/ui/radarr_ui/edit_movie_ui.rs index 5381540..868e70f 100644 --- a/src/ui/radarr_ui/edit_movie_ui.rs +++ b/src/ui/radarr_ui/edit_movie_ui.rs @@ -112,7 +112,7 @@ fn draw_edit_movie_confirmation_prompt( let chunks = vertical_chunks_with_margin( vec![ - Constraint::Percentage(25), + Constraint::Length(6), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), diff --git a/src/ui/radarr_ui/mod.rs b/src/ui/radarr_ui/mod.rs index ed755e1..9d0aa84 100644 --- a/src/ui/radarr_ui/mod.rs +++ b/src/ui/radarr_ui/mod.rs @@ -879,3 +879,16 @@ fn draw_select_quality_profile_popup( |quality_profile| ListItem::new(quality_profile.clone()), ); } + +fn draw_select_root_folder_popup( + f: &mut Frame<'_, B>, + app: &mut App, + popup_area: Rect, +) { + draw_drop_down_list( + f, + popup_area, + &mut app.data.radarr_data.root_folder_list, + |root_folder| ListItem::new(root_folder.path.to_owned()), + ); +} diff --git a/src/utils.rs b/src/utils.rs index ffc74bb..7fa5bd4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -34,8 +34,8 @@ pub fn convert_runtime(runtime: u64) -> (u64, u64) { (hours, minutes) } -pub fn strip_non_alphanumeric_characters(input: &str) -> String { - Regex::new(r"[^a-zA-Z0-9\s]") +pub fn strip_non_search_characters(input: &str) -> String { + Regex::new(r"[^a-zA-Z0-9.,/'\-:\s]") .unwrap() .replace_all(&input.to_lowercase(), "") .to_string() @@ -45,7 +45,7 @@ pub fn strip_non_alphanumeric_characters(input: &str) -> String { mod tests { use pretty_assertions::assert_eq; - use crate::utils::{convert_runtime, convert_to_gb, strip_non_alphanumeric_characters}; + use crate::utils::{convert_runtime, convert_to_gb, strip_non_search_characters}; #[test] fn test_convert_to_gb() { @@ -64,8 +64,8 @@ mod tests { #[test] fn test_strip_non_alphanumeric_characters() { assert_eq!( - strip_non_alphanumeric_characters("Te$t S7r!ng::'~-_}"), - "tet s7rng".to_owned() + strip_non_search_characters("Te$t S7r!ng::'~-@_`,(.)/*}^&%#+="), + "tet s7rng::'-,./".to_owned() ) } }