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)]
pub enum ActiveRadarrBlock {
AddMovieAlreadyInLibrary,
AddMovieSearchInput,
AddMovieSearchResults,
AddMoviePrompt,
@@ -208,6 +209,31 @@ pub enum ActiveRadarrBlock {
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 {
pub fn next_add_prompt_block(&self) -> Self {
match self {
@@ -184,36 +184,58 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
.items
.is_empty() =>
{
self
.app
.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
self
let tmdb_id = self
.app
.data
.radarr_data
.add_movie_monitor_list
.set_items(Vec::from_iter(Monitor::iter()));
self
.add_searched_movies
.current_selection()
.tmdb_id
.clone();
if self
.app
.data
.radarr_data
.add_movie_minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
let mut quality_profile_names: Vec<String> = 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);
.movies
.items
.iter()
.any(|movie| movie.tmdb_id == tmdb_id)
{
self
.app
.push_navigation_stack(ActiveRadarrBlock::AddMovieAlreadyInLibrary.into());
} else {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
self
.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<String> = 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);
}
}
ActiveRadarrBlock::AddMoviePrompt => match self.app.data.radarr_data.selected_block {
ActiveRadarrBlock::AddMovieConfirmPrompt => {
@@ -261,7 +283,8 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
}
ActiveRadarrBlock::AddMovieSelectMonitor
| 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 crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::models::radarr_models::Movie;
use crate::network::radarr_network::RadarrEvent;
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]
fn test_add_movie_prompt_prompt_decline() {
let mut app = App::default();
@@ -633,6 +684,26 @@ mod tests {
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]
fn test_add_movie_prompt_esc() {
let mut app = App::default();
+8 -18
View File
@@ -1,5 +1,7 @@
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::collection_details_handler::CollectionDetailsHandler;
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> {
fn handle(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::RefreshAndScanPrompt
| ActiveRadarrBlock::ManualSearch
| ActiveRadarrBlock::ManualSearchConfirmPrompt => {
_ if MOVIE_DETAILS_BLOCKS.contains(self.active_radarr_block) => {
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()
}
ActiveRadarrBlock::AddMovieSearchInput
| ActiveRadarrBlock::AddMovieSearchResults
| ActiveRadarrBlock::AddMoviePrompt
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectQualityProfile => {
_ if ADD_MOVIE_BLOCKS.contains(self.active_radarr_block) => {
AddMovieHandler::with(self.key, self.app, self.active_radarr_block).handle()
}
_ => self.handle_key_event(),
@@ -1263,7 +1252,8 @@ mod tests {
ActiveRadarrBlock::AddMoviePrompt,
ActiveRadarrBlock::AddMovieSelectMonitor,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
ActiveRadarrBlock::AddMovieSelectQualityProfile
ActiveRadarrBlock::AddMovieSelectQualityProfile,
ActiveRadarrBlock::AddMovieAlreadyInLibrary
)]
active_radarr_block: ActiveRadarrBlock,
) {
+2
View File
@@ -52,6 +52,8 @@ pub struct Movie {
#[derivative(Default(value = "Number::from(0)"))]
pub runtime: Number,
#[derivative(Default(value = "Number::from(0)"))]
pub tmdb_id: Number,
#[derivative(Default(value = "Number::from(0)"))]
pub quality_profile_id: Number,
pub certification: Option<String>,
pub ratings: RatingsList,
+3
View File
@@ -895,6 +895,7 @@ mod test {
const MOVIE_JSON: &str = r#"{
"id": 1,
"title": "Test",
"tmdbId": 1234,
"originalLanguage": {
"name": "English"
},
@@ -1462,6 +1463,7 @@ mod test {
"monitored": true,
"hasFile": false,
"runtime": 120,
"tmdbId": 1234,
"qualityProfileId": 2222,
"ratings": {}
});
@@ -2161,6 +2163,7 @@ mod test {
monitored: true,
has_file: true,
runtime: Number::from(120),
tmdb_id: Number::from(1234),
quality_profile_id: Number::from(2222),
certification: Some("R".to_owned()),
ratings: ratings_list(),
+25
View File
@@ -1,5 +1,6 @@
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Rect};
use tui::style::Modifier;
use tui::text::{Span, Spans, Text};
use tui::widgets::Paragraph;
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>(
f: &mut Frame<'_, B>,
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::Route;
use crate::ui::utils::{
borderless_block, centered_rect, get_width_with_margin, horizontal_chunks, layout_block,
layout_error_paragraph, layout_paragraph_borderless, show_cursor, style_default, style_help,
style_primary, title_block_centered, vertical_chunks_with_margin,
borderless_block, get_width_with_margin, horizontal_chunks, layout_block,
layout_paragraph_borderless, show_cursor, style_default, style_help, style_primary,
title_block_centered, vertical_chunks_with_margin,
};
use crate::ui::{
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::App;
@@ -35,6 +35,13 @@ pub(super) fn draw_add_movie_search_popup<B: Backend>(
| ActiveRadarrBlock::AddMovieSelectQualityProfile => {
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::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectQualityProfile => {
| ActiveRadarrBlock::AddMovieSelectQualityProfile
| ActiveRadarrBlock::AddMovieAlreadyInLibrary => {
let mut help_text = Text::from("<enter> details | <esc> edit search");
help_text.patch_style(style_help());
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
{
f.render_widget(layout_block(), chunks[1]);
f.render_widget(
layout_error_paragraph("No movies found matching your query!"),
centered_rect(30, 10, chunks[1]),
);
draw_error_popup(f, "No movies found matching your query!");
} else {
draw_table(
f,
+12 -25
View File
@@ -9,7 +9,9 @@ use tui::text::Text;
use tui::widgets::{Cell, Paragraph, Row};
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::logos::RADARR_LOGO;
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::Collections => draw_collections(f, app, content_rect),
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::RefreshAndScanPrompt
| ActiveRadarrBlock::ManualSearch
| ActiveRadarrBlock::ManualSearchConfirmPrompt => {
_ if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) => {
draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info_popup)
}
ActiveRadarrBlock::AddMovieSearchInput
| ActiveRadarrBlock::AddMovieSearchResults
| ActiveRadarrBlock::AddMoviePrompt
| ActiveRadarrBlock::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectQualityProfile => draw_large_popup_over(
_ if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over(
f,
app,
content_rect,
draw_library,
draw_add_movie_search_popup,
),
ActiveRadarrBlock::CollectionDetails | ActiveRadarrBlock::ViewMovieOverview => {
draw_large_popup_over(
f,
app,
content_rect,
draw_collections,
draw_collection_details_popup,
)
}
_ if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over(
f,
app,
content_rect,
draw_collections,
draw_collection_details_popup,
),
ActiveRadarrBlock::DeleteMoviePrompt => {
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)
}
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> {
Block::default()
}