From ec9d23ede70a2a6a6eb166a24d939f2508db5ea1 Mon Sep 17 00:00:00 2001 From: Dark-Alex-17 Date: Fri, 11 Aug 2023 16:37:21 -0600 Subject: [PATCH] Refactored the movie_details_popup data into the MovieDetailsModal so things are neater and to follow the same format I've already established for other modals --- Cargo.toml | 4 +- src/app/radarr/mod.rs | 23 +- src/app/radarr/radarr_tests.rs | 36 +- src/handlers/handler_test_utils.rs | 38 - .../library/movie_details_handler.rs | 261 +++++-- .../library/movie_details_handler_tests.rs | 696 +++++++++++++++--- .../system/system_details_handler_tests.rs | 68 +- src/models/servarr_data/radarr/modals.rs | 21 +- src/models/servarr_data/radarr/radarr_data.rs | 39 +- .../servarr_data/radarr/radarr_data_tests.rs | 11 +- .../servarr_data/radarr/radarr_test_utils.rs | 50 +- src/network/radarr_network.rs | 101 +-- src/network/radarr_network_tests.rs | 114 +-- src/ui/radarr_ui/library/movie_details_ui.rs | 274 ++++--- 14 files changed, 1207 insertions(+), 529 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90dd92b..546c38d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "managarr" -version = "0.0.27" +version = "0.0.28" authors = ["Alex Clarke "] description = "A TUI for managing *arr servers" -keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui", "terminal"] +keywords = ["managarr", "tui-rs", "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/radarr/mod.rs b/src/app/radarr/mod.rs index b70fdba..65d8b38 100644 --- a/src/app/radarr/mod.rs +++ b/src/app/radarr/mod.rs @@ -81,21 +81,26 @@ impl<'a> App<'a> { .await; } ActiveRadarrBlock::Cast | ActiveRadarrBlock::Crew => { - if self.data.radarr_data.movie_cast.items.is_empty() - || self.data.radarr_data.movie_crew.items.is_empty() - { - self - .dispatch_network_event(RadarrEvent::GetMovieCredits.into()) - .await; + match self.data.radarr_data.movie_details_modal.as_ref() { + Some(movie_details_modal) + if movie_details_modal.movie_cast.items.is_empty() + || movie_details_modal.movie_crew.items.is_empty() => + { + self + .dispatch_network_event(RadarrEvent::GetMovieCredits.into()) + .await; + } + _ => (), } } - ActiveRadarrBlock::ManualSearch => { - if self.data.radarr_data.movie_releases.items.is_empty() { + ActiveRadarrBlock::ManualSearch => match self.data.radarr_data.movie_details_modal.as_ref() { + Some(movie_details_modal) if movie_details_modal.movie_releases.items.is_empty() => { self .dispatch_network_event(RadarrEvent::GetReleases.into()) .await; } - } + _ => (), + }, _ => (), } diff --git a/src/app/radarr/radarr_tests.rs b/src/app/radarr/radarr_tests.rs index a095b87..01854ed 100644 --- a/src/app/radarr/radarr_tests.rs +++ b/src/app/radarr/radarr_tests.rs @@ -7,7 +7,8 @@ mod tests { use crate::app::radarr::ActiveRadarrBlock; use crate::app::App; use crate::models::radarr_models::{Collection, CollectionMovie, Credit, Release}; - use crate::models::StatefulTable; + use crate::models::servarr_data::radarr::modals::MovieDetailsModal; + use crate::network::radarr_network::RadarrEvent; use crate::network::NetworkEvent; @@ -275,9 +276,7 @@ mod tests { let (mut app, mut sync_network_rx) = construct_app_unit(); for active_radarr_block in &[ActiveRadarrBlock::Cast, ActiveRadarrBlock::Crew] { - app.data.radarr_data.movie_cast = StatefulTable::default(); - app.data.radarr_data.movie_crew = StatefulTable::default(); - + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); app.dispatch_by_radarr_block(active_radarr_block).await; assert!(app.is_loading); @@ -295,11 +294,11 @@ mod tests { let (mut app, mut sync_network_rx) = construct_app_unit(); for active_radarr_block in &[ActiveRadarrBlock::Cast, ActiveRadarrBlock::Crew] { - app - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal .movie_cast .set_items(vec![Credit::default()]); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app.dispatch_by_radarr_block(active_radarr_block).await; @@ -318,11 +317,11 @@ mod tests { let (mut app, mut sync_network_rx) = construct_app_unit(); for active_radarr_block in &[ActiveRadarrBlock::Cast, ActiveRadarrBlock::Crew] { - app - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal .movie_crew .set_items(vec![Credit::default()]); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app.dispatch_by_radarr_block(active_radarr_block).await; @@ -341,16 +340,14 @@ mod tests { let mut app = App::default(); for active_radarr_block in &[ActiveRadarrBlock::Cast, ActiveRadarrBlock::Crew] { - app - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal .movie_cast .set_items(vec![Credit::default()]); - app - .data - .radarr_data + movie_details_modal .movie_crew .set_items(vec![Credit::default()]); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app.dispatch_by_radarr_block(active_radarr_block).await; @@ -363,6 +360,7 @@ mod tests { #[tokio::test] async fn test_dispatch_by_manual_search_block() { let (mut app, mut sync_network_rx) = construct_app_unit(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); app .dispatch_by_radarr_block(&ActiveRadarrBlock::ManualSearch) @@ -380,11 +378,11 @@ mod tests { #[tokio::test] async fn test_dispatch_by_manual_search_block_movie_releases_non_empty() { let mut app = App::default(); - app - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal .movie_releases .set_items(vec![Release::default()]); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); app .dispatch_by_radarr_block(&ActiveRadarrBlock::ManualSearch) diff --git a/src/handlers/handler_test_utils.rs b/src/handlers/handler_test_utils.rs index 65171ed..ca71192 100644 --- a/src/handlers/handler_test_utils.rs +++ b/src/handlers/handler_test_utils.rs @@ -202,25 +202,6 @@ mod test_utils { }; } - #[macro_export] - macro_rules! test_scrollable_text_scroll { - ($func:ident, $handler:ident, $data_ref:ident, $block:expr) => { - #[test] - fn $func() { - let mut app = App::default(); - app.data.radarr_data.$data_ref = ScrollableText::with_string("Test 1\nTest 2".to_owned()); - - $handler::with(&DEFAULT_KEYBINDINGS.up.key, &mut app, &$block, &None).handle(); - - assert_eq!(app.data.radarr_data.$data_ref.offset, 0); - - $handler::with(&DEFAULT_KEYBINDINGS.down.key, &mut app, &$block, &None).handle(); - - assert_eq!(app.data.radarr_data.$data_ref.offset, 1); - } - }; - } - #[macro_export] macro_rules! test_iterable_home_and_end { ($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => { @@ -326,25 +307,6 @@ mod test_utils { }; } - #[macro_export] - macro_rules! test_scrollable_text_home_and_end { - ($func:ident, $handler:ident, $data_ref:ident, $block:expr) => { - #[test] - fn $func() { - let mut app = App::default(); - app.data.radarr_data.$data_ref = ScrollableText::with_string("Test 1\nTest 2".to_owned()); - - $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &None).handle(); - - assert_eq!(app.data.radarr_data.$data_ref.offset, 1); - - $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &None).handle(); - - assert_eq!(app.data.radarr_data.$data_ref.offset, 0); - } - }; - } - #[macro_export] macro_rules! test_handler_delegation { ($handler:ident, $base:expr, $active_block:expr) => { diff --git a/src/handlers/radarr_handlers/library/movie_details_handler.rs b/src/handlers/radarr_handlers/library/movie_details_handler.rs index b3f2734..a6e9cbe 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler.rs @@ -50,43 +50,178 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler< fn handle_scroll_up(&mut self) { match self.active_radarr_block { - ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_up(), - ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_up(), - ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_up(), - ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_up(), - ActiveRadarrBlock::ManualSearch => self.app.data.radarr_data.movie_releases.scroll_up(), - ActiveRadarrBlock::ManualSearchSortPrompt => { - self.app.data.radarr_data.movie_releases_sort.scroll_up() - } + ActiveRadarrBlock::MovieDetails => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_details + .scroll_up(), + ActiveRadarrBlock::MovieHistory => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_history + .scroll_up(), + ActiveRadarrBlock::Cast => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_cast + .scroll_up(), + ActiveRadarrBlock::Crew => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_crew + .scroll_up(), + ActiveRadarrBlock::ManualSearch => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases + .scroll_up(), + ActiveRadarrBlock::ManualSearchSortPrompt => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases_sort + .scroll_up(), _ => (), } } fn handle_scroll_down(&mut self) { match self.active_radarr_block { - ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_down(), - ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_down(), - ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_down(), - ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_down(), - ActiveRadarrBlock::ManualSearch => self.app.data.radarr_data.movie_releases.scroll_down(), - ActiveRadarrBlock::ManualSearchSortPrompt => { - self.app.data.radarr_data.movie_releases_sort.scroll_down() - } + ActiveRadarrBlock::MovieDetails => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_details + .scroll_down(), + ActiveRadarrBlock::MovieHistory => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_history + .scroll_down(), + ActiveRadarrBlock::Cast => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_cast + .scroll_down(), + ActiveRadarrBlock::Crew => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_crew + .scroll_down(), + ActiveRadarrBlock::ManualSearch => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases + .scroll_down(), + ActiveRadarrBlock::ManualSearchSortPrompt => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases_sort + .scroll_down(), _ => (), } } fn handle_home(&mut self) { match self.active_radarr_block { - ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_to_top(), - ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_to_top(), - ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_to_top(), - ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_to_top(), - ActiveRadarrBlock::ManualSearch => self.app.data.radarr_data.movie_releases.scroll_to_top(), + ActiveRadarrBlock::MovieDetails => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_details + .scroll_to_top(), + ActiveRadarrBlock::MovieHistory => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_history + .scroll_to_top(), + ActiveRadarrBlock::Cast => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_cast + .scroll_to_top(), + ActiveRadarrBlock::Crew => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_crew + .scroll_to_top(), + ActiveRadarrBlock::ManualSearch => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases + .scroll_to_top(), ActiveRadarrBlock::ManualSearchSortPrompt => self .app .data .radarr_data + .movie_details_modal + .as_mut() + .unwrap() .movie_releases_sort .scroll_to_top(), _ => (), @@ -95,17 +230,58 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler< fn handle_end(&mut self) { match self.active_radarr_block { - ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_to_bottom(), - ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_to_bottom(), - ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_to_bottom(), - ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_to_bottom(), - ActiveRadarrBlock::ManualSearch => { - self.app.data.radarr_data.movie_releases.scroll_to_bottom() - } + ActiveRadarrBlock::MovieDetails => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_details + .scroll_to_bottom(), + ActiveRadarrBlock::MovieHistory => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_history + .scroll_to_bottom(), + ActiveRadarrBlock::Cast => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_cast + .scroll_to_bottom(), + ActiveRadarrBlock::Crew => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_crew + .scroll_to_bottom(), + ActiveRadarrBlock::ManualSearch => self + .app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases + .scroll_to_bottom(), ActiveRadarrBlock::ManualSearchSortPrompt => self .app .data .radarr_data + .movie_details_modal + .as_mut() + .unwrap() .movie_releases_sort .scroll_to_bottom(), _ => (), @@ -173,20 +349,19 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler< self.app.pop_navigation_stack(); } ActiveRadarrBlock::ManualSearchSortPrompt => { - let movie_releases = self.app.data.radarr_data.movie_releases.items.clone(); - let field = self + let movie_details_modal = self .app .data .radarr_data - .movie_releases_sort - .current_selection(); - let sort_ascending = !self.app.data.radarr_data.sort_ascending.unwrap(); - self.app.data.radarr_data.sort_ascending = Some(sort_ascending); + .movie_details_modal + .as_mut() + .unwrap(); + let movie_releases = movie_details_modal.movie_releases.items.clone(); + let field = movie_details_modal.movie_releases_sort.current_selection(); + let sort_ascending = !movie_details_modal.sort_ascending.unwrap(); + movie_details_modal.sort_ascending = Some(sort_ascending); - self - .app - .data - .radarr_data + movie_details_modal .movie_releases .set_items(sort_releases_by_selected_field( movie_releases, @@ -258,14 +433,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler< .pop_and_push_navigation_stack((*self.active_radarr_block).into()); } _ if *key == DEFAULT_KEYBINDINGS.sort.key => { - self + let movie_details_modal = self .app .data .radarr_data + .movie_details_modal + .as_mut() + .unwrap(); + movie_details_modal .movie_releases_sort .set_items(Vec::from_iter(ReleaseField::iter())); - let sort_ascending = self.app.data.radarr_data.sort_ascending; - self.app.data.radarr_data.sort_ascending = + let sort_ascending = movie_details_modal.sort_ascending; + movie_details_modal.sort_ascending = Some(sort_ascending.is_some() && sort_ascending.unwrap()); self .app 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 0b67eb5..7870a63 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs @@ -24,60 +24,238 @@ mod tests { use strum::IntoEnumIterator; use crate::models::radarr_models::ReleaseField; - use crate::{simple_stateful_iterable_vec, test_iterable_scroll, test_scrollable_text_scroll}; + use crate::models::servarr_data::radarr::modals::MovieDetailsModal; + use crate::simple_stateful_iterable_vec; use super::*; - test_scrollable_text_scroll!( - test_movie_details_scroll, - MovieDetailsHandler, - movie_details, - ActiveRadarrBlock::MovieDetails - ); + #[test] + fn test_movie_details_scroll() { + let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("Test 1\nTest 2".to_owned()), + ..MovieDetailsModal::default() + }); - test_iterable_scroll!( - test_movie_history_scroll, - MovieDetailsHandler, - movie_history, - simple_stateful_iterable_vec!(MovieHistoryItem, HorizontallyScrollableText, source_title), - ActiveRadarrBlock::MovieHistory, - None, - source_title, - to_string - ); + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.up.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); - test_iterable_scroll!( - test_cast_scroll, - MovieDetailsHandler, - movie_cast, - simple_stateful_iterable_vec!(Credit, String, person_name), - ActiveRadarrBlock::Cast, - None, - person_name, - to_owned - ); + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 0 + ); - test_iterable_scroll!( - test_crew_scroll, - MovieDetailsHandler, - movie_crew, - simple_stateful_iterable_vec!(Credit, String, person_name), - ActiveRadarrBlock::Crew, - None, - person_name, - to_owned - ); + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.down.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); - test_iterable_scroll!( - test_manual_search_scroll, - MovieDetailsHandler, - movie_releases, - simple_stateful_iterable_vec!(Release, HorizontallyScrollableText), - ActiveRadarrBlock::ManualSearch, - None, - title, - to_string - ); + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 1 + ); + } + + #[rstest] + fn test_movie_history_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_history + .set_items(simple_stateful_iterable_vec!( + MovieHistoryItem, + HorizontallyScrollableText, + source_title + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::MovieHistory, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 2" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::MovieHistory, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + } + + #[rstest] + fn test_cast_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_cast + .set_items(simple_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Cast, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 2" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Cast, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 1" + ); + } + + #[rstest] + fn test_crew_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_crew + .set_items(simple_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Crew, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 2" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Crew, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 1" + ); + } + + #[rstest] + fn test_manual_search_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_releases + .set_items(simple_stateful_iterable_vec!( + Release, + HorizontallyScrollableText + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::ManualSearch, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 2" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::ManualSearch, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 1" + ); + } #[rstest] fn test_manual_search_sort_scroll( @@ -85,11 +263,11 @@ mod tests { ) { let release_field_vec = Vec::from_iter(ReleaseField::iter()); let mut app = App::default(); - app - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal .movie_releases_sort .set_items(release_field_vec.clone()); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); if key == Key::Up { for i in (0..release_field_vec.len()).rev() { @@ -102,7 +280,14 @@ mod tests { .handle(); assert_eq!( - app.data.radarr_data.movie_releases_sort.current_selection(), + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases_sort + .current_selection(), &release_field_vec[i] ); } @@ -117,7 +302,14 @@ mod tests { .handle(); assert_eq!( - app.data.radarr_data.movie_releases_sort.current_selection(), + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases_sort + .current_selection(), &release_field_vec[(i + 1) % release_field_vec.len()] ); } @@ -128,73 +320,290 @@ mod tests { mod test_handle_home_end { use strum::IntoEnumIterator; + use crate::extended_stateful_iterable_vec; use crate::models::radarr_models::ReleaseField; - use crate::{ - extended_stateful_iterable_vec, test_iterable_home_and_end, test_scrollable_text_home_and_end, - }; + use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use super::*; - test_scrollable_text_home_and_end!( - test_movie_details_home_end, - MovieDetailsHandler, - movie_details, - ActiveRadarrBlock::MovieDetails - ); + #[test] + fn test_movie_details_home_end() { + let mut app = App::default(); + let movie_details_modal = MovieDetailsModal { + movie_details: ScrollableText::with_string("Test 1\nTest 2".to_owned()), + ..MovieDetailsModal::default() + }; + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); - test_iterable_home_and_end!( - test_movie_history_home_end, - MovieDetailsHandler, - movie_history, - extended_stateful_iterable_vec!(MovieHistoryItem, HorizontallyScrollableText, source_title), - ActiveRadarrBlock::MovieHistory, - None, - source_title, - to_string - ); + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); - test_iterable_home_and_end!( - test_cast_home_end, - MovieDetailsHandler, - movie_cast, - extended_stateful_iterable_vec!(Credit, String, person_name), - ActiveRadarrBlock::Cast, - None, - person_name, - to_owned - ); + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 1 + ); - test_iterable_home_and_end!( - test_crew_home_end, - MovieDetailsHandler, - movie_crew, - extended_stateful_iterable_vec!(Credit, String, person_name), - ActiveRadarrBlock::Crew, - None, - person_name, - to_owned - ); + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); - test_iterable_home_and_end!( - test_manual_search_home_end, - MovieDetailsHandler, - movie_releases, - extended_stateful_iterable_vec!(Release, HorizontallyScrollableText), - ActiveRadarrBlock::ManualSearch, - None, - title, - to_string - ); + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 0 + ); + } + + #[test] + fn test_movie_history_home_end() { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_history + .set_items(extended_stateful_iterable_vec!( + MovieHistoryItem, + HorizontallyScrollableText, + source_title + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::MovieHistory, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 3" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::MovieHistory, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + } + + #[test] + fn test_cast_home_end() { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_cast + .set_items(extended_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Cast, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 3" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Cast, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 1" + ); + } + + #[test] + fn test_crew_home_end() { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_crew + .set_items(extended_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Crew, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 3" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Crew, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 1" + ); + } + + #[test] + fn test_manual_search_home_end() { + let mut app = App::default(); + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_releases + .set_items(extended_stateful_iterable_vec!( + Release, + HorizontallyScrollableText + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 3" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 1" + ); + } #[test] fn test_manual_search_sort_home_end() { let release_field_vec = Vec::from_iter(ReleaseField::iter()); let mut app = App::default(); - app - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal .movie_releases_sort .set_items(release_field_vec.clone()); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); MovieDetailsHandler::with( &DEFAULT_KEYBINDINGS.end.key, @@ -205,7 +614,14 @@ mod tests { .handle(); assert_eq!( - app.data.radarr_data.movie_releases_sort.current_selection(), + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases_sort + .current_selection(), &release_field_vec[release_field_vec.len() - 1] ); @@ -218,7 +634,14 @@ mod tests { .handle(); assert_eq!( - app.data.radarr_data.movie_releases_sort.current_selection(), + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases_sort + .current_selection(), &release_field_vec[0] ); } @@ -298,6 +721,7 @@ mod tests { use rstest::rstest; use crate::models::radarr_models::ReleaseField; + use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::network::radarr_network::RadarrEvent; use super::*; @@ -381,15 +805,17 @@ mod tests { #[test] fn test_manual_search_sort_prompt_submit() { let mut app = App::default(); - app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); - app.push_navigation_stack(ActiveRadarrBlock::ManualSearchSortPrompt.into()); - app.data.radarr_data.sort_ascending = Some(true); - app - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal { + sort_ascending: Some(true), + ..MovieDetailsModal::default() + }; + movie_details_modal .movie_releases_sort .set_items(vec![ReleaseField::default()]); - app.data.radarr_data.movie_releases.set_items(release_vec()); + movie_details_modal.movie_releases.set_items(release_vec()); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); + app.push_navigation_stack(ActiveRadarrBlock::ManualSearchSortPrompt.into()); let mut expected_vec = release_vec(); expected_vec.reverse(); @@ -406,7 +832,17 @@ mod tests { app.get_current_route(), &ActiveRadarrBlock::ManualSearch.into() ); - assert_eq!(app.data.radarr_data.movie_releases.items, expected_vec); + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .items, + expected_vec + ); } } @@ -473,6 +909,7 @@ mod tests { use strum::IntoEnumIterator; use crate::models::radarr_models::{MinimumAvailability, Movie}; + use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::radarr_test_utils::utils::create_test_radarr_data; use crate::models::servarr_data::radarr::radarr_data::{ RadarrData, EDIT_MOVIE_SELECTION_BLOCKS, @@ -513,6 +950,7 @@ mod tests { #[test] fn test_sort_key() { let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); MovieDetailsHandler::with( &DEFAULT_KEYBINDINGS.sort.key, @@ -526,9 +964,33 @@ mod tests { app.get_current_route(), &ActiveRadarrBlock::ManualSearchSortPrompt.into() ); - assert!(!app.data.radarr_data.movie_releases_sort.items.is_empty()); - assert!(app.data.radarr_data.sort_ascending.is_some()); - assert_eq!(app.data.radarr_data.sort_ascending, Some(false)); + assert!(!app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases_sort + .items + .is_empty()); + assert!(app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .sort_ascending + .is_some()); + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .sort_ascending, + Some(false) + ); } #[rstest] 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 6216f99..f23809a 100644 --- a/src/handlers/radarr_handlers/system/system_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/system/system_details_handler_tests.rs @@ -17,7 +17,7 @@ mod tests { use rstest::rstest; use crate::models::{HorizontallyScrollableText, ScrollableText}; - use crate::{simple_stateful_iterable_vec, test_iterable_scroll, test_scrollable_text_scroll}; + use crate::{simple_stateful_iterable_vec, test_iterable_scroll}; use super::*; @@ -51,19 +51,36 @@ mod tests { name ); - test_scrollable_text_scroll!( - test_system_updates_scroll, - SystemDetailsHandler, - updates, - ActiveRadarrBlock::SystemUpdates - ); + #[test] + fn test_system_updates_scroll() { + let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.up.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 0); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.down.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 1); + } } mod test_handle_home_end { use crate::models::{HorizontallyScrollableText, ScrollableText}; - use crate::{ - extended_stateful_iterable_vec, test_iterable_home_and_end, test_scrollable_text_home_and_end, - }; + use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end}; use super::*; @@ -97,12 +114,31 @@ mod tests { name ); - test_scrollable_text_home_and_end!( - test_system_updates_home_end, - SystemDetailsHandler, - updates, - ActiveRadarrBlock::SystemUpdates - ); + #[test] + fn test_system_updates_home_end() { + let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 1); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 0); + } } mod test_handle_left_right_action { diff --git a/src/models/servarr_data/radarr/modals.rs b/src/models/servarr_data/radarr/modals.rs index 358ee09..4936cb2 100644 --- a/src/models/servarr_data/radarr/modals.rs +++ b/src/models/servarr_data/radarr/modals.rs @@ -1,12 +1,29 @@ -use crate::models::radarr_models::{Collection, MinimumAvailability, Monitor, Movie, RootFolder}; +use crate::models::radarr_models::{ + Collection, Credit, MinimumAvailability, Monitor, Movie, MovieHistoryItem, Release, ReleaseField, + RootFolder, +}; use crate::models::servarr_data::radarr::radarr_data::RadarrData; -use crate::models::{HorizontallyScrollableText, StatefulList}; +use crate::models::{HorizontallyScrollableText, ScrollableText, StatefulList, StatefulTable}; use strum::IntoEnumIterator; #[cfg(test)] #[path = "modals_tests.rs"] mod modals_tests; +#[derive(Default)] +pub struct MovieDetailsModal { + pub movie_details: ScrollableText, + pub file_details: String, + pub audio_details: String, + pub video_details: String, + pub movie_history: StatefulTable, + pub movie_cast: StatefulTable, + pub movie_crew: StatefulTable, + pub movie_releases: StatefulTable, + pub movie_releases_sort: StatefulList, + pub sort_ascending: Option, +} + #[derive(Default)] pub struct EditMovieModal { pub minimum_availability_list: StatefulList, diff --git a/src/models/servarr_data/radarr/radarr_data.rs b/src/models/servarr_data/radarr/radarr_data.rs index 0b944a5..382e143 100644 --- a/src/models/servarr_data/radarr/radarr_data.rs +++ b/src/models/servarr_data/radarr/radarr_data.rs @@ -6,11 +6,11 @@ use crate::app::radarr::radarr_context_clues::{ SYSTEM_CONTEXT_CLUES, }; use crate::models::radarr_models::{ - AddMovieSearchResult, Collection, CollectionMovie, Credit, DiskSpace, DownloadRecord, Indexer, - IndexerSettings, Movie, MovieHistoryItem, QueueEvent, Release, ReleaseField, RootFolder, Task, + AddMovieSearchResult, Collection, CollectionMovie, DiskSpace, DownloadRecord, Indexer, + IndexerSettings, Movie, QueueEvent, RootFolder, Task, }; use crate::models::servarr_data::radarr::modals::{ - AddMovieModal, EditCollectionModal, EditMovieModal, + AddMovieModal, EditCollectionModal, EditMovieModal, MovieDetailsModal, }; use crate::models::{ BlockSelectionState, HorizontallyScrollableText, Route, ScrollableText, StatefulList, @@ -43,15 +43,6 @@ pub struct RadarrData<'a> { pub indexer_settings: Option, pub quality_profile_map: BiMap, pub tags_map: BiMap, - pub movie_details: ScrollableText, - pub file_details: String, - pub audio_details: String, - pub video_details: String, - pub movie_history: StatefulTable, - pub movie_cast: StatefulTable, - pub movie_crew: StatefulTable, - pub movie_releases: StatefulTable, - pub movie_releases_sort: StatefulList, pub collections: StatefulTable, pub filtered_collections: StatefulTable, pub collection_movies: StatefulTable, @@ -69,7 +60,7 @@ pub struct RadarrData<'a> { pub edit_movie_modal: Option, pub edit_collection_modal: Option, pub edit_root_folder: Option, - pub sort_ascending: Option, + pub movie_details_modal: Option, pub prompt_confirm: bool, pub delete_movie_files: bool, pub add_list_exclusion: bool, @@ -100,16 +91,7 @@ impl<'a> RadarrData<'a> { } pub fn reset_movie_info_tabs(&mut self) { - self.file_details = String::default(); - self.audio_details = String::default(); - self.video_details = String::default(); - self.movie_details = ScrollableText::default(); - self.movie_history = StatefulTable::default(); - self.movie_cast = StatefulTable::default(); - self.movie_crew = StatefulTable::default(); - self.movie_releases = StatefulTable::default(); - self.movie_releases_sort = StatefulList::default(); - self.sort_ascending = None; + self.movie_details_modal = None; self.movie_info_tabs.index = 0; } } @@ -130,15 +112,6 @@ impl<'a> Default for RadarrData<'a> { indexer_settings: None, quality_profile_map: BiMap::default(), tags_map: BiMap::default(), - file_details: String::default(), - audio_details: String::default(), - video_details: String::default(), - movie_details: ScrollableText::default(), - movie_history: StatefulTable::default(), - movie_cast: StatefulTable::default(), - movie_crew: StatefulTable::default(), - movie_releases: StatefulTable::default(), - movie_releases_sort: StatefulList::default(), collections: StatefulTable::default(), filtered_collections: StatefulTable::default(), collection_movies: StatefulTable::default(), @@ -154,7 +127,7 @@ impl<'a> Default for RadarrData<'a> { edit_movie_modal: None, edit_collection_modal: None, edit_root_folder: None, - sort_ascending: None, + movie_details_modal: None, is_searching: false, is_filtering: false, prompt_confirm: false, diff --git a/src/models/servarr_data/radarr/radarr_data_tests.rs b/src/models/servarr_data/radarr/radarr_data_tests.rs index c08d12a..5bf3f39 100644 --- a/src/models/servarr_data/radarr/radarr_data_tests.rs +++ b/src/models/servarr_data/radarr/radarr_data_tests.rs @@ -87,15 +87,6 @@ mod tests { assert!(radarr_data.indexer_settings.is_none()); assert!(radarr_data.quality_profile_map.is_empty()); assert!(radarr_data.tags_map.is_empty()); - assert!(radarr_data.file_details.is_empty()); - assert!(radarr_data.audio_details.is_empty()); - assert!(radarr_data.video_details.is_empty()); - assert!(radarr_data.movie_details.get_text().is_empty()); - assert!(radarr_data.movie_history.items.is_empty()); - assert!(radarr_data.movie_cast.items.is_empty()); - assert!(radarr_data.movie_crew.items.is_empty()); - assert!(radarr_data.movie_releases.items.is_empty()); - assert!(radarr_data.movie_releases_sort.items.is_empty()); assert!(radarr_data.collections.items.is_empty()); assert!(radarr_data.filtered_collections.items.is_empty()); assert!(radarr_data.collection_movies.items.is_empty()); @@ -111,7 +102,7 @@ mod tests { assert!(radarr_data.edit_movie_modal.is_none()); assert!(radarr_data.edit_collection_modal.is_none()); assert!(radarr_data.edit_root_folder.is_none()); - assert!(radarr_data.sort_ascending.is_none()); + assert!(radarr_data.movie_details_modal.is_none()); assert!(!radarr_data.is_searching); assert!(!radarr_data.is_filtering); assert!(!radarr_data.prompt_confirm); diff --git a/src/models/servarr_data/radarr/radarr_test_utils.rs b/src/models/servarr_data/radarr/radarr_test_utils.rs index c6d3b4e..b0b9f96 100644 --- a/src/models/servarr_data/radarr/radarr_test_utils.rs +++ b/src/models/servarr_data/radarr/radarr_test_utils.rs @@ -4,10 +4,32 @@ pub mod utils { AddMovieSearchResult, Collection, CollectionMovie, Credit, Movie, MovieHistoryItem, Release, ReleaseField, }; + use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::RadarrData; use crate::models::{HorizontallyScrollableText, ScrollableText}; pub fn create_test_radarr_data<'a>() -> RadarrData<'a> { + let mut movie_details_modal = MovieDetailsModal { + movie_details: ScrollableText::with_string("test movie details".to_owned()), + ..MovieDetailsModal::default() + }; + movie_details_modal + .movie_history + .set_items(vec![MovieHistoryItem::default()]); + movie_details_modal + .movie_cast + .set_items(vec![Credit::default()]); + movie_details_modal + .movie_crew + .set_items(vec![Credit::default()]); + movie_details_modal + .movie_releases + .set_items(vec![Release::default()]); + movie_details_modal + .movie_releases_sort + .set_items(vec![ReleaseField::default()]); + movie_details_modal.sort_ascending = Some(true); + let mut radarr_data = RadarrData { is_searching: true, is_filtering: true, @@ -16,25 +38,10 @@ pub mod utils { search: Some("test search".into()), filter: Some("test filter".into()), edit_root_folder: Some("test path".into()), - file_details: "test file details".to_owned(), - audio_details: "test audio details".to_owned(), - video_details: "test video details".to_owned(), - movie_details: ScrollableText::with_string("test movie details".to_owned()), + movie_details_modal: Some(movie_details_modal), ..RadarrData::default() }; - radarr_data - .movie_history - .set_items(vec![MovieHistoryItem::default()]); - radarr_data.movie_cast.set_items(vec![Credit::default()]); - radarr_data.movie_crew.set_items(vec![Credit::default()]); - radarr_data - .movie_releases - .set_items(vec![Release::default()]); radarr_data.movie_info_tabs.index = 1; - radarr_data - .movie_releases_sort - .set_items(vec![ReleaseField::default()]); - radarr_data.sort_ascending = Some(true); radarr_data .filtered_movies .set_items(vec![Movie::default()]); @@ -79,16 +86,7 @@ pub mod utils { #[macro_export] macro_rules! assert_movie_info_tabs_reset { ($radarr_data:expr) => { - assert!($radarr_data.file_details.is_empty()); - assert!($radarr_data.audio_details.is_empty()); - assert!($radarr_data.video_details.is_empty()); - assert!($radarr_data.movie_details.get_text().is_empty()); - assert!($radarr_data.movie_history.items.is_empty()); - assert!($radarr_data.movie_cast.items.is_empty()); - assert!($radarr_data.movie_crew.items.is_empty()); - assert!($radarr_data.movie_releases.items.is_empty()); - assert!($radarr_data.movie_releases_sort.items.is_empty()); - assert!($radarr_data.sort_ascending.is_none()); + assert!($radarr_data.movie_details_modal.is_none()); assert_eq!($radarr_data.movie_info_tabs.index, 0); }; } diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index bc7d3ac..7a75511 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -15,7 +15,7 @@ use crate::models::radarr_models::{ QueueEvent, Release, ReleaseDownloadBody, RootFolder, SystemStatus, Tag, Task, Update, }; use crate::models::servarr_data::radarr::modals::{ - AddMovieModal, EditCollectionModal, EditMovieModal, + AddMovieModal, EditCollectionModal, EditMovieModal, MovieDetailsModal, }; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText}; @@ -451,7 +451,14 @@ impl<'a, 'b> Network<'a, 'b> { title, indexer_id, .. - } = app.data.radarr_data.movie_releases.current_selection(); + } = app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection(); (guid.clone(), title.clone(), indexer_id.as_u64().unwrap()) }; @@ -693,8 +700,10 @@ impl<'a, 'b> Network<'a, 'b> { .filter(|credit| credit.credit_type == CreditType::Crew) .collect(); - app.data.radarr_data.movie_cast.set_items(cast_vec); - app.data.radarr_data.movie_crew.set_items(crew_vec); + debug!("Assuming the movie_details_modal is already a Some and was created by the get_movie_details request"); + + app.data.radarr_data.movie_details_modal.as_mut().unwrap().movie_cast.set_items(cast_vec); + app.data.radarr_data.movie_details_modal.as_mut().unwrap().movie_crew.set_items(crew_vec); }) .await; } @@ -913,42 +922,45 @@ impl<'a, 'b> Network<'a, 'b> { let status = get_movie_status(has_file, &app.data.radarr_data.downloads.items, id); let collection = collection.unwrap_or_default(); - app.data.radarr_data.movie_details = ScrollableText::with_string(formatdoc!( - "Title: {} - Year: {} - Runtime: {}h {}m - Rating: {} - Collection: {} - Status: {} - Description: {} - TMDB: {} - IMDB: {} - Rotten Tomatoes: {} - Quality Profile: {} - Size: {:.2} GB - Path: {} - Studio: {} - Genres: {}", - title, - year, - hours, - minutes, - certification.unwrap_or_default(), - collection.title, - status, - overview, - tmdb_rating, - imdb_rating, - rotten_tomatoes_rating, - quality_profile, - size, - path, - studio, - genres.join(", ") - )); + let mut movie_details_modal = MovieDetailsModal { + movie_details: ScrollableText::with_string(formatdoc!( + "Title: {} + Year: {} + Runtime: {}h {}m + Rating: {} + Collection: {} + Status: {} + Description: {} + TMDB: {} + IMDB: {} + Rotten Tomatoes: {} + Quality Profile: {} + Size: {:.2} GB + Path: {} + Studio: {} + Genres: {}", + title, + year, + hours, + minutes, + certification.unwrap_or_default(), + collection.title, + status, + overview, + tmdb_rating, + imdb_rating, + rotten_tomatoes_rating, + quality_profile, + size, + path, + studio, + genres.join(", ") + )), + ..MovieDetailsModal::default() + }; if let Some(file) = movie_file { - app.data.radarr_data.file_details = formatdoc!( + movie_details_modal.file_details = formatdoc!( "Relative Path: {} Absolute Path: {} Size: {:.2} GB @@ -960,7 +972,7 @@ impl<'a, 'b> Network<'a, 'b> { ); if let Some(media_info) = file.media_info { - app.data.radarr_data.audio_details = formatdoc!( + movie_details_modal.audio_details = formatdoc!( "Bitrate: {} Channels: {:.1} Codec: {} @@ -973,7 +985,7 @@ impl<'a, 'b> Network<'a, 'b> { media_info.audio_stream_count.as_u64().unwrap() ); - app.data.radarr_data.video_details = formatdoc!( + movie_details_modal.video_details = formatdoc!( "Bit Depth: {} Bitrate: {} Codec: {} @@ -991,6 +1003,8 @@ impl<'a, 'b> Network<'a, 'b> { ); } } + + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); }) .await; } @@ -1007,11 +1021,15 @@ impl<'a, 'b> Network<'a, 'b> { self .handle_request::<(), Vec>(request_props, |movie_history_vec, mut app| { + debug!("Assuming the movie_details_modal is already a Some and was created by the get_movie_details request"); let mut reversed_movie_history_vec = movie_history_vec.to_vec(); reversed_movie_history_vec.reverse(); app .data .radarr_data + .movie_details_modal + .as_mut() + .unwrap() .movie_history .set_items(reversed_movie_history_vec) }) @@ -1101,7 +1119,8 @@ impl<'a, 'b> Network<'a, 'b> { self .handle_request::<(), Vec>(request_props, |release_vec, mut app| { - app.data.radarr_data.movie_releases.set_items(release_vec) + debug!("Assuming the movie_details_modal is already a Some and was created by the get_movie_details request"); + app.data.radarr_data.movie_details_modal.as_mut().unwrap().movie_releases.set_items(release_vec); }) .await; } diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index 6255e2a..7c1bed4 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -312,13 +312,23 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); + app_arc.lock().await.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetReleases).await; async_server.assert_async().await; assert_eq!( - app_arc.lock().await.data.radarr_data.movie_releases.items, + app_arc + .lock() + .await + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .items, vec![release()] ); } @@ -666,14 +676,18 @@ mod test { .await; async_server.assert_async().await; + assert!(app_arc + .lock() + .await + .data + .radarr_data + .movie_details_modal + .is_some()); + + let app = app_arc.lock().await; + let movie_details_modal = app.data.radarr_data.movie_details_modal.as_ref().unwrap(); assert_str_eq!( - app_arc - .lock() - .await - .data - .radarr_data - .movie_details - .get_text(), + movie_details_modal.movie_details.get_text(), formatdoc!( "Title: Test Year: 2023 @@ -693,7 +707,7 @@ mod test { ) ); assert_str_eq!( - app_arc.lock().await.data.radarr_data.file_details, + movie_details_modal.file_details, formatdoc!( "Relative Path: Test.mkv Absolute Path: /nfs/movies/Test.mkv @@ -702,7 +716,7 @@ mod test { ) ); assert_str_eq!( - app_arc.lock().await.data.radarr_data.audio_details, + movie_details_modal.audio_details, formatdoc!( "Bitrate: 0 Channels: 7.1 @@ -712,7 +726,7 @@ mod test { ) ); assert_str_eq!( - app_arc.lock().await.data.radarr_data.video_details, + movie_details_modal.video_details, formatdoc!( "Bit Depth: 10 Bitrate: 0 @@ -773,14 +787,18 @@ mod test { .await; async_server.assert_async().await; + assert!(app_arc + .lock() + .await + .data + .radarr_data + .movie_details_modal + .is_some()); + + let app = app_arc.lock().await; + let movie_details_modal = app.data.radarr_data.movie_details_modal.as_ref().unwrap(); assert_str_eq!( - app_arc - .lock() - .await - .data - .radarr_data - .movie_details - .get_text(), + movie_details_modal.movie_details.get_text(), formatdoc!( "Title: Test Year: 2023 @@ -799,27 +817,9 @@ mod test { Genres: cool, family, fun" ) ); - assert!(app_arc - .lock() - .await - .data - .radarr_data - .file_details - .is_empty()); - assert!(app_arc - .lock() - .await - .data - .radarr_data - .audio_details - .is_empty()); - assert!(app_arc - .lock() - .await - .data - .radarr_data - .video_details - .is_empty()); + assert!(movie_details_modal.file_details.is_empty()); + assert!(movie_details_modal.audio_details.is_empty()); + assert!(movie_details_modal.video_details.is_empty()); } #[tokio::test] @@ -846,6 +846,7 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); + app_arc.lock().await.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); let mut network = Network::new(&app_arc, CancellationToken::new()); network @@ -854,7 +855,16 @@ mod test { async_server.assert_async().await; assert_eq!( - app_arc.lock().await.data.radarr_data.movie_history.items, + app_arc + .lock() + .await + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .items, vec![movie_history_item()] ); } @@ -1420,21 +1430,19 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); + app_arc.lock().await.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetMovieCredits) .await; + let app = app_arc.lock().await; + let movie_details_modal = app.data.radarr_data.movie_details_modal.as_ref().unwrap(); + async_server.assert_async().await; - assert_eq!( - app_arc.lock().await.data.radarr_data.movie_cast.items, - vec![cast_credit()] - ); - assert_eq!( - app_arc.lock().await.data.radarr_data.movie_crew.items, - vec![crew_credit()] - ); + assert_eq!(movie_details_modal.movie_cast.items, vec![cast_credit()]); + assert_eq!(movie_details_modal.movie_crew.items, vec![crew_credit()]); } #[tokio::test] @@ -1701,7 +1709,6 @@ mod test { let app = app_arc.lock().await; assert!(app.data.radarr_data.edit_movie_modal.is_none()); - assert!(app.data.radarr_data.movie_details.items.is_empty()); } #[tokio::test] @@ -1796,7 +1803,6 @@ mod test { let app = app_arc.lock().await; assert!(app.data.radarr_data.edit_collection_modal.is_none()); - assert!(app.data.radarr_data.movie_details.items.is_empty()); } #[tokio::test] @@ -1812,13 +1818,11 @@ mod test { RadarrEvent::DownloadRelease.resource(), ) .await; - app_arc - .lock() - .await - .data - .radarr_data + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal .movie_releases .set_items(vec![release()]); + app_arc.lock().await.data.radarr_data.movie_details_modal = Some(movie_details_modal); app_arc .lock() .await diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index 72d3aaf..ddc10f6 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -14,8 +14,8 @@ use crate::models::Route; use crate::ui::radarr_ui::library::draw_library; use crate::ui::utils::{ borderless_block, get_width_from_percentage, layout_block_bottom_border, layout_block_top_border, - line_info_default, style_awaiting_import, style_bold, style_default, style_failure, - style_primary, style_success, style_warning, vertical_chunks, + line_info_default, style_awaiting_import, style_bold, style_failure, style_primary, + style_success, style_warning, vertical_chunks, }; use crate::ui::{ draw_drop_down_popup, draw_large_popup_over, draw_prompt_box, draw_prompt_box_with_content, @@ -73,7 +73,13 @@ impl DrawUi for MovieDetailsUi { draw_selectable_list( f, content_area, - &mut app.data.radarr_data.movie_releases_sort, + &mut app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases_sort, |sort_option| ListItem::new(sort_option.to_string()), ) }, @@ -147,130 +153,123 @@ fn draw_update_and_scan_prompt( } fn draw_file_info(f: &mut Frame<'_, B>, app: &App<'_>, content_area: Rect) { - let file_info = app.data.radarr_data.file_details.to_owned(); + match app.data.radarr_data.movie_details_modal.as_ref() { + Some(movie_details_modal) + if !movie_details_modal.file_details.is_empty() && !app.is_loading => + { + let file_info = movie_details_modal.file_details.to_owned(); + let audio_details = movie_details_modal.audio_details.to_owned(); + let video_details = movie_details_modal.video_details.to_owned(); + let chunks = vertical_chunks( + vec![ + Constraint::Length(2), + Constraint::Length(5), + Constraint::Length(1), + Constraint::Length(6), + Constraint::Length(1), + Constraint::Length(7), + ], + content_area, + ); + let mut file_details_title = Text::from("File Details"); + let mut audio_details_title = Text::from("Audio Details"); + let mut video_details_title = Text::from("Video Details"); + file_details_title.patch_style(style_bold()); + audio_details_title.patch_style(style_bold()); + video_details_title.patch_style(style_bold()); - if !file_info.is_empty() { - let audio_details = app.data.radarr_data.audio_details.to_owned(); - let video_details = app.data.radarr_data.video_details.to_owned(); - let chunks = vertical_chunks( - vec![ - Constraint::Length(2), - Constraint::Length(5), - Constraint::Length(1), - Constraint::Length(6), - Constraint::Length(1), - Constraint::Length(7), - ], - content_area, - ); - let mut file_details_title = Text::from("File Details"); - let mut audio_details_title = Text::from("Audio Details"); - let mut video_details_title = Text::from("Video Details"); - file_details_title.patch_style(style_bold()); - audio_details_title.patch_style(style_bold()); - video_details_title.patch_style(style_bold()); + let file_details_title_paragraph = + Paragraph::new(file_details_title).block(layout_block_top_border()); + let audio_details_title_paragraph = + Paragraph::new(audio_details_title).block(borderless_block()); + let video_details_title_paragraph = + Paragraph::new(video_details_title).block(borderless_block()); - let file_details_title_paragraph = - Paragraph::new(file_details_title).block(layout_block_top_border()); - let audio_details_title_paragraph = - Paragraph::new(audio_details_title).block(borderless_block()); - let video_details_title_paragraph = - Paragraph::new(video_details_title).block(borderless_block()); + let file_details = Text::from(file_info); + let audio_details = Text::from(audio_details); + let video_details = Text::from(video_details); - let file_details = Text::from(file_info); - let audio_details = Text::from(audio_details); - let video_details = Text::from(video_details); + let file_details_paragraph = Paragraph::new(file_details) + .block(layout_block_bottom_border()) + .wrap(Wrap { trim: false }); + let audio_details_paragraph = Paragraph::new(audio_details) + .block(layout_block_bottom_border()) + .wrap(Wrap { trim: false }); + let video_details_paragraph = Paragraph::new(video_details) + .block(borderless_block()) + .wrap(Wrap { trim: false }); - let file_details_paragraph = Paragraph::new(file_details) - .block(layout_block_bottom_border()) - .wrap(Wrap { trim: false }); - let audio_details_paragraph = Paragraph::new(audio_details) - .block(layout_block_bottom_border()) - .wrap(Wrap { trim: false }); - let video_details_paragraph = Paragraph::new(video_details) - .block(borderless_block()) - .wrap(Wrap { trim: false }); - - f.render_widget(file_details_title_paragraph, chunks[0]); - f.render_widget(file_details_paragraph, chunks[1]); - f.render_widget(audio_details_title_paragraph, chunks[2]); - f.render_widget(audio_details_paragraph, chunks[3]); - f.render_widget(video_details_title_paragraph, chunks[4]); - f.render_widget(video_details_paragraph, chunks[5]); - } else { - loading(f, layout_block_top_border(), content_area, app.is_loading); + f.render_widget(file_details_title_paragraph, chunks[0]); + f.render_widget(file_details_paragraph, chunks[1]); + f.render_widget(audio_details_title_paragraph, chunks[2]); + f.render_widget(audio_details_paragraph, chunks[3]); + f.render_widget(video_details_title_paragraph, chunks[4]); + f.render_widget(video_details_paragraph, chunks[5]); + } + _ => loading(f, layout_block_top_border(), content_area, app.is_loading), } } fn draw_movie_details(f: &mut Frame<'_, B>, app: &App<'_>, content_area: Rect) { - let movie_details = app.data.radarr_data.movie_details.get_text(); let block = layout_block_top_border(); - if !movie_details.is_empty() { - let download_status = app - .data - .radarr_data - .movie_details - .items - .iter() - .find(|&line| line.starts_with("Status: ")) - .unwrap() - .split(": ") - .collect::>()[1]; - let mut text = Text::from( - app - .data - .radarr_data - .movie_details + match app.data.radarr_data.movie_details_modal.as_ref() { + Some(movie_details_modal) if !app.is_loading => { + let movie_details = &movie_details_modal.movie_details; + let download_status = movie_details .items .iter() - .map(|line| { - let split = line.split(':').collect::>(); - let title = format!("{}:", split[0]); + .find(|&line| line.starts_with("Status: ")) + .unwrap() + .split(": ") + .collect::>()[1]; + let mut text = Text::from( + movie_details + .items + .iter() + .map(|line| { + let split = line.split(':').collect::>(); + let title = format!("{}:", split[0]); - line_info_default(title, split[1..].join(":")) - }) - .collect::>>(), - ); - text.patch_style(determine_style_from_download_status(download_status)); + line_info_default(title, split[1..].join(":")) + }) + .collect::>>(), + ); + text.patch_style(determine_style_from_download_status(download_status)); - let paragraph = Paragraph::new(text) - .block(block) - .wrap(Wrap { trim: false }) - .scroll((app.data.radarr_data.movie_details.offset, 0)); + let paragraph = Paragraph::new(text) + .block(block) + .wrap(Wrap { trim: false }) + .scroll((movie_details.offset, 0)); - f.render_widget(paragraph, content_area); - } else { - loading(f, block, content_area, app.is_loading); + f.render_widget(paragraph, content_area); + } + _ => loading( + f, + block, + content_area, + app.is_loading || app.data.radarr_data.movie_details_modal.is_none(), + ), } } fn draw_movie_history(f: &mut Frame<'_, B>, app: &mut App<'_>, content_area: Rect) { - let current_selection = if app.data.radarr_data.movie_history.items.is_empty() { - MovieHistoryItem::default() - } else { - app - .data - .radarr_data - .movie_history - .current_selection() - .clone() - }; - let block = layout_block_top_border(); + if let Some(movie_details_modal) = app.data.radarr_data.movie_details_modal.as_mut() { + let current_selection = if movie_details_modal.movie_history.items.is_empty() { + MovieHistoryItem::default() + } else { + movie_details_modal + .movie_history + .current_selection() + .clone() + }; - if app.data.radarr_data.movie_history.items.is_empty() && !app.is_loading { - let no_history_paragraph = Paragraph::new(Text::from("No history")) - .style(style_default()) - .block(block); - - f.render_widget(no_history_paragraph, content_area); - } else { draw_table( f, content_area, - block, + layout_block_top_border(), TableProps { - content: &mut app.data.radarr_data.movie_history, + content: &mut movie_details_modal.movie_history, table_headers: vec!["Source Title", "Event Type", "Languages", "Quality", "Date"], constraints: vec![ Constraint::Percentage(34), @@ -327,7 +326,13 @@ fn draw_movie_cast(f: &mut Frame<'_, B>, app: &mut App<'_>, content_ content_area, layout_block_top_border(), TableProps { - content: &mut app.data.radarr_data.movie_cast, + content: &mut app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_cast, constraints: iter::repeat(Constraint::Ratio(1, 2)).take(2).collect(), table_headers: vec!["Cast Member", "Character"], help: app @@ -360,7 +365,13 @@ fn draw_movie_crew(f: &mut Frame<'_, B>, app: &mut App<'_>, content_ content_area, layout_block_top_border(), TableProps { - content: &mut app.data.radarr_data.movie_crew, + content: &mut app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_crew, constraints: iter::repeat(Constraint::Ratio(1, 3)).take(3).collect(), table_headers: vec!["Crew Member", "Job", "Department"], help: app @@ -390,16 +401,18 @@ fn draw_movie_crew(f: &mut Frame<'_, B>, app: &mut App<'_>, content_ } fn draw_movie_releases(f: &mut Frame<'_, B>, app: &mut App<'_>, content_area: Rect) { - let current_selection = if app.data.radarr_data.movie_releases.items.is_empty() { - Release::default() - } else { - app - .data - .radarr_data - .movie_releases - .current_selection() - .clone() - }; + let (current_selection, is_empty, sort_ascending) = + match app.data.radarr_data.movie_details_modal.as_ref() { + Some(movie_details_modal) if !movie_details_modal.movie_releases.items.is_empty() => ( + movie_details_modal + .movie_releases + .current_selection() + .clone(), + movie_details_modal.movie_releases.items.is_empty(), + movie_details_modal.sort_ascending, + ), + _ => (Release::default(), true, None), + }; let current_route = *app.get_current_route(); let mut table_headers_vec = vec![ "Source".to_owned(), @@ -413,10 +426,18 @@ fn draw_movie_releases(f: &mut Frame<'_, B>, app: &mut App<'_>, cont "Quality".to_owned(), ]; - if let Some(ascending) = app.data.radarr_data.sort_ascending { + if let Some(ascending) = sort_ascending { let direction = if ascending { " ▲" } else { " ▼" }; - match app.data.radarr_data.movie_releases_sort.current_selection() { + match app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases_sort + .current_selection() + { ReleaseField::Source => table_headers_vec[0].push_str(direction), ReleaseField::Age => table_headers_vec[1].push_str(direction), ReleaseField::Rejected => table_headers_vec[2].push_str(direction), @@ -434,7 +455,13 @@ fn draw_movie_releases(f: &mut Frame<'_, B>, app: &mut App<'_>, cont content_area, layout_block_top_border(), TableProps { - content: &mut app.data.radarr_data.movie_releases, + content: &mut app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases, constraints: vec![ Constraint::Length(9), Constraint::Length(10), @@ -507,7 +534,7 @@ fn draw_movie_releases(f: &mut Frame<'_, B>, app: &mut App<'_>, cont ]) .style(style_primary()) }, - app.is_loading, + app.is_loading || is_empty, true, ); } @@ -517,7 +544,14 @@ fn draw_manual_search_confirm_prompt( app: &mut App<'_>, prompt_area: Rect, ) { - let current_selection = app.data.radarr_data.movie_releases.current_selection(); + let current_selection = app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection(); let title = if current_selection.rejected { "Download Rejected Release" } else {