diff --git a/Cargo.toml b/Cargo.toml index 030f3c5..3eb1b24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "managarr" version = "0.1.5" authors = ["Alex Clarke "] description = "A TUI and CLI to manage your Servarrs" -keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"] +keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"] documentation = "https://github.com/Dark-Alex-17/managarr" repository = "https://github.com/Dark-Alex-17/managarr" homepage = "https://github.com/Dark-Alex-17/managarr" diff --git a/src/app/key_binding.rs b/src/app/key_binding.rs index 712628a..44f0518 100644 --- a/src/app/key_binding.rs +++ b/src/app/key_binding.rs @@ -33,6 +33,7 @@ generate_keybindings! { tab, delete, submit, + confirm, quit, esc } @@ -140,6 +141,10 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings { key: Key::Enter, desc: "submit", }, + confirm: KeyBinding { + key: Key::Ctrl('s'), + desc: "submit", + }, quit: KeyBinding { key: Key::Char('q'), desc: "quit", diff --git a/src/app/key_binding_tests.rs b/src/app/key_binding_tests.rs index c9a776a..1a17a95 100644 --- a/src/app/key_binding_tests.rs +++ b/src/app/key_binding_tests.rs @@ -31,6 +31,7 @@ mod test { #[case(DEFAULT_KEYBINDINGS.tab, Key::Tab, "tab")] #[case(DEFAULT_KEYBINDINGS.delete, Key::Delete, "delete")] #[case(DEFAULT_KEYBINDINGS.submit, Key::Enter, "submit")] + #[case(DEFAULT_KEYBINDINGS.confirm, Key::Ctrl('s'), "submit")] #[case(DEFAULT_KEYBINDINGS.quit, Key::Char('q'), "quit")] #[case(DEFAULT_KEYBINDINGS.esc, Key::Esc, "close")] fn test_default_key_bindings_and_descriptions( diff --git a/src/app/radarr/radarr_context_clues.rs b/src/app/radarr/radarr_context_clues.rs index daa17cc..a9d6a40 100644 --- a/src/app/radarr/radarr_context_clues.rs +++ b/src/app/radarr/radarr_context_clues.rs @@ -120,6 +120,11 @@ pub static ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [ (DEFAULT_KEYBINDINGS.esc, "edit search"), ]; +pub static CONFIRMATION_PROMPT_CONTEXT_CLUES: [ContextClue; 2] = [ + (DEFAULT_KEYBINDINGS.confirm, "submit"), + (DEFAULT_KEYBINDINGS.esc, "cancel"), +]; + pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [ (DEFAULT_KEYBINDINGS.submit, "start task"), (DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc), diff --git a/src/app/radarr/radarr_context_clues_tests.rs b/src/app/radarr/radarr_context_clues_tests.rs index 7fc93bc..b20475a 100644 --- a/src/app/radarr/radarr_context_clues_tests.rs +++ b/src/app/radarr/radarr_context_clues_tests.rs @@ -5,8 +5,8 @@ mod tests { use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::radarr::radarr_context_clues::{ ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES, - COLLECTION_DETAILS_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES, - LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, + COLLECTION_DETAILS_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, + INDEXERS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES, }; @@ -349,6 +349,22 @@ mod tests { assert_eq!(add_movie_search_results_context_clues_iter.next(), None); } + #[test] + fn test_confirmation_prompt_context_clues() { + let mut confirmation_prompt_context_clues_iter = CONFIRMATION_PROMPT_CONTEXT_CLUES.iter(); + + let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap(); + + assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.confirm); + assert_str_eq!(*description, "submit"); + + let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap(); + + assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc); + assert_str_eq!(*description, "cancel"); + assert_eq!(confirmation_prompt_context_clues_iter.next(), None); + } + #[test] fn test_system_tasks_context_clues() { let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter(); diff --git a/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs b/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs index 5dae8fc..2dd8551 100644 --- a/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs +++ b/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs @@ -584,6 +584,9 @@ mod tests { mod test_handle_key_char { use pretty_assertions::assert_eq; + use rstest::rstest; + + use crate::network::radarr_network::RadarrEvent; use super::*; @@ -716,6 +719,43 @@ mod tests { assert!(app.data.radarr_data.blocklist.sort.is_none()); assert!(!app.data.radarr_data.blocklist.sort_asc); } + + #[rstest] + #[case( + ActiveRadarrBlock::Blocklist, + ActiveRadarrBlock::DeleteBlocklistItemPrompt, + RadarrEvent::DeleteBlocklistItem(None) + )] + #[case( + ActiveRadarrBlock::Blocklist, + ActiveRadarrBlock::BlocklistClearAllItemsPrompt, + RadarrEvent::ClearBlocklist + )] + fn test_blocklist_prompt_confirm( + #[case] base_route: ActiveRadarrBlock, + #[case] prompt_block: ActiveRadarrBlock, + #[case] expected_action: RadarrEvent, + ) { + let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + app.push_navigation_stack(base_route.into()); + app.push_navigation_stack(prompt_block.into()); + + BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &prompt_block, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(expected_action) + ); + assert_eq!(app.get_current_route(), &base_route.into()); + } } #[test] diff --git a/src/handlers/radarr_handlers/blocklist/mod.rs b/src/handlers/radarr_handlers/blocklist/mod.rs index f67d3b4..88bc21f 100644 --- a/src/handlers/radarr_handlers/blocklist/mod.rs +++ b/src/handlers/radarr_handlers/blocklist/mod.rs @@ -182,8 +182,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, fn handle_char_key_event(&mut self) { let key = self.key; - if self.active_radarr_block == &ActiveRadarrBlock::Blocklist { - match self.key { + match self.active_radarr_block { + ActiveRadarrBlock::Blocklist => match self.key { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => { self.app.should_refresh = true; } @@ -204,7 +204,25 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, .push_navigation_stack(ActiveRadarrBlock::BlocklistSortPrompt.into()); } _ => (), + }, + ActiveRadarrBlock::DeleteBlocklistItemPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = + Some(RadarrEvent::DeleteBlocklistItem(None)); + + self.app.pop_navigation_stack(); + } } + ActiveRadarrBlock::BlocklistClearAllItemsPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::ClearBlocklist); + + self.app.pop_navigation_stack(); + } + } + _ => (), } } } diff --git a/src/handlers/radarr_handlers/collections/collections_handler_tests.rs b/src/handlers/radarr_handlers/collections/collections_handler_tests.rs index 2134064..1eb268b 100644 --- a/src/handlers/radarr_handlers/collections/collections_handler_tests.rs +++ b/src/handlers/radarr_handlers/collections/collections_handler_tests.rs @@ -1040,6 +1040,7 @@ mod tests { use crate::models::servarr_data::radarr::radarr_data::{ RadarrData, EDIT_COLLECTION_SELECTION_BLOCKS, }; + use crate::network::radarr_network::RadarrEvent; use crate::test_edit_collection_key; use super::*; @@ -1509,6 +1510,36 @@ mod tests { assert!(app.data.radarr_data.collections.sort.is_none()); assert!(!app.data.radarr_data.collections.sort_asc); } + + #[test] + fn test_update_all_collections_prompt_confirm_confirm() { + let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into()); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::UpdateAllCollectionsPrompt, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::UpdateCollections) + ); + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + } } #[rstest] diff --git a/src/handlers/radarr_handlers/collections/edit_collection_handler.rs b/src/handlers/radarr_handlers/collections/edit_collection_handler.rs index d7bb213..5a7c1bc 100644 --- a/src/handlers/radarr_handlers/collections/edit_collection_handler.rs +++ b/src/handlers/radarr_handlers/collections/edit_collection_handler.rs @@ -291,19 +291,34 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle fn handle_char_key_event(&mut self) { let key = self.key; - if self.active_radarr_block == &ActiveRadarrBlock::EditCollectionRootFolderPathInput { - handle_text_box_keys!( - self, - key, - self - .app - .data - .radarr_data - .edit_collection_modal - .as_mut() - .unwrap() - .path - ) + match self.active_radarr_block { + ActiveRadarrBlock::EditCollectionRootFolderPathInput => { + handle_text_box_keys!( + self, + key, + self + .app + .data + .radarr_data + .edit_collection_modal + .as_mut() + .unwrap() + .path + ) + } + ActiveRadarrBlock::EditCollectionPrompt => { + if self.app.data.radarr_data.selected_block.get_active_block() + == &ActiveRadarrBlock::EditCollectionConfirmPrompt + && *key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(None)); + self.app.should_refresh = true; + + self.app.pop_navigation_stack(); + } + } + _ => (), } } } diff --git a/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs b/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs index 4aaed4e..395b574 100644 --- a/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs +++ b/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs @@ -871,7 +871,15 @@ mod tests { mod test_handle_key_char { use super::*; - use crate::models::servarr_data::radarr::modals::EditCollectionModal; + use crate::{ + models::{ + servarr_data::radarr::{ + modals::EditCollectionModal, radarr_data::EDIT_COLLECTION_SELECTION_BLOCKS, + }, + BlockSelectionState, + }, + network::radarr_network::RadarrEvent, + }; #[test] fn test_edit_collection_root_folder_path_input_backspace() { @@ -927,6 +935,39 @@ mod tests { "h" ); } + + #[test] + fn test_edit_collection_confirm_prompt_prompt_confirmation_confirm() { + let mut app = App::default(); + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); + app.data.radarr_data.selected_block = + BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); + app + .data + .radarr_data + .selected_block + .set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); + + EditCollectionHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::EditCollectionPrompt, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::EditCollection(None)) + ); + assert!(app.should_refresh); + } } #[test] diff --git a/src/handlers/radarr_handlers/collections/mod.rs b/src/handlers/radarr_handlers/collections/mod.rs index 942a855..595262f 100644 --- a/src/handlers/radarr_handlers/collections/mod.rs +++ b/src/handlers/radarr_handlers/collections/mod.rs @@ -385,6 +385,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<' .unwrap() ) } + ActiveRadarrBlock::UpdateAllCollectionsPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections); + + self.app.pop_navigation_stack(); + } + } _ => (), } } diff --git a/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs b/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs index 0edcc0a..17fed42 100644 --- a/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs +++ b/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs @@ -349,6 +349,9 @@ mod tests { mod test_handle_key_char { use pretty_assertions::assert_eq; + use rstest::rstest; + + use crate::network::radarr_network::RadarrEvent; use super::*; @@ -450,6 +453,47 @@ mod tests { ); assert!(!app.should_refresh); } + + #[rstest] + #[case( + ActiveRadarrBlock::Downloads, + ActiveRadarrBlock::DeleteDownloadPrompt, + RadarrEvent::DeleteDownload(None) + )] + #[case( + ActiveRadarrBlock::Downloads, + ActiveRadarrBlock::UpdateDownloadsPrompt, + RadarrEvent::UpdateDownloads + )] + fn test_downloads_prompt_confirm_submit( + #[case] base_route: ActiveRadarrBlock, + #[case] prompt_block: ActiveRadarrBlock, + #[case] expected_action: RadarrEvent, + ) { + let mut app = App::default(); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); + app.push_navigation_stack(base_route.into()); + app.push_navigation_stack(prompt_block.into()); + + DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &prompt_block, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(expected_action) + ); + assert_eq!(app.get_current_route(), &base_route.into()); + } } #[test] diff --git a/src/handlers/radarr_handlers/downloads/mod.rs b/src/handlers/radarr_handlers/downloads/mod.rs index b6d4caa..d3b64a8 100644 --- a/src/handlers/radarr_handlers/downloads/mod.rs +++ b/src/handlers/radarr_handlers/downloads/mod.rs @@ -119,8 +119,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, fn handle_char_key_event(&mut self) { let key = self.key; - if self.active_radarr_block == &ActiveRadarrBlock::Downloads { - match self.key { + match self.active_radarr_block { + ActiveRadarrBlock::Downloads => match self.key { _ if *key == DEFAULT_KEYBINDINGS.update.key => { self .app @@ -130,7 +130,24 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, self.app.should_refresh = true; } _ => (), + }, + ActiveRadarrBlock::DeleteDownloadPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None)); + + self.app.pop_navigation_stack(); + } } + ActiveRadarrBlock::UpdateDownloadsPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateDownloads); + + self.app.pop_navigation_stack(); + } + } + _ => (), } } } diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs index 44ff33b..3ed6856 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs @@ -429,6 +429,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<' .tags ); } + ActiveRadarrBlock::EditIndexerPrompt => { + if self.app.data.radarr_data.selected_block.get_active_block() + == &ActiveRadarrBlock::EditIndexerConfirmPrompt + && *self.key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer(None)); + self.app.should_refresh = true; + + self.app.pop_navigation_stack(); + } + } _ => (), } } diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs index c53b708..057b5ab 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs @@ -1282,6 +1282,9 @@ mod tests { mod test_handle_key_char { use crate::app::App; use crate::models::servarr_data::radarr::modals::EditIndexerModal; + use crate::models::servarr_data::radarr::radarr_data::EDIT_INDEXER_TORRENT_SELECTION_BLOCKS; + use crate::models::BlockSelectionState; + use crate::network::radarr_network::RadarrEvent; use pretty_assertions::assert_str_eq; use super::*; @@ -1560,6 +1563,37 @@ mod tests { "h" ); } + + #[test] + fn test_edit_indexer_prompt_prompt_confirmation_confirm() { + let mut app = App::default(); + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app.push_navigation_stack(ActiveRadarrBlock::EditIndexerPrompt.into()); + app.data.radarr_data.selected_block = + BlockSelectionState::new(&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS); + app + .data + .radarr_data + .selected_block + .set_index(EDIT_INDEXER_TORRENT_SELECTION_BLOCKS.len() - 1); + app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default()); + + EditIndexerHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::EditIndexerPrompt, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + assert!(app.data.radarr_data.edit_indexer_modal.is_some()); + assert!(app.should_refresh); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::EditIndexer(None)) + ); + } } #[test] diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs index 8bb7c91..0d3641c 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs @@ -241,19 +241,35 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl } fn handle_char_key_event(&mut self) { - if self.active_radarr_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput { - handle_text_box_keys!( - self, - self.key, - self - .app - .data - .radarr_data - .indexer_settings - .as_mut() - .unwrap() - .whitelisted_hardcoded_subs - ) + match self.active_radarr_block { + ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput => { + handle_text_box_keys!( + self, + self.key, + self + .app + .data + .radarr_data + .indexer_settings + .as_mut() + .unwrap() + .whitelisted_hardcoded_subs + ) + } + ActiveRadarrBlock::AllIndexerSettingsPrompt => { + if self.app.data.radarr_data.selected_block.get_active_block() + == &ActiveRadarrBlock::IndexerSettingsConfirmPrompt + && *self.key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = + Some(RadarrEvent::EditAllIndexerSettings(None)); + self.app.should_refresh = true; + + self.app.pop_navigation_stack(); + } + } + _ => (), } } } diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs index 23c723b..9371ed5 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs @@ -851,7 +851,13 @@ mod tests { mod test_handle_key_char { use pretty_assertions::assert_str_eq; - use crate::models::radarr_models::IndexerSettings; + use crate::{ + models::{ + radarr_models::IndexerSettings, + servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS, BlockSelectionState, + }, + network::radarr_network::RadarrEvent, + }; use super::*; @@ -909,6 +915,37 @@ mod tests { "h" ); } + + #[test] + fn test_edit_indexer_settings_prompt_prompt_confirmation_confirm() { + let mut app = App::default(); + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into()); + app.data.radarr_data.selected_block = + BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); + app + .data + .radarr_data + .selected_block + .set_index(INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1); + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); + + IndexerSettingsHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::AllIndexerSettingsPrompt, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::EditAllIndexerSettings(None)) + ); + assert!(app.data.radarr_data.indexer_settings.is_some()); + assert!(app.should_refresh); + } } #[test] diff --git a/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs b/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs index c1fda63..1d3d59e 100644 --- a/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs @@ -464,7 +464,10 @@ mod tests { mod test_handle_key_char { use pretty_assertions::assert_eq; - use crate::models::servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS; + use crate::{ + models::servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS, + network::radarr_network::RadarrEvent, + }; use super::*; @@ -696,6 +699,33 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); } + + #[test] + fn test_delete_indexer_prompt_confirm() { + let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::DeleteIndexerPrompt, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::DeleteIndexer(None)) + ); + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + } } #[rstest] diff --git a/src/handlers/radarr_handlers/indexers/mod.rs b/src/handlers/radarr_handlers/indexers/mod.rs index c5757de..f8d00fc 100644 --- a/src/handlers/radarr_handlers/indexers/mod.rs +++ b/src/handlers/radarr_handlers/indexers/mod.rs @@ -166,8 +166,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, fn handle_char_key_event(&mut self) { let key = self.key; - if self.active_radarr_block == &ActiveRadarrBlock::Indexers { - match self.key { + match self.active_radarr_block { + ActiveRadarrBlock::Indexers => match self.key { _ if *key == DEFAULT_KEYBINDINGS.add.key => { self .app @@ -194,7 +194,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); } _ => (), + }, + ActiveRadarrBlock::DeleteIndexerPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None)); + + self.app.pop_navigation_stack(); + } } + _ => (), } } } diff --git a/src/handlers/radarr_handlers/library/add_movie_handler.rs b/src/handlers/radarr_handlers/library/add_movie_handler.rs index 93b524a..c47e22a 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler.rs @@ -461,6 +461,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, .tags ) } + ActiveRadarrBlock::AddMoviePrompt => { + if self.app.data.radarr_data.selected_block.get_active_block() + == &ActiveRadarrBlock::AddMovieConfirmPrompt + && *key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None)); + self.app.pop_navigation_stack(); + } + } _ => (), } } diff --git a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs index 875a7f4..7da2d8f 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs @@ -1494,7 +1494,13 @@ mod tests { mod test_handle_key_char { use super::*; - use crate::models::servarr_data::radarr::modals::AddMovieModal; + use crate::{ + models::{ + servarr_data::radarr::{modals::AddMovieModal, radarr_data::ADD_MOVIE_SELECTION_BLOCKS}, + BlockSelectionState, + }, + network::radarr_network::RadarrEvent, + }; #[test] fn test_add_movie_search_input_backspace() { @@ -1588,6 +1594,35 @@ mod tests { "h" ); } + + #[test] + fn test_add_movie_confirm_prompt_prompt_confirmation_confirm() { + let mut app = App::default(); + app.data.radarr_data.add_movie_modal = Some(AddMovieModal::default()); + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); + app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS); + app + .data + .radarr_data + .selected_block + .set_index(ADD_MOVIE_SELECTION_BLOCKS.len() - 1); + + AddMovieHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::AddMoviePrompt, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::AddMovie(None)) + ); + assert!(app.data.radarr_data.add_movie_modal.is_some()); + } } #[test] diff --git a/src/handlers/radarr_handlers/library/delete_movie_handler.rs b/src/handlers/radarr_handlers/library/delete_movie_handler.rs index fa8a956..2113be7 100644 --- a/src/handlers/radarr_handlers/library/delete_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/delete_movie_handler.rs @@ -1,3 +1,4 @@ +use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::App; use crate::event::Key; use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; @@ -100,5 +101,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<' } } - fn handle_char_key_event(&mut self) {} + fn handle_char_key_event(&mut self) { + if self.active_radarr_block == &ActiveRadarrBlock::DeleteMoviePrompt + && self.app.data.radarr_data.selected_block.get_active_block() + == &ActiveRadarrBlock::DeleteMovieConfirmPrompt + && *self.key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie(None)); + self.app.should_refresh = true; + + self.app.pop_navigation_stack(); + } + } } diff --git a/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs index 9eaa7f1..65cda31 100644 --- a/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs @@ -250,6 +250,51 @@ mod tests { } } + mod test_handle_key_char { + use crate::{ + models::{ + servarr_data::radarr::radarr_data::DELETE_MOVIE_SELECTION_BLOCKS, BlockSelectionState, + }, + network::radarr_network::RadarrEvent, + }; + + use super::*; + + #[test] + fn test_delete_movie_confirm_prompt_prompt_confirm() { + let mut app = App::default(); + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into()); + app.data.radarr_data.delete_movie_files = true; + app.data.radarr_data.add_list_exclusion = true; + app.data.radarr_data.selected_block = + BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS); + app + .data + .radarr_data + .selected_block + .set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1); + + DeleteMovieHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::DeleteMoviePrompt, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::DeleteMovie(None)) + ); + assert!(app.should_refresh); + assert!(app.data.radarr_data.prompt_confirm); + assert!(app.data.radarr_data.delete_movie_files); + assert!(app.data.radarr_data.add_list_exclusion); + } + } + #[test] fn test_delete_movie_handler_accepts() { ActiveRadarrBlock::iter().for_each(|active_radarr_block| { diff --git a/src/handlers/radarr_handlers/library/edit_movie_handler.rs b/src/handlers/radarr_handlers/library/edit_movie_handler.rs index 3419095..0664668 100644 --- a/src/handlers/radarr_handlers/library/edit_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/edit_movie_handler.rs @@ -327,6 +327,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a, .tags ) } + ActiveRadarrBlock::EditMoviePrompt => { + if self.app.data.radarr_data.selected_block.get_active_block() + == &ActiveRadarrBlock::EditMovieConfirmPrompt + && *key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie(None)); + self.app.should_refresh = true; + + self.app.pop_navigation_stack(); + } + } _ => (), } } diff --git a/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs index 5386a5d..ab8181d 100644 --- a/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs @@ -940,7 +940,16 @@ mod tests { mod test_handle_key_char { use super::*; - use crate::models::servarr_data::radarr::modals::EditMovieModal; + use crate::{ + models::{ + servarr_data::radarr::{ + modals::EditMovieModal, + radarr_data::{EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS}, + }, + BlockSelectionState, + }, + network::radarr_network::RadarrEvent, + }; #[test] fn test_edit_movie_path_input_backspace() { @@ -1051,6 +1060,36 @@ mod tests { "h" ); } + + #[test] + fn test_edit_movie_confirm_prompt_prompt_confirm() { + let mut app = App::default(); + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); + app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); + app + .data + .radarr_data + .selected_block + .set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); + + EditMovieHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::EditMoviePrompt, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::EditMovie(None)) + ); + assert!(app.data.radarr_data.edit_movie_modal.is_some()); + assert!(app.should_refresh); + } } #[test] diff --git a/src/handlers/radarr_handlers/library/library_handler_tests.rs b/src/handlers/radarr_handlers/library/library_handler_tests.rs index 0142691..c5711b9 100644 --- a/src/handlers/radarr_handlers/library/library_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/library_handler_tests.rs @@ -996,6 +996,7 @@ mod tests { RadarrData, EDIT_MOVIE_SELECTION_BLOCKS, }; + use crate::network::radarr_network::RadarrEvent; use crate::test_edit_movie_key; use super::*; @@ -1452,6 +1453,33 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert!(app.data.radarr_data.movies.sort.is_none()); } + + #[test] + fn test_update_all_movies_prompt_confirm() { + let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into()); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::UpdateAllMoviesPrompt, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::UpdateAllMovies) + ); + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + } } #[rstest] diff --git a/src/handlers/radarr_handlers/library/mod.rs b/src/handlers/radarr_handlers/library/mod.rs index a31eb5b..acb88b1 100644 --- a/src/handlers/radarr_handlers/library/mod.rs +++ b/src/handlers/radarr_handlers/library/mod.rs @@ -386,6 +386,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, ' self.app.data.radarr_data.movies.filter.as_mut().unwrap() ) } + ActiveRadarrBlock::UpdateAllMoviesPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies); + + self.app.pop_navigation_stack(); + } + } _ => (), } } diff --git a/src/handlers/radarr_handlers/library/movie_details_handler.rs b/src/handlers/radarr_handlers/library/movie_details_handler.rs index 26bbcc9..15fe154 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler.rs @@ -464,6 +464,32 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler< } _ => (), }, + ActiveRadarrBlock::AutomaticallySearchMoviePrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = + Some(RadarrEvent::TriggerAutomaticSearch(None)); + + self.app.pop_navigation_stack(); + } + } + ActiveRadarrBlock::UpdateAndScanPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan(None)); + + self.app.pop_navigation_stack(); + } + } + ActiveRadarrBlock::ManualSearchConfirmPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = + Some(RadarrEvent::DownloadRelease(None)); + + self.app.pop_navigation_stack(); + } + } _ => (), } } diff --git a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs index 326291a..27894e7 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs @@ -1468,6 +1468,7 @@ mod tests { use crate::models::servarr_data::radarr::radarr_data::{ RadarrData, EDIT_MOVIE_SELECTION_BLOCKS, }; + use crate::network::radarr_network::RadarrEvent; use crate::test_edit_movie_key; use super::*; @@ -1780,6 +1781,50 @@ mod tests { assert_eq!(app.get_current_route(), &active_radarr_block.into()); assert!(app.is_routing); } + + #[rstest] + #[case( + ActiveRadarrBlock::AutomaticallySearchMoviePrompt, + RadarrEvent::TriggerAutomaticSearch(None) + )] + #[case( + ActiveRadarrBlock::UpdateAndScanPrompt, + RadarrEvent::UpdateAndScan(None) + )] + #[case( + ActiveRadarrBlock::ManualSearchConfirmPrompt, + RadarrEvent::DownloadRelease(None) + )] + fn test_movie_info_prompt_confirm( + #[case] prompt_block: ActiveRadarrBlock, + #[case] expected_action: RadarrEvent, + ) { + let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); + app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()); + app.push_navigation_stack(prompt_block.into()); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &prompt_block, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::MovieDetails.into() + ); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(expected_action) + ); + } } #[test] diff --git a/src/handlers/radarr_handlers/root_folders/mod.rs b/src/handlers/radarr_handlers/root_folders/mod.rs index 4422df0..ef357ae 100644 --- a/src/handlers/radarr_handlers/root_folders/mod.rs +++ b/src/handlers/radarr_handlers/root_folders/mod.rs @@ -180,6 +180,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<' self.app.data.radarr_data.edit_root_folder.as_mut().unwrap() ) } + ActiveRadarrBlock::DeleteRootFolderPrompt => { + if *key == DEFAULT_KEYBINDINGS.confirm.key { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = + Some(RadarrEvent::DeleteRootFolder(None)); + + self.app.pop_navigation_stack(); + } + } _ => (), } } diff --git a/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs b/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs index e9840db..8a62dde 100644 --- a/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs +++ b/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs @@ -554,6 +554,8 @@ mod tests { mod test_handle_key_char { use pretty_assertions::{assert_eq, assert_str_eq}; + use crate::network::radarr_network::RadarrEvent; + use super::*; #[test] @@ -706,6 +708,36 @@ mod tests { "h" ); } + + #[test] + fn test_delete_root_folder_prompt_confirm() { + let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); + app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); + app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()); + + RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::DeleteRootFolderPrompt, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::DeleteRootFolder(None)) + ); + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::RootFolders.into() + ); + } } #[test] diff --git a/src/handlers/radarr_handlers/system/system_details_handler.rs b/src/handlers/radarr_handlers/system/system_details_handler.rs index 00f55c9..3cbe7c9 100644 --- a/src/handlers/radarr_handlers/system/system_details_handler.rs +++ b/src/handlers/radarr_handlers/system/system_details_handler.rs @@ -168,5 +168,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler { self.app.should_refresh = true; } + + if self.active_radarr_block == &ActiveRadarrBlock::SystemTaskStartConfirmPrompt + && *self.key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.radarr_data.prompt_confirm = true; + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask(None)); + self.app.pop_navigation_stack(); + } } } diff --git a/src/handlers/radarr_handlers/system/system_details_handler_tests.rs b/src/handlers/radarr_handlers/system/system_details_handler_tests.rs index 5d22296..c8947a8 100644 --- a/src/handlers/radarr_handlers/system/system_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/system/system_details_handler_tests.rs @@ -858,6 +858,8 @@ mod tests { mod test_handle_key_char { use rstest::rstest; + use crate::network::radarr_network::RadarrEvent; + use super::*; #[rstest] @@ -912,6 +914,32 @@ mod tests { assert_eq!(app.get_current_route(), &active_radarr_block.into()); assert!(!app.should_refresh); } + + #[test] + fn test_system_tasks_start_task_prompt_confirm() { + let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); + app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into()); + app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + &ActiveRadarrBlock::SystemTaskStartConfirmPrompt, + &None, + ) + .handle(); + + assert!(app.data.radarr_data.prompt_confirm); + assert_eq!( + app.data.radarr_data.prompt_confirm_action, + Some(RadarrEvent::StartTask(None)) + ); + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::SystemTasks.into() + ); + } } #[test] diff --git a/src/main.rs b/src/main.rs index 0c7ecb1..3ed8f6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -247,7 +247,7 @@ fn build_network_client(config: &AppConfig) -> Client { let mut client_builder = Client::builder(); if config.radarr.use_ssl { - let cert = add_cert_to_builder(config.radarr.ssl_cert_path.clone(), "Radarr"); + let cert = create_cert(config.radarr.ssl_cert_path.clone(), "Radarr"); client_builder = client_builder.add_root_certificate(cert); } @@ -261,7 +261,7 @@ fn build_network_client(config: &AppConfig) -> Client { } } -fn add_cert_to_builder(cert_path: Option, servarr_name: &str) -> Certificate { +fn create_cert(cert_path: Option, servarr_name: &str) -> Certificate { let err = |error: String| { error!("{}", error); eprintln!("error: {}", error.red()); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 2959710..5aa982b 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,6 +1,6 @@ use std::sync::atomic::Ordering; -use ratatui::layout::{Alignment, Constraint, Flex, Layout, Rect}; +use ratatui::layout::{Constraint, Flex, Layout, Rect}; use ratatui::style::{Style, Stylize}; use ratatui::text::{Line, Text}; use ratatui::widgets::Clear; @@ -83,7 +83,7 @@ fn draw_header_row(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .select(app.server_tabs.index); let help = Paragraph::new(help_text) .block(borderless_block()) - .alignment(Alignment::Right); + .right_aligned(); f.render_widget(tabs, tabs_area); f.render_widget(help, help_area); @@ -170,7 +170,7 @@ fn draw_tabs(f: &mut Frame<'_>, area: Rect, title: &str, tab_state: &TabState) - .select(tab_state.index); let help = Paragraph::new(Text::from(tab_state.get_active_tab_help().help())) .block(borderless_block()) - .alignment(Alignment::Right); + .right_aligned(); f.render_widget(tabs, tabs_area); f.render_widget(help, help_area); @@ -197,7 +197,7 @@ pub fn draw_input_box_popup( let help = Paragraph::new(" cancel") .help() - .alignment(Alignment::Center) + .centered() .block(borderless_block()); f.render_widget(help, help_area); } diff --git a/src/ui/radarr_ui/collections/collection_details_ui.rs b/src/ui/radarr_ui/collections/collection_details_ui.rs index 1e03b7b..25f19e4 100644 --- a/src/ui/radarr_ui/collections/collection_details_ui.rs +++ b/src/ui/radarr_ui/collections/collection_details_ui.rs @@ -259,7 +259,7 @@ fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .wrap(Wrap { trim: false }); let help_paragraph = Paragraph::new(help_text) .block(borderless_block()) - .alignment(Alignment::Center); + .centered(); f.render_widget(paragraph, paragraph_area); f.render_widget(help_paragraph, help_area); diff --git a/src/ui/radarr_ui/collections/edit_collection_ui.rs b/src/ui/radarr_ui/collections/edit_collection_ui.rs index 01a0083..c005f99 100644 --- a/src/ui/radarr_ui/collections/edit_collection_ui.rs +++ b/src/ui/radarr_ui/collections/edit_collection_ui.rs @@ -1,9 +1,12 @@ use std::sync::atomic::Ordering; use ratatui::layout::{Constraint, Layout, Rect}; -use ratatui::widgets::ListItem; +use ratatui::text::Text; +use ratatui::widgets::{ListItem, Paragraph}; use ratatui::Frame; +use crate::app::context_clues::build_context_clue_string; +use crate::app::radarr::radarr_context_clues::CONFIRMATION_PROMPT_CONTEXT_CLUES; use crate::app::App; use crate::models::servarr_data::radarr::modals::EditCollectionModal; use crate::models::servarr_data::radarr::radarr_data::{ @@ -110,7 +113,7 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_> let selected_minimum_availability = minimum_availability_list.current_selection(); let selected_quality_profile = quality_profile_list.current_selection(); - let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, root_folder_area, search_on_add_area, _, buttons_area] = + let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, root_folder_area, search_on_add_area, _, buttons_area, help_area] = Layout::vertical([ Constraint::Length(6), Constraint::Length(3), @@ -118,8 +121,9 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_> Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Fill(0), + Constraint::Fill(1), Constraint::Length(3), + Constraint::Length(1), ]) .margin(1) .areas(area); @@ -127,6 +131,8 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_> Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]) .areas(buttons_area); + let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help()); + let help_paragraph = Paragraph::new(help_text).centered(); let prompt_paragraph = layout_paragraph_borderless(&collection_overview); let monitored_checkbox = Checkbox::new("Monitored") .highlighted(selected_block == &ActiveRadarrBlock::EditCollectionToggleMonitored) @@ -169,6 +175,7 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_> f.render_widget(search_on_add_checkbox, search_on_add_area); f.render_widget(save_button, save_area); f.render_widget(cancel_button, cancel_area); + f.render_widget(help_paragraph, help_area); } fn draw_edit_collection_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mut App<'_>) { diff --git a/src/ui/radarr_ui/indexers/edit_indexer_ui.rs b/src/ui/radarr_ui/indexers/edit_indexer_ui.rs index 33ee7c5..b185adb 100644 --- a/src/ui/radarr_ui/indexers/edit_indexer_ui.rs +++ b/src/ui/radarr_ui/indexers/edit_indexer_ui.rs @@ -1,5 +1,7 @@ use std::sync::atomic::Ordering; +use crate::app::context_clues::build_context_clue_string; +use crate::app::radarr::radarr_context_clues::CONFIRMATION_PROMPT_CONTEXT_CLUES; use crate::app::App; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS}; use crate::models::Route; @@ -14,6 +16,8 @@ use crate::ui::widgets::loading_block::LoadingBlock; use crate::ui::widgets::popup::Size; use crate::ui::{draw_popup_over, DrawUi}; use ratatui::layout::{Constraint, Flex, Layout, Rect}; +use ratatui::text::Text; +use ratatui::widgets::Paragraph; use ratatui::Frame; #[cfg(test)] @@ -50,32 +54,36 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { let highlight_yes_no = selected_block == &ActiveRadarrBlock::EditIndexerConfirmPrompt; let edit_indexer_modal_option = &app.data.radarr_data.edit_indexer_modal; let protocol = &app.data.radarr_data.indexers.current_selection().protocol; + let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help()); + let help_paragraph = Paragraph::new(help_text).centered(); if edit_indexer_modal_option.is_some() { let edit_indexer_modal = edit_indexer_modal_option.as_ref().unwrap(); - let [settings_area, buttons_area] = - Layout::vertical([Constraint::Fill(0), Constraint::Length(3)]) - .margin(1) - .areas(area); + let [settings_area, _, buttons_area, help_area] = Layout::vertical([ + Constraint::Length(15), + Constraint::Fill(1), + Constraint::Length(3), + Constraint::Length(1), + ]) + .margin(1) + .areas(area); let [left_side_area, right_side_area] = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]) .margin(1) .areas(settings_area); - let [name_area, rss_area, auto_search_area, interactive_search_area, _] = Layout::vertical([ + let [name_area, rss_area, auto_search_area, interactive_search_area] = Layout::vertical([ Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Fill(0), ]) .areas(left_side_area); - let [url_area, api_key_area, seed_ratio_area, tags_area, _] = Layout::vertical([ + let [url_area, api_key_area, seed_ratio_area, tags_area] = Layout::vertical([ Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Fill(0), ]) .areas(right_side_area); @@ -161,6 +169,7 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { f.render_widget(interactive_search_checkbox, interactive_search_area); f.render_widget(save_button, save_area); f.render_widget(cancel_button, cancel_area); + f.render_widget(help_paragraph, help_area); } } else { f.render_widget(LoadingBlock::new(app.is_loading, block), area); diff --git a/src/ui/radarr_ui/indexers/indexer_settings_ui.rs b/src/ui/radarr_ui/indexers/indexer_settings_ui.rs index 631d2ad..a9bc0ea 100644 --- a/src/ui/radarr_ui/indexers/indexer_settings_ui.rs +++ b/src/ui/radarr_ui/indexers/indexer_settings_ui.rs @@ -1,8 +1,12 @@ use std::sync::atomic::Ordering; use ratatui::layout::{Constraint, Flex, Layout, Rect}; +use ratatui::text::Text; +use ratatui::widgets::Paragraph; use ratatui::Frame; +use crate::app::context_clues::build_context_clue_string; +use crate::app::radarr::radarr_context_clues::CONFIRMATION_PROMPT_CONTEXT_CLUES; use crate::app::App; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS, @@ -52,33 +56,37 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: let selected_block = app.data.radarr_data.selected_block.get_active_block(); let highlight_yes_no = selected_block == &ActiveRadarrBlock::IndexerSettingsConfirmPrompt; let indexer_settings_option = &app.data.radarr_data.indexer_settings; + let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help()); + let help_paragraph = Paragraph::new(help_text).centered(); if indexer_settings_option.is_some() { let indexer_settings = indexer_settings_option.as_ref().unwrap(); - let [settings_area, buttons_area] = - Layout::vertical([Constraint::Fill(0), Constraint::Length(3)]) - .margin(1) - .areas(area); + let [settings_area, _, buttons_area, help_area] = Layout::vertical([ + Constraint::Length(15), + Constraint::Fill(1), + Constraint::Length(3), + Constraint::Length(1), + ]) + .margin(1) + .areas(area); let [left_side_area, right_side_area] = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]) .margin(1) .areas(settings_area); - let [min_age_area, retention_area, max_size_area, prefer_flags_area, _] = Layout::vertical([ + let [min_age_area, retention_area, max_size_area, prefer_flags_area] = Layout::vertical([ Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Fill(0), ]) .areas(left_side_area); - let [availability_delay_area, rss_sync_interval_area, whitelisted_sub_tags_area, allow_hardcoded_subs_area, _] = + let [availability_delay_area, rss_sync_interval_area, whitelisted_sub_tags_area, allow_hardcoded_subs_area] = Layout::vertical([ Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Fill(0), ]) .areas(right_side_area); @@ -162,6 +170,7 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: f.render_widget(allow_hardcoded_subs_checkbox, allow_hardcoded_subs_area); f.render_widget(save_button, save_area); f.render_widget(cancel_button, cancel_area); + f.render_widget(help_paragraph, help_area); } else { f.render_widget(LoadingBlock::new(app.is_loading, block), area); } diff --git a/src/ui/radarr_ui/library/add_movie_ui.rs b/src/ui/radarr_ui/library/add_movie_ui.rs index ac80cb2..0008ac1 100644 --- a/src/ui/radarr_ui/library/add_movie_ui.rs +++ b/src/ui/radarr_ui/library/add_movie_ui.rs @@ -1,12 +1,14 @@ use std::sync::atomic::Ordering; -use ratatui::layout::{Alignment, Constraint, Layout, Rect}; +use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::text::Text; use ratatui::widgets::{Cell, ListItem, Paragraph, Row}; use ratatui::Frame; use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES}; -use crate::app::radarr::radarr_context_clues::ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES; +use crate::app::radarr::radarr_context_clues::{ + ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, +}; use crate::models::radarr_models::AddMovieSearchResult; use crate::models::servarr_data::radarr::modals::AddMovieModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ADD_MOVIE_BLOCKS}; @@ -209,7 +211,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help()); let help_paragraph = Paragraph::new(help_text) .block(borderless_block()) - .alignment(Alignment::Center); + .centered(); search_box.show_cursor(f, search_box_area); f.render_widget(layout_block(), results_area); @@ -220,7 +222,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help()); let help_paragraph = Paragraph::new(help_text) .block(borderless_block()) - .alignment(Alignment::Center); + .centered(); let error_message = Message::new("No movies found matching your query!"); let error_message_popup = Popup::new(error_message).size(Size::Message); @@ -240,7 +242,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { Text::from(build_context_clue_string(&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES).help()); let help_paragraph = Paragraph::new(help_text) .block(borderless_block()) - .alignment(Alignment::Center); + .centered(); let search_results_table = ManagarrTable::new( app.data.radarr_data.add_searched_movies.as_mut(), search_results_row_mapping, @@ -369,7 +371,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { f.render_widget(title_block_centered(&title), area); - let [paragraph_area, root_folder_area, monitor_area, min_availability_area, quality_profile_area, tags_area, _, buttons_area] = + let [paragraph_area, root_folder_area, monitor_area, min_availability_area, quality_profile_area, tags_area, _, buttons_area, help_area] = Layout::vertical([ Constraint::Length(6), Constraint::Length(3), @@ -377,14 +379,18 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Fill(0), + Constraint::Fill(1), Constraint::Length(3), + Constraint::Length(1), ]) .margin(1) .areas(area); let prompt_paragraph = layout_paragraph_borderless(&prompt); + let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help()); + let help_paragraph = Paragraph::new(help_text).centered(); f.render_widget(prompt_paragraph, paragraph_area); + f.render_widget(help_paragraph, help_area); let [add_area, cancel_area] = Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]) diff --git a/src/ui/radarr_ui/library/edit_movie_ui.rs b/src/ui/radarr_ui/library/edit_movie_ui.rs index 072950f..7930315 100644 --- a/src/ui/radarr_ui/library/edit_movie_ui.rs +++ b/src/ui/radarr_ui/library/edit_movie_ui.rs @@ -2,9 +2,12 @@ use std::sync::atomic::Ordering; use ratatui::layout::{Constraint, Rect}; use ratatui::prelude::Layout; -use ratatui::widgets::ListItem; +use ratatui::text::Text; +use ratatui::widgets::{ListItem, Paragraph}; use ratatui::Frame; +use crate::app::context_clues::build_context_clue_string; +use crate::app::radarr::radarr_context_clues::CONFIRMATION_PROMPT_CONTEXT_CLUES; use crate::app::App; use crate::models::servarr_data::radarr::modals::EditMovieModal; use crate::models::servarr_data::radarr::radarr_data::{ @@ -113,7 +116,7 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are let selected_minimum_availability = minimum_availability_list.current_selection(); let selected_quality_profile = quality_profile_list.current_selection(); - let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, path_area, tags_area, _, buttons_area] = + let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, path_area, tags_area, _, buttons_area, help_area] = Layout::vertical([ Constraint::Length(6), Constraint::Length(3), @@ -121,8 +124,9 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are Constraint::Length(3), Constraint::Length(3), Constraint::Length(3), - Constraint::Fill(0), + Constraint::Fill(1), Constraint::Length(3), + Constraint::Length(1), ]) .margin(1) .areas(area); @@ -130,6 +134,8 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]) .areas(buttons_area); + let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help()); + let help_paragraph = Paragraph::new(help_text).centered(); let prompt_paragraph = layout_paragraph_borderless(&movie_overview); let monitored_checkbox = Checkbox::new("Monitored") .checked(monitored.unwrap_or_default()) @@ -181,6 +187,7 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are f.render_widget(quality_profile_drop_down_button, quality_profile_area); f.render_widget(save_button, save_area); f.render_widget(cancel_button, cancel_area); + f.render_widget(help_paragraph, help_area); } fn draw_edit_movie_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mut App<'_>) { diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index 0a7bd6f..c3f43bf 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -1,6 +1,6 @@ use std::iter; -use ratatui::layout::{Alignment, Constraint, Layout, Rect}; +use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Style, Stylize}; use ratatui::text::{Line, Span, Text}; use ratatui::widgets::{Cell, Paragraph, Row, Wrap}; @@ -510,7 +510,7 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>) { let content_paragraph = Paragraph::new(lines_vec) .block(borderless_block()) .wrap(Wrap { trim: false }) - .alignment(Alignment::Left); + .left_aligned(); let confirmation_prompt = ConfirmationPrompt::new() .title(title) .prompt(&prompt) diff --git a/src/ui/radarr_ui/mod.rs b/src/ui/radarr_ui/mod.rs index 524c77f..864ed57 100644 --- a/src/ui/radarr_ui/mod.rs +++ b/src/ui/radarr_ui/mod.rs @@ -1,7 +1,7 @@ use std::iter; use chrono::{Duration, Utc}; -use ratatui::layout::{Alignment, Constraint, Layout, Rect}; +use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::prelude::Stylize; use ratatui::text::Text; use ratatui::widgets::{Paragraph, Row}; @@ -244,6 +244,6 @@ fn draw_radarr_logo(f: &mut Frame<'_>, area: Rect) { let logo = Paragraph::new(logo_text) .light_yellow() .block(layout_block().default()) - .alignment(Alignment::Center); + .centered(); f.render_widget(logo, area); } diff --git a/src/ui/radarr_ui/system/mod.rs b/src/ui/radarr_ui/system/mod.rs index e472901..93c1a04 100644 --- a/src/ui/radarr_ui/system/mod.rs +++ b/src/ui/radarr_ui/system/mod.rs @@ -1,7 +1,7 @@ use std::ops::Sub; use chrono::Utc; -use ratatui::layout::{Alignment, Layout}; +use ratatui::layout::Layout; use ratatui::style::Style; use ratatui::text::{Span, Text}; use ratatui::widgets::{Cell, Paragraph, Row}; @@ -204,7 +204,7 @@ fn draw_help(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { ); let help_paragraph = Paragraph::new(help_text) .block(layout_block_top_border()) - .alignment(Alignment::Left); + .left_aligned(); f.render_widget(help_paragraph, area); } diff --git a/src/ui/utils.rs b/src/ui/utils.rs index 94125e8..bf1e24b 100644 --- a/src/ui/utils.rs +++ b/src/ui/utils.rs @@ -43,7 +43,7 @@ pub fn layout_paragraph_borderless(string: &str) -> Paragraph<'_> { .primary() .bold() .wrap(Wrap { trim: false }) - .alignment(Alignment::Center) + .centered() } pub fn borderless_block<'a>() -> Block<'a> { diff --git a/src/ui/widgets/button.rs b/src/ui/widgets/button.rs index ca18a8a..26217b9 100644 --- a/src/ui/widgets/button.rs +++ b/src/ui/widgets/button.rs @@ -1,7 +1,7 @@ use crate::ui::styles::ManagarrStyle; use crate::ui::utils::{layout_block, style_block_highlight}; use ratatui::buffer::Buffer; -use ratatui::layout::{Alignment, Constraint, Flex, Layout, Rect}; +use ratatui::layout::{Constraint, Flex, Layout, Rect}; use ratatui::prelude::{Style, Text, Widget}; use ratatui::style::Styled; use ratatui::widgets::Paragraph; @@ -58,11 +58,11 @@ impl<'a> Button<'a> { if let Some(icon) = self.icon { layout_block().style(style).render(area, buf); Paragraph::new(Text::from(self.title)) - .alignment(Alignment::Left) + .left_aligned() .style(style) .render(title_area, buf); Paragraph::new(Text::from(format!("{icon} "))) - .alignment(Alignment::Right) + .right_aligned() .style(style) .render(icon_area, buf); } @@ -72,7 +72,7 @@ impl<'a> Button<'a> { let [label_area, button_area] = Layout::horizontal([Constraint::Percentage(48), Constraint::Percentage(48)]).areas(area); let label_paragraph = Paragraph::new(Text::from(format!("\n{}: ", self.label.unwrap()))) - .alignment(Alignment::Right) + .right_aligned() .primary(); if self.icon.is_some() { @@ -87,7 +87,7 @@ impl<'a> Button<'a> { fn render_button(self, area: Rect, buf: &mut Buffer) { Paragraph::new(Text::from(self.title)) .block(layout_block()) - .alignment(Alignment::Center) + .centered() .style(style_block_highlight(self.is_selected)) .render(area, buf); } diff --git a/src/ui/widgets/checkbox.rs b/src/ui/widgets/checkbox.rs index 962673b..08ee9d2 100644 --- a/src/ui/widgets/checkbox.rs +++ b/src/ui/widgets/checkbox.rs @@ -1,7 +1,7 @@ use crate::ui::styles::ManagarrStyle; use crate::ui::utils::{borderless_block, layout_block, style_block_highlight}; use ratatui::buffer::Buffer; -use ratatui::layout::{Alignment, Constraint, Layout, Rect}; +use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::prelude::Text; use ratatui::style::Stylize; use ratatui::widgets::{Paragraph, Widget}; @@ -47,13 +47,13 @@ impl<'a> Checkbox<'a> { Paragraph::new(Text::from(format!("\n{}: ", self.label))) .block(borderless_block()) - .alignment(Alignment::Right) + .right_aligned() .primary() .render(label_area, buf); Paragraph::new(Text::from(check)) .block(layout_block()) - .alignment(Alignment::Center) + .centered() .style(style_block_highlight(self.is_highlighted).bold()) .render(checkbox_box_area, buf); } diff --git a/src/ui/widgets/confirmation_prompt.rs b/src/ui/widgets/confirmation_prompt.rs index 543557b..521f638 100644 --- a/src/ui/widgets/confirmation_prompt.rs +++ b/src/ui/widgets/confirmation_prompt.rs @@ -1,9 +1,12 @@ +use crate::app::context_clues::build_context_clue_string; +use crate::app::radarr::radarr_context_clues::CONFIRMATION_PROMPT_CONTEXT_CLUES; use crate::ui::styles::ManagarrStyle; use crate::ui::utils::{layout_paragraph_borderless, title_block_centered}; use crate::ui::widgets::button::Button; use crate::ui::widgets::checkbox::Checkbox; use ratatui::buffer::Buffer; use ratatui::layout::{Constraint, Flex, Layout, Rect}; +use ratatui::text::Text; use ratatui::widgets::{Paragraph, Widget}; use std::iter; @@ -64,12 +67,16 @@ impl<'a> ConfirmationPrompt<'a> { fn render_confirmation_prompt_with_checkboxes(self, area: Rect, buf: &mut Buffer) { title_block_centered(self.title).render(area, buf); + let help_text = + Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help()); + let help_paragraph = Paragraph::new(help_text).centered(); if let Some(checkboxes) = self.checkboxes { let mut constraints = vec![ Constraint::Length(4), - Constraint::Fill(0), + Constraint::Fill(1), Constraint::Length(3), + Constraint::Length(1), ]; constraints.splice( 1..1, @@ -81,6 +88,7 @@ impl<'a> ConfirmationPrompt<'a> { .areas(chunks[checkboxes.len() + 2]); layout_paragraph_borderless(self.prompt).render(chunks[0], buf); + help_paragraph.render(chunks[checkboxes.len() + 3], buf); checkboxes .into_iter() @@ -102,26 +110,37 @@ impl<'a> ConfirmationPrompt<'a> { fn render_confirmation_prompt(self, area: Rect, buf: &mut Buffer) { title_block_centered(self.title).render(area, buf); + let help_text = + Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help()); + let help_paragraph = Paragraph::new(help_text).centered(); let [prompt_area, buttons_area] = if let Some(content_paragraph) = self.content { - let [prompt_area, content_area, _, buttons_area] = Layout::vertical([ + let [prompt_area, content_area, _, buttons_area, help_area] = Layout::vertical([ Constraint::Length(4), Constraint::Length(7), - Constraint::Fill(0), + Constraint::Fill(1), Constraint::Length(3), + Constraint::Length(1), ]) .margin(1) .areas(area); content_paragraph.render(content_area, buf); + help_paragraph.render(help_area, buf); [prompt_area, buttons_area] } else { - let [prompt_area, buttons_area] = - Layout::vertical([Constraint::Percentage(72), Constraint::Length(3)]) - .margin(1) - .flex(Flex::SpaceBetween) - .areas(area); + let [prompt_area, buttons_area, _, help_area] = Layout::vertical([ + Constraint::Percentage(72), + Constraint::Length(3), + Constraint::Fill(0), + Constraint::Min(1), + ]) + .margin(1) + .flex(Flex::SpaceBetween) + .areas(area); + + help_paragraph.render(help_area, buf); [prompt_area, buttons_area] }; diff --git a/src/ui/widgets/input_box.rs b/src/ui/widgets/input_box.rs index a9e87a9..fa0f87c 100644 --- a/src/ui/widgets/input_box.rs +++ b/src/ui/widgets/input_box.rs @@ -1,5 +1,5 @@ use ratatui::buffer::Buffer; -use ratatui::layout::{Alignment, Constraint, Layout, Position, Rect}; +use ratatui::layout::{Constraint, Layout, Position, Rect}; use ratatui::prelude::Text; use ratatui::style::{Style, Styled, Stylize}; use ratatui::widgets::{Block, Paragraph, Widget}; @@ -114,7 +114,7 @@ impl<'a> InputBox<'a> { Paragraph::new(Text::from(format!("\n{label}: "))) .block(borderless_block()) - .alignment(Alignment::Right) + .right_aligned() .primary() .render(label_area, buf); input_box_paragraph.render(text_box_area, buf); diff --git a/src/ui/widgets/popup.rs b/src/ui/widgets/popup.rs index 563dadc..0716918 100644 --- a/src/ui/widgets/popup.rs +++ b/src/ui/widgets/popup.rs @@ -1,7 +1,7 @@ use crate::ui::styles::ManagarrStyle; use crate::ui::utils::{background_block, centered_rect, layout_block_top_border}; use ratatui::buffer::Buffer; -use ratatui::layout::{Alignment, Constraint, Layout, Rect}; +use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::prelude::Text; use ratatui::widgets::{Block, Clear, Paragraph, Widget}; @@ -27,7 +27,7 @@ impl Size { pub fn to_percent(&self) -> (u16, u16) { match self { Size::SmallPrompt => (20, 20), - Size::Prompt => (35, 35), + Size::Prompt => (37, 37), Size::LargePrompt => (70, 45), Size::Message => (25, 8), Size::NarrowMessage => (50, 20), @@ -100,7 +100,7 @@ impl<'a, T: Widget> Popup<'a, T> { Paragraph::new(Text::from(format!(" {footer}").help())) .block(layout_block_top_border()) - .alignment(Alignment::Left) + .left_aligned() .render(help_footer_area, buf); content_area diff --git a/src/ui/widgets/popup_tests.rs b/src/ui/widgets/popup_tests.rs index 9da904f..8b68cde 100644 --- a/src/ui/widgets/popup_tests.rs +++ b/src/ui/widgets/popup_tests.rs @@ -7,7 +7,7 @@ mod tests { #[test] fn test_dimensions_to_percent() { assert_eq!(Size::SmallPrompt.to_percent(), (20, 20)); - assert_eq!(Size::Prompt.to_percent(), (35, 35)); + assert_eq!(Size::Prompt.to_percent(), (37, 37)); assert_eq!(Size::LargePrompt.to_percent(), (70, 45)); assert_eq!(Size::Message.to_percent(), (25, 8)); assert_eq!(Size::NarrowMessage.to_percent(), (50, 20));