From 907fa9a2ecba6a72450aed7c03df0bd4e2d3e394 Mon Sep 17 00:00:00 2001 From: Dark-Alex-17 Date: Mon, 14 Aug 2023 13:02:50 -0600 Subject: [PATCH] Refactored unnecessary data fields into Options to make the code cleaner, and to reduce the memory usage of the application --- src/app/radarr/mod.rs | 36 ++-- src/app/radarr/radarr_tests.rs | 15 +- .../collection_details_handler_tests.rs | 1 - .../collections/collections_handler_tests.rs | 153 ++++++++++++---- .../radarr_handlers/collections/mod.rs | 70 ++------ .../library/add_movie_handler.rs | 40 +++-- .../library/add_movie_handler_tests.rs | 168 +++++++++++++----- .../library/library_handler_tests.rs | 153 ++++++++++++---- src/handlers/radarr_handlers/library/mod.rs | 27 +-- .../library/movie_details_handler.rs | 2 +- .../library/movie_details_handler_tests.rs | 2 +- src/handlers/radarr_handlers/mod.rs | 105 ++++++----- .../radarr_handler_test_utils.rs | 4 +- .../radarr_handlers/radarr_handler_tests.rs | 25 ++- src/models/servarr_data/radarr/modals.rs | 12 +- .../servarr_data/radarr/modals_tests.rs | 12 +- src/models/servarr_data/radarr/radarr_data.rs | 30 ++-- .../servarr_data/radarr/radarr_data_tests.rs | 10 +- .../servarr_data/radarr/radarr_test_utils.rs | 21 ++- src/network/radarr_network.rs | 101 ++++++----- src/network/radarr_network_tests.rs | 156 ++++++++++++---- src/ui/mod.rs | 76 ++++++-- .../collections/collection_details_ui.rs | 18 +- .../collections/edit_collection_ui.rs | 20 +-- src/ui/radarr_ui/collections/mod.rs | 30 ++-- src/ui/radarr_ui/downloads/mod.rs | 3 +- src/ui/radarr_ui/indexers/mod.rs | 3 +- src/ui/radarr_ui/library/add_movie_ui.rs | 26 +-- src/ui/radarr_ui/library/edit_movie_ui.rs | 62 +++---- src/ui/radarr_ui/library/mod.rs | 30 ++-- src/ui/radarr_ui/library/movie_details_ui.rs | 54 +++--- src/ui/radarr_ui/root_folders/mod.rs | 3 +- src/ui/radarr_ui/system/mod.rs | 6 +- src/ui/radarr_ui/system/system_details_ui.rs | 3 +- 34 files changed, 932 insertions(+), 545 deletions(-) diff --git a/src/app/radarr/mod.rs b/src/app/radarr/mod.rs index 65d8b38..84d6233 100644 --- a/src/app/radarr/mod.rs +++ b/src/app/radarr/mod.rs @@ -177,25 +177,23 @@ impl<'a> App<'a> { } async fn populate_movie_collection_table(&mut self) { - let collection_movies = if !self.data.radarr_data.filtered_collections.items.is_empty() { - self - .data - .radarr_data - .filtered_collections - .current_selection() - .clone() - .movies - .unwrap_or_default() - } else { - self - .data - .radarr_data - .collections - .current_selection() - .clone() - .movies - .unwrap_or_default() - }; + let collection_movies = + if let Some(filtered_collections) = self.data.radarr_data.filtered_collections.as_ref() { + filtered_collections + .current_selection() + .clone() + .movies + .unwrap_or_default() + } else { + self + .data + .radarr_data + .collections + .current_selection() + .clone() + .movies + .unwrap_or_default() + }; self .data .radarr_data diff --git a/src/app/radarr/radarr_tests.rs b/src/app/radarr/radarr_tests.rs index 01854ed..b67f7df 100644 --- a/src/app/radarr/radarr_tests.rs +++ b/src/app/radarr/radarr_tests.rs @@ -8,6 +8,7 @@ mod tests { use crate::app::App; use crate::models::radarr_models::{Collection, CollectionMovie, Credit, Release}; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; + use crate::models::StatefulTable; use crate::network::radarr_network::RadarrEvent; use crate::network::NetworkEvent; @@ -627,14 +628,12 @@ mod tests { #[tokio::test] async fn test_populate_movie_collection_table_filtered() { let mut app = App::default(); - app - .data - .radarr_data - .filtered_collections - .set_items(vec![Collection { - movies: Some(vec![CollectionMovie::default()]), - ..Collection::default() - }]); + let mut filtered_collections = StatefulTable::default(); + filtered_collections.set_items(vec![Collection { + movies: Some(vec![CollectionMovie::default()]), + ..Collection::default() + }]); + app.data.radarr_data.filtered_collections = Some(filtered_collections); app.populate_movie_collection_table().await; diff --git a/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs b/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs index e626456..6b9e647 100644 --- a/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs @@ -234,7 +234,6 @@ mod tests { RadarrData, EDIT_COLLECTION_SELECTION_BLOCKS, }; - use crate::models::StatefulTable; use crate::test_edit_collection_key; use super::*; diff --git a/src/handlers/radarr_handlers/collections/collections_handler_tests.rs b/src/handlers/radarr_handlers/collections/collections_handler_tests.rs index bd4c800..7249278 100644 --- a/src/handlers/radarr_handlers/collections/collections_handler_tests.rs +++ b/src/handlers/radarr_handlers/collections/collections_handler_tests.rs @@ -19,6 +19,7 @@ mod tests { mod test_handle_scroll_up_and_down { use rstest::rstest; + use crate::models::StatefulTable; use crate::{simple_stateful_iterable_vec, test_iterable_scroll}; use super::*; @@ -34,21 +35,54 @@ mod tests { to_string ); - test_iterable_scroll!( - test_filtered_collections_scroll, - CollectionsHandler, - filtered_collections, - simple_stateful_iterable_vec!(Collection, HorizontallyScrollableText), - ActiveRadarrBlock::Collections, - None, - title, - to_string - ); + #[rstest] + fn test_filtered_collections_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + let mut filtered_collections = StatefulTable::default(); + filtered_collections.set_items(simple_stateful_iterable_vec!( + Collection, + HorizontallyScrollableText + )); + app.data.radarr_data.filtered_collections = Some(filtered_collections); + + CollectionsHandler::with(&key, &mut app, &ActiveRadarrBlock::Collections, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_collections + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 2" + ); + + CollectionsHandler::with(&key, &mut app, &ActiveRadarrBlock::Collections, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_collections + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } } mod test_handle_home_end { use pretty_assertions::assert_eq; + use crate::models::StatefulTable; use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end}; use super::*; @@ -64,16 +98,58 @@ mod tests { to_string ); - test_iterable_home_and_end!( - test_filtered_collections_home_end, - CollectionsHandler, - filtered_collections, - extended_stateful_iterable_vec!(Collection, HorizontallyScrollableText), - ActiveRadarrBlock::Collections, - None, - title, - to_string - ); + #[test] + fn test_filtered_collections_home_end() { + let mut app = App::default(); + let mut filtered_collections = StatefulTable::default(); + filtered_collections.set_items(extended_stateful_iterable_vec!( + Collection, + HorizontallyScrollableText + )); + app.data.radarr_data.filtered_collections = Some(filtered_collections); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_collections + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 3" + ); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_collections + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } #[test] fn test_collection_search_box_home_end_keys() { @@ -341,6 +417,7 @@ mod tests { } mod test_handle_submit { + use crate::models::StatefulTable; use pretty_assertions::assert_eq; use crate::network::radarr_network::RadarrEvent; @@ -450,14 +527,12 @@ mod tests { let mut app = App::default(); app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::SearchCollection.into()); - app - .data - .radarr_data - .filtered_collections - .set_items(extended_stateful_iterable_vec!( - Collection, - HorizontallyScrollableText - )); + let mut filtered_collections = StatefulTable::default(); + filtered_collections.set_items(extended_stateful_iterable_vec!( + Collection, + HorizontallyScrollableText + )); + app.data.radarr_data.filtered_collections = Some(filtered_collections); app.data.radarr_data.search = Some("Test 2".into()); CollectionsHandler::with( @@ -473,6 +548,8 @@ mod tests { .data .radarr_data .filtered_collections + .as_ref() + .unwrap() .current_selection() .title .text, @@ -507,12 +584,25 @@ mod tests { ) .handle(); - assert_eq!(app.data.radarr_data.filtered_collections.items.len(), 3); + assert!(app.data.radarr_data.filtered_collections.is_some()); + assert_eq!( + app + .data + .radarr_data + .filtered_collections + .as_ref() + .unwrap() + .items + .len(), + 3 + ); assert_str_eq!( app .data .radarr_data .filtered_collections + .as_ref() + .unwrap() .current_selection() .title .text, @@ -547,7 +637,7 @@ mod tests { ) .handle(); - assert!(app.data.radarr_data.filtered_collections.items.is_empty()); + assert!(app.data.radarr_data.filtered_collections.is_none()); assert_eq!( app.get_current_route(), &ActiveRadarrBlock::FilterCollectionsError.into() @@ -715,7 +805,6 @@ mod tests { RadarrData, EDIT_COLLECTION_SELECTION_BLOCKS, }; - use crate::models::StatefulTable; use crate::{assert_refresh_key, test_edit_collection_key}; use super::*; @@ -784,7 +873,7 @@ mod tests { assert!(app.data.radarr_data.is_filtering); assert!(app.should_ignore_quit_key); assert!(app.data.radarr_data.filter.is_some()); - assert!(app.data.radarr_data.filtered_collections.items.is_empty()); + assert!(app.data.radarr_data.filtered_collections.is_none()); } #[test] diff --git a/src/handlers/radarr_handlers/collections/mod.rs b/src/handlers/radarr_handlers/collections/mod.rs index 92410f1..a9d598a 100644 --- a/src/handlers/radarr_handlers/collections/mod.rs +++ b/src/handlers/radarr_handlers/collections/mod.rs @@ -8,7 +8,7 @@ use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, COLLECTIONS_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS, }; -use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable}; +use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable, StatefulTable}; use crate::network::radarr_network::RadarrEvent; use crate::utils::strip_non_search_characters; use crate::{filter_table, handle_text_box_keys, handle_text_box_left_right_keys, search_table}; @@ -68,15 +68,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<' fn handle_scroll_up(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::Collections { - if !self - .app - .data - .radarr_data - .filtered_collections - .items - .is_empty() - { - self.app.data.radarr_data.filtered_collections.scroll_up(); + if let Some(filtered_collections) = self.app.data.radarr_data.filtered_collections.as_mut() { + filtered_collections.scroll_up(); } else { self.app.data.radarr_data.collections.scroll_up() } @@ -85,15 +78,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<' fn handle_scroll_down(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::Collections { - if !self - .app - .data - .radarr_data - .filtered_collections - .items - .is_empty() - { - self.app.data.radarr_data.filtered_collections.scroll_down(); + if let Some(filtered_collections) = self.app.data.radarr_data.filtered_collections.as_mut() { + filtered_collections.scroll_down(); } else { self.app.data.radarr_data.collections.scroll_down() } @@ -103,20 +89,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<' fn handle_home(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::Collections => { - if !self - .app - .data - .radarr_data - .filtered_collections - .items - .is_empty() + if let Some(filtered_collections) = self.app.data.radarr_data.filtered_collections.as_mut() { - self - .app - .data - .radarr_data - .filtered_collections - .scroll_to_top(); + filtered_collections.scroll_to_top(); } else { self.app.data.radarr_data.collections.scroll_to_top() } @@ -144,20 +119,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<' fn handle_end(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::Collections => { - if !self - .app - .data - .radarr_data - .filtered_collections - .items - .is_empty() + if let Some(filtered_collections) = self.app.data.radarr_data.filtered_collections.as_mut() { - self - .app - .data - .radarr_data - .filtered_collections - .scroll_to_bottom(); + filtered_collections.scroll_to_bottom(); } else { self.app.data.radarr_data.collections.scroll_to_bottom() } @@ -212,23 +176,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<' .app .push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()), ActiveRadarrBlock::SearchCollection => { - if self - .app - .data - .radarr_data - .filtered_collections - .items - .is_empty() - { + if self.app.data.radarr_data.filtered_collections.is_some() { search_table!( self.app, - collections, - ActiveRadarrBlock::SearchCollectionError + filtered_collections, + ActiveRadarrBlock::SearchCollectionError, + true ); } else { search_table!( self.app, - filtered_collections, + collections, ActiveRadarrBlock::SearchCollectionError ); } diff --git a/src/handlers/radarr_handlers/library/add_movie_handler.rs b/src/handlers/radarr_handlers/library/add_movie_handler.rs index 9f00e66..9c40784 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler.rs @@ -3,7 +3,7 @@ use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, }; -use crate::models::{BlockSelectionState, Scrollable, StatefulTable}; +use crate::models::{BlockSelectionState, Scrollable}; use crate::network::radarr_network::RadarrEvent; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, App, Key}; @@ -43,9 +43,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, fn handle_scroll_up(&mut self) { match self.active_radarr_block { - ActiveRadarrBlock::AddMovieSearchResults => { - self.app.data.radarr_data.add_searched_movies.scroll_up() - } + ActiveRadarrBlock::AddMovieSearchResults => self + .app + .data + .radarr_data + .add_searched_movies + .as_mut() + .unwrap() + .scroll_up(), ActiveRadarrBlock::AddMovieSelectMonitor => self .app .data @@ -89,9 +94,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, fn handle_scroll_down(&mut self) { match self.active_radarr_block { - ActiveRadarrBlock::AddMovieSearchResults => { - self.app.data.radarr_data.add_searched_movies.scroll_down() - } + ActiveRadarrBlock::AddMovieSearchResults => self + .app + .data + .radarr_data + .add_searched_movies + .as_mut() + .unwrap() + .scroll_down(), ActiveRadarrBlock::AddMovieSelectMonitor => self .app .data @@ -140,6 +150,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, .data .radarr_data .add_searched_movies + .as_mut() + .unwrap() .scroll_to_top(), ActiveRadarrBlock::AddMovieSelectMonitor => self .app @@ -205,6 +217,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, .data .radarr_data .add_searched_movies + .as_mut() + .unwrap() .scroll_to_bottom(), ActiveRadarrBlock::AddMovieSelectMonitor => self .app @@ -312,19 +326,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, self.app.should_ignore_quit_key = false; } _ if *self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchResults - && !self - .app - .data - .radarr_data - .add_searched_movies - .items - .is_empty() => + && self.app.data.radarr_data.add_searched_movies.is_some() => { let tmdb_id = self .app .data .radarr_data .add_searched_movies + .as_ref() + .unwrap() .current_selection() .tmdb_id .clone(); @@ -403,7 +413,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, } ActiveRadarrBlock::AddMovieSearchResults | ActiveRadarrBlock::AddMovieEmptySearchResults => { self.app.pop_navigation_stack(); - self.app.data.radarr_data.add_searched_movies = StatefulTable::default(); + self.app.data.radarr_data.add_searched_movies = None; self.app.should_ignore_quit_key = true; } ActiveRadarrBlock::AddMoviePrompt => { diff --git a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs index 958204d..9ef7779 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs @@ -21,21 +21,65 @@ mod tests { use crate::models::servarr_data::radarr::modals::AddMovieModal; use crate::models::servarr_data::radarr::radarr_data::ADD_MOVIE_SELECTION_BLOCKS; - use crate::models::BlockSelectionState; - use crate::{simple_stateful_iterable_vec, test_iterable_scroll}; + use crate::models::{BlockSelectionState, StatefulTable}; + use crate::simple_stateful_iterable_vec; use super::*; - test_iterable_scroll!( - test_add_movie_search_results_scroll, - AddMovieHandler, - add_searched_movies, - simple_stateful_iterable_vec!(AddMovieSearchResult, HorizontallyScrollableText), - ActiveRadarrBlock::AddMovieSearchResults, - None, - title, - to_string - ); + #[rstest] + fn test_add_movie_search_results_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(simple_stateful_iterable_vec!( + AddMovieSearchResult, + HorizontallyScrollableText + )); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); + + AddMovieHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 2" + ); + + AddMovieHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } #[rstest] fn test_add_movie_select_monitor_scroll( @@ -302,21 +346,64 @@ mod tests { mod test_handle_home_end { use strum::IntoEnumIterator; + use crate::extended_stateful_iterable_vec; use crate::models::servarr_data::radarr::modals::AddMovieModal; - use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end}; + use crate::models::StatefulTable; use super::*; - test_iterable_home_and_end!( - test_add_movie_search_results_home_end, - AddMovieHandler, - add_searched_movies, - extended_stateful_iterable_vec!(AddMovieSearchResult, HorizontallyScrollableText), - ActiveRadarrBlock::AddMovieSearchResults, - None, - title, - to_string - ); + #[test] + fn test_add_movie_search_results_home_end() { + let mut app = App::default(); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(extended_stateful_iterable_vec!( + AddMovieSearchResult, + HorizontallyScrollableText + )); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); + + AddMovieHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 3" + ); + + AddMovieHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } #[test] fn test_add_movie_select_monitor_home_end() { @@ -765,7 +852,7 @@ mod tests { use crate::models::radarr_models::Movie; use crate::models::servarr_data::radarr::modals::AddMovieModal; use crate::models::servarr_data::radarr::radarr_data::ADD_MOVIE_SELECTION_BLOCKS; - use crate::models::BlockSelectionState; + use crate::models::{BlockSelectionState, StatefulTable}; use crate::network::radarr_network::RadarrEvent; use super::*; @@ -818,11 +905,9 @@ mod tests { #[test] fn test_add_movie_search_results_submit() { let mut app = App::default(); - app - .data - .radarr_data - .add_searched_movies - .set_items(vec![AddMovieSearchResult::default()]); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(vec![AddMovieSearchResult::default()]); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); app.data.radarr_data.quality_profile_map = BiMap::from_iter([(1, "B - Test 2".to_owned()), (0, "A - Test 1".to_owned())]); @@ -904,11 +989,9 @@ mod tests { #[test] fn test_add_movie_search_results_submit_movie_already_in_library() { let mut app = App::default(); - app - .data - .radarr_data - .add_searched_movies - .set_items(vec![AddMovieSearchResult::default()]); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(vec![AddMovieSearchResult::default()]); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); app .data .radarr_data @@ -1063,6 +1146,7 @@ mod tests { use crate::models::servarr_data::radarr::modals::AddMovieModal; use crate::models::servarr_data::radarr::radarr_data::radarr_test_utils::utils::create_test_radarr_data; + use crate::models::StatefulTable; use crate::{assert_search_reset, simple_stateful_iterable_vec}; use super::*; @@ -1123,14 +1207,12 @@ mod tests { let mut app = App::default(); app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into()); app.push_navigation_stack(active_radarr_block.into()); - app - .data - .radarr_data - .add_searched_movies - .set_items(simple_stateful_iterable_vec!( - AddMovieSearchResult, - HorizontallyScrollableText - )); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(simple_stateful_iterable_vec!( + AddMovieSearchResult, + HorizontallyScrollableText + )); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); AddMovieHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle(); @@ -1138,7 +1220,7 @@ mod tests { app.get_current_route(), &ActiveRadarrBlock::AddMovieSearchInput.into() ); - assert!(app.data.radarr_data.add_searched_movies.items.is_empty()); + assert!(app.data.radarr_data.add_searched_movies.is_none()); assert!(app.should_ignore_quit_key); } diff --git a/src/handlers/radarr_handlers/library/library_handler_tests.rs b/src/handlers/radarr_handlers/library/library_handler_tests.rs index 8fda17e..e02cc46 100644 --- a/src/handlers/radarr_handlers/library/library_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/library_handler_tests.rs @@ -18,6 +18,7 @@ mod tests { use crate::test_handler_delegation; mod test_handle_scroll_up_and_down { + use crate::models::StatefulTable; use crate::{simple_stateful_iterable_vec, test_iterable_scroll}; use super::*; @@ -33,21 +34,54 @@ mod tests { to_string ); - test_iterable_scroll!( - test_filtered_movies_scroll, - LibraryHandler, - filtered_movies, - simple_stateful_iterable_vec!(Movie, HorizontallyScrollableText), - ActiveRadarrBlock::Movies, - None, - title, - to_string - ); + #[rstest] + fn test_filtered_movies_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + let mut filtered_movies = StatefulTable::default(); + filtered_movies.set_items(simple_stateful_iterable_vec!( + Movie, + HorizontallyScrollableText + )); + app.data.radarr_data.filtered_movies = Some(filtered_movies); + + LibraryHandler::with(&key, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 2" + ); + + LibraryHandler::with(&key, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } } mod test_handle_home_end { use pretty_assertions::assert_eq; + use crate::models::StatefulTable; use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end}; use super::*; @@ -63,16 +97,58 @@ mod tests { to_string ); - test_iterable_home_and_end!( - test_filtered_movies_home_end, - LibraryHandler, - filtered_movies, - extended_stateful_iterable_vec!(Movie, HorizontallyScrollableText), - ActiveRadarrBlock::Movies, - None, - title, - to_string - ); + #[test] + fn test_filtered_movies_home_end() { + let mut app = App::default(); + let mut filtered_movies = StatefulTable::default(); + filtered_movies.set_items(extended_stateful_iterable_vec!( + Movie, + HorizontallyScrollableText + )); + app.data.radarr_data.filtered_movies = Some(filtered_movies); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 3" + ); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .filtered_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } #[test] fn test_movie_search_box_home_end_keys() { @@ -367,6 +443,7 @@ mod tests { use pretty_assertions::assert_eq; use crate::extended_stateful_iterable_vec; + use crate::models::StatefulTable; use crate::network::radarr_network::RadarrEvent; use super::*; @@ -453,14 +530,12 @@ mod tests { let mut app = App::default(); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::SearchMovie.into()); - app - .data - .radarr_data - .filtered_movies - .set_items(extended_stateful_iterable_vec!( - Movie, - HorizontallyScrollableText - )); + let mut filtered_movies = StatefulTable::default(); + filtered_movies.set_items(extended_stateful_iterable_vec!( + Movie, + HorizontallyScrollableText + )); + app.data.radarr_data.filtered_movies = Some(filtered_movies); app.data.radarr_data.search = Some("Test 2".into()); LibraryHandler::with( @@ -476,6 +551,8 @@ mod tests { .data .radarr_data .filtered_movies + .as_ref() + .unwrap() .current_selection() .title .text, @@ -507,12 +584,25 @@ mod tests { ) .handle(); - assert_eq!(app.data.radarr_data.filtered_movies.items.len(), 3); + assert!(app.data.radarr_data.filtered_movies.is_some()); + assert_eq!( + app + .data + .radarr_data + .filtered_movies + .as_ref() + .unwrap() + .items + .len(), + 3 + ); assert_str_eq!( app .data .radarr_data .filtered_movies + .as_ref() + .unwrap() .current_selection() .title .text, @@ -544,7 +634,7 @@ mod tests { ) .handle(); - assert!(app.data.radarr_data.filtered_movies.items.is_empty()); + assert!(app.data.radarr_data.filtered_movies.is_none()); assert_eq!( app.get_current_route(), &ActiveRadarrBlock::FilterMoviesError.into() @@ -688,7 +778,6 @@ mod tests { RadarrData, EDIT_MOVIE_SELECTION_BLOCKS, }; - use crate::models::StatefulTable; use crate::{assert_refresh_key, test_edit_movie_key}; use super::*; @@ -757,7 +846,7 @@ mod tests { assert!(app.data.radarr_data.is_filtering); assert!(app.should_ignore_quit_key); assert!(app.data.radarr_data.filter.is_some()); - assert!(app.data.radarr_data.filtered_movies.items.is_empty()); + assert!(app.data.radarr_data.filtered_movies.is_none()); } #[test] diff --git a/src/handlers/radarr_handlers/library/mod.rs b/src/handlers/radarr_handlers/library/mod.rs index 5d6c164..b406763 100644 --- a/src/handlers/radarr_handlers/library/mod.rs +++ b/src/handlers/radarr_handlers/library/mod.rs @@ -11,7 +11,7 @@ use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, DELETE_MOVIE_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, LIBRARY_BLOCKS, }; -use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable}; +use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable, StatefulTable}; use crate::network::radarr_network::RadarrEvent; use crate::utils::strip_non_search_characters; use crate::{filter_table, handle_text_box_keys, handle_text_box_left_right_keys, search_table}; @@ -81,8 +81,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, ' fn handle_scroll_up(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::Movies { - if !self.app.data.radarr_data.filtered_movies.items.is_empty() { - self.app.data.radarr_data.filtered_movies.scroll_up(); + if let Some(filtered_movies) = self.app.data.radarr_data.filtered_movies.as_mut() { + filtered_movies.scroll_up(); } else { self.app.data.radarr_data.movies.scroll_up() } @@ -91,8 +91,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, ' fn handle_scroll_down(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::Movies { - if !self.app.data.radarr_data.filtered_movies.items.is_empty() { - self.app.data.radarr_data.filtered_movies.scroll_down(); + if let Some(filtered_movies) = self.app.data.radarr_data.filtered_movies.as_mut() { + filtered_movies.scroll_down(); } else { self.app.data.radarr_data.movies.scroll_down() } @@ -102,8 +102,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, ' fn handle_home(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::Movies => { - if !self.app.data.radarr_data.filtered_movies.items.is_empty() { - self.app.data.radarr_data.filtered_movies.scroll_to_top(); + if let Some(filtered_movies) = self.app.data.radarr_data.filtered_movies.as_mut() { + filtered_movies.scroll_to_top(); } else { self.app.data.radarr_data.movies.scroll_to_top() } @@ -135,8 +135,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, ' fn handle_end(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::Movies => { - if !self.app.data.radarr_data.filtered_movies.items.is_empty() { - self.app.data.radarr_data.filtered_movies.scroll_to_bottom(); + if let Some(filtered_movies) = self.app.data.radarr_data.filtered_movies.as_mut() { + filtered_movies.scroll_to_bottom(); } else { self.app.data.radarr_data.movies.scroll_to_bottom() } @@ -199,14 +199,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, ' .app .push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()), ActiveRadarrBlock::SearchMovie => { - if self.app.data.radarr_data.filtered_movies.items.is_empty() { - search_table!(self.app, movies, ActiveRadarrBlock::SearchMovieError); - } else { + if self.app.data.radarr_data.filtered_movies.is_some() { search_table!( self.app, filtered_movies, - ActiveRadarrBlock::SearchMovieError + ActiveRadarrBlock::SearchMovieError, + true ); + } else { + search_table!(self.app, movies, ActiveRadarrBlock::SearchMovieError); } } ActiveRadarrBlock::FilterMovies => { diff --git a/src/handlers/radarr_handlers/library/movie_details_handler.rs b/src/handlers/radarr_handlers/library/movie_details_handler.rs index a6e9cbe..d15a213 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler.rs @@ -477,7 +477,7 @@ fn sort_releases_by_selected_field( .cmp(release_b.size.as_u64().as_ref().unwrap()) }, ReleaseField::Peers => |release_a, release_b| { - let default_number = Number::from(i64::max_value()); + let default_number = Number::from(i64::MAX); let seeder_a = release_a .seeders .as_ref() 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 7870a63..75f7b0d 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs @@ -914,7 +914,7 @@ mod tests { use crate::models::servarr_data::radarr::radarr_data::{ RadarrData, EDIT_MOVIE_SELECTION_BLOCKS, }; - use crate::models::StatefulTable; + use crate::test_edit_movie_key; use super::*; diff --git a/src/handlers/radarr_handlers/mod.rs b/src/handlers/radarr_handlers/mod.rs index 7e7c2f5..71d7e1d 100644 --- a/src/handlers/radarr_handlers/mod.rs +++ b/src/handlers/radarr_handlers/mod.rs @@ -117,16 +117,8 @@ pub fn handle_change_tab_left_right_keys(app: &mut App<'_>, key: &Key) { #[macro_export] macro_rules! search_table { ($app:expr, $data_ref:ident, $error_block:expr) => { - let search_index = if $app.data.radarr_data.search.is_some() { - let search_string = $app - .data - .radarr_data - .search - .as_ref() - .unwrap() - .text - .clone() - .to_lowercase(); + let search_index = if let Some(search_str) = $app.data.radarr_data.search.as_ref() { + let search_string = search_str.text.clone().to_lowercase(); $app.data.radarr_data.search = None; @@ -151,44 +143,65 @@ macro_rules! search_table { $app.pop_and_push_navigation_stack($error_block.into()); } }; + ($app:expr, $data_ref:ident, $error_block:expr, $option:ident) => { + let search_index = if let Some(search_str) = $app.data.radarr_data.search.as_ref() { + let search_string = search_str.text.clone().to_lowercase(); + + $app.data.radarr_data.search = None; + + $app + .data + .radarr_data + .$data_ref + .as_ref() + .unwrap() + .items + .iter() + .position(|item| strip_non_search_characters(&item.title.text).contains(&search_string)) + } else { + None + }; + + $app.data.radarr_data.is_searching = false; + $app.should_ignore_quit_key = false; + + if search_index.is_some() { + $app.pop_navigation_stack(); + $app + .data + .radarr_data + .$data_ref + .as_mut() + .unwrap() + .select_index(search_index); + } else { + $app.pop_and_push_navigation_stack($error_block.into()); + } + }; } #[macro_export] macro_rules! filter_table { ($app:expr, $source_table_ref:ident, $filter_table_ref:ident, $error_block:expr) => { - let empty_filter = $app.data.radarr_data.filter.is_some() - && $app - .data - .radarr_data - .filter - .as_ref() - .unwrap() - .text - .is_empty(); - let filter_matches = if $app.data.radarr_data.filter.is_some() - && !$app - .data - .radarr_data - .filter - .as_ref() - .unwrap() - .text - .is_empty() - { - let filter = - strip_non_search_characters(&$app.data.radarr_data.filter.as_ref().unwrap().text.clone()); + let empty_filter = match $app.data.radarr_data.filter.as_ref() { + Some(filter) if filter.text.is_empty() => true, + _ => false, + }; + let filter_matches = match $app.data.radarr_data.filter.as_ref() { + Some(filter) if !filter.text.is_empty() => { + let scrubbed_filter = strip_non_search_characters(&filter.text.clone()); - $app - .data - .radarr_data - .$source_table_ref - .items - .iter() - .filter(|item| strip_non_search_characters(&item.title.text).contains(&filter)) - .cloned() - .collect() - } else { - Vec::new() + $app + .data + .radarr_data + .$source_table_ref + .items + .iter() + .filter(|item| strip_non_search_characters(&item.title.text).contains(&scrubbed_filter)) + .cloned() + .collect() + } + _ => Vec::new(), }; $app.data.radarr_data.filter = None; @@ -201,11 +214,9 @@ macro_rules! filter_table { $app.pop_navigation_stack(); } else { $app.pop_navigation_stack(); - $app - .data - .radarr_data - .$filter_table_ref - .set_items(filter_matches); + let mut filter_table = StatefulTable::default(); + filter_table.set_items(filter_matches); + $app.data.radarr_data.$filter_table_ref = Some(filter_table); } }; } diff --git a/src/handlers/radarr_handlers/radarr_handler_test_utils.rs b/src/handlers/radarr_handlers/radarr_handler_test_utils.rs index b0a4b35..7418b9a 100644 --- a/src/handlers/radarr_handlers/radarr_handler_test_utils.rs +++ b/src/handlers/radarr_handlers/radarr_handler_test_utils.rs @@ -11,7 +11,7 @@ mod utils { (1111, "Any".to_owned()), ]), tags_map: BiMap::from_iter([(1, "test".to_owned())]), - filtered_movies: StatefulTable::default(), + filtered_movies: None, ..create_test_radarr_data() }; radarr_data.movies.set_items(vec![Movie { @@ -126,7 +126,7 @@ mod utils { (2222, "HD - 1080p".to_owned()), (1111, "Any".to_owned()), ]), - filtered_collections: StatefulTable::default(), + filtered_collections: None, ..create_test_radarr_data() }; radarr_data.collections.set_items(vec![Collection { diff --git a/src/handlers/radarr_handlers/radarr_handler_tests.rs b/src/handlers/radarr_handlers/radarr_handler_tests.rs index 37c4a17..c500356 100644 --- a/src/handlers/radarr_handlers/radarr_handler_tests.rs +++ b/src/handlers/radarr_handlers/radarr_handler_tests.rs @@ -10,7 +10,7 @@ mod tests { use crate::handlers::KeyEventHandler; use crate::models::radarr_models::Movie; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; - use crate::models::HorizontallyScrollableText; + use crate::models::{HorizontallyScrollableText, StatefulTable}; use crate::utils::strip_non_search_characters; use crate::{ extended_stateful_iterable_vec, filter_table, search_table, test_handler_delegation, @@ -130,9 +130,22 @@ mod tests { ActiveRadarrBlock::FilterMoviesError ); - assert_eq!(app.data.radarr_data.filtered_movies.items.len(), 1); + assert!(app.data.radarr_data.filtered_movies.is_some()); + assert_eq!( + app + .data + .radarr_data + .filtered_movies + .as_ref() + .unwrap() + .items + .len(), + 1 + ); assert_str_eq!( - app.data.radarr_data.filtered_movies.items[0].title.text, + app.data.radarr_data.filtered_movies.as_ref().unwrap().items[0] + .title + .text, "Test 2" ); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); @@ -165,7 +178,7 @@ mod tests { ActiveRadarrBlock::FilterMoviesError ); - assert!(app.data.radarr_data.filtered_movies.items.is_empty()); + assert!(app.data.radarr_data.filtered_movies.is_none()); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert!(!app.data.radarr_data.is_filtering); assert!(!app.should_ignore_quit_key); @@ -196,7 +209,7 @@ mod tests { ActiveRadarrBlock::FilterMoviesError ); - assert!(app.data.radarr_data.filtered_movies.items.is_empty()); + assert!(app.data.radarr_data.filtered_movies.is_none()); assert_eq!( app.get_current_route(), &ActiveRadarrBlock::FilterMoviesError.into() @@ -229,7 +242,7 @@ mod tests { ActiveRadarrBlock::FilterMoviesError ); - assert!(app.data.radarr_data.filtered_movies.items.is_empty()); + assert!(app.data.radarr_data.filtered_movies.is_none()); assert_eq!( app.get_current_route(), &ActiveRadarrBlock::FilterMoviesError.into() diff --git a/src/models/servarr_data/radarr/modals.rs b/src/models/servarr_data/radarr/modals.rs index 4936cb2..fc9fa26 100644 --- a/src/models/servarr_data/radarr/modals.rs +++ b/src/models/servarr_data/radarr/modals.rs @@ -43,10 +43,10 @@ impl From<&RadarrData<'_>> for EditMovieModal { minimum_availability, quality_profile_id, .. - } = if radarr_data.filtered_movies.items.is_empty() { - radarr_data.movies.current_selection() + } = if let Some(filtered_movies) = radarr_data.filtered_movies.as_ref() { + filtered_movies.current_selection() } else { - radarr_data.filtered_movies.current_selection() + radarr_data.movies.current_selection() }; edit_movie_modal @@ -159,10 +159,10 @@ impl From<&RadarrData<'_>> for EditCollectionModal { minimum_availability, quality_profile_id, .. - } = if radarr_data.filtered_collections.items.is_empty() { - radarr_data.collections.current_selection() + } = if let Some(filtered_collections) = radarr_data.filtered_collections.as_ref() { + filtered_collections.current_selection() } else { - radarr_data.filtered_collections.current_selection() + radarr_data.collections.current_selection() }; edit_collection_modal.path = root_folder_path.clone().unwrap_or_default().into(); diff --git a/src/models/servarr_data/radarr/modals_tests.rs b/src/models/servarr_data/radarr/modals_tests.rs index 4369d87..60f6c05 100644 --- a/src/models/servarr_data/radarr/modals_tests.rs +++ b/src/models/servarr_data/radarr/modals_tests.rs @@ -21,7 +21,7 @@ mod test { (1111, "Any".to_owned()), ]), tags_map: BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]), - filtered_movies: StatefulTable::default(), + filtered_movies: None, ..create_test_radarr_data() }; let movie = Movie { @@ -34,7 +34,9 @@ mod test { }; if test_filtered_movies { - radarr_data.filtered_movies.set_items(vec![movie]); + let mut filtered_movies = StatefulTable::default(); + filtered_movies.set_items(vec![movie]); + radarr_data.filtered_movies = Some(filtered_movies); } else { radarr_data.movies.set_items(vec![movie]); } @@ -111,7 +113,7 @@ mod test { (2222, "HD - 1080p".to_owned()), (1111, "Any".to_owned()), ]), - filtered_collections: StatefulTable::default(), + filtered_collections: None, ..create_test_radarr_data() }; let collection = Collection { @@ -124,7 +126,9 @@ mod test { }; if test_filtered_collections { - radarr_data.filtered_collections.set_items(vec![collection]); + let mut filtered_collections = StatefulTable::default(); + filtered_collections.set_items(vec![collection]); + radarr_data.filtered_collections = Some(filtered_collections); } else { radarr_data.collections.set_items(vec![collection]); } diff --git a/src/models/servarr_data/radarr/radarr_data.rs b/src/models/servarr_data/radarr/radarr_data.rs index 382e143..5bc73c1 100644 --- a/src/models/servarr_data/radarr/radarr_data.rs +++ b/src/models/servarr_data/radarr/radarr_data.rs @@ -35,33 +35,33 @@ pub struct RadarrData<'a> { pub version: String, pub start_time: DateTime, pub movies: StatefulTable, - pub filtered_movies: StatefulTable, - pub add_searched_movies: StatefulTable, pub selected_block: BlockSelectionState<'a, ActiveRadarrBlock>, pub downloads: StatefulTable, pub indexers: StatefulTable, - pub indexer_settings: Option, pub quality_profile_map: BiMap, pub tags_map: BiMap, pub collections: StatefulTable, - pub filtered_collections: StatefulTable, pub collection_movies: StatefulTable, pub logs: StatefulList, pub log_details: StatefulList, pub tasks: StatefulTable, pub queued_events: StatefulTable, pub updates: ScrollableText, - pub prompt_confirm_action: Option, pub main_tabs: TabState, pub movie_info_tabs: TabState, pub search: Option, pub filter: Option, pub add_movie_modal: Option, + pub add_searched_movies: Option>, pub edit_movie_modal: Option, pub edit_collection_modal: Option, pub edit_root_folder: Option, + pub filtered_collections: Option>, + pub filtered_movies: Option>, + pub indexer_settings: Option, pub movie_details_modal: Option, pub prompt_confirm: bool, + pub prompt_confirm_action: Option, pub delete_movie_files: bool, pub add_list_exclusion: bool, pub is_searching: bool, @@ -78,16 +78,16 @@ impl<'a> RadarrData<'a> { self.is_searching = false; self.search = None; self.filter = None; - self.filtered_movies = StatefulTable::default(); - self.filtered_collections = StatefulTable::default(); - self.add_searched_movies = StatefulTable::default(); + self.filtered_movies = None; + self.filtered_collections = None; + self.add_searched_movies = None; } pub fn reset_filter(&mut self) { self.is_filtering = false; self.filter = None; - self.filtered_movies = StatefulTable::default(); - self.filtered_collections = StatefulTable::default(); + self.filtered_movies = None; + self.filtered_collections = None; } pub fn reset_movie_info_tabs(&mut self) { @@ -104,33 +104,33 @@ impl<'a> Default for RadarrData<'a> { version: String::default(), start_time: DateTime::default(), movies: StatefulTable::default(), - add_searched_movies: StatefulTable::default(), selected_block: BlockSelectionState::default(), - filtered_movies: StatefulTable::default(), downloads: StatefulTable::default(), indexers: StatefulTable::default(), - indexer_settings: None, quality_profile_map: BiMap::default(), tags_map: BiMap::default(), collections: StatefulTable::default(), - filtered_collections: StatefulTable::default(), collection_movies: StatefulTable::default(), logs: StatefulList::default(), log_details: StatefulList::default(), tasks: StatefulTable::default(), queued_events: StatefulTable::default(), updates: ScrollableText::default(), - prompt_confirm_action: None, search: None, filter: None, add_movie_modal: None, + add_searched_movies: None, edit_movie_modal: None, edit_collection_modal: None, edit_root_folder: None, + filtered_collections: None, + filtered_movies: None, + indexer_settings: None, movie_details_modal: None, is_searching: false, is_filtering: false, prompt_confirm: false, + prompt_confirm_action: None, delete_movie_files: false, add_list_exclusion: false, main_tabs: TabState::new(vec![ diff --git a/src/models/servarr_data/radarr/radarr_data_tests.rs b/src/models/servarr_data/radarr/radarr_data_tests.rs index 5bf3f39..0b9ac22 100644 --- a/src/models/servarr_data/radarr/radarr_data_tests.rs +++ b/src/models/servarr_data/radarr/radarr_data_tests.rs @@ -79,32 +79,32 @@ mod tests { assert!(radarr_data.version.is_empty()); assert_eq!(radarr_data.start_time, >::default()); assert!(radarr_data.movies.items.is_empty()); - assert!(radarr_data.add_searched_movies.items.is_empty()); assert_eq!(radarr_data.selected_block, BlockSelectionState::default()); - assert!(radarr_data.filtered_movies.items.is_empty()); assert!(radarr_data.downloads.items.is_empty()); assert!(radarr_data.indexers.items.is_empty()); - 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.collections.items.is_empty()); - assert!(radarr_data.filtered_collections.items.is_empty()); assert!(radarr_data.collection_movies.items.is_empty()); assert!(radarr_data.logs.items.is_empty()); assert!(radarr_data.log_details.items.is_empty()); assert!(radarr_data.tasks.items.is_empty()); assert!(radarr_data.queued_events.items.is_empty()); assert!(radarr_data.updates.get_text().is_empty()); - assert!(radarr_data.prompt_confirm_action.is_none()); assert!(radarr_data.search.is_none()); assert!(radarr_data.filter.is_none()); assert!(radarr_data.add_movie_modal.is_none()); + assert!(radarr_data.add_searched_movies.is_none()); 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.filtered_collections.is_none()); + assert!(radarr_data.filtered_movies.is_none()); + assert!(radarr_data.indexer_settings.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_action.is_none()); assert!(!radarr_data.prompt_confirm); assert!(!radarr_data.delete_movie_files); assert!(!radarr_data.add_list_exclusion); diff --git a/src/models/servarr_data/radarr/radarr_test_utils.rs b/src/models/servarr_data/radarr/radarr_test_utils.rs index b0b9f96..585bd28 100644 --- a/src/models/servarr_data/radarr/radarr_test_utils.rs +++ b/src/models/servarr_data/radarr/radarr_test_utils.rs @@ -6,7 +6,7 @@ pub mod utils { }; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::RadarrData; - use crate::models::{HorizontallyScrollableText, ScrollableText}; + use crate::models::{HorizontallyScrollableText, ScrollableText, StatefulTable}; pub fn create_test_radarr_data<'a>() -> RadarrData<'a> { let mut movie_details_modal = MovieDetailsModal { @@ -39,17 +39,26 @@ pub mod utils { filter: Some("test filter".into()), edit_root_folder: Some("test path".into()), movie_details_modal: Some(movie_details_modal), + filtered_movies: Some(StatefulTable::default()), + filtered_collections: Some(StatefulTable::default()), + add_searched_movies: Some(StatefulTable::default()), ..RadarrData::default() }; radarr_data.movie_info_tabs.index = 1; radarr_data .filtered_movies + .as_mut() + .unwrap() .set_items(vec![Movie::default()]); radarr_data .filtered_collections + .as_mut() + .unwrap() .set_items(vec![Collection::default()]); radarr_data .add_searched_movies + .as_mut() + .unwrap() .set_items(vec![AddMovieSearchResult::default()]); radarr_data .collection_movies @@ -67,9 +76,9 @@ pub mod utils { assert!(!$radarr_data.is_searching); assert!($radarr_data.search.is_none()); assert!($radarr_data.filter.is_none()); - assert!($radarr_data.filtered_movies.items.is_empty()); - assert!($radarr_data.filtered_collections.items.is_empty()); - assert!($radarr_data.add_searched_movies.items.is_empty()); + assert!($radarr_data.filtered_movies.is_none()); + assert!($radarr_data.filtered_collections.is_none()); + assert!($radarr_data.add_searched_movies.is_none()); }; } @@ -78,8 +87,8 @@ pub mod utils { ($radarr_data:expr) => { assert!(!$radarr_data.is_filtering); assert!($radarr_data.filter.is_none()); - assert!($radarr_data.filtered_movies.items.is_empty()); - assert!($radarr_data.filtered_collections.items.is_empty()); + assert!($radarr_data.filtered_movies.is_none()); + assert!($radarr_data.filtered_collections.is_none()); }; } diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index 62595cc..e9848fd 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -18,7 +18,7 @@ use crate::models::servarr_data::radarr::modals::{ AddMovieModal, EditCollectionModal, EditMovieModal, MovieDetailsModal, }; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; -use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText}; +use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText, StatefulTable}; use crate::network::{Network, NetworkEvent, RequestMethod, RequestProps}; use crate::utils::{convert_runtime, convert_to_gb}; @@ -191,6 +191,8 @@ impl<'a, 'b> Network<'a, 'b> { .data .radarr_data .add_searched_movies + .as_ref() + .unwrap() .current_selection() .clone(); (tmdb_id, title.text) @@ -200,6 +202,8 @@ impl<'a, 'b> Network<'a, 'b> { .data .radarr_data .add_searched_movies + .as_ref() + .unwrap() .current_selection() .clone(); (tmdb_id, title.text) @@ -688,24 +692,24 @@ impl<'a, 'b> Network<'a, 'b> { .await; self - .handle_request::<(), Vec>(request_props, |credit_vec, mut app| { - let cast_vec: Vec = credit_vec - .iter() - .cloned() - .filter(|credit| credit.credit_type == CreditType::Cast) - .collect(); - let crew_vec: Vec = credit_vec - .iter() - .cloned() - .filter(|credit| credit.credit_type == CreditType::Crew) - .collect(); + .handle_request::<(), Vec>(request_props, |credit_vec, mut app| { + let cast_vec: Vec = credit_vec + .iter() + .cloned() + .filter(|credit| credit.credit_type == CreditType::Cast) + .collect(); + let crew_vec: Vec = credit_vec + .iter() + .cloned() + .filter(|credit| credit.credit_type == CreditType::Crew) + .collect(); - debug!("Assuming the movie_details_modal is already a Some and was created by the get_movie_details request"); + 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; + 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; } async fn get_diskspace(&mut self) { @@ -1020,20 +1024,20 @@ impl<'a, 'b> Network<'a, 'b> { .await; 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) - }) - .await; + .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) + }) + .await; } async fn get_movies(&mut self) { @@ -1118,11 +1122,11 @@ impl<'a, 'b> Network<'a, 'b> { .await; self - .handle_request::<(), Vec>(request_props, |release_vec, mut app| { - 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; + .handle_request::<(), Vec>(request_props, |release_vec, mut app| { + 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; } async fn get_root_folders(&mut self) { @@ -1325,12 +1329,14 @@ impl<'a, 'b> Network<'a, 'b> { app.pop_and_push_navigation_stack( ActiveRadarrBlock::AddMovieEmptySearchResults.into(), ); + } else if let Some(add_searched_movies) = + app.data.radarr_data.add_searched_movies.as_mut() + { + add_searched_movies.set_items(movie_vec); } else { - app - .data - .radarr_data - .add_searched_movies - .set_items(movie_vec); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(movie_vec); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); } }) .await; @@ -1568,12 +1574,14 @@ impl<'a, 'b> Network<'a, 'b> { async fn extract_movie_id(&mut self) -> (u64, u64) { let app = self.app.lock().await; - if !app.data.radarr_data.filtered_movies.items.is_empty() { + if app.data.radarr_data.filtered_movies.is_some() { ( app .data .radarr_data .filtered_movies + .as_ref() + .unwrap() .current_selection() .id .as_u64() @@ -1582,6 +1590,8 @@ impl<'a, 'b> Network<'a, 'b> { .data .radarr_data .filtered_movies + .as_ref() + .unwrap() .current_selection() .tmdb_id .as_u64() @@ -1610,15 +1620,14 @@ impl<'a, 'b> Network<'a, 'b> { } async fn extract_collection_id(&mut self) -> u64 { - if !self + if self .app .lock() .await .data .radarr_data .filtered_collections - .items - .is_empty() + .is_some() { self .app @@ -1627,6 +1636,8 @@ impl<'a, 'b> Network<'a, 'b> { .data .radarr_data .filtered_collections + .as_ref() + .unwrap() .current_selection() .id .as_u64() diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index abb32dd..4f44cbd 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -17,7 +17,7 @@ mod test { Monitor, MovieFile, Quality, QualityWrapper, Rating, RatingsList, }; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; - use crate::models::HorizontallyScrollableText; + use crate::models::{HorizontallyScrollableText, StatefulTable}; use crate::App; use super::super::*; @@ -375,6 +375,13 @@ mod test { .await; async_server.assert_async().await; + assert!(app_arc + .lock() + .await + .data + .radarr_data + .add_searched_movies + .is_some()); assert_eq!( app_arc .lock() @@ -382,6 +389,8 @@ mod test { .data .radarr_data .add_searched_movies + .as_ref() + .unwrap() .items, vec![add_movie_search_result()] ); @@ -437,8 +446,7 @@ mod test { .data .radarr_data .add_searched_movies - .items - .is_empty()); + .is_none()); assert_eq!( app_arc.lock().await.get_current_route(), &ActiveRadarrBlock::AddMovieEmptySearchResults.into() @@ -487,8 +495,7 @@ mod test { .data .radarr_data .add_searched_movies - .items - .is_empty()); + .is_none()); assert_eq!( app_arc.lock().await.get_current_route(), &ActiveRadarrBlock::Movies.into() @@ -1599,11 +1606,9 @@ mod test { .set_items(vec![collection_movie()]); app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()); } else { - app - .data - .radarr_data - .add_searched_movies - .set_items(vec![add_movie_search_result()]); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(vec![add_movie_search_result()]); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); } } let mut network = Network::new(&app_arc, CancellationToken::new()); @@ -1620,6 +1625,103 @@ mod test { .is_none()); } + #[tokio::test] + async fn test_handle_add_movie_event_reuse_existing_table_if_search_already_performed() { + let (async_server, app_arc, _server) = mock_radarr_api( + RequestMethod::Post, + Some(json!({ + "tmdbId": 5678, + "title": "Test", + "rootFolderPath": "/nfs2", + "minimumAvailability": "announced", + "monitored": true, + "qualityProfileId": 2222, + "tags": [1, 2], + "addOptions": { + "monitor": "movieOnly", + "searchForMovie": true + } + })), + None, + RadarrEvent::AddMovie.resource(), + ) + .await; + + { + let mut app = app_arc.lock().await; + let mut add_movie_modal = AddMovieModal { + tags: "usenet, testing".into(), + ..AddMovieModal::default() + }; + add_movie_modal.root_folder_list.set_items(vec![ + RootFolder { + id: Number::from(1), + path: "/nfs".to_owned(), + accessible: true, + free_space: Number::from(219902325555200u64), + unmapped_folders: None, + }, + RootFolder { + id: Number::from(2), + path: "/nfs2".to_owned(), + accessible: true, + free_space: Number::from(21990232555520u64), + unmapped_folders: None, + }, + ]); + add_movie_modal.root_folder_list.state.select(Some(1)); + add_movie_modal + .quality_profile_list + .set_items(vec!["HD - 1080p".to_owned()]); + add_movie_modal + .monitor_list + .set_items(Vec::from_iter(Monitor::iter())); + add_movie_modal + .minimum_availability_list + .set_items(Vec::from_iter(MinimumAvailability::iter())); + app.data.radarr_data.add_movie_modal = Some(add_movie_modal); + app.data.radarr_data.quality_profile_map = + BiMap::from_iter([(2222, "HD - 1080p".to_owned())]); + app.data.radarr_data.tags_map = + BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]); + let secondary_search_result = AddMovieSearchResult { + tmdb_id: Number::from(5678), + ..add_movie_search_result() + }; + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(vec![add_movie_search_result(), secondary_search_result]); + add_searched_movies.scroll_to_bottom(); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); + } + let mut network = Network::new(&app_arc, CancellationToken::new()); + + network.handle_radarr_event(RadarrEvent::AddMovie).await; + + async_server.assert_async().await; + assert!(app_arc + .lock() + .await + .data + .radarr_data + .add_movie_modal + .is_none()); + assert_eq!( + app_arc + .lock() + .await + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .tmdb_id + .as_u64() + .unwrap(), + 5678 + ); + } + #[tokio::test] async fn test_handle_add_root_folder_event() { let (async_server, app_arc, _server) = mock_radarr_api( @@ -1916,17 +2018,13 @@ mod test { #[tokio::test] async fn test_extract_movie_id_filtered_movies() { let app_arc = Arc::new(Mutex::new(App::default())); - app_arc - .lock() - .await - .data - .radarr_data - .filtered_movies - .set_items(vec![Movie { - id: Number::from(1), - tmdb_id: Number::from(2), - ..Movie::default() - }]); + let mut filtered_movies = StatefulTable::default(); + filtered_movies.set_items(vec![Movie { + id: Number::from(1), + tmdb_id: Number::from(2), + ..Movie::default() + }]); + app_arc.lock().await.data.radarr_data.filtered_movies = Some(filtered_movies); let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_movie_id().await, (1, 2)); @@ -1953,16 +2051,12 @@ mod test { #[tokio::test] async fn test_extract_collection_id_filtered_collection() { let app_arc = Arc::new(Mutex::new(App::default())); - app_arc - .lock() - .await - .data - .radarr_data - .filtered_collections - .set_items(vec![Collection { - id: Number::from(1), - ..Collection::default() - }]); + let mut filtered_collections = StatefulTable::default(); + filtered_collections.set_items(vec![Collection { + id: Number::from(1), + ..Collection::default() + }]); + app_arc.lock().await.data.radarr_data.filtered_collections = Some(filtered_collections); let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_collection_id().await, 1); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 4a3f41d..2d2c942 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -291,7 +291,8 @@ fn draw_tabs<'a, B: Backend>( } pub struct TableProps<'a, T> { - pub content: &'a mut StatefulTable, + pub content: Option<&'a mut StatefulTable>, + pub wrapped_content: Option>>, pub table_headers: Vec<&'a str>, pub constraints: Vec, pub help: Option, @@ -319,6 +320,7 @@ fn draw_table<'a, B, T, F>( { let TableProps { content, + wrapped_content, table_headers, constraints, help, @@ -326,29 +328,65 @@ fn draw_table<'a, B, T, F>( let content_area = draw_help_and_get_content_rect(f, content_area, help); - if !content.items.is_empty() { - let rows = content.items.iter().map(row_mapper); - - let headers = Row::new(table_headers) - .style(style_default_bold()) - .bottom_margin(0); - - let mut table = Table::new(rows).header(headers).block(block); - - if highlight { - table = table - .highlight_style(style_highlight()) - .highlight_symbol(HIGHLIGHT_SYMBOL); - } - - table = table.widths(&constraints); - - f.render_stateful_widget(table, content_area, &mut content.state); + if wrapped_content.is_some() && wrapped_content.as_ref().unwrap().is_some() { + draw_table_contents( + f, + block, + row_mapper, + highlight, + wrapped_content.unwrap().as_mut().unwrap(), + table_headers, + &constraints, + content_area, + ); + } else if content.is_some() && !content.as_ref().unwrap().items.is_empty() { + draw_table_contents( + f, + block, + row_mapper, + highlight, + content.unwrap(), + table_headers, + &constraints, + content_area, + ); } else { loading(f, block, content_area, is_loading); } } +fn draw_table_contents<'a, B, T, F>( + f: &mut Frame<'_, B>, + block: Block<'_>, + row_mapper: F, + highlight: bool, + content: &mut StatefulTable, + table_headers: Vec<&str>, + constraints: &Vec, + content_area: Rect, +) where + B: Backend, + F: Fn(&T) -> Row<'a>, +{ + let rows = content.items.iter().map(row_mapper); + + let headers = Row::new(table_headers) + .style(style_default_bold()) + .bottom_margin(0); + + let mut table = Table::new(rows).header(headers).block(block); + + if highlight { + table = table + .highlight_style(style_highlight()) + .highlight_symbol(HIGHLIGHT_SYMBOL); + } + + table = table.widths(&constraints); + + f.render_stateful_widget(table, content_area, &mut content.state); +} + pub fn loading(f: &mut Frame<'_, B>, block: Block<'_>, area: Rect, is_loading: bool) { if is_loading { let text = "\n\n Loading ...\n\n".to_owned(); diff --git a/src/ui/radarr_ui/collections/collection_details_ui.rs b/src/ui/radarr_ui/collections/collection_details_ui.rs index e09ca9b..1545d5f 100644 --- a/src/ui/radarr_ui/collections/collection_details_ui.rs +++ b/src/ui/radarr_ui/collections/collection_details_ui.rs @@ -80,15 +80,12 @@ pub fn draw_collection_details( content_area, 1, ); - let collection_selection = if !app.data.radarr_data.filtered_collections.items.is_empty() { - app - .data - .radarr_data - .filtered_collections - .current_selection() - } else { - app.data.radarr_data.collections.current_selection() - }; + let collection_selection = + if let Some(filtered_collections) = app.data.radarr_data.filtered_collections.as_ref() { + filtered_collections.current_selection() + } else { + app.data.radarr_data.collections.current_selection() + }; let quality_profile = app .data .radarr_data @@ -161,7 +158,8 @@ pub fn draw_collection_details( chunks[1], layout_block_top_border_with_title(title_style("Movies")), TableProps { - content: &mut app.data.radarr_data.collection_movies, + content: Some(&mut app.data.radarr_data.collection_movies), + wrapped_content: None, table_headers: vec![ "✔", "Title", diff --git a/src/ui/radarr_ui/collections/edit_collection_ui.rs b/src/ui/radarr_ui/collections/edit_collection_ui.rs index 3d7aca6..ec216be 100644 --- a/src/ui/radarr_ui/collections/edit_collection_ui.rs +++ b/src/ui/radarr_ui/collections/edit_collection_ui.rs @@ -97,20 +97,10 @@ fn draw_edit_collection_confirmation_prompt( prompt_area: Rect, ) { let (collection_title, collection_overview) = - if app.data.radarr_data.filtered_collections.items.is_empty() { + if let Some(filtered_collections) = app.data.radarr_data.filtered_collections.as_ref() { ( - app - .data - .radarr_data - .collections - .current_selection() - .title - .text - .clone(), - app - .data - .radarr_data - .collections + filtered_collections.current_selection().title.text.clone(), + filtered_collections .current_selection() .overview .clone() @@ -121,7 +111,7 @@ fn draw_edit_collection_confirmation_prompt( app .data .radarr_data - .filtered_collections + .collections .current_selection() .title .text @@ -129,7 +119,7 @@ fn draw_edit_collection_confirmation_prompt( app .data .radarr_data - .filtered_collections + .collections .current_selection() .overview .clone() diff --git a/src/ui/radarr_ui/collections/mod.rs b/src/ui/radarr_ui/collections/mod.rs index 58218c1..b0075a0 100644 --- a/src/ui/radarr_ui/collections/mod.rs +++ b/src/ui/radarr_ui/collections/mod.rs @@ -100,25 +100,18 @@ impl DrawUi for CollectionsUi { } pub(super) fn draw_collections(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) { - let current_selection = if !app.data.radarr_data.filtered_collections.items.is_empty() { - app - .data - .radarr_data - .filtered_collections - .current_selection() - .clone() - } else if !app.data.radarr_data.collections.items.is_empty() { - app.data.radarr_data.collections.current_selection().clone() - } else { - Collection::default() - }; + let current_selection = + if let Some(filtered_collections) = app.data.radarr_data.filtered_collections.as_ref() { + filtered_collections.current_selection().clone() + } else if !app.data.radarr_data.collections.items.is_empty() { + app.data.radarr_data.collections.current_selection().clone() + } else { + Collection::default() + }; let quality_profile_map = &app.data.radarr_data.quality_profile_map; - let content = if !app.data.radarr_data.filtered_collections.items.is_empty() - && !app.data.radarr_data.is_filtering - { - &mut app.data.radarr_data.filtered_collections - } else { - &mut app.data.radarr_data.collections + let content = match app.data.radarr_data.filtered_collections.as_mut() { + Some(filtered_collections) if !app.data.radarr_data.is_filtering => Some(filtered_collections), + _ => Some(&mut app.data.radarr_data.collections), }; draw_table( f, @@ -126,6 +119,7 @@ pub(super) fn draw_collections(f: &mut Frame<'_, B>, app: &mut App<' layout_block_top_border(), TableProps { content, + wrapped_content: None, table_headers: vec![ "Collection", "Number of Movies", diff --git a/src/ui/radarr_ui/downloads/mod.rs b/src/ui/radarr_ui/downloads/mod.rs index 51708af..f1c6d6e 100644 --- a/src/ui/radarr_ui/downloads/mod.rs +++ b/src/ui/radarr_ui/downloads/mod.rs @@ -62,7 +62,8 @@ fn draw_downloads(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rec area, layout_block_top_border(), TableProps { - content: &mut app.data.radarr_data.downloads, + content: Some(&mut app.data.radarr_data.downloads), + wrapped_content: None, table_headers: vec![ "Title", "Percent Complete", diff --git a/src/ui/radarr_ui/indexers/mod.rs b/src/ui/radarr_ui/indexers/mod.rs index 447959c..70de7d7 100644 --- a/src/ui/radarr_ui/indexers/mod.rs +++ b/src/ui/radarr_ui/indexers/mod.rs @@ -59,7 +59,8 @@ fn draw_indexers(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect area, layout_block_top_border(), TableProps { - content: &mut app.data.radarr_data.indexers, + content: Some(&mut app.data.radarr_data.indexers), + wrapped_content: None, table_headers: vec![ "Indexer", "RSS", diff --git a/src/ui/radarr_ui/library/add_movie_ui.rs b/src/ui/radarr_ui/library/add_movie_ui.rs index a85c8a6..e7a9748 100644 --- a/src/ui/radarr_ui/library/add_movie_ui.rs +++ b/src/ui/radarr_ui/library/add_movie_ui.rs @@ -104,16 +104,13 @@ impl DrawUi for AddMovieUi { } fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) { - let current_selection = if app.data.radarr_data.add_searched_movies.items.is_empty() { - AddMovieSearchResult::default() - } else { - app - .data - .radarr_data - .add_searched_movies - .current_selection() - .clone() - }; + let is_loading = app.is_loading || app.data.radarr_data.add_searched_movies.is_none(); + let current_selection = + if let Some(add_searched_movies) = app.data.radarr_data.add_searched_movies.as_ref() { + add_searched_movies.current_selection().clone() + } else { + AddMovieSearchResult::default() + }; let chunks = vertical_chunks_with_margin( vec![ @@ -181,7 +178,8 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App<'_>, ar chunks[1], layout_block(), TableProps { - content: &mut app.data.radarr_data.add_searched_movies, + content: None, + wrapped_content: Some(app.data.radarr_data.add_searched_movies.as_mut()), table_headers: vec![ "✔", "Title", @@ -260,7 +258,7 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App<'_>, ar ]) .style(style_primary()) }, - app.is_loading, + is_loading, true, ); } @@ -354,6 +352,8 @@ fn draw_confirmation_prompt( .data .radarr_data .add_searched_movies + .as_ref() + .unwrap() .current_selection() .title .text, @@ -361,6 +361,8 @@ fn draw_confirmation_prompt( .data .radarr_data .add_searched_movies + .as_ref() + .unwrap() .current_selection() .overview .clone(), diff --git a/src/ui/radarr_ui/library/edit_movie_ui.rs b/src/ui/radarr_ui/library/edit_movie_ui.rs index 1d020bd..ca3bb4e 100644 --- a/src/ui/radarr_ui/library/edit_movie_ui.rs +++ b/src/ui/radarr_ui/library/edit_movie_ui.rs @@ -93,43 +93,31 @@ fn draw_edit_movie_confirmation_prompt( app: &mut App<'_>, prompt_area: Rect, ) { - let (movie_title, movie_overview) = if app.data.radarr_data.filtered_movies.items.is_empty() { - ( - app - .data - .radarr_data - .movies - .current_selection() - .title - .text - .clone(), - app - .data - .radarr_data - .movies - .current_selection() - .overview - .clone(), - ) - } else { - ( - app - .data - .radarr_data - .filtered_movies - .current_selection() - .title - .text - .clone(), - app - .data - .radarr_data - .filtered_movies - .current_selection() - .overview - .clone(), - ) - }; + let (movie_title, movie_overview) = + if let Some(filtered_movies) = app.data.radarr_data.filtered_movies.as_ref() { + ( + filtered_movies.current_selection().title.text.clone(), + filtered_movies.current_selection().overview.clone(), + ) + } else { + ( + app + .data + .radarr_data + .movies + .current_selection() + .title + .text + .clone(), + app + .data + .radarr_data + .movies + .current_selection() + .overview + .clone(), + ) + }; let title = format!("Edit - {}", movie_title); let yes_no_value = app.data.radarr_data.prompt_confirm; let selected_block = app.data.radarr_data.selected_block.get_active_block(); diff --git a/src/ui/radarr_ui/library/mod.rs b/src/ui/radarr_ui/library/mod.rs index 054b18e..ce531b7 100644 --- a/src/ui/radarr_ui/library/mod.rs +++ b/src/ui/radarr_ui/library/mod.rs @@ -108,27 +108,20 @@ impl DrawUi for LibraryUi { } pub(super) fn draw_library(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) { - let current_selection = if !app.data.radarr_data.filtered_movies.items.is_empty() { - app - .data - .radarr_data - .filtered_movies - .current_selection() - .clone() - } else if !app.data.radarr_data.movies.items.is_empty() { - app.data.radarr_data.movies.current_selection().clone() - } else { - Movie::default() - }; + let current_selection = + if let Some(filtered_movies) = app.data.radarr_data.filtered_movies.as_ref() { + filtered_movies.current_selection().clone() + } else if !app.data.radarr_data.movies.items.is_empty() { + app.data.radarr_data.movies.current_selection().clone() + } else { + Movie::default() + }; let quality_profile_map = &app.data.radarr_data.quality_profile_map; let tags_map = &app.data.radarr_data.tags_map; let downloads_vec = &app.data.radarr_data.downloads.items; - let content = if !app.data.radarr_data.filtered_movies.items.is_empty() - && !app.data.radarr_data.is_filtering - { - &mut app.data.radarr_data.filtered_movies - } else { - &mut app.data.radarr_data.movies + let content = match app.data.radarr_data.filtered_movies.as_mut() { + Some(filtered_movies) if !app.data.radarr_data.is_filtering => Some(filtered_movies), + _ => Some(&mut app.data.radarr_data.movies), }; draw_table( @@ -137,6 +130,7 @@ pub(super) fn draw_library(f: &mut Frame<'_, B>, app: &mut App<'_>, layout_block_top_border(), TableProps { content, + wrapped_content: None, table_headers: vec![ "Title", "Year", diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index ddc10f6..2035ada 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -269,7 +269,8 @@ fn draw_movie_history(f: &mut Frame<'_, B>, app: &mut App<'_>, conte content_area, layout_block_top_border(), TableProps { - content: &mut movie_details_modal.movie_history, + content: Some(&mut movie_details_modal.movie_history), + wrapped_content: None, table_headers: vec!["Source Title", "Event Type", "Languages", "Quality", "Date"], constraints: vec![ Constraint::Percentage(34), @@ -326,13 +327,16 @@ 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_details_modal - .as_mut() - .unwrap() - .movie_cast, + content: Some( + &mut app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_cast, + ), + wrapped_content: None, constraints: iter::repeat(Constraint::Ratio(1, 2)).take(2).collect(), table_headers: vec!["Cast Member", "Character"], help: app @@ -365,13 +369,16 @@ 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_details_modal - .as_mut() - .unwrap() - .movie_crew, + content: Some( + &mut app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_crew, + ), + wrapped_content: None, constraints: iter::repeat(Constraint::Ratio(1, 3)).take(3).collect(), table_headers: vec!["Crew Member", "Job", "Department"], help: app @@ -455,13 +462,16 @@ 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_details_modal - .as_mut() - .unwrap() - .movie_releases, + content: Some( + &mut app + .data + .radarr_data + .movie_details_modal + .as_mut() + .unwrap() + .movie_releases, + ), + wrapped_content: None, constraints: vec![ Constraint::Length(9), Constraint::Length(10), diff --git a/src/ui/radarr_ui/root_folders/mod.rs b/src/ui/radarr_ui/root_folders/mod.rs index a5dbdc8..765e671 100644 --- a/src/ui/radarr_ui/root_folders/mod.rs +++ b/src/ui/radarr_ui/root_folders/mod.rs @@ -61,7 +61,8 @@ fn draw_root_folders(f: &mut Frame<'_, B>, app: &mut App<'_>, area: area, layout_block_top_border(), TableProps { - content: &mut app.data.radarr_data.root_folders, + content: Some(&mut app.data.radarr_data.root_folders), + wrapped_content: None, table_headers: vec!["Path", "Free Space", "Unmapped Folders"], constraints: vec![ Constraint::Percentage(60), diff --git a/src/ui/radarr_ui/system/mod.rs b/src/ui/radarr_ui/system/mod.rs index 84e4bb9..ca38ce8 100644 --- a/src/ui/radarr_ui/system/mod.rs +++ b/src/ui/radarr_ui/system/mod.rs @@ -106,7 +106,8 @@ fn draw_tasks(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) { area, title_block("Tasks"), TableProps { - content: &mut app.data.radarr_data.tasks, + content: Some(&mut app.data.radarr_data.tasks), + wrapped_content: None, table_headers: TASK_TABLE_HEADERS.to_vec(), constraints: TASK_TABLE_CONSTRAINTS.to_vec(), help: None, @@ -134,7 +135,8 @@ pub(super) fn draw_queued_events(f: &mut Frame<'_, B>, app: &mut App area, title_block("Queued Events"), TableProps { - content: &mut app.data.radarr_data.queued_events, + content: Some(&mut app.data.radarr_data.queued_events), + wrapped_content: None, table_headers: vec!["Trigger", "Status", "Name", "Queued", "Started", "Duration"], constraints: vec![ Constraint::Percentage(13), diff --git a/src/ui/radarr_ui/system/system_details_ui.rs b/src/ui/radarr_ui/system/system_details_ui.rs index a076a01..bff2e31 100644 --- a/src/ui/radarr_ui/system/system_details_ui.rs +++ b/src/ui/radarr_ui/system/system_details_ui.rs @@ -109,7 +109,8 @@ fn draw_tasks_popup(f: &mut Frame<'_, B>, app: &mut App<'_>, area: R context_area, borderless_block(), TableProps { - content: &mut app.data.radarr_data.tasks, + content: Some(&mut app.data.radarr_data.tasks), + wrapped_content: None, table_headers: TASK_TABLE_HEADERS.to_vec(), constraints: TASK_TABLE_CONSTRAINTS.to_vec(), help: None,