From 6866f903296beeb1d1333f3c7e1ad38900a8980a Mon Sep 17 00:00:00 2001 From: Dark-Alex-17 Date: Tue, 8 Aug 2023 10:50:05 -0600 Subject: [PATCH] Added support for adding movies directly from the collection details UI, refactored to support contexts for different routes, and fixed the horizontal scrolling bug with the get_width_with_margin function. --- src/app/mod.rs | 9 +- src/app/radarr.rs | 70 ++++++- src/handlers/mod.rs | 65 ++++--- .../radarr_handlers/add_movie_handler.rs | 179 ++++++++++++------ .../collection_details_handler.rs | 144 ++++++++++++-- src/handlers/radarr_handlers/mod.rs | 107 ++++++++--- .../radarr_handlers/movie_details_handler.rs | 52 +++-- src/models/mod.rs | 2 +- src/models/radarr_models.rs | 4 +- src/network/radarr_network.rs | 5 +- src/ui/mod.rs | 4 +- src/ui/radarr_ui/add_movie_ui.rs | 81 +++++--- src/ui/radarr_ui/collection_details_ui.rs | 49 ++++- src/ui/radarr_ui/mod.rs | 40 ++-- src/ui/radarr_ui/movie_details_ui.rs | 20 +- src/ui/utils.rs | 23 ++- 16 files changed, 646 insertions(+), 208 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 896d851..5fa50ba 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -14,7 +14,7 @@ use crate::network::NetworkEvent; pub(crate) mod key_binding; pub mod radarr; -const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies); +const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies, None); pub struct App { navigation_stack: Vec, @@ -72,7 +72,7 @@ impl App { pub async fn on_tick(&mut self, is_first_render: bool) { if self.tick_count % self.tick_until_poll == 0 || self.is_routing || self.should_refresh { - if let Route::Radarr(active_radarr_block) = self.get_current_route() { + if let Route::Radarr(active_radarr_block, _) = self.get_current_route() { self .radarr_on_tick(*active_radarr_block, is_first_render) .await; @@ -117,14 +117,13 @@ impl Default for App { TabRoute { title: "Radarr".to_owned(), route: ActiveRadarrBlock::Movies.into(), - help: "<↑↓> scroll | ←→ change tab | change servarr | help | quit " - .to_owned(), + help: "<↑↓> scroll | ←→ change tab | change servarr | quit ".to_owned(), contextual_help: None, }, TabRoute { title: "Sonarr".to_owned(), route: Route::Sonarr, - help: " change servarr | help | quit ".to_owned(), + help: " change servarr | quit ".to_owned(), contextual_help: None, }, ]), diff --git a/src/app/radarr.rs b/src/app/radarr.rs index a62b81c..5df0f56 100644 --- a/src/app/radarr.rs +++ b/src/app/radarr.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::time::Duration; use chrono::{DateTime, Utc}; +use strum::IntoEnumIterator; use crate::app::{App, Route}; use crate::models::radarr_models::{ @@ -88,6 +89,21 @@ impl RadarrData { self.add_movie_minimum_availability_list = StatefulList::default(); self.add_movie_quality_profile_list = StatefulList::default(); } + + pub fn populate_add_movie_preferences_lists(&mut self) { + self + .add_movie_monitor_list + .set_items(Vec::from_iter(Monitor::iter())); + self + .add_movie_minimum_availability_list + .set_items(Vec::from_iter(MinimumAvailability::iter())); + let mut quality_profile_names: Vec = + self.quality_profile_map.values().cloned().collect(); + quality_profile_names.sort(); + self + .add_movie_quality_profile_list + .set_items(quality_profile_names); + } } impl Default for RadarrData { @@ -290,7 +306,13 @@ impl ActiveRadarrBlock { impl From for Route { fn from(active_radarr_block: ActiveRadarrBlock) -> Route { - Route::Radarr(active_radarr_block) + Route::Radarr(active_radarr_block, None) + } +} + +impl From<(ActiveRadarrBlock, Option)> for Route { + fn from(value: (ActiveRadarrBlock, Option)) -> Route { + Route::Radarr(value.0, value.1) } } @@ -565,9 +587,29 @@ pub mod radarr_test_utils { #[cfg(test)] mod tests { mod radarr_data_tests { + use std::collections::HashMap; + use pretty_assertions::assert_eq; + use strum::IntoEnumIterator; use crate::app::radarr::radarr_test_utils::create_test_radarr_data; + use crate::app::radarr::{ActiveRadarrBlock, RadarrData}; + use crate::models::radarr_models::{MinimumAvailability, Monitor}; + use crate::models::Route; + + #[test] + fn test_from_tuple_to_route_with_context() { + assert_eq!( + Route::from(( + ActiveRadarrBlock::AddMoviePrompt, + Some(ActiveRadarrBlock::AddMovieSearchResults) + )), + Route::Radarr( + ActiveRadarrBlock::AddMoviePrompt, + Some(ActiveRadarrBlock::AddMovieSearchResults) + ) + ); + } #[test] fn test_reset_movie_collection_table() { @@ -613,6 +655,32 @@ mod tests { assert_add_movie_selections_reset!(radarr_data); } + + #[test] + fn test_populate_add_movie_preferences_lists() { + let mut radarr_data = RadarrData { + quality_profile_map: HashMap::from([ + (2222, "HD - 1080p".to_owned()), + (1111, "Any".to_owned()), + ]), + ..RadarrData::default() + }; + + radarr_data.populate_add_movie_preferences_lists(); + + assert_eq!( + radarr_data.add_movie_monitor_list.items, + Vec::from_iter(Monitor::iter()) + ); + assert_eq!( + radarr_data.add_movie_minimum_availability_list.items, + Vec::from_iter(MinimumAvailability::iter()) + ); + assert_eq!( + radarr_data.add_movie_quality_profile_list.items, + vec!["Any".to_owned(), "HD - 1080p".to_owned()] + ); + } } mod active_radarr_block_tests { diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 90478fb..0d16983 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -29,7 +29,7 @@ pub trait KeyEventHandler<'a, T: Into> { self.handle_key_event(); } - fn with(key: &'a Key, app: &'a mut App, active_block: &'a T) -> Self; + fn with(key: &'a Key, app: &'a mut App, active_block: &'a T, context: &'a Option) -> Self; fn get_key(&self) -> &Key; fn handle_scroll_up(&mut self); fn handle_scroll_down(&mut self); @@ -43,8 +43,8 @@ pub trait KeyEventHandler<'a, T: Into> { } pub fn handle_events(key: Key, app: &mut App) { - if let Route::Radarr(active_radarr_block) = *app.get_current_route() { - RadarrHandler::with(&key, app, &active_radarr_block).handle() + if let Route::Radarr(active_radarr_block, context) = *app.get_current_route() { + RadarrHandler::with(&key, app, &active_radarr_block, &context).handle() } } @@ -57,7 +57,7 @@ fn handle_clear_errors(app: &mut App) { fn handle_prompt_toggle(app: &mut App, key: &Key) { match key { _ if *key == DEFAULT_KEYBINDINGS.left.key || *key == DEFAULT_KEYBINDINGS.right.key => { - if let Route::Radarr(_) = *app.get_current_route() { + if let Route::Radarr(_, _) = *app.get_current_route() { app.data.radarr_data.prompt_confirm = !app.data.radarr_data.prompt_confirm; } } @@ -177,7 +177,7 @@ mod test_utils { #[macro_export] macro_rules! test_iterable_scroll { - ($func:ident, $handler:ident, $data_ref:ident, $block:expr) => { + ($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => { #[rstest] fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { let mut app = App::default(); @@ -187,16 +187,16 @@ mod test_utils { .$data_ref .set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]); - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 2"); - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 1"); } }; - ($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $field:ident) => { + ($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => { #[rstest] fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { let mut app = App::default(); @@ -206,14 +206,14 @@ mod test_utils { .$data_ref .set_items(simple_stateful_iterable_vec!($items)); - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_str_eq!( app.data.radarr_data.$data_ref.current_selection().$field, "Test 2" ); - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_str_eq!( app.data.radarr_data.$data_ref.current_selection().$field, @@ -221,13 +221,13 @@ mod test_utils { ); } }; - ($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $field:ident, $conversion_fn:ident) => { + ($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => { #[rstest] fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { let mut app = App::default(); app.data.radarr_data.$data_ref.set_items($items); - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_str_eq!( app @@ -240,7 +240,7 @@ mod test_utils { "Test 2" ); - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_str_eq!( app @@ -258,7 +258,7 @@ mod test_utils { #[macro_export] macro_rules! test_enum_scroll { - ($func:ident, $handler:ident, $name:ident, $data_ref:ident, $block:expr) => { + ($func:ident, $handler:ident, $name:ident, $data_ref:ident, $block:expr, $context:expr) => { #[rstest] fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) { let reference_vec = Vec::from_iter($name::iter()); @@ -271,7 +271,7 @@ mod test_utils { if key == Key::Up { for i in (0..reference_vec.len()).rev() { - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_eq!( app.data.radarr_data.$data_ref.current_selection(), @@ -280,7 +280,7 @@ mod test_utils { } } else { for i in 0..reference_vec.len() { - $handler::with(&key, &mut app, &$block).handle(); + $handler::with(&key, &mut app, &$block, &$context).handle(); assert_eq!( app.data.radarr_data.$data_ref.current_selection(), @@ -294,7 +294,7 @@ mod test_utils { #[macro_export] macro_rules! test_iterable_home_and_end { - ($func:ident, $handler:ident, $data_ref:ident, $block:expr) => { + ($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => { #[test] fn $func() { let mut app = App::default(); @@ -304,16 +304,16 @@ mod test_utils { "Test 3".to_owned(), ]); - $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle(); assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 3"); - $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle(); assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 1"); } }; - ($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $field:ident) => { + ($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => { #[test] fn $func() { let mut app = App::default(); @@ -323,14 +323,14 @@ mod test_utils { .$data_ref .set_items(extended_stateful_iterable_vec!($items)); - $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle(); assert_str_eq!( app.data.radarr_data.$data_ref.current_selection().$field, "Test 3" ); - $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle(); assert_str_eq!( app.data.radarr_data.$data_ref.current_selection().$field, @@ -338,13 +338,13 @@ mod test_utils { ); } }; - ($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $field:ident, $conversion_fn:ident) => { + ($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => { #[test] fn $func() { let mut app = App::default(); app.data.radarr_data.$data_ref.set_items($items); - $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle(); assert_str_eq!( app @@ -357,7 +357,7 @@ mod test_utils { "Test 3" ); - $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle(); assert_str_eq!( app @@ -375,7 +375,7 @@ mod test_utils { #[macro_export] macro_rules! test_enum_home_and_end { - ($func:ident, $handler:ident, $name:ident, $data_ref:ident, $block:expr) => { + ($func:ident, $handler:ident, $name:ident, $data_ref:ident, $block:expr, $context:expr) => { #[test] fn $func() { let reference_vec = Vec::from_iter($name::iter()); @@ -386,14 +386,14 @@ mod test_utils { .$data_ref .set_items(reference_vec.clone()); - $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle(); assert_eq!( app.data.radarr_data.$data_ref.current_selection(), &reference_vec[reference_vec.len() - 1] ); - $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block).handle(); + $handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle(); assert_eq!( app.data.radarr_data.$data_ref.current_selection(), @@ -410,12 +410,19 @@ mod test_utils { app.push_navigation_stack($base.clone().into()); app.push_navigation_stack($active_block.clone().into()); - RadarrHandler::with(&DEFAULT_KEYBINDINGS.esc.key, &mut app, &$active_block).handle(); + RadarrHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &$active_block, + &None, + ) + .handle(); assert_eq!(app.get_current_route(), &$base.into()); }; } } + #[cfg(test)] mod tests { use rstest::rstest; diff --git a/src/handlers/radarr_handlers/add_movie_handler.rs b/src/handlers/radarr_handlers/add_movie_handler.rs index abca057..d888bfc 100644 --- a/src/handlers/radarr_handlers/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/add_movie_handler.rs @@ -1,9 +1,6 @@ -use strum::IntoEnumIterator; - use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::radarr::ActiveRadarrBlock; use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; -use crate::models::radarr_models::{MinimumAvailability, Monitor}; use crate::models::{Scrollable, StatefulTable}; use crate::network::radarr_network::RadarrEvent; use crate::{handle_text_box_keys, App, Key}; @@ -12,6 +9,7 @@ pub(super) struct AddMovieHandler<'a> { key: &'a Key, app: &'a mut App, active_radarr_block: &'a ActiveRadarrBlock, + context: &'a Option, } impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { @@ -19,11 +17,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { key: &'a Key, app: &'a mut App, active_block: &'a ActiveRadarrBlock, + context: &'a Option, ) -> AddMovieHandler<'a> { AddMovieHandler { key, app, active_radarr_block: active_block, + context, } } @@ -192,6 +192,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { .current_selection() .tmdb_id .clone(); + if self .app .data @@ -212,29 +213,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { .app .data .radarr_data - .add_movie_monitor_list - .set_items(Vec::from_iter(Monitor::iter())); - self - .app - .data - .radarr_data - .add_movie_minimum_availability_list - .set_items(Vec::from_iter(MinimumAvailability::iter())); - let mut quality_profile_names: Vec = self - .app - .data - .radarr_data - .quality_profile_map - .values() - .cloned() - .collect(); - quality_profile_names.sort(); - self - .app - .data - .radarr_data - .add_movie_quality_profile_list - .set_items(quality_profile_names); + .populate_add_movie_preferences_lists(); } } ActiveRadarrBlock::AddMoviePrompt => match self.app.data.radarr_data.selected_block { @@ -248,13 +227,21 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { } ActiveRadarrBlock::AddMovieSelectMonitor => self .app - .push_navigation_stack(ActiveRadarrBlock::AddMovieSelectMonitor.into()), - ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self - .app - .push_navigation_stack(ActiveRadarrBlock::AddMovieSelectMinimumAvailability.into()), - ActiveRadarrBlock::AddMovieSelectQualityProfile => self - .app - .push_navigation_stack(ActiveRadarrBlock::AddMovieSelectQualityProfile.into()), + .push_navigation_stack((ActiveRadarrBlock::AddMovieSelectMonitor, *self.context).into()), + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self.app.push_navigation_stack( + ( + ActiveRadarrBlock::AddMovieSelectMinimumAvailability, + *self.context, + ) + .into(), + ), + ActiveRadarrBlock::AddMovieSelectQualityProfile => self.app.push_navigation_stack( + ( + ActiveRadarrBlock::AddMovieSelectQualityProfile, + *self.context, + ) + .into(), + ), _ => (), }, ActiveRadarrBlock::AddMovieSelectMonitor @@ -298,6 +285,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { } #[cfg(test)] +#[allow(unused_imports)] mod tests { use pretty_assertions::assert_str_eq; @@ -325,6 +313,7 @@ mod tests { add_searched_movies, simple_stateful_iterable_vec!(AddMovieSearchResult, HorizontallyScrollableText), ActiveRadarrBlock::AddMovieSearchResults, + None, title, stationary_style ); @@ -334,7 +323,8 @@ mod tests { AddMovieHandler, Monitor, add_movie_monitor_list, - ActiveRadarrBlock::AddMovieSelectMonitor + ActiveRadarrBlock::AddMovieSelectMonitor, + None ); test_enum_scroll!( @@ -342,14 +332,16 @@ mod tests { AddMovieHandler, MinimumAvailability, add_movie_minimum_availability_list, - ActiveRadarrBlock::AddMovieSelectMinimumAvailability + ActiveRadarrBlock::AddMovieSelectMinimumAvailability, + None ); test_iterable_scroll!( test_add_movie_select_quality_profile_scroll, AddMovieHandler, add_movie_quality_profile_list, - ActiveRadarrBlock::AddMovieSelectQualityProfile + ActiveRadarrBlock::AddMovieSelectQualityProfile, + None ); #[rstest] @@ -357,7 +349,7 @@ mod tests { let mut app = App::default(); app.data.radarr_data.selected_block = ActiveRadarrBlock::AddMovieSelectMinimumAvailability; - AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle(); if key == Key::Up { assert_eq!( @@ -388,6 +380,7 @@ mod tests { add_searched_movies, extended_stateful_iterable_vec!(AddMovieSearchResult, HorizontallyScrollableText), ActiveRadarrBlock::AddMovieSearchResults, + None, title, stationary_style ); @@ -397,7 +390,8 @@ mod tests { AddMovieHandler, Monitor, add_movie_monitor_list, - ActiveRadarrBlock::AddMovieSelectMonitor + ActiveRadarrBlock::AddMovieSelectMonitor, + None ); test_enum_home_and_end!( @@ -405,14 +399,16 @@ mod tests { AddMovieHandler, MinimumAvailability, add_movie_minimum_availability_list, - ActiveRadarrBlock::AddMovieSelectMinimumAvailability + ActiveRadarrBlock::AddMovieSelectMinimumAvailability, + None ); test_iterable_home_and_end!( test_add_movie_select_quality_profile_scroll, AddMovieHandler, add_movie_quality_profile_list, - ActiveRadarrBlock::AddMovieSelectQualityProfile + ActiveRadarrBlock::AddMovieSelectQualityProfile, + None ); } @@ -425,11 +421,11 @@ mod tests { fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) { let mut app = App::default(); - AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle(); assert!(app.data.radarr_data.prompt_confirm); - AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle(); assert!(!app.data.radarr_data.prompt_confirm); } @@ -458,6 +454,7 @@ mod tests { &SUBMIT_KEY, &mut app, &ActiveRadarrBlock::AddMovieSearchInput, + &None, ) .handle(); @@ -483,6 +480,7 @@ mod tests { &SUBMIT_KEY, &mut app, &ActiveRadarrBlock::AddMovieSearchResults, + &None, ) .handle(); @@ -521,6 +519,7 @@ mod tests { &SUBMIT_KEY, &mut app, &ActiveRadarrBlock::AddMovieSearchResults, + &None, ) .handle(); @@ -548,6 +547,7 @@ mod tests { &SUBMIT_KEY, &mut app, &ActiveRadarrBlock::AddMovieSearchResults, + &None, ) .handle(); @@ -564,7 +564,13 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); app.data.radarr_data.selected_block = ActiveRadarrBlock::AddMovieConfirmPrompt; - AddMovieHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::AddMoviePrompt, + &None, + ) + .handle(); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.data.radarr_data.prompt_confirm_action, None); @@ -578,7 +584,13 @@ mod tests { app.data.radarr_data.prompt_confirm = true; app.data.radarr_data.selected_block = ActiveRadarrBlock::AddMovieConfirmPrompt; - AddMovieHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::AddMoviePrompt, + &None, + ) + .handle(); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!( @@ -597,12 +609,27 @@ mod tests { selected_block: ActiveRadarrBlock, ) { let mut app = App::default(); - app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); + app.push_navigation_stack( + ( + ActiveRadarrBlock::AddMoviePrompt, + Some(ActiveRadarrBlock::CollectionDetails), + ) + .into(), + ); app.data.radarr_data.selected_block = selected_block; - AddMovieHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::AddMoviePrompt, + &Some(ActiveRadarrBlock::CollectionDetails), + ) + .handle(); - assert_eq!(app.get_current_route(), &selected_block.into()); + assert_eq!( + app.get_current_route(), + &(selected_block, Some(ActiveRadarrBlock::CollectionDetails)).into() + ); assert_eq!(app.data.radarr_data.prompt_confirm_action, None); } @@ -619,7 +646,13 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); app.push_navigation_stack(active_radarr_block.into()); - AddMovieHandler::with(&SUBMIT_KEY, &mut app, &active_radarr_block).handle(); + AddMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &active_radarr_block, + &Some(ActiveRadarrBlock::CollectionDetails), + ) + .handle(); assert_eq!( app.get_current_route(), @@ -648,7 +681,13 @@ mod tests { app.should_ignore_quit_key = true; app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into()); - AddMovieHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::AddMovieSearchInput).handle(); + AddMovieHandler::with( + &ESC_KEY, + &mut app, + &ActiveRadarrBlock::AddMovieSearchInput, + &None, + ) + .handle(); assert!(!app.should_ignore_quit_key); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); @@ -673,6 +712,7 @@ mod tests { &ESC_KEY, &mut app, &ActiveRadarrBlock::AddMovieSearchResults, + &None, ) .handle(); @@ -695,6 +735,7 @@ mod tests { &ESC_KEY, &mut app, &ActiveRadarrBlock::AddMovieAlreadyInLibrary, + &None, ) .handle(); @@ -711,7 +752,13 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into()); app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); - AddMovieHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with( + &ESC_KEY, + &mut app, + &ActiveRadarrBlock::AddMoviePrompt, + &None, + ) + .handle(); assert!(!app.data.radarr_data.prompt_confirm); assert_eq!( @@ -731,14 +778,36 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); - app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); - app.push_navigation_stack(active_radarr_block.into()); + app.push_navigation_stack( + ( + ActiveRadarrBlock::AddMoviePrompt, + Some(ActiveRadarrBlock::CollectionDetails), + ) + .into(), + ); + app.push_navigation_stack( + ( + active_radarr_block, + Some(ActiveRadarrBlock::CollectionDetails), + ) + .into(), + ); - AddMovieHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::AddMoviePrompt).handle(); + AddMovieHandler::with( + &ESC_KEY, + &mut app, + &active_radarr_block, + &Some(ActiveRadarrBlock::CollectionDetails), + ) + .handle(); assert_eq!( app.get_current_route(), - &ActiveRadarrBlock::AddMoviePrompt.into() + &( + ActiveRadarrBlock::AddMoviePrompt, + Some(ActiveRadarrBlock::CollectionDetails), + ) + .into() ); } } @@ -755,6 +824,7 @@ mod tests { &DEFAULT_KEYBINDINGS.backspace.key, &mut app, &ActiveRadarrBlock::AddMovieSearchInput, + &None, ) .handle(); @@ -769,6 +839,7 @@ mod tests { &Key::Char('h'), &mut app, &ActiveRadarrBlock::AddMovieSearchInput, + &None, ) .handle(); diff --git a/src/handlers/radarr_handlers/collection_details_handler.rs b/src/handlers/radarr_handlers/collection_details_handler.rs index fbc246b..f85681e 100644 --- a/src/handlers/radarr_handlers/collection_details_handler.rs +++ b/src/handlers/radarr_handlers/collection_details_handler.rs @@ -8,6 +8,7 @@ pub(super) struct CollectionDetailsHandler<'a> { key: &'a Key, app: &'a mut App, active_radarr_block: &'a ActiveRadarrBlock, + _context: &'a Option, } impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for CollectionDetailsHandler<'a> { @@ -15,11 +16,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for CollectionDetailsHandler<'a> key: &'a Key, app: &'a mut App, active_block: &'a ActiveRadarrBlock, + _context: &'a Option, ) -> CollectionDetailsHandler<'a> { CollectionDetailsHandler { key, app, active_radarr_block: active_block, + _context, } } @@ -62,9 +65,41 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for CollectionDetailsHandler<'a> fn handle_submit(&mut self) { if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block { - self + let tmdb_id = self .app - .push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into()) + .data + .radarr_data + .collection_movies + .current_selection() + .tmdb_id + .clone(); + + if self + .app + .data + .radarr_data + .movies + .items + .iter() + .any(|movie| movie.tmdb_id == tmdb_id) + { + self + .app + .push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into()); + } else { + self.app.push_navigation_stack( + ( + ActiveRadarrBlock::AddMoviePrompt, + Some(ActiveRadarrBlock::CollectionDetails), + ) + .into(), + ); + self + .app + .data + .radarr_data + .populate_add_movie_preferences_lists(); + } } } @@ -93,6 +128,7 @@ mod tests { use crate::handlers::radarr_handlers::collection_details_handler::CollectionDetailsHandler; use crate::handlers::KeyEventHandler; use crate::models::radarr_models::CollectionMovie; + use crate::models::HorizontallyScrollableText; mod test_handle_scroll_up_and_down { use rstest::rstest; @@ -105,9 +141,11 @@ mod tests { test_collection_details_scroll, CollectionDetailsHandler, collection_movies, - CollectionMovie, + simple_stateful_iterable_vec!(CollectionMovie, HorizontallyScrollableText), ActiveRadarrBlock::CollectionDetails, - title + None, + title, + stationary_style ); } @@ -120,15 +158,21 @@ mod tests { test_collection_details_home_end, CollectionDetailsHandler, collection_movies, - CollectionMovie, + extended_stateful_iterable_vec!(CollectionMovie, HorizontallyScrollableText), ActiveRadarrBlock::CollectionDetails, - title + None, + title, + stationary_style ); } mod test_handle_submit { + use std::collections::HashMap; + use pretty_assertions::assert_eq; + use crate::models::radarr_models::Movie; + use super::*; const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key; @@ -136,10 +180,74 @@ mod tests { #[test] fn test_collection_details_submit() { let mut app = App::default(); - app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()); + app + .data + .radarr_data + .collection_movies + .set_items(vec![CollectionMovie::default()]); + app.data.radarr_data.quality_profile_map = + HashMap::from([(1, "B - Test 2".to_owned()), (0, "A - Test 1".to_owned())]); - CollectionDetailsHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::CollectionDetails) - .handle(); + CollectionDetailsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &( + ActiveRadarrBlock::AddMoviePrompt, + Some(ActiveRadarrBlock::CollectionDetails) + ) + .into() + ); + assert!(!app.data.radarr_data.add_movie_monitor_list.items.is_empty()); + assert!(!app + .data + .radarr_data + .add_movie_minimum_availability_list + .items + .is_empty()); + assert!(!app + .data + .radarr_data + .add_movie_quality_profile_list + .items + .is_empty()); + assert_str_eq!( + app + .data + .radarr_data + .add_movie_quality_profile_list + .current_selection(), + "A - Test 1" + ); + } + + #[test] + fn test_collection_details_submit_movie_already_in_library() { + let mut app = App::default(); + app + .data + .radarr_data + .collection_movies + .set_items(vec![CollectionMovie::default()]); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + CollectionDetailsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ) + .handle(); assert_eq!( app.get_current_route(), @@ -166,8 +274,13 @@ mod tests { .collection_movies .set_items(vec![CollectionMovie::default()]); - CollectionDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::CollectionDetails) - .handle(); + CollectionDetailsHandler::with( + &ESC_KEY, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ) + .handle(); assert_eq!( app.get_current_route(), @@ -182,8 +295,13 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()); app.push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into()); - CollectionDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::ViewMovieOverview) - .handle(); + CollectionDetailsHandler::with( + &ESC_KEY, + &mut app, + &ActiveRadarrBlock::ViewMovieOverview, + &None, + ) + .handle(); assert_eq!( app.get_current_route(), diff --git a/src/handlers/radarr_handlers/mod.rs b/src/handlers/radarr_handlers/mod.rs index f0c6e83..aab0332 100644 --- a/src/handlers/radarr_handlers/mod.rs +++ b/src/handlers/radarr_handlers/mod.rs @@ -20,19 +20,22 @@ pub(super) struct RadarrHandler<'a> { key: &'a Key, app: &'a mut App, active_radarr_block: &'a ActiveRadarrBlock, + context: &'a Option, } impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> { fn handle(&mut self) { match self.active_radarr_block { _ if MOVIE_DETAILS_BLOCKS.contains(self.active_radarr_block) => { - MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block).handle() + MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context) + .handle() } _ if COLLECTION_DETAILS_BLOCKS.contains(self.active_radarr_block) => { - CollectionDetailsHandler::with(self.key, self.app, self.active_radarr_block).handle() + CollectionDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context) + .handle() } _ if ADD_MOVIE_BLOCKS.contains(self.active_radarr_block) => { - AddMovieHandler::with(self.key, self.app, self.active_radarr_block).handle() + AddMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context).handle() } _ => self.handle_key_event(), } @@ -42,11 +45,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> { key: &'a Key, app: &'a mut App, active_block: &'a ActiveRadarrBlock, + context: &'a Option, ) -> RadarrHandler<'a> { RadarrHandler { key, app, active_radarr_block: active_block, + context, } } @@ -536,6 +541,7 @@ mod tests { collections, Collection, ActiveRadarrBlock::Collections, + None, title ); @@ -545,6 +551,7 @@ mod tests { filtered_collections, Collection, ActiveRadarrBlock::Collections, + None, title ); @@ -554,6 +561,7 @@ mod tests { movies, Movie, ActiveRadarrBlock::Movies, + None, title ); @@ -563,6 +571,7 @@ mod tests { filtered_movies, Movie, ActiveRadarrBlock::Movies, + None, title ); @@ -572,6 +581,7 @@ mod tests { downloads, DownloadRecord, ActiveRadarrBlock::Downloads, + None, title ); } @@ -588,6 +598,7 @@ mod tests { collections, Collection, ActiveRadarrBlock::Collections, + None, title ); @@ -597,6 +608,7 @@ mod tests { filtered_collections, Collection, ActiveRadarrBlock::Collections, + None, title ); @@ -606,6 +618,7 @@ mod tests { movies, Movie, ActiveRadarrBlock::Movies, + None, title ); @@ -615,6 +628,7 @@ mod tests { filtered_movies, Movie, ActiveRadarrBlock::Movies, + None, title ); @@ -624,6 +638,7 @@ mod tests { downloads, DownloadRecord, ActiveRadarrBlock::Downloads, + None, title ); } @@ -639,7 +654,7 @@ mod tests { fn test_movies_delete() { let mut app = App::default(); - RadarrHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Movies).handle(); + RadarrHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); assert_eq!( app.get_current_route(), @@ -651,7 +666,7 @@ mod tests { fn test_downloads_delete() { let mut app = App::default(); - RadarrHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads).handle(); + RadarrHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle(); assert_eq!( app.get_current_route(), @@ -682,6 +697,7 @@ mod tests { &DEFAULT_KEYBINDINGS.left.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -708,6 +724,7 @@ mod tests { &DEFAULT_KEYBINDINGS.right.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -732,11 +749,11 @@ mod tests { ) { let mut app = App::default(); - RadarrHandler::with(&key, &mut app, &active_radarr_block).handle(); + RadarrHandler::with(&key, &mut app, &active_radarr_block, &None).handle(); assert!(app.data.radarr_data.prompt_confirm); - RadarrHandler::with(&key, &mut app, &active_radarr_block).handle(); + RadarrHandler::with(&key, &mut app, &active_radarr_block, &None).handle(); assert!(!app.data.radarr_data.prompt_confirm); } @@ -761,7 +778,7 @@ mod tests { ) { let mut app = App::default(); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &active_radarr_block).handle(); + RadarrHandler::with(&SUBMIT_KEY, &mut app, &active_radarr_block, &None).handle(); assert_eq!(app.get_current_route(), &expected_radarr_block.into()); } @@ -776,7 +793,13 @@ mod tests { .set_items(extended_stateful_iterable_vec!(Movie)); app.data.radarr_data.search = "Test 2".to_owned(); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::SearchMovie).handle(); + RadarrHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::SearchMovie, + &None, + ) + .handle(); assert_str_eq!( app.data.radarr_data.movies.current_selection().title, @@ -794,7 +817,13 @@ mod tests { .set_items(extended_stateful_iterable_vec!(Movie)); app.data.radarr_data.search = "Test 2".to_owned(); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::SearchMovie).handle(); + RadarrHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::SearchMovie, + &None, + ) + .handle(); assert_str_eq!( app @@ -817,7 +846,13 @@ mod tests { .set_items(extended_stateful_iterable_vec!(Collection)); app.data.radarr_data.search = "Test 2".to_owned(); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::SearchCollection).handle(); + RadarrHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::SearchCollection, + &None, + ) + .handle(); assert_str_eq!( app.data.radarr_data.collections.current_selection().title, @@ -835,7 +870,13 @@ mod tests { .set_items(extended_stateful_iterable_vec!(Collection)); app.data.radarr_data.search = "Test 2".to_owned(); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::SearchCollection).handle(); + RadarrHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::SearchCollection, + &None, + ) + .handle(); assert_str_eq!( app @@ -858,7 +899,13 @@ mod tests { .set_items(extended_stateful_iterable_vec!(Movie)); app.data.radarr_data.filter = "Test".to_owned(); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::FilterMovies).handle(); + RadarrHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::FilterMovies, + &None, + ) + .handle(); assert_eq!(app.data.radarr_data.filtered_movies.items.len(), 3); assert_str_eq!( @@ -882,7 +929,13 @@ mod tests { .set_items(extended_stateful_iterable_vec!(Collection)); app.data.radarr_data.filter = "Test".to_owned(); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::FilterCollections).handle(); + RadarrHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::FilterCollections, + &None, + ) + .handle(); assert_eq!(app.data.radarr_data.filtered_collections.items.len(), 3); assert_str_eq!( @@ -932,7 +985,7 @@ mod tests { app.push_navigation_stack(base_route.into()); app.push_navigation_stack(prompt_block.into()); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &prompt_block).handle(); + RadarrHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle(); assert!(app.data.radarr_data.prompt_confirm); assert_eq!( @@ -962,7 +1015,7 @@ mod tests { app.push_navigation_stack(base_route.into()); app.push_navigation_stack(prompt_block.into()); - RadarrHandler::with(&SUBMIT_KEY, &mut app, &prompt_block).handle(); + RadarrHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle(); assert!(!app.data.radarr_data.prompt_confirm); assert_eq!(app.data.radarr_data.prompt_confirm_action, None); @@ -994,7 +1047,7 @@ mod tests { app.push_navigation_stack(search_block.into()); app.data.radarr_data = create_test_radarr_data(); - RadarrHandler::with(&ESC_KEY, &mut app, &search_block).handle(); + RadarrHandler::with(&ESC_KEY, &mut app, &search_block, &None).handle(); assert_eq!(app.get_current_route(), &base_block.into()); assert!(!app.should_ignore_quit_key); @@ -1014,7 +1067,7 @@ mod tests { app.push_navigation_stack(filter_block.into()); app.data.radarr_data = create_test_radarr_data(); - RadarrHandler::with(&ESC_KEY, &mut app, &filter_block).handle(); + RadarrHandler::with(&ESC_KEY, &mut app, &filter_block, &None).handle(); assert_eq!(app.get_current_route(), &base_block.into()); assert!(!app.should_ignore_quit_key); @@ -1042,7 +1095,7 @@ mod tests { app.push_navigation_stack(prompt_block.into()); app.data.radarr_data.prompt_confirm = true; - RadarrHandler::with(&ESC_KEY, &mut app, &prompt_block).handle(); + RadarrHandler::with(&ESC_KEY, &mut app, &prompt_block, &None).handle(); assert_eq!(app.get_current_route(), &base_block.into()); assert!(!app.data.radarr_data.prompt_confirm); @@ -1056,7 +1109,7 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); app.data.radarr_data = create_test_radarr_data(); - RadarrHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Downloads).handle(); + RadarrHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle(); assert_eq!( app.get_current_route(), @@ -1089,6 +1142,7 @@ mod tests { &DEFAULT_KEYBINDINGS.search.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -1110,6 +1164,7 @@ mod tests { &DEFAULT_KEYBINDINGS.filter.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -1126,6 +1181,7 @@ mod tests { &DEFAULT_KEYBINDINGS.add.key, &mut app, &ActiveRadarrBlock::Movies, + &None, ) .handle(); @@ -1156,6 +1212,7 @@ mod tests { &DEFAULT_KEYBINDINGS.refresh.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -1174,6 +1231,7 @@ mod tests { &DEFAULT_KEYBINDINGS.backspace.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -1192,6 +1250,7 @@ mod tests { &DEFAULT_KEYBINDINGS.backspace.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -1205,7 +1264,7 @@ mod tests { ) { let mut app = App::default(); - RadarrHandler::with(&Key::Char('h'), &mut app, &active_radarr_block).handle(); + RadarrHandler::with(&Key::Char('h'), &mut app, &active_radarr_block, &None).handle(); assert_str_eq!(app.data.radarr_data.search, "h"); } @@ -1217,7 +1276,7 @@ mod tests { ) { let mut app = App::default(); - RadarrHandler::with(&Key::Char('h'), &mut app, &active_radarr_block).handle(); + RadarrHandler::with(&Key::Char('h'), &mut app, &active_radarr_block, &None).handle(); assert_str_eq!(app.data.radarr_data.filter, "h"); } @@ -1242,6 +1301,7 @@ mod tests { &DEFAULT_KEYBINDINGS.submit.key, &mut app, &ActiveRadarrBlock::SearchMovie, + &None, ) .search_table(movies, |movie| &movie.title); @@ -1271,6 +1331,7 @@ mod tests { &DEFAULT_KEYBINDINGS.submit.key, &mut app, &ActiveRadarrBlock::SearchMovie, + &None, ) .search_table(movies, |movie| &movie.title); @@ -1303,6 +1364,7 @@ mod tests { &DEFAULT_KEYBINDINGS.submit.key, &mut app, &ActiveRadarrBlock::FilterMovies, + &None, ) .filter_table(movies, |movie| &movie.title); @@ -1333,6 +1395,7 @@ mod tests { &DEFAULT_KEYBINDINGS.submit.key, &mut app, &ActiveRadarrBlock::FilterMovies, + &None, ) .filter_table(movies, |movie| &movie.title); diff --git a/src/handlers/radarr_handlers/movie_details_handler.rs b/src/handlers/radarr_handlers/movie_details_handler.rs index 089d3e6..c999105 100644 --- a/src/handlers/radarr_handlers/movie_details_handler.rs +++ b/src/handlers/radarr_handlers/movie_details_handler.rs @@ -16,6 +16,7 @@ pub(super) struct MovieDetailsHandler<'a> { key: &'a Key, app: &'a mut App, active_radarr_block: &'a ActiveRadarrBlock, + _context: &'a Option, } impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for MovieDetailsHandler<'a> { @@ -23,11 +24,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for MovieDetailsHandler<'a> { key: &'a Key, app: &'a mut App, active_block: &'a ActiveRadarrBlock, + _context: &'a Option, ) -> MovieDetailsHandler<'a> { MovieDetailsHandler { key, app, active_radarr_block: active_block, + _context, } } @@ -317,6 +320,7 @@ fn sort_releases_by_selected_field( } #[cfg(test)] +#[allow(unused_imports)] mod tests { use pretty_assertions::assert_str_eq; use rstest::rstest; @@ -354,6 +358,7 @@ mod tests { &DEFAULT_KEYBINDINGS.up.key, &mut app, &ActiveRadarrBlock::MovieDetails, + &None, ) .handle(); @@ -363,6 +368,7 @@ mod tests { &DEFAULT_KEYBINDINGS.down.key, &mut app, &ActiveRadarrBlock::MovieDetails, + &None, ) .handle(); @@ -375,6 +381,7 @@ mod tests { movie_history, simple_stateful_iterable_vec!(MovieHistoryItem, HorizontallyScrollableText, source_title), ActiveRadarrBlock::MovieHistory, + None, source_title, stationary_style ); @@ -385,6 +392,7 @@ mod tests { movie_cast, simple_stateful_iterable_vec!(Credit, String, person_name), ActiveRadarrBlock::Cast, + None, person_name, to_owned ); @@ -395,6 +403,7 @@ mod tests { movie_crew, simple_stateful_iterable_vec!(Credit, String, person_name), ActiveRadarrBlock::Crew, + None, person_name, to_owned ); @@ -405,6 +414,7 @@ mod tests { movie_releases, simple_stateful_iterable_vec!(Release, HorizontallyScrollableText), ActiveRadarrBlock::ManualSearch, + None, title, stationary_style ); @@ -414,7 +424,8 @@ mod tests { MovieDetailsHandler, ReleaseField, movie_releases_sort, - ActiveRadarrBlock::ManualSearchSortPrompt + ActiveRadarrBlock::ManualSearchSortPrompt, + None ); } @@ -437,6 +448,7 @@ mod tests { &DEFAULT_KEYBINDINGS.end.key, &mut app, &ActiveRadarrBlock::MovieDetails, + &None, ) .handle(); @@ -446,6 +458,7 @@ mod tests { &DEFAULT_KEYBINDINGS.home.key, &mut app, &ActiveRadarrBlock::MovieDetails, + &None, ) .handle(); @@ -458,6 +471,7 @@ mod tests { movie_history, extended_stateful_iterable_vec!(MovieHistoryItem, HorizontallyScrollableText, source_title), ActiveRadarrBlock::MovieHistory, + None, source_title, stationary_style ); @@ -468,6 +482,7 @@ mod tests { movie_cast, extended_stateful_iterable_vec!(Credit, String, person_name), ActiveRadarrBlock::Cast, + None, person_name, to_owned ); @@ -478,6 +493,7 @@ mod tests { movie_crew, extended_stateful_iterable_vec!(Credit, String, person_name), ActiveRadarrBlock::Crew, + None, person_name, to_owned ); @@ -488,6 +504,7 @@ mod tests { movie_releases, extended_stateful_iterable_vec!(Release, HorizontallyScrollableText), ActiveRadarrBlock::ManualSearch, + None, title, stationary_style ); @@ -497,7 +514,8 @@ mod tests { MovieDetailsHandler, ReleaseField, movie_releases_sort, - ActiveRadarrBlock::ManualSearchSortPrompt + ActiveRadarrBlock::ManualSearchSortPrompt, + None ); } @@ -519,11 +537,11 @@ mod tests { ) { let mut app = App::default(); - MovieDetailsHandler::with(&key, &mut app, &active_radarr_block).handle(); + MovieDetailsHandler::with(&key, &mut app, &active_radarr_block, &None).handle(); assert!(app.data.radarr_data.prompt_confirm); - MovieDetailsHandler::with(&key, &mut app, &active_radarr_block).handle(); + MovieDetailsHandler::with(&key, &mut app, &active_radarr_block, &None).handle(); assert!(!app.data.radarr_data.prompt_confirm); } @@ -550,7 +568,8 @@ mod tests { .position(|tab_route| tab_route.route == right_block.into()) .unwrap_or_default(); - MovieDetailsHandler::with(&DEFAULT_KEYBINDINGS.left.key, &mut app, &right_block).handle(); + MovieDetailsHandler::with(&DEFAULT_KEYBINDINGS.left.key, &mut app, &right_block, &None) + .handle(); assert_eq!( app.get_current_route(), @@ -558,7 +577,8 @@ mod tests { ); assert_eq!(app.get_current_route(), &left_block.into()); - MovieDetailsHandler::with(&DEFAULT_KEYBINDINGS.right.key, &mut app, &left_block).handle(); + MovieDetailsHandler::with(&DEFAULT_KEYBINDINGS.right.key, &mut app, &left_block, &None) + .handle(); assert_eq!( app.get_current_route(), @@ -584,7 +604,13 @@ mod tests { let mut app = App::default(); app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); - MovieDetailsHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::ManualSearch).handle(); + MovieDetailsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ) + .handle(); assert_eq!( app.get_current_route(), @@ -611,7 +637,7 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()); app.push_navigation_stack(prompt_block.into()); - MovieDetailsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block).handle(); + MovieDetailsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle(); assert!(app.data.radarr_data.prompt_confirm); assert_eq!( @@ -637,7 +663,7 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()); app.push_navigation_stack(prompt_block.into()); - MovieDetailsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block).handle(); + MovieDetailsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle(); assert!(!app.data.radarr_data.prompt_confirm); assert_eq!( @@ -667,6 +693,7 @@ mod tests { &SUBMIT_KEY, &mut app, &ActiveRadarrBlock::ManualSearchSortPrompt, + &None, ) .handle(); @@ -706,7 +733,7 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(active_radarr_block.into()); - MovieDetailsHandler::with(&ESC_KEY, &mut app, &active_radarr_block).handle(); + MovieDetailsHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle(); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_movie_info_tabs_reset!(app.data.radarr_data); @@ -727,7 +754,7 @@ mod tests { app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(prompt_block.into()); - MovieDetailsHandler::with(&ESC_KEY, &mut app, &prompt_block).handle(); + MovieDetailsHandler::with(&ESC_KEY, &mut app, &prompt_block, &None).handle(); assert!(!app.data.radarr_data.prompt_confirm); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); @@ -758,6 +785,7 @@ mod tests { &DEFAULT_KEYBINDINGS.search.key, &mut app, &active_radarr_block, + &None, ) .handle(); @@ -775,6 +803,7 @@ mod tests { &DEFAULT_KEYBINDINGS.sort.key, &mut app, &ActiveRadarrBlock::ManualSearch, + &None, ) .handle(); @@ -805,6 +834,7 @@ mod tests { &DEFAULT_KEYBINDINGS.refresh.key, &mut app, &active_radarr_block, + &None, ) .handle(); diff --git a/src/models/mod.rs b/src/models/mod.rs index ed8effa..b0942a6 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -10,7 +10,7 @@ pub mod radarr_models; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Route { - Radarr(ActiveRadarrBlock), + Radarr(ActiveRadarrBlock, Option), Sonarr, Readarr, Lidarr, diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index 068b8e6..2944178 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -65,12 +65,14 @@ pub struct Movie { #[derivative(Default)] #[serde(rename_all = "camelCase")] pub struct CollectionMovie { - pub title: String, + pub title: HorizontallyScrollableText, pub overview: String, #[derivative(Default(value = "Number::from(0)"))] pub year: Number, #[derivative(Default(value = "Number::from(0)"))] pub runtime: Number, + #[derivative(Default(value = "Number::from(0)"))] + pub tmdb_id: Number, pub genres: Vec, pub ratings: RatingsList, } diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index e4c74da..d50c702 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -952,6 +952,7 @@ mod test { "overview": "Collection blah blah blah", "year": 2023, "runtime": 120, + "tmdbId": 1234, "genres": ["cool", "family", "fun"], "ratings": { "imdb": { @@ -1587,6 +1588,7 @@ mod test { "overview": "Collection blah blah blah", "year": 2023, "runtime": 120, + "tmdbId": 1234, "genres": ["cool", "family", "fun"], "ratings": { "imdb": { @@ -2128,10 +2130,11 @@ mod test { fn collection_movie() -> CollectionMovie { CollectionMovie { - title: "Test".to_owned(), + title: "Test".to_owned().into(), overview: "Collection blah blah blah".to_owned(), year: Number::from(2023), runtime: Number::from(120), + tmdb_id: Number::from(1234), genres: genres(), ratings: ratings_list(), } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index f0c0833..90754dd 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -56,7 +56,7 @@ pub fn ui(f: &mut Frame, app: &mut App) { draw_header_row(f, app, main_chunks[0]); draw_context_row(f, app, main_chunks[1]); - if let Route::Radarr(_) = app.get_current_route() { + if let Route::Radarr(_, _) = app.get_current_route() { radarr_ui::draw_radarr_ui(f, app, main_chunks[2]) } } @@ -173,7 +173,7 @@ pub fn draw_drop_down_popup( } fn draw_context_row(f: &mut Frame<'_, B>, app: &App, area: Rect) { - if let Route::Radarr(_) = app.get_current_route() { + if let Route::Radarr(_, _) = app.get_current_route() { radarr_ui::draw_radarr_context_row(f, app, area) } } diff --git a/src/ui/radarr_ui/add_movie_ui.rs b/src/ui/radarr_ui/add_movie_ui.rs index 59359e9..3ea6231 100644 --- a/src/ui/radarr_ui/add_movie_ui.rs +++ b/src/ui/radarr_ui/add_movie_ui.rs @@ -7,8 +7,9 @@ use tui::Frame; use crate::app::radarr::ActiveRadarrBlock; use crate::models::radarr_models::AddMovieSearchResult; use crate::models::Route; +use crate::ui::radarr_ui::collection_details_ui::draw_collection_details; use crate::ui::utils::{ - borderless_block, get_width_with_margin, horizontal_chunks, layout_block, + borderless_block, get_width_from_percentage, horizontal_chunks, layout_block, layout_paragraph_borderless, show_cursor, style_default, style_help, style_primary, title_block_centered, vertical_chunks_with_margin, }; @@ -24,7 +25,7 @@ pub(super) fn draw_add_movie_search_popup( app: &mut App, area: Rect, ) { - if let Route::Radarr(active_radarr_block) = *app.get_current_route() { + if let Route::Radarr(active_radarr_block, context_option) = *app.get_current_route() { match active_radarr_block { ActiveRadarrBlock::AddMovieSearchInput | ActiveRadarrBlock::AddMovieSearchResults => { draw_add_movie_search(f, app, area); @@ -33,7 +34,17 @@ pub(super) fn draw_add_movie_search_popup( | ActiveRadarrBlock::AddMovieSelectMonitor | ActiveRadarrBlock::AddMovieSelectMinimumAvailability | ActiveRadarrBlock::AddMovieSelectQualityProfile => { - draw_medium_popup_over(f, app, area, draw_add_movie_search, draw_confirmation_popup); + if context_option.is_some() { + draw_medium_popup_over( + f, + app, + area, + draw_collection_details, + draw_confirmation_popup, + ); + } else { + draw_medium_popup_over(f, app, area, draw_add_movie_search, draw_confirmation_popup); + } } ActiveRadarrBlock::AddMovieAlreadyInLibrary => draw_error_popup_over( f, @@ -73,7 +84,7 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App, area: .style(style_default()) .block(title_block_centered("Add Movie")); - if let Route::Radarr(active_radarr_block) = *app.get_current_route() { + if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() { match active_radarr_block { ActiveRadarrBlock::AddMovieSearchInput => { show_cursor(f, chunks[0], block_content); @@ -173,9 +184,10 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App, area: "" }; - movie - .title - .scroll_or_reset(get_width_with_margin(area), *movie == current_selection); + movie.title.scroll_or_reset( + get_width_from_percentage(area, 27), + *movie == current_selection, + ); Row::new(vec![ Cell::from(in_library), @@ -200,7 +212,7 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App, area: } fn draw_confirmation_popup(f: &mut Frame<'_, B>, app: &mut App, prompt_area: Rect) { - if let Route::Radarr(active_radarr_block) = *app.get_current_route() { + if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() { match active_radarr_block { ActiveRadarrBlock::AddMovieSelectMonitor => { draw_drop_down_popup( @@ -272,23 +284,42 @@ fn draw_select_quality_profile_popup( fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, prompt_area: Rect) { let title = "Add Movie"; - let prompt = format!( - "{}:\n\n{}", - app - .data - .radarr_data - .add_searched_movies - .current_selection() - .title - .to_string() - .trim(), - app - .data - .radarr_data - .add_searched_movies - .current_selection() - .overview - ); + let (movie_title, movie_overview) = if let Route::Radarr(_, Some(_)) = app.get_current_route() { + ( + app + .data + .radarr_data + .collection_movies + .current_selection() + .title + .to_string(), + app + .data + .radarr_data + .collection_movies + .current_selection() + .overview + .clone(), + ) + } else { + ( + app + .data + .radarr_data + .add_searched_movies + .current_selection() + .title + .stationary_style(), + app + .data + .radarr_data + .add_searched_movies + .current_selection() + .overview + .clone(), + ) + }; + let prompt = format!("{}:\n\n{}", movie_title, movie_overview); let yes_no_value = &app.data.radarr_data.prompt_confirm; let selected_block = &app.data.radarr_data.selected_block; let highlight_yes_no = *selected_block == ActiveRadarrBlock::AddMovieConfirmPrompt; diff --git a/src/ui/radarr_ui/collection_details_ui.rs b/src/ui/radarr_ui/collection_details_ui.rs index 241daa2..a8bc8d3 100644 --- a/src/ui/radarr_ui/collection_details_ui.rs +++ b/src/ui/radarr_ui/collection_details_ui.rs @@ -6,10 +6,12 @@ use tui::Frame; use crate::app::radarr::ActiveRadarrBlock; use crate::app::App; +use crate::models::radarr_models::CollectionMovie; use crate::models::Route; use crate::ui::utils::{ - borderless_block, layout_block_top_border_with_title, spans_info_primary, style_default, - style_help, style_primary, title_block, title_style, vertical_chunks_with_margin, + borderless_block, get_width_from_percentage, layout_block_top_border_with_title, + spans_info_primary, style_default, style_help, style_primary, title_block, title_style, + vertical_chunks_with_margin, }; use crate::ui::{draw_small_popup_over, draw_table, TableProps}; use crate::utils::convert_runtime; @@ -19,7 +21,7 @@ pub(super) fn draw_collection_details_popup( app: &mut App, content_area: Rect, ) { - if let Route::Radarr(active_radarr_block) = app.get_current_route() { + if let Route::Radarr(active_radarr_block, _) = app.get_current_route() { match active_radarr_block { ActiveRadarrBlock::ViewMovieOverview => { draw_small_popup_over( @@ -36,7 +38,11 @@ pub(super) fn draw_collection_details_popup( } } -fn draw_collection_details(f: &mut Frame<'_, B>, app: &mut App, content_area: Rect) { +pub(super) fn draw_collection_details( + f: &mut Frame<'_, B>, + app: &mut App, + content_area: Rect, +) { let chunks = vertical_chunks_with_margin( vec![ Constraint::Percentage(20), @@ -62,7 +68,17 @@ fn draw_collection_details(f: &mut Frame<'_, B>, app: &mut App, cont .get(&collection_selection.quality_profile_id.as_u64().unwrap()) .unwrap() .to_owned(); - let mut help_text = Text::from("<↑↓> scroll table | show overview | close"); + let current_selection = if app.data.radarr_data.collection_movies.items.is_empty() { + CollectionMovie::default() + } else { + app + .data + .radarr_data + .collection_movies + .current_selection_clone() + }; + let mut help_text = + Text::from("<↑↓> scroll table | show overview/add movie | close"); help_text.patch_style(style_help()); let collection_description = Text::from(vec![ @@ -103,6 +119,7 @@ fn draw_collection_details(f: &mut Frame<'_, B>, app: &mut App, cont TableProps { content: &mut app.data.radarr_data.collection_movies, table_headers: vec![ + "✓", "Title", "Year", "Runtime", @@ -111,16 +128,33 @@ fn draw_collection_details(f: &mut Frame<'_, B>, app: &mut App, cont "Genres", ], constraints: vec![ + Constraint::Percentage(2), Constraint::Percentage(20), Constraint::Percentage(8), Constraint::Percentage(10), Constraint::Percentage(10), Constraint::Percentage(18), - Constraint::Percentage(30), + Constraint::Percentage(28), ], help: None, }, |movie| { + let in_library = if app + .data + .radarr_data + .movies + .items + .iter() + .any(|mov| mov.tmdb_id == movie.tmdb_id) + { + "✓" + } else { + "" + }; + movie.title.scroll_or_reset( + get_width_from_percentage(chunks[1], 20), + current_selection == *movie, + ); let (hours, minutes) = convert_runtime(movie.runtime.as_u64().unwrap()); let imdb_rating = movie .ratings @@ -150,7 +184,8 @@ fn draw_collection_details(f: &mut Frame<'_, B>, app: &mut App, cont }; Row::new(vec![ - Cell::from(movie.title.to_owned()), + Cell::from(in_library), + Cell::from(movie.title.to_string()), Cell::from(movie.year.as_u64().unwrap().to_string()), Cell::from(format!("{}h {}m", hours, minutes)), Cell::from(imdb_rating), diff --git a/src/ui/radarr_ui/mod.rs b/src/ui/radarr_ui/mod.rs index ff68f8e..2f5be77 100644 --- a/src/ui/radarr_ui/mod.rs +++ b/src/ui/radarr_ui/mod.rs @@ -21,7 +21,7 @@ use crate::ui::radarr_ui::add_movie_ui::draw_add_movie_search_popup; use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup; use crate::ui::radarr_ui::movie_details_ui::draw_movie_info_popup; use crate::ui::utils::{ - borderless_block, get_width_with_margin, horizontal_chunks, layout_block, + borderless_block, get_width_from_percentage, horizontal_chunks, layout_block, layout_block_top_border, line_gauge_with_label, line_gauge_with_title, show_cursor, style_bold, style_default, style_failure, style_primary, style_success, style_warning, title_block, title_block_centered, vertical_chunks_with_margin, @@ -39,7 +39,7 @@ mod movie_details_ui; pub(super) fn draw_radarr_ui(f: &mut Frame<'_, B>, app: &mut App, area: Rect) { let (content_rect, _) = draw_tabs(f, area, "Movies", &app.data.radarr_data.main_tabs); - if let Route::Radarr(active_radarr_block) = *app.get_current_route() { + if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() { match active_radarr_block { ActiveRadarrBlock::Movies => draw_library(f, app, content_rect), ActiveRadarrBlock::SearchMovie => { @@ -71,13 +71,25 @@ pub(super) fn draw_radarr_ui(f: &mut Frame<'_, B>, app: &mut App, ar _ if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) => { draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info_popup) } - _ if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over( - f, - app, - content_rect, - draw_library, - draw_add_movie_search_popup, - ), + _ if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) => { + if let Route::Radarr(_, Some(_)) = app.get_current_route() { + draw_large_popup_over( + f, + app, + content_rect, + draw_collections, + draw_add_movie_search_popup, + ) + } else { + draw_large_popup_over( + f, + app, + content_rect, + draw_library, + draw_add_movie_search_popup, + ) + } + } _ if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over( f, app, @@ -269,7 +281,7 @@ fn draw_search_box(f: &mut Frame<'_, B>, app: &mut App, area: Rect) vertical_chunks_with_margin(vec![Constraint::Length(3), Constraint::Min(0)], area, 1); if !app.data.radarr_data.is_searching { let error_msg = match app.get_current_route() { - Route::Radarr(active_radarr_block) => match active_radarr_block { + Route::Radarr(active_radarr_block, _) => match active_radarr_block { ActiveRadarrBlock::SearchMovie => "Movie not found!", ActiveRadarrBlock::SearchCollection => "Collection not found!", _ => "", @@ -284,7 +296,7 @@ fn draw_search_box(f: &mut Frame<'_, B>, app: &mut App, area: Rect) f.render_widget(input, chunks[0]); } else { let (block_title, block_content) = match app.get_current_route() { - Route::Radarr(active_radarr_block) => match active_radarr_block { + Route::Radarr(active_radarr_block, _) => match active_radarr_block { _ if SEARCH_BLOCKS.contains(active_radarr_block) => { ("Search", app.data.radarr_data.search.as_str()) } @@ -307,7 +319,7 @@ fn draw_filter_box(f: &mut Frame<'_, B>, app: &mut App, area: Rect) vertical_chunks_with_margin(vec![Constraint::Length(3), Constraint::Min(0)], area, 1); if !app.data.radarr_data.is_filtering { let error_msg = match app.get_current_route() { - Route::Radarr(active_radarr_block) => match active_radarr_block { + Route::Radarr(active_radarr_block, _) => match active_radarr_block { ActiveRadarrBlock::FilterMovies => "No movies found matching filter!", ActiveRadarrBlock::FilterCollections => "No collections found matching filter!", _ => "", @@ -322,7 +334,7 @@ fn draw_filter_box(f: &mut Frame<'_, B>, app: &mut App, area: Rect) f.render_widget(input, chunks[0]); } else { let (block_title, block_content) = match app.get_current_route() { - Route::Radarr(active_radarr_block) => match active_radarr_block { + Route::Radarr(active_radarr_block, _) => match active_radarr_block { _ if FILTER_BLOCKS.contains(active_radarr_block) => { ("Filter", app.data.radarr_data.filter.as_str()) } @@ -418,7 +430,7 @@ fn draw_downloads(f: &mut Frame<'_, B>, app: &mut App, area: Rect) { let path = output_path.clone().unwrap_or_default(); path.scroll_or_reset( - get_width_with_margin(area), + get_width_from_percentage(area, 18), current_selection == *download_record, ); diff --git a/src/ui/radarr_ui/movie_details_ui.rs b/src/ui/radarr_ui/movie_details_ui.rs index cd6c2b8..717325b 100644 --- a/src/ui/radarr_ui/movie_details_ui.rs +++ b/src/ui/radarr_ui/movie_details_ui.rs @@ -12,7 +12,7 @@ use crate::app::App; use crate::models::radarr_models::{Credit, MovieHistoryItem, Release, ReleaseField}; use crate::models::Route; use crate::ui::utils::{ - borderless_block, get_width_with_margin, layout_block_bottom_border, layout_block_top_border, + borderless_block, get_width_from_percentage, layout_block_bottom_border, layout_block_top_border, spans_info_default, style_bold, style_default, style_failure, style_primary, style_success, style_warning, vertical_chunks, }; @@ -25,7 +25,7 @@ use crate::utils::convert_to_gb; pub(super) fn draw_movie_info_popup(f: &mut Frame<'_, B>, app: &mut App, area: Rect) { let (content_area, _) = draw_tabs(f, area, "Movie Info", &app.data.radarr_data.movie_info_tabs); - if let Route::Radarr(active_radarr_block) = app.get_current_route() { + if let Route::Radarr(active_radarr_block, _) = app.get_current_route() { match active_radarr_block { ActiveRadarrBlock::AutomaticallySearchMoviePrompt => draw_prompt_popup_over( f, @@ -68,7 +68,7 @@ pub(super) fn draw_movie_info_popup(f: &mut Frame<'_, B>, app: &mut } fn draw_movie_info(f: &mut Frame<'_, B>, app: &mut App, area: Rect) { - if let Route::Radarr(active_radarr_block) = + if let Route::Radarr(active_radarr_block, _) = app.data.radarr_data.movie_info_tabs.get_active_route() { match active_radarr_block { @@ -258,14 +258,10 @@ fn draw_movie_history(f: &mut Frame<'_, B>, app: &mut App, content_a event_type, } = movie_history_item; - if current_selection == *movie_history_item - && movie_history_item.source_title.text.len() - > (content_area.width as f64 * 0.34) as usize - { - source_title.scroll_text(); - } else { - source_title.reset_offset(); - } + movie_history_item.source_title.scroll_or_reset( + get_width_from_percentage(content_area, 34), + current_selection == *movie_history_item, + ); Row::new(vec![ Cell::from(source_title.to_string()), @@ -432,7 +428,7 @@ fn draw_movie_releases(f: &mut Frame<'_, B>, app: &mut App, content_ } = release; let age = format!("{} days", age.as_u64().unwrap_or(0)); title.scroll_or_reset( - get_width_with_margin(content_area), + get_width_from_percentage(content_area, 30), current_selection == *release && current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(), ); diff --git a/src/ui/utils.rs b/src/ui/utils.rs index ac1c33a..ff83564 100644 --- a/src/ui/utils.rs +++ b/src/ui/utils.rs @@ -241,8 +241,8 @@ pub fn show_cursor(f: &mut Frame<'_, B>, area: Rect, string: &str) { f.set_cursor(area.x + string.len() as u16 + 1, area.y + 1); } -pub fn get_width_with_margin(area: Rect) -> usize { - (area.width as f32 * 0.30) as usize +pub fn get_width_from_percentage(area: Rect, percentage: u16) -> usize { + (area.width as f64 * (percentage as f64 / 100.0)) as usize } #[cfg(test)] @@ -254,7 +254,7 @@ mod test { use tui::widgets::{Block, BorderType, Borders}; use crate::ui::utils::{ - borderless_block, centered_rect, get_width_with_margin, horizontal_chunks, + borderless_block, centered_rect, get_width_from_percentage, horizontal_chunks, horizontal_chunks_with_margin, layout_block, layout_block_bottom_border, layout_block_top_border, layout_block_top_border_with_title, layout_block_with_title, layout_with_constraints, logo_block, spans_info_default, spans_info_primary, @@ -625,14 +625,17 @@ mod test { } #[test] - fn test_get_width_with_margin() { + fn test_get_width_from_percentage() { assert_eq!( - get_width_with_margin(Rect { - x: 0, - y: 0, - width: 100, - height: 10 - }), + get_width_from_percentage( + Rect { + x: 0, + y: 0, + width: 100, + height: 10 + }, + 30 + ), 30 ); }