Cleaned up active radarr block delegation to not have giant match arms and instead just check an array, and fixed a UI bug that shows an error message if a movie is already in a user's library.

This commit is contained in:
2023-08-08 10:50:05 -06:00
parent fa381cea01
commit dcc251ae01
9 changed files with 186 additions and 85 deletions
+26
View File
@@ -176,6 +176,7 @@ impl Default for RadarrData {
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ActiveRadarrBlock { pub enum ActiveRadarrBlock {
AddMovieAlreadyInLibrary,
AddMovieSearchInput, AddMovieSearchInput,
AddMovieSearchResults, AddMovieSearchResults,
AddMoviePrompt, AddMoviePrompt,
@@ -208,6 +209,31 @@ pub enum ActiveRadarrBlock {
ViewMovieOverview, ViewMovieOverview,
} }
pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 7] = [
ActiveRadarrBlock::AddMovieSearchInput,
ActiveRadarrBlock::AddMovieSearchResults,
ActiveRadarrBlock::AddMoviePrompt,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
ActiveRadarrBlock::AddMovieSelectMonitor,
ActiveRadarrBlock::AddMovieSelectQualityProfile,
ActiveRadarrBlock::AddMovieAlreadyInLibrary,
];
pub const MOVIE_DETAILS_BLOCKS: [ActiveRadarrBlock; 9] = [
ActiveRadarrBlock::MovieDetails,
ActiveRadarrBlock::MovieHistory,
ActiveRadarrBlock::FileInfo,
ActiveRadarrBlock::Cast,
ActiveRadarrBlock::Crew,
ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
ActiveRadarrBlock::RefreshAndScanPrompt,
ActiveRadarrBlock::ManualSearch,
ActiveRadarrBlock::ManualSearchConfirmPrompt,
];
pub const COLLECTION_DETAILS_BLOCKS: [ActiveRadarrBlock; 2] = [
ActiveRadarrBlock::CollectionDetails,
ActiveRadarrBlock::ViewMovieOverview,
];
impl ActiveRadarrBlock { impl ActiveRadarrBlock {
pub fn next_add_prompt_block(&self) -> Self { pub fn next_add_prompt_block(&self) -> Self {
match self { match self {
@@ -184,6 +184,27 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
.items .items
.is_empty() => .is_empty() =>
{ {
let tmdb_id = self
.app
.data
.radarr_data
.add_searched_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::AddMovieAlreadyInLibrary.into());
} else {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); .push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
@@ -215,6 +236,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
.add_movie_quality_profile_list .add_movie_quality_profile_list
.set_items(quality_profile_names); .set_items(quality_profile_names);
} }
}
ActiveRadarrBlock::AddMoviePrompt => match self.app.data.radarr_data.selected_block { ActiveRadarrBlock::AddMoviePrompt => match self.app.data.radarr_data.selected_block {
ActiveRadarrBlock::AddMovieConfirmPrompt => { ActiveRadarrBlock::AddMovieConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
@@ -261,7 +283,8 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
} }
ActiveRadarrBlock::AddMovieSelectMonitor ActiveRadarrBlock::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability | ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectQualityProfile => self.app.pop_navigation_stack(), | ActiveRadarrBlock::AddMovieSelectQualityProfile
| ActiveRadarrBlock::AddMovieAlreadyInLibrary => self.app.pop_navigation_stack(),
_ => (), _ => (),
} }
} }
@@ -419,6 +442,7 @@ mod tests {
use rstest::rstest; use rstest::rstest;
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::models::radarr_models::Movie;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use super::*; use super::*;
@@ -506,6 +530,33 @@ 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()]);
app
.data
.radarr_data
.movies
.set_items(vec![Movie::default()]);
AddMovieHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieAlreadyInLibrary.into()
);
}
#[test] #[test]
fn test_add_movie_prompt_prompt_decline() { fn test_add_movie_prompt_prompt_decline() {
let mut app = App::default(); let mut app = App::default();
@@ -633,6 +684,26 @@ mod tests {
assert!(app.should_ignore_quit_key); assert!(app.should_ignore_quit_key);
} }
#[test]
fn test_add_movie_already_in_library_esc() {
let mut app = App::default();
app.data.radarr_data = create_test_radarr_data();
app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into());
app.push_navigation_stack(ActiveRadarrBlock::AddMovieAlreadyInLibrary.into());
AddMovieHandler::with(
&ESC_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieAlreadyInLibrary,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into()
);
}
#[test] #[test]
fn test_add_movie_prompt_esc() { fn test_add_movie_prompt_esc() {
let mut app = App::default(); let mut app = App::default();
+8 -18
View File
@@ -1,5 +1,7 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::ActiveRadarrBlock; use crate::app::radarr::{
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, MOVIE_DETAILS_BLOCKS,
};
use crate::handlers::radarr_handlers::add_movie_handler::AddMovieHandler; use crate::handlers::radarr_handlers::add_movie_handler::AddMovieHandler;
use crate::handlers::radarr_handlers::collection_details_handler::CollectionDetailsHandler; use crate::handlers::radarr_handlers::collection_details_handler::CollectionDetailsHandler;
use crate::handlers::radarr_handlers::movie_details_handler::MovieDetailsHandler; use crate::handlers::radarr_handlers::movie_details_handler::MovieDetailsHandler;
@@ -22,26 +24,13 @@ pub(super) struct RadarrHandler<'a> {
impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> { impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
fn handle(&mut self) { fn handle(&mut self) {
match self.active_radarr_block { match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails _ if MOVIE_DETAILS_BLOCKS.contains(self.active_radarr_block) => {
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::RefreshAndScanPrompt
| ActiveRadarrBlock::ManualSearch
| ActiveRadarrBlock::ManualSearchConfirmPrompt => {
MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block).handle() MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block).handle()
} }
ActiveRadarrBlock::CollectionDetails | ActiveRadarrBlock::ViewMovieOverview => { _ 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).handle()
} }
ActiveRadarrBlock::AddMovieSearchInput _ if ADD_MOVIE_BLOCKS.contains(self.active_radarr_block) => {
| ActiveRadarrBlock::AddMovieSearchResults
| ActiveRadarrBlock::AddMoviePrompt
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectQualityProfile => {
AddMovieHandler::with(self.key, self.app, self.active_radarr_block).handle() AddMovieHandler::with(self.key, self.app, self.active_radarr_block).handle()
} }
_ => self.handle_key_event(), _ => self.handle_key_event(),
@@ -1263,7 +1252,8 @@ mod tests {
ActiveRadarrBlock::AddMoviePrompt, ActiveRadarrBlock::AddMoviePrompt,
ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMonitor,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
ActiveRadarrBlock::AddMovieSelectQualityProfile ActiveRadarrBlock::AddMovieSelectQualityProfile,
ActiveRadarrBlock::AddMovieAlreadyInLibrary
)] )]
active_radarr_block: ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
) { ) {
+2
View File
@@ -52,6 +52,8 @@ pub struct Movie {
#[derivative(Default(value = "Number::from(0)"))] #[derivative(Default(value = "Number::from(0)"))]
pub runtime: Number, pub runtime: Number,
#[derivative(Default(value = "Number::from(0)"))] #[derivative(Default(value = "Number::from(0)"))]
pub tmdb_id: Number,
#[derivative(Default(value = "Number::from(0)"))]
pub quality_profile_id: Number, pub quality_profile_id: Number,
pub certification: Option<String>, pub certification: Option<String>,
pub ratings: RatingsList, pub ratings: RatingsList,
+3
View File
@@ -895,6 +895,7 @@ mod test {
const MOVIE_JSON: &str = r#"{ const MOVIE_JSON: &str = r#"{
"id": 1, "id": 1,
"title": "Test", "title": "Test",
"tmdbId": 1234,
"originalLanguage": { "originalLanguage": {
"name": "English" "name": "English"
}, },
@@ -1462,6 +1463,7 @@ mod test {
"monitored": true, "monitored": true,
"hasFile": false, "hasFile": false,
"runtime": 120, "runtime": 120,
"tmdbId": 1234,
"qualityProfileId": 2222, "qualityProfileId": 2222,
"ratings": {} "ratings": {}
}); });
@@ -2161,6 +2163,7 @@ mod test {
monitored: true, monitored: true,
has_file: true, has_file: true,
runtime: Number::from(120), runtime: Number::from(120),
tmdb_id: Number::from(1234),
quality_profile_id: Number::from(2222), quality_profile_id: Number::from(2222),
certification: Some("R".to_owned()), certification: Some("R".to_owned()),
ratings: ratings_list(), ratings: ratings_list(),
+25
View File
@@ -1,5 +1,6 @@
use tui::backend::Backend; use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Rect}; use tui::layout::{Alignment, Constraint, Rect};
use tui::style::Modifier;
use tui::text::{Span, Spans, Text}; use tui::text::{Span, Spans, Text};
use tui::widgets::Paragraph; use tui::widgets::Paragraph;
use tui::widgets::Row; use tui::widgets::Row;
@@ -291,6 +292,30 @@ pub fn loading<B: Backend>(f: &mut Frame<'_, B>, block: Block<'_>, area: Rect, i
} }
} }
pub fn draw_error_popup_over<B: Backend>(
f: &mut Frame<'_, B>,
app: &mut App,
area: Rect,
message: &str,
background_fn: fn(&mut Frame<'_, B>, &mut App, Rect),
) {
background_fn(f, app, area);
draw_error_popup(f, message);
}
pub fn draw_error_popup<B: Backend>(f: &mut Frame<'_, B>, message: &str) {
let prompt_area = centered_rect(25, 8, f.size());
f.render_widget(Clear, prompt_area);
let error_message = Paragraph::new(Text::from(message))
.block(title_block_centered("Error").style(style_failure()))
.style(style_failure().add_modifier(Modifier::BOLD))
.wrap(Wrap { trim: false })
.alignment(Alignment::Center);
f.render_widget(error_message, prompt_area);
}
pub fn draw_prompt_box<B: Backend>( pub fn draw_prompt_box<B: Backend>(
f: &mut Frame<'_, B>, f: &mut Frame<'_, B>,
prompt_area: Rect, prompt_area: Rect,
+14 -9
View File
@@ -8,13 +8,13 @@ use crate::app::radarr::ActiveRadarrBlock;
use crate::models::radarr_models::AddMovieSearchResult; use crate::models::radarr_models::AddMovieSearchResult;
use crate::models::Route; use crate::models::Route;
use crate::ui::utils::{ use crate::ui::utils::{
borderless_block, centered_rect, get_width_with_margin, horizontal_chunks, layout_block, borderless_block, get_width_with_margin, horizontal_chunks, layout_block,
layout_error_paragraph, layout_paragraph_borderless, show_cursor, style_default, style_help, layout_paragraph_borderless, show_cursor, style_default, style_help, style_primary,
style_primary, title_block_centered, vertical_chunks_with_margin, title_block_centered, vertical_chunks_with_margin,
}; };
use crate::ui::{ use crate::ui::{
draw_button, draw_drop_down_list, draw_drop_down_menu_button, draw_drop_down_popup, draw_button, draw_drop_down_list, draw_drop_down_menu_button, draw_drop_down_popup,
draw_medium_popup_over, draw_table, TableProps, draw_error_popup, draw_error_popup_over, draw_medium_popup_over, draw_table, TableProps,
}; };
use crate::utils::convert_runtime; use crate::utils::convert_runtime;
use crate::App; use crate::App;
@@ -35,6 +35,13 @@ pub(super) fn draw_add_movie_search_popup<B: Backend>(
| ActiveRadarrBlock::AddMovieSelectQualityProfile => { | ActiveRadarrBlock::AddMovieSelectQualityProfile => {
draw_medium_popup_over(f, app, area, draw_add_movie_search, draw_confirmation_popup); draw_medium_popup_over(f, app, area, draw_add_movie_search, draw_confirmation_popup);
} }
ActiveRadarrBlock::AddMovieAlreadyInLibrary => draw_error_popup_over(
f,
app,
area,
"This film is already in your library",
draw_add_movie_search,
),
_ => (), _ => (),
} }
} }
@@ -83,7 +90,8 @@ fn draw_add_movie_search<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area:
| ActiveRadarrBlock::AddMoviePrompt | ActiveRadarrBlock::AddMoviePrompt
| ActiveRadarrBlock::AddMovieSelectMonitor | ActiveRadarrBlock::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability | ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectQualityProfile => { | ActiveRadarrBlock::AddMovieSelectQualityProfile
| ActiveRadarrBlock::AddMovieAlreadyInLibrary => {
let mut help_text = Text::from("<enter> details | <esc> edit search"); let mut help_text = Text::from("<enter> details | <esc> edit search");
help_text.patch_style(style_help()); help_text.patch_style(style_help());
let help_paragraph = Paragraph::new(help_text) let help_paragraph = Paragraph::new(help_text)
@@ -96,10 +104,7 @@ fn draw_add_movie_search<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area:
&& !app.is_routing && !app.is_routing
{ {
f.render_widget(layout_block(), chunks[1]); f.render_widget(layout_block(), chunks[1]);
f.render_widget( draw_error_popup(f, "No movies found matching your query!");
layout_error_paragraph("No movies found matching your query!"),
centered_rect(30, 10, chunks[1]),
);
} else { } else {
draw_table( draw_table(
f, f,
+7 -20
View File
@@ -9,7 +9,9 @@ use tui::text::Text;
use tui::widgets::{Cell, Paragraph, Row}; use tui::widgets::{Cell, Paragraph, Row};
use tui::Frame; use tui::Frame;
use crate::app::radarr::{ActiveRadarrBlock, RadarrData}; use crate::app::radarr::{
ActiveRadarrBlock, RadarrData, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, MOVIE_DETAILS_BLOCKS,
};
use crate::app::App; use crate::app::App;
use crate::logos::RADARR_LOGO; use crate::logos::RADARR_LOGO;
use crate::models::radarr_models::{DiskSpace, DownloadRecord, Movie}; use crate::models::radarr_models::{DiskSpace, DownloadRecord, Movie};
@@ -55,38 +57,23 @@ pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, ar
} }
ActiveRadarrBlock::Downloads => draw_downloads(f, app, content_rect), ActiveRadarrBlock::Downloads => draw_downloads(f, app, content_rect),
ActiveRadarrBlock::Collections => draw_collections(f, app, content_rect), ActiveRadarrBlock::Collections => draw_collections(f, app, content_rect),
ActiveRadarrBlock::MovieDetails _ if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) => {
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::RefreshAndScanPrompt
| ActiveRadarrBlock::ManualSearch
| ActiveRadarrBlock::ManualSearchConfirmPrompt => {
draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info_popup) draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info_popup)
} }
ActiveRadarrBlock::AddMovieSearchInput _ if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over(
| ActiveRadarrBlock::AddMovieSearchResults
| ActiveRadarrBlock::AddMoviePrompt
| ActiveRadarrBlock::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectQualityProfile => draw_large_popup_over(
f, f,
app, app,
content_rect, content_rect,
draw_library, draw_library,
draw_add_movie_search_popup, draw_add_movie_search_popup,
), ),
ActiveRadarrBlock::CollectionDetails | ActiveRadarrBlock::ViewMovieOverview => { _ if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over(
draw_large_popup_over(
f, f,
app, app,
content_rect, content_rect,
draw_collections, draw_collections,
draw_collection_details_popup, draw_collection_details_popup,
) ),
}
ActiveRadarrBlock::DeleteMoviePrompt => { ActiveRadarrBlock::DeleteMoviePrompt => {
draw_prompt_popup_over(f, app, content_rect, draw_library, draw_delete_movie_prompt) draw_prompt_popup_over(f, app, content_rect, draw_library, draw_delete_movie_prompt)
} }
-8
View File
@@ -93,14 +93,6 @@ pub fn layout_paragraph_borderless(string: &str) -> Paragraph {
.alignment(Alignment::Center) .alignment(Alignment::Center)
} }
pub fn layout_error_paragraph(string: &str) -> Paragraph {
Paragraph::new(Text::from(string))
.block(layout_block_with_title(title_style("Error")))
.style(style_failure().add_modifier(Modifier::BOLD))
.wrap(Wrap { trim: false })
.alignment(Alignment::Center)
}
pub fn borderless_block<'a>() -> Block<'a> { pub fn borderless_block<'a>() -> Block<'a> {
Block::default() Block::default()
} }