From a360c83431b1610e99183fb53beedcfc67fa4b66 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 15 Feb 2024 11:11:10 -0700 Subject: [PATCH] Refactored the ErrorMessage widget into a generic Message widget for enhanced reuse. Added support for testing a single indexer at a time. --- src/app/key_binding.rs | 5 + src/app/key_binding_tests.rs | 1 + src/app/radarr/mod.rs | 5 + src/app/radarr/radarr_context_clues.rs | 5 +- src/app/radarr/radarr_context_clues_tests.rs | 5 + src/app/radarr/radarr_tests.rs | 16 +++ .../indexers/indexers_handler_tests.rs | 31 ++++ src/handlers/radarr_handlers/indexers/mod.rs | 9 ++ src/models/servarr_data/radarr/radarr_data.rs | 6 +- .../servarr_data/radarr/radarr_data_tests.rs | 4 +- src/network/radarr_network.rs | 58 ++++++++ src/network/radarr_network_tests.rs | 134 ++++++++++++++++++ src/ui/radarr_ui/collections/mod.rs | 8 +- src/ui/radarr_ui/indexers/mod.rs | 27 +++- src/ui/radarr_ui/library/add_movie_ui.rs | 9 +- src/ui/radarr_ui/library/mod.rs | 10 +- src/ui/widgets/error_message.rs | 40 ------ src/ui/widgets/error_message_tests.rs | 14 -- src/ui/widgets/message.rs | 55 +++++++ src/ui/widgets/message_tests.rs | 43 ++++++ src/ui/widgets/mod.rs | 2 +- src/ui/widgets/popup.rs | 6 +- src/ui/widgets/popup_tests.rs | 3 +- 23 files changed, 418 insertions(+), 78 deletions(-) delete mode 100644 src/ui/widgets/error_message.rs delete mode 100644 src/ui/widgets/error_message_tests.rs create mode 100644 src/ui/widgets/message.rs create mode 100644 src/ui/widgets/message_tests.rs diff --git a/src/app/key_binding.rs b/src/app/key_binding.rs index 067cd38..a568975 100644 --- a/src/app/key_binding.rs +++ b/src/app/key_binding.rs @@ -23,6 +23,7 @@ generate_keybindings! { logs, tasks, test, + test_all, refresh, update, events, @@ -102,6 +103,10 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings { key: Key::Char('t'), desc: "test", }, + test_all: KeyBinding { + key: Key::Char('T'), + desc: "test all", + }, refresh: KeyBinding { key: Key::Ctrl('r'), desc: "refresh", diff --git a/src/app/key_binding_tests.rs b/src/app/key_binding_tests.rs index 3fc4fe2..05027aa 100644 --- a/src/app/key_binding_tests.rs +++ b/src/app/key_binding_tests.rs @@ -22,6 +22,7 @@ mod test { #[case(DEFAULT_KEYBINDINGS.logs, Key::Char('l'), "logs")] #[case(DEFAULT_KEYBINDINGS.tasks, Key::Char('t'), "tasks")] #[case(DEFAULT_KEYBINDINGS.test, Key::Char('t'), "test")] + #[case(DEFAULT_KEYBINDINGS.test_all, Key::Char('T'), "test all")] #[case(DEFAULT_KEYBINDINGS.refresh, Key::Ctrl('r'), "refresh")] #[case(DEFAULT_KEYBINDINGS.update, Key::Char('u'), "update")] #[case(DEFAULT_KEYBINDINGS.home, Key::Home, "home")] diff --git a/src/app/radarr/mod.rs b/src/app/radarr/mod.rs index 1daa1af..c7513aa 100644 --- a/src/app/radarr/mod.rs +++ b/src/app/radarr/mod.rs @@ -49,6 +49,11 @@ impl<'a> App<'a> { .dispatch_network_event(RadarrEvent::GetIndexerSettings.into()) .await; } + ActiveRadarrBlock::TestIndexer => { + self + .dispatch_network_event(RadarrEvent::TestIndexer.into()) + .await; + } ActiveRadarrBlock::TestAllIndexers => { self .dispatch_network_event(RadarrEvent::TestAllIndexers.into()) diff --git a/src/app/radarr/radarr_context_clues.rs b/src/app/radarr/radarr_context_clues.rs index e4cae95..9a3335b 100644 --- a/src/app/radarr/radarr_context_clues.rs +++ b/src/app/radarr/radarr_context_clues.rs @@ -52,7 +52,7 @@ pub static ROOT_FOLDERS_CONTEXT_CLUES: [ContextClue; 3] = [ ), ]; -pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [ +pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 7] = [ (DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc), (DEFAULT_KEYBINDINGS.submit, "edit indexer"), ( @@ -60,7 +60,8 @@ pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [ DEFAULT_KEYBINDINGS.settings.desc, ), (DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc), - (DEFAULT_KEYBINDINGS.test, "test all indexers"), + (DEFAULT_KEYBINDINGS.test, "test indexer"), + (DEFAULT_KEYBINDINGS.test_all, "test all indexers"), ( DEFAULT_KEYBINDINGS.refresh, DEFAULT_KEYBINDINGS.refresh.desc, diff --git a/src/app/radarr/radarr_context_clues_tests.rs b/src/app/radarr/radarr_context_clues_tests.rs index 73a1893..2f1f50c 100644 --- a/src/app/radarr/radarr_context_clues_tests.rs +++ b/src/app/radarr/radarr_context_clues_tests.rs @@ -177,6 +177,11 @@ mod tests { let (key_binding, description) = indexers_context_clues_iter.next().unwrap(); assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test); + assert_str_eq!(*description, "test indexer"); + + let (key_binding, description) = indexers_context_clues_iter.next().unwrap(); + + assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test_all); assert_str_eq!(*description, "test all indexers"); let (key_binding, description) = indexers_context_clues_iter.next().unwrap(); diff --git a/src/app/radarr/radarr_tests.rs b/src/app/radarr/radarr_tests.rs index 8aa8431..71f9275 100644 --- a/src/app/radarr/radarr_tests.rs +++ b/src/app/radarr/radarr_tests.rs @@ -161,6 +161,22 @@ mod tests { assert_eq!(app.tick_count, 0); } + #[tokio::test] + async fn test_dispatch_by_test_indexer_block() { + let (mut app, mut sync_network_rx) = construct_app_unit(); + + app + .dispatch_by_radarr_block(&ActiveRadarrBlock::TestIndexer) + .await; + + assert!(app.is_loading); + assert_eq!( + sync_network_rx.recv().await.unwrap(), + RadarrEvent::TestIndexer.into() + ); + assert_eq!(app.tick_count, 0); + } + #[tokio::test] async fn test_dispatch_by_test_all_indexers_block() { let (mut app, mut sync_network_rx) = construct_app_unit(); diff --git a/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs b/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs index 17cf83c..aa2bd9e 100644 --- a/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs @@ -312,6 +312,19 @@ mod tests { assert!(!app.data.radarr_data.prompt_confirm); } + #[test] + fn test_test_indexer_esc() { + let mut app = App::default(); + app.data.radarr_data.indexer_test_error = Some("test result".to_owned()); + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app.push_navigation_stack(ActiveRadarrBlock::TestIndexer.into()); + + IndexersHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::TestIndexer, &None).handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + assert_eq!(app.data.radarr_data.indexer_test_error, None); + } + #[test] fn test_default_esc() { let mut app = App::default(); @@ -391,6 +404,24 @@ mod tests { ) .handle(); + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::TestIndexer.into() + ); + } + + #[test] + fn test_test_all_key() { + let mut app = App::default(); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.test_all.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + assert_eq!( app.get_current_route(), &ActiveRadarrBlock::TestAllIndexers.into() diff --git a/src/handlers/radarr_handlers/indexers/mod.rs b/src/handlers/radarr_handlers/indexers/mod.rs index 45b2833..793600e 100644 --- a/src/handlers/radarr_handlers/indexers/mod.rs +++ b/src/handlers/radarr_handlers/indexers/mod.rs @@ -152,6 +152,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, self.app.pop_navigation_stack(); self.app.data.radarr_data.prompt_confirm = false; } + ActiveRadarrBlock::TestIndexer => { + self.app.pop_navigation_stack(); + self.app.data.radarr_data.indexer_test_error = None; + } _ => handle_clear_errors(self.app), } } @@ -169,6 +173,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, self.app.should_refresh = true; } _ if *key == DEFAULT_KEYBINDINGS.test.key => { + self + .app + .push_navigation_stack(ActiveRadarrBlock::TestIndexer.into()); + } + _ if *key == DEFAULT_KEYBINDINGS.test_all.key => { self .app .push_navigation_stack(ActiveRadarrBlock::TestAllIndexers.into()); diff --git a/src/models/servarr_data/radarr/radarr_data.rs b/src/models/servarr_data/radarr/radarr_data.rs index c14294f..9fd936e 100644 --- a/src/models/servarr_data/radarr/radarr_data.rs +++ b/src/models/servarr_data/radarr/radarr_data.rs @@ -59,6 +59,7 @@ pub struct RadarrData<'a> { pub edit_indexer_modal: Option, pub edit_root_folder: Option, pub indexer_settings: Option, + pub indexer_test_error: Option, pub indexer_test_all_results: Option>, pub movie_details_modal: Option, pub prompt_confirm: bool, @@ -107,6 +108,7 @@ impl<'a> Default for RadarrData<'a> { edit_indexer_modal: None, edit_root_folder: None, indexer_settings: None, + indexer_test_error: None, indexer_test_all_results: None, movie_details_modal: None, prompt_confirm: false, @@ -279,6 +281,7 @@ pub enum ActiveRadarrBlock { SystemTasks, SystemTaskStartConfirmPrompt, SystemUpdates, + TestIndexer, TestAllIndexers, UpdateAndScanPrompt, UpdateAllCollectionsPrompt, @@ -309,10 +312,11 @@ pub static COLLECTIONS_BLOCKS: [ActiveRadarrBlock; 7] = [ ActiveRadarrBlock::FilterCollectionsError, ActiveRadarrBlock::UpdateAllCollectionsPrompt, ]; -pub static INDEXERS_BLOCKS: [ActiveRadarrBlock; 3] = [ +pub static INDEXERS_BLOCKS: [ActiveRadarrBlock; 4] = [ ActiveRadarrBlock::AddIndexer, ActiveRadarrBlock::DeleteIndexerPrompt, ActiveRadarrBlock::Indexers, + ActiveRadarrBlock::TestIndexer, ]; pub static ROOT_FOLDERS_BLOCKS: [ActiveRadarrBlock; 3] = [ ActiveRadarrBlock::RootFolders, diff --git a/src/models/servarr_data/radarr/radarr_data_tests.rs b/src/models/servarr_data/radarr/radarr_data_tests.rs index 1b09d8b..ec38c60 100644 --- a/src/models/servarr_data/radarr/radarr_data_tests.rs +++ b/src/models/servarr_data/radarr/radarr_data_tests.rs @@ -81,6 +81,7 @@ mod tests { assert!(radarr_data.edit_root_folder.is_none()); assert!(radarr_data.edit_indexer_modal.is_none()); assert!(radarr_data.indexer_settings.is_none()); + assert!(radarr_data.indexer_test_error.is_none()); assert!(radarr_data.indexer_test_all_results.is_none()); assert!(radarr_data.movie_details_modal.is_none()); assert!(radarr_data.prompt_confirm_action.is_none()); @@ -280,10 +281,11 @@ mod tests { #[test] fn test_indexers_blocks_contents() { - assert_eq!(INDEXERS_BLOCKS.len(), 3); + assert_eq!(INDEXERS_BLOCKS.len(), 4); assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::AddIndexer)); assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::DeleteIndexerPrompt)); assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::Indexers)); + assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::TestIndexer)); } #[test] diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index 28fb9a5..5d66398 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -63,6 +63,7 @@ pub enum RadarrEvent { HealthCheck, SearchNewMovie, StartTask, + TestIndexer, TestAllIndexers, TriggerAutomaticSearch, UpdateAllMovies, @@ -99,6 +100,7 @@ impl RadarrEvent { RadarrEvent::GetTags => "/tag", RadarrEvent::GetTasks => "/system/task", RadarrEvent::GetUpdates => "/update", + RadarrEvent::TestIndexer => "/indexer/test", RadarrEvent::TestAllIndexers => "/indexer/testall", RadarrEvent::StartTask | RadarrEvent::GetQueuedEvents @@ -153,6 +155,7 @@ impl<'a, 'b> Network<'a, 'b> { RadarrEvent::HealthCheck => self.get_healthcheck().await, RadarrEvent::SearchNewMovie => self.search_movie().await, RadarrEvent::StartTask => self.start_task().await, + RadarrEvent::TestIndexer => self.test_indexer().await, RadarrEvent::TestAllIndexers => self.test_all_indexers().await, RadarrEvent::TriggerAutomaticSearch => self.trigger_automatic_search().await, RadarrEvent::UpdateAllMovies => self.update_all_movies().await, @@ -1524,6 +1527,61 @@ impl<'a, 'b> Network<'a, 'b> { .await; } + async fn test_indexer(&mut self) { + let id = self + .app + .lock() + .await + .data + .radarr_data + .indexers + .current_selection() + .id; + info!("Testing Radarr indexer with ID: {id}"); + + info!("Fetching indexer details for indexer with ID: {id}"); + + let request_props = self + .radarr_request_props_from( + format!("{}/{id}", RadarrEvent::GetIndexers.resource()).as_str(), + RequestMethod::Get, + None::<()>, + ) + .await; + + let mut test_body: Value = Value::default(); + + self + .handle_request::<(), Value>(request_props, |detailed_indexer_body, _| { + test_body = detailed_indexer_body; + }) + .await; + + info!("Testing indexer"); + + let mut request_props = self + .radarr_request_props_from( + RadarrEvent::TestIndexer.resource(), + RequestMethod::Post, + Some(test_body), + ) + .await; + request_props.ignore_status_code = true; + + self + .handle_request::(request_props, |test_results, mut app| { + if test_results.as_object().is_none() { + app.data.radarr_data.indexer_test_error = Some( + test_results.as_array().unwrap()[0] + .get("errorMessage") + .unwrap() + .to_string(), + ); + }; + }) + .await; + } + async fn test_all_indexers(&mut self) { info!("Testing all indexers"); diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index 109ee46..126f876 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -196,6 +196,7 @@ mod test { #[case(RadarrEvent::GetTags, "/tag")] #[case(RadarrEvent::GetTasks, "/system/task")] #[case(RadarrEvent::GetUpdates, "/update")] + #[case(RadarrEvent::TestIndexer, "/indexer/test")] #[case(RadarrEvent::TestAllIndexers, "/indexer/testall")] #[case(RadarrEvent::HealthCheck, "/health")] fn test_resource(#[case] event: RadarrEvent, #[case] expected_uri: String) { @@ -686,6 +687,139 @@ mod test { ); } + #[tokio::test] + async fn test_handle_test_indexer_event_error() { + let indexer_details_json = json!({ + "enableRss": true, + "enableAutomaticSearch": true, + "enableInteractiveSearch": true, + "name": "Test Indexer", + "fields": [ + { + "name": "baseUrl", + "value": "https://test.com", + }, + { + "name": "apiKey", + "value": "", + }, + { + "name": "seedCriteria.seedRatio", + "value": "1.2", + }, + ], + "tags": [1], + "id": 1 + }); + let response_json = json!([ + { + "isWarning": false, + "propertyName": "", + "errorMessage": "test failure", + "severity": "error" + }]); + let resource = format!("{}/1", RadarrEvent::GetIndexers.resource()); + let (async_details_server, app_arc, mut server) = mock_radarr_api( + RequestMethod::Get, + None, + Some(indexer_details_json.clone()), + None, + &resource, + ) + .await; + let async_test_server = server + .mock( + "POST", + format!("/api/v3{}", RadarrEvent::TestIndexer.resource()).as_str(), + ) + .with_status(400) + .match_header("X-Api-Key", "test1234") + .match_body(Matcher::Json(indexer_details_json)) + .with_body(response_json.to_string()) + .create_async() + .await; + app_arc + .lock() + .await + .data + .radarr_data + .indexers + .set_items(vec![indexer()]); + let mut network = Network::new(&app_arc, CancellationToken::new()); + + network.handle_radarr_event(RadarrEvent::TestIndexer).await; + + async_details_server.assert_async().await; + async_test_server.assert_async().await; + assert_eq!( + app_arc.lock().await.data.radarr_data.indexer_test_error, + Some("\"test failure\"".to_owned()) + ); + } + + #[tokio::test] + async fn test_handle_test_indexer_event_success() { + let indexer_details_json = json!({ + "enableRss": true, + "enableAutomaticSearch": true, + "enableInteractiveSearch": true, + "name": "Test Indexer", + "fields": [ + { + "name": "baseUrl", + "value": "https://test.com", + }, + { + "name": "apiKey", + "value": "", + }, + { + "name": "seedCriteria.seedRatio", + "value": "1.2", + }, + ], + "tags": [1], + "id": 1 + }); + let resource = format!("{}/1", RadarrEvent::GetIndexers.resource()); + let (async_details_server, app_arc, mut server) = mock_radarr_api( + RequestMethod::Get, + None, + Some(indexer_details_json.clone()), + None, + &resource, + ) + .await; + let async_test_server = server + .mock( + "POST", + format!("/api/v3{}", RadarrEvent::TestIndexer.resource()).as_str(), + ) + .with_status(200) + .match_header("X-Api-Key", "test1234") + .match_body(Matcher::Json(indexer_details_json)) + .with_body("{}") + .create_async() + .await; + app_arc + .lock() + .await + .data + .radarr_data + .indexers + .set_items(vec![indexer()]); + let mut network = Network::new(&app_arc, CancellationToken::new()); + + network.handle_radarr_event(RadarrEvent::TestIndexer).await; + + async_details_server.assert_async().await; + async_test_server.assert_async().await; + assert_eq!( + app_arc.lock().await.data.radarr_data.indexer_test_error, + None + ); + } + #[tokio::test] async fn test_handle_test_all_indexers_event() { let indexers = vec![ diff --git a/src/ui/radarr_ui/collections/mod.rs b/src/ui/radarr_ui/collections/mod.rs index 81fd139..d53687e 100644 --- a/src/ui/radarr_ui/collections/mod.rs +++ b/src/ui/radarr_ui/collections/mod.rs @@ -13,8 +13,8 @@ use crate::ui::radarr_ui::collections::edit_collection_ui::EditCollectionUi; use crate::ui::styles::ManagarrStyle; use crate::ui::utils::{get_width_from_percentage, layout_block_top_border}; use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt; -use crate::ui::widgets::error_message::ErrorMessage; use crate::ui::widgets::managarr_table::ManagarrTable; +use crate::ui::widgets::message::Message; use crate::ui::widgets::popup::{Popup, Size}; use crate::ui::{draw_input_box_popup, draw_popup_over, DrawUi}; @@ -52,7 +52,7 @@ impl DrawUi for CollectionsUi { Size::InputBox, ), ActiveRadarrBlock::SearchCollectionError => { - let popup = Popup::new(ErrorMessage::new("Collection not found!")).size(Size::Error); + let popup = Popup::new(Message::new("Collection not found!")).size(Size::Message); draw_collections(f, app, area); f.render_widget(popup, f.size()); @@ -66,10 +66,10 @@ impl DrawUi for CollectionsUi { Size::InputBox, ), ActiveRadarrBlock::FilterCollectionsError => { - let popup = Popup::new(ErrorMessage::new( + let popup = Popup::new(Message::new( "No collections found matching the given filter!", )) - .size(Size::Error); + .size(Size::Message); draw_collections(f, app, area); f.render_widget(popup, f.size()); diff --git a/src/ui/radarr_ui/indexers/mod.rs b/src/ui/radarr_ui/indexers/mod.rs index ffa00b4..9729587 100644 --- a/src/ui/radarr_ui/indexers/mod.rs +++ b/src/ui/radarr_ui/indexers/mod.rs @@ -1,4 +1,5 @@ use ratatui::layout::{Constraint, Rect}; +use ratatui::style::{Style, Stylize}; use ratatui::text::Text; use ratatui::widgets::{Cell, Row}; use ratatui::Frame; @@ -11,9 +12,11 @@ use crate::ui::radarr_ui::indexers::edit_indexer_ui::EditIndexerUi; use crate::ui::radarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi; use crate::ui::radarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi; use crate::ui::styles::ManagarrStyle; -use crate::ui::utils::layout_block_top_border; +use crate::ui::utils::{layout_block_top_border, title_block}; use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt; +use crate::ui::widgets::loading_block::LoadingBlock; use crate::ui::widgets::managarr_table::ManagarrTable; +use crate::ui::widgets::message::Message; use crate::ui::widgets::popup::{Popup, Size}; use crate::ui::DrawUi; @@ -43,6 +46,28 @@ impl DrawUi for IndexersUi { let route = *app.get_current_route(); let mut indexers_matchers = |active_radarr_block| match active_radarr_block { ActiveRadarrBlock::Indexers => draw_indexers(f, app, area), + ActiveRadarrBlock::TestIndexer => { + draw_indexers(f, app, area); + if app.is_loading { + let loading_popup = Popup::new(LoadingBlock::new( + app.is_loading, + title_block("Testing Indexer"), + )) + .size(Size::LargeMessage); + f.render_widget(loading_popup, f.size()); + } else { + let popup = if let Some(result) = app.data.radarr_data.indexer_test_error.as_ref() { + Popup::new(Message::new(result.clone())).size(Size::LargeMessage) + } else { + let message = Message::new("Indexer test succeeded!") + .title("Success") + .style(Style::new().success().bold()); + Popup::new(message).size(Size::Message) + }; + + f.render_widget(popup, f.size()); + } + } ActiveRadarrBlock::DeleteIndexerPrompt => { let prompt = format!( "Do you really want to delete this indexer: \n{}?", diff --git a/src/ui/radarr_ui/library/add_movie_ui.rs b/src/ui/radarr_ui/library/add_movie_ui.rs index 9cf8af1..a4c0939 100644 --- a/src/ui/radarr_ui/library/add_movie_ui.rs +++ b/src/ui/radarr_ui/library/add_movie_ui.rs @@ -17,9 +17,9 @@ use crate::ui::utils::{ title_block_centered, }; use crate::ui::widgets::button::Button; -use crate::ui::widgets::error_message::ErrorMessage; use crate::ui::widgets::input_box::InputBox; use crate::ui::widgets::managarr_table::ManagarrTable; +use crate::ui::widgets::message::Message; use crate::ui::widgets::popup::{Popup, Size}; use crate::ui::widgets::selectable_list::SelectableList; use crate::ui::{draw_popup_over, DrawUi}; @@ -79,8 +79,7 @@ impl DrawUi for AddMovieUi { ActiveRadarrBlock::AddMovieAlreadyInLibrary => { draw_add_movie_search(f, app, area); f.render_widget( - Popup::new(ErrorMessage::new("This film is already in your library")) - .size(Size::Error), + Popup::new(Message::new("This film is already in your library")).size(Size::Message), f.size(), ); } @@ -220,8 +219,8 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { let help_paragraph = Paragraph::new(help_text) .block(borderless_block()) .alignment(Alignment::Center); - let error_message = ErrorMessage::new("No movies found matching your query!"); - let error_message_popup = Popup::new(error_message).size(Size::Error); + let error_message = Message::new("No movies found matching your query!"); + let error_message_popup = Popup::new(error_message).size(Size::Message); f.render_widget(layout_block(), results_area); f.render_widget(error_message_popup, f.size()); diff --git a/src/ui/radarr_ui/library/mod.rs b/src/ui/radarr_ui/library/mod.rs index 31827b4..d193113 100644 --- a/src/ui/radarr_ui/library/mod.rs +++ b/src/ui/radarr_ui/library/mod.rs @@ -13,8 +13,8 @@ use crate::ui::radarr_ui::library::edit_movie_ui::EditMovieUi; use crate::ui::radarr_ui::library::movie_details_ui::MovieDetailsUi; use crate::ui::utils::{get_width_from_percentage, layout_block_top_border}; use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt; -use crate::ui::widgets::error_message::ErrorMessage; use crate::ui::widgets::managarr_table::ManagarrTable; +use crate::ui::widgets::message::Message; use crate::ui::widgets::popup::{Popup, Size}; use crate::ui::{draw_input_box_popup, draw_popup_over, DrawUi}; use crate::utils::{convert_runtime, convert_to_gb}; @@ -57,7 +57,7 @@ impl DrawUi for LibraryUi { Size::InputBox, ), ActiveRadarrBlock::SearchMovieError => { - let popup = Popup::new(ErrorMessage::new("Movie not found!")).size(Size::Error); + let popup = Popup::new(Message::new("Movie not found!")).size(Size::Message); draw_library(f, app, area); f.render_widget(popup, f.size()); @@ -71,10 +71,8 @@ impl DrawUi for LibraryUi { Size::InputBox, ), ActiveRadarrBlock::FilterMoviesError => { - let popup = Popup::new(ErrorMessage::new( - "No movies found matching the given filter!", - )) - .size(Size::Error); + let popup = Popup::new(Message::new("No movies found matching the given filter!")) + .size(Size::Message); draw_library(f, app, area); f.render_widget(popup, f.size()); diff --git a/src/ui/widgets/error_message.rs b/src/ui/widgets/error_message.rs deleted file mode 100644 index 0146da4..0000000 --- a/src/ui/widgets/error_message.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::ui::styles::ManagarrStyle; -use crate::ui::utils::title_block_centered; -use ratatui::buffer::Buffer; -use ratatui::layout::{Alignment, Rect}; -use ratatui::style::Stylize; -use ratatui::text::Text; -use ratatui::widgets::{Paragraph, Widget}; - -#[cfg(test)] -#[path = "error_message_tests.rs"] -mod error_message_tests; - -pub struct ErrorMessage<'a> { - text: Text<'a>, -} - -impl<'a> ErrorMessage<'a> { - pub fn new(message: T) -> Self - where - T: Into>, - { - ErrorMessage { - text: message.into(), - } - } - - fn render_error_message(self, area: Rect, buf: &mut Buffer) { - Paragraph::new(self.text) - .failure() - .alignment(Alignment::Center) - .block(title_block_centered("Error").failure().bold()) - .render(area, buf); - } -} - -impl<'a> Widget for ErrorMessage<'a> { - fn render(self, area: Rect, buf: &mut Buffer) { - self.render_error_message(area, buf); - } -} diff --git a/src/ui/widgets/error_message_tests.rs b/src/ui/widgets/error_message_tests.rs deleted file mode 100644 index 8c5df9c..0000000 --- a/src/ui/widgets/error_message_tests.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::ui::widgets::error_message::ErrorMessage; - use pretty_assertions::assert_eq; - use ratatui::text::Text; - - #[test] - fn test_error_message_new() { - let message = "This is an error message"; - let error_message = ErrorMessage::new(message); - - assert_eq!(error_message.text, Text::from(message)); - } -} diff --git a/src/ui/widgets/message.rs b/src/ui/widgets/message.rs new file mode 100644 index 0000000..de880ea --- /dev/null +++ b/src/ui/widgets/message.rs @@ -0,0 +1,55 @@ +use crate::ui::styles::ManagarrStyle; +use crate::ui::utils::title_block_centered; +use ratatui::buffer::Buffer; +use ratatui::layout::{Alignment, Rect}; +use ratatui::style::{Style, Stylize}; +use ratatui::text::Text; +use ratatui::widgets::{Paragraph, Widget, Wrap}; + +#[cfg(test)] +#[path = "message_tests.rs"] +mod message_tests; + +pub struct Message<'a> { + text: Text<'a>, + title: &'a str, + style: Style, +} + +impl<'a> Message<'a> { + pub fn new(message: T) -> Self + where + T: Into>, + { + Message { + text: message.into(), + title: "Error", + style: Style::new().failure().bold(), + } + } + + pub fn title(mut self, title: &'a str) -> Self { + self.title = title; + self + } + + pub fn style(mut self, style: Style) -> Self { + self.style = style; + self + } + + fn render_message(self, area: Rect, buf: &mut Buffer) { + Paragraph::new(self.text) + .style(self.style) + .alignment(Alignment::Center) + .block(title_block_centered(self.title).style(self.style)) + .wrap(Wrap { trim: true }) + .render(area, buf); + } +} + +impl<'a> Widget for Message<'a> { + fn render(self, area: Rect, buf: &mut Buffer) { + self.render_message(area, buf); + } +} diff --git a/src/ui/widgets/message_tests.rs b/src/ui/widgets/message_tests.rs new file mode 100644 index 0000000..efa875a --- /dev/null +++ b/src/ui/widgets/message_tests.rs @@ -0,0 +1,43 @@ +#[cfg(test)] +mod tests { + use crate::ui::styles::ManagarrStyle; + use crate::ui::widgets::message::Message; + use pretty_assertions::{assert_eq, assert_str_eq}; + use ratatui::style::{Style, Stylize}; + use ratatui::text::Text; + + #[test] + fn test_error_message_new() { + let test_message = "This is a message"; + + let message = Message::new(test_message); + + assert_eq!(message.text, Text::from(test_message)); + assert_str_eq!(message.title, "Error"); + assert_eq!(message.style, Style::new().failure().bold()); + } + + #[test] + fn test_message_title() { + let test_message = "This is a message"; + let title = "Success"; + + let message = Message::new(test_message).title(title); + + assert_str_eq!(message.title, title); + assert_eq!(message.text, Text::from(test_message)); + assert_eq!(message.style, Style::new().failure().bold()); + } + + #[test] + fn test_message_style() { + let test_message = "This is a message"; + let style = Style::new().success().bold(); + + let message = Message::new(test_message).style(style); + + assert_eq!(message.style, style); + assert_eq!(message.text, Text::from(test_message)); + assert_str_eq!(message.title, "Error"); + } +} diff --git a/src/ui/widgets/mod.rs b/src/ui/widgets/mod.rs index b0502b0..8b279a5 100644 --- a/src/ui/widgets/mod.rs +++ b/src/ui/widgets/mod.rs @@ -1,9 +1,9 @@ pub(super) mod button; pub(super) mod checkbox; pub(super) mod confirmation_prompt; -pub(super) mod error_message; pub(super) mod input_box; pub(super) mod loading_block; pub(super) mod managarr_table; +pub(super) mod message; pub(super) mod popup; pub(super) mod selectable_list; diff --git a/src/ui/widgets/popup.rs b/src/ui/widgets/popup.rs index 22a8156..c4ac287 100644 --- a/src/ui/widgets/popup.rs +++ b/src/ui/widgets/popup.rs @@ -12,7 +12,8 @@ mod popup_tests; pub enum Size { Prompt, LargePrompt, - Error, + Message, + LargeMessage, InputBox, Dropdown, Small, @@ -25,7 +26,8 @@ impl Size { match self { Size::Prompt => (35, 35), Size::LargePrompt => (70, 45), - Size::Error => (25, 8), + Size::Message => (25, 8), + Size::LargeMessage => (25, 25), Size::InputBox => (30, 13), Size::Dropdown => (20, 30), Size::Small => (40, 40), diff --git a/src/ui/widgets/popup_tests.rs b/src/ui/widgets/popup_tests.rs index 52fe461..4d4223d 100644 --- a/src/ui/widgets/popup_tests.rs +++ b/src/ui/widgets/popup_tests.rs @@ -8,7 +8,8 @@ mod tests { fn test_dimensions_to_percent() { assert_eq!(Size::Prompt.to_percent(), (35, 35)); assert_eq!(Size::LargePrompt.to_percent(), (70, 45)); - assert_eq!(Size::Error.to_percent(), (25, 8)); + assert_eq!(Size::Message.to_percent(), (25, 8)); + assert_eq!(Size::LargeMessage.to_percent(), (25, 25)); assert_eq!(Size::InputBox.to_percent(), (30, 13)); assert_eq!(Size::Dropdown.to_percent(), (20, 30)); assert_eq!(Size::Small.to_percent(), (40, 40));