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:
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user