From b748d27a06dde37be390ae454aef8d206793400e Mon Sep 17 00:00:00 2001 From: Dark-Alex-17 Date: Tue, 8 Aug 2023 10:50:04 -0600 Subject: [PATCH] Partial implementation for additional add-movie details. Need to implement selection menus now but that's it! --- Cargo.toml | 2 +- src/app/radarr.rs | 56 +++++- .../radarr_handlers/add_movie_handler.rs | 182 ++++++++++++++++-- src/handlers/radarr_handlers/mod.rs | 5 +- src/models/mod.rs | 83 +++++++- src/models/radarr_models.rs | 82 ++++++++ src/network/radarr_network.rs | 104 +++++----- src/ui/mod.rs | 27 +++ src/ui/radarr_ui/add_movie_ui.rs | 151 ++++++++++++--- src/ui/radarr_ui/mod.rs | 13 +- 10 files changed, 602 insertions(+), 103 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7abe6db..37c4f68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ reqwest = { version = "0.11.13", features = ["json"] } serde_yaml = "0.9.16" serde_json = "1.0.91" serde = { version = "1.0", features = ["derive"] } -strum = { version = "0.24.1", features = ["derive"] } +strum = { version = "0.24.1", features = ["derive", "strum_macros"] } tokio = { version = "1.24.1", features = ["full"] } tui = "0.19.0" urlencoding = "2.1.2" diff --git a/src/app/radarr.rs b/src/app/radarr.rs index b3b341c..45d5901 100644 --- a/src/app/radarr.rs +++ b/src/app/radarr.rs @@ -2,14 +2,13 @@ use std::collections::HashMap; use std::time::Duration; use chrono::{DateTime, Utc}; -use strum::EnumIter; use crate::app::{App, Route}; use crate::models::radarr_models::{ - AddMovieSearchResult, Collection, CollectionMovie, Credit, DiskSpace, DownloadRecord, Movie, - MovieHistoryItem, RootFolder, + AddMovieSearchResult, Collection, CollectionMovie, Credit, DiskSpace, DownloadRecord, + MinimumAvailability, Monitor, Movie, MovieHistoryItem, RootFolder, }; -use crate::models::{ScrollableText, StatefulTable, TabRoute, TabState}; +use crate::models::{ScrollableText, StatefulList, StatefulTable, TabRoute, TabState}; use crate::network::radarr_network::RadarrEvent; pub struct RadarrData { @@ -20,6 +19,10 @@ pub struct RadarrData { pub movies: StatefulTable, pub filtered_movies: StatefulTable, pub add_searched_movies: StatefulTable, + pub add_movie_monitor_list: StatefulList, + pub add_movie_minimum_availability_list: StatefulList, + pub add_movie_quality_profile_list: StatefulList, + pub selected_block: ActiveRadarrBlock, pub downloads: StatefulTable, pub quality_profile_map: HashMap, pub movie_details: ScrollableText, @@ -66,6 +69,12 @@ impl RadarrData { self.movie_info_tabs.index = 0; } + pub fn reset_add_movie_selections(&mut self) { + self.add_movie_monitor_list = StatefulList::default(); + self.add_movie_minimum_availability_list = StatefulList::default(); + self.add_movie_quality_profile_list = StatefulList::default(); + } + pub fn reset_main_tab_index(&mut self) { self.main_tabs.index = 0; } @@ -80,6 +89,10 @@ impl Default for RadarrData { start_time: DateTime::default(), movies: StatefulTable::default(), add_searched_movies: StatefulTable::default(), + add_movie_monitor_list: StatefulList::default(), + add_movie_minimum_availability_list: StatefulList::default(), + add_movie_quality_profile_list: StatefulList::default(), + selected_block: ActiveRadarrBlock::AddMovieSelectMonitor, filtered_movies: StatefulTable::default(), downloads: StatefulTable::default(), quality_profile_map: HashMap::default(), @@ -148,11 +161,15 @@ impl Default for RadarrData { } } -#[derive(Clone, PartialEq, Eq, Debug, EnumIter)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum ActiveRadarrBlock { AddMovieSearchInput, AddMovieSearchResults, AddMoviePrompt, + AddMovieSelectMinimumAvailability, + AddMovieSelectQualityProfile, + AddMovieSelectMonitor, + AddMovieConfirmPrompt, Calendar, Collections, CollectionDetails, @@ -172,6 +189,35 @@ pub enum ActiveRadarrBlock { ViewMovieOverview, } +impl ActiveRadarrBlock { + pub fn next_add_prompt_block(&self) -> Self { + match self { + ActiveRadarrBlock::AddMovieSelectMonitor => { + ActiveRadarrBlock::AddMovieSelectMinimumAvailability + } + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => { + ActiveRadarrBlock::AddMovieSelectQualityProfile + } + ActiveRadarrBlock::AddMovieSelectQualityProfile => ActiveRadarrBlock::AddMovieConfirmPrompt, + _ => ActiveRadarrBlock::AddMovieSelectMonitor, + } + } + + pub fn previous_add_prompt_block(&self) -> Self { + match self { + ActiveRadarrBlock::AddMovieSelectMonitor => ActiveRadarrBlock::AddMovieConfirmPrompt, + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => { + ActiveRadarrBlock::AddMovieSelectMonitor + } + ActiveRadarrBlock::AddMovieSelectQualityProfile => { + ActiveRadarrBlock::AddMovieSelectMinimumAvailability + } + ActiveRadarrBlock::AddMovieConfirmPrompt => ActiveRadarrBlock::AddMovieSelectQualityProfile, + _ => ActiveRadarrBlock::AddMovieSelectMonitor, + } + } +} + impl From for Route { fn from(active_radarr_block: ActiveRadarrBlock) -> Route { Route::Radarr(active_radarr_block) diff --git a/src/handlers/radarr_handlers/add_movie_handler.rs b/src/handlers/radarr_handlers/add_movie_handler.rs index 7c346f2..75769a3 100644 --- a/src/handlers/radarr_handlers/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/add_movie_handler.rs @@ -1,7 +1,10 @@ +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::{Scrollable, StatefulTable}; +use crate::models::radarr_models::{MinimumAvailability, Monitor}; +use crate::models::{Route, Scrollable, StatefulTable}; use crate::network::radarr_network::RadarrEvent; use crate::{App, Key}; @@ -29,44 +32,138 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { } fn handle_scroll_up(&mut self) { - if self.active_radarr_block == &ActiveRadarrBlock::AddMovieSearchResults { - self.app.data.radarr_data.add_searched_movies.scroll_up() + match self.active_radarr_block { + ActiveRadarrBlock::AddMovieSearchResults => { + self.app.data.radarr_data.add_searched_movies.scroll_up() + } + ActiveRadarrBlock::AddMovieSelectMonitor => { + self.app.data.radarr_data.add_movie_monitor_list.scroll_up() + } + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self + .app + .data + .radarr_data + .add_movie_minimum_availability_list + .scroll_up(), + ActiveRadarrBlock::AddMovieSelectQualityProfile => self + .app + .data + .radarr_data + .add_movie_quality_profile_list + .scroll_up(), + ActiveRadarrBlock::AddMoviePrompt => { + self.app.data.radarr_data.selected_block = self + .app + .data + .radarr_data + .selected_block + .clone() + .previous_add_prompt_block() + } + _ => (), } } fn handle_scroll_down(&mut self) { - if self.active_radarr_block == &ActiveRadarrBlock::AddMovieSearchResults { - self.app.data.radarr_data.add_searched_movies.scroll_down() + match self.active_radarr_block { + ActiveRadarrBlock::AddMovieSearchResults => { + self.app.data.radarr_data.add_searched_movies.scroll_down() + } + ActiveRadarrBlock::AddMovieSelectMonitor => self + .app + .data + .radarr_data + .add_movie_monitor_list + .scroll_down(), + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self + .app + .data + .radarr_data + .add_movie_minimum_availability_list + .scroll_down(), + ActiveRadarrBlock::AddMovieSelectQualityProfile => self + .app + .data + .radarr_data + .add_movie_quality_profile_list + .scroll_down(), + ActiveRadarrBlock::AddMoviePrompt => { + self.app.data.radarr_data.selected_block = self + .app + .data + .radarr_data + .selected_block + .next_add_prompt_block() + } + _ => (), } } fn handle_home(&mut self) { - if self.active_radarr_block == &ActiveRadarrBlock::AddMovieSearchResults { - self + match self.active_radarr_block { + ActiveRadarrBlock::AddMovieSearchResults => self .app .data .radarr_data .add_searched_movies - .scroll_to_top() + .scroll_to_top(), + ActiveRadarrBlock::AddMovieSelectMonitor => self + .app + .data + .radarr_data + .add_movie_monitor_list + .scroll_to_top(), + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self + .app + .data + .radarr_data + .add_movie_minimum_availability_list + .scroll_to_top(), + ActiveRadarrBlock::AddMovieSelectQualityProfile => self + .app + .data + .radarr_data + .add_movie_quality_profile_list + .scroll_to_top(), + _ => (), } } fn handle_end(&mut self) { - if self.active_radarr_block == &ActiveRadarrBlock::AddMovieSearchResults { - self + match self.active_radarr_block { + ActiveRadarrBlock::AddMovieSearchResults => self .app .data .radarr_data .add_searched_movies - .scroll_to_bottom() + .scroll_to_bottom(), + ActiveRadarrBlock::AddMovieSelectMonitor => self + .app + .data + .radarr_data + .add_movie_monitor_list + .scroll_to_bottom(), + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self + .app + .data + .radarr_data + .add_movie_minimum_availability_list + .scroll_to_bottom(), + ActiveRadarrBlock::AddMovieSelectQualityProfile => self + .app + .data + .radarr_data + .add_movie_quality_profile_list + .scroll_to_bottom(), + _ => (), } } fn handle_delete(&mut self) {} fn handle_left_right_action(&mut self) { - if *self.active_radarr_block == ActiveRadarrBlock::AddMoviePrompt { - handle_prompt_toggle(self.app, self.key); + if let ActiveRadarrBlock::AddMoviePrompt = self.active_radarr_block { + handle_prompt_toggle(self.app, self.key) } } @@ -82,15 +179,56 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { self .app .push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); + self + .app + .data + .radarr_data + .add_movie_monitor_list + .set_items(Monitor::vec()); + self + .app + .data + .radarr_data + .add_movie_minimum_availability_list + .set_items(MinimumAvailability::vec()); + let quality_profile_names = self + .app + .data + .radarr_data + .quality_profile_map + .iter() + .map(|(_, value)| value.clone()) + .collect(); + self + .app + .data + .radarr_data + .add_movie_quality_profile_list + .set_items(quality_profile_names); } - ActiveRadarrBlock::AddMoviePrompt => { - if self.app.data.radarr_data.prompt_confirm { - self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie); - self.app.pop_navigation_stack(); - } else { - self.app.pop_navigation_stack(); + ActiveRadarrBlock::AddMoviePrompt => match self.app.data.radarr_data.selected_block { + ActiveRadarrBlock::AddMovieConfirmPrompt => { + if self.app.data.radarr_data.prompt_confirm { + self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie); + self.app.pop_navigation_stack(); + } else { + self.app.pop_navigation_stack(); + } } - } + 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()), + _ => (), + }, + ActiveRadarrBlock::AddMovieSelectMonitor + | ActiveRadarrBlock::AddMovieSelectMinimumAvailability + | ActiveRadarrBlock::AddMovieSelectQualityProfile => self.app.pop_navigation_stack(), _ => (), } } @@ -109,8 +247,12 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { } ActiveRadarrBlock::AddMoviePrompt => { self.app.pop_navigation_stack(); + self.app.data.radarr_data.reset_add_movie_selections(); self.app.data.radarr_data.prompt_confirm = false; } + ActiveRadarrBlock::AddMovieSelectMonitor + | ActiveRadarrBlock::AddMovieSelectMinimumAvailability + | ActiveRadarrBlock::AddMovieSelectQualityProfile => self.app.pop_navigation_stack(), _ => (), } } diff --git a/src/handlers/radarr_handlers/mod.rs b/src/handlers/radarr_handlers/mod.rs index 2bb9efb..0fc979f 100644 --- a/src/handlers/radarr_handlers/mod.rs +++ b/src/handlers/radarr_handlers/mod.rs @@ -34,7 +34,10 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> { } ActiveRadarrBlock::AddMovieSearchInput | ActiveRadarrBlock::AddMovieSearchResults - | ActiveRadarrBlock::AddMoviePrompt => { + | ActiveRadarrBlock::AddMoviePrompt + | ActiveRadarrBlock::AddMovieSelectMinimumAvailability + | ActiveRadarrBlock::AddMovieSelectMonitor + | ActiveRadarrBlock::AddMovieSelectQualityProfile => { AddMovieHandler::with(self.key, self.app, self.active_radarr_block).handle() } _ => self.handle_key_event(), diff --git a/src/models/mod.rs b/src/models/mod.rs index e520508..34b1922 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::fmt::{Debug, Display, Formatter}; use serde::Deserialize; -use tui::widgets::TableState; +use tui::widgets::{ListState, TableState}; use crate::app::radarr::ActiveRadarrBlock; @@ -28,6 +28,87 @@ pub trait Scrollable { fn scroll_to_bottom(&mut self); } +pub struct StatefulList { + pub state: ListState, + pub items: Vec, +} + +impl Default for StatefulList { + fn default() -> StatefulList { + StatefulList { + state: ListState::default(), + items: Vec::new(), + } + } +} + +impl Scrollable for StatefulList { + fn scroll_down(&mut self) { + let selected_row = match self.state.selected() { + Some(i) => { + if i >= self.items.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; + + self.state.select(Some(selected_row)); + } + + fn scroll_up(&mut self) { + let selected_row = match self.state.selected() { + Some(i) => { + if i == 0 { + self.items.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + + self.state.select(Some(selected_row)); + } + + fn scroll_to_top(&mut self) { + self.state.select(Some(0)); + } + + fn scroll_to_bottom(&mut self) { + self.state.select(Some(self.items.len() - 1)); + } +} + +impl StatefulList { + pub fn set_items(&mut self, items: Vec) { + let items_len = items.len(); + self.items = items; + if !self.items.is_empty() { + let selected_row = self.state.selected().map_or(0, |i| { + if i > 0 && i < items_len { + i + } else if i >= items_len { + items_len - 1 + } else { + 0 + } + }); + self.state.select(Some(selected_row)); + } + } + + pub fn current_selection(&self) -> &T { + &self.items[self.state.selected().unwrap_or(0)] + } + + pub fn current_selection_clone(&self) -> T { + self.items[self.state.selected().unwrap_or(0)].clone() + } +} + pub struct StatefulTable { pub state: TableState, pub items: Vec, diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index b0f3578..8c30d8b 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -1,7 +1,10 @@ +use std::fmt::{Display, Formatter}; + use chrono::{DateTime, Utc}; use derivative::Derivative; use serde::{Deserialize, Serialize}; use serde_json::Number; +use strum::EnumIter; use crate::models::HorizontallyScrollableText; @@ -223,6 +226,7 @@ pub struct AddMovieBody { #[derive(Default, Serialize, Debug, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AddOptions { + pub monitor: String, pub search_for_movie: bool, } @@ -243,3 +247,81 @@ pub struct AddMovieSearchResult { pub runtime: Number, pub ratings: RatingsList, } + +#[derive(Default, PartialEq, Eq, Clone, Debug)] +pub enum MinimumAvailability { + Tba, + Announced, + InCinemas, + #[default] + Released, +} + +impl Display for MinimumAvailability { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let minimum_availability = match self { + MinimumAvailability::Tba => "tba", + MinimumAvailability::Announced => "announced", + MinimumAvailability::InCinemas => "inCinemas", + MinimumAvailability::Released => "released", + }; + write!(f, "{}", minimum_availability) + } +} + +impl MinimumAvailability { + pub fn vec() -> Vec { + vec![ + MinimumAvailability::Tba, + MinimumAvailability::Announced, + MinimumAvailability::InCinemas, + MinimumAvailability::Released, + ] + } + + pub fn to_display_str(&self) -> &str { + match self { + MinimumAvailability::Tba => "TBA", + MinimumAvailability::Announced => "Announced", + MinimumAvailability::InCinemas => "In Cinemas", + MinimumAvailability::Released => "Released", + } + } +} + +#[derive(Default, PartialEq, Eq, Clone, Debug)] +pub enum Monitor { + #[default] + MovieOnly, + MovieAndCollection, + None, +} + +impl Display for Monitor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let monitor = match self { + Monitor::MovieOnly => "movieOnly", + Monitor::MovieAndCollection => "movieAndCollection", + Monitor::None => "none", + }; + write!(f, "{}", monitor) + } +} + +impl Monitor { + pub fn vec() -> Vec { + vec![ + Monitor::MovieOnly, + Monitor::MovieAndCollection, + Monitor::None, + ] + } + + pub fn to_display_str(&self) -> &str { + match self { + Monitor::MovieOnly => "Movie only", + Monitor::MovieAndCollection => "Movie and Collection", + Monitor::None => "None", + } + } +} diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index 39c180e..1fa967d 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -8,7 +8,8 @@ use urlencoding::encode; use crate::app::RadarrConfig; use crate::models::radarr_models::{ AddMovieBody, AddMovieSearchResult, AddOptions, Collection, Credit, CreditType, DiskSpace, - DownloadsResponse, Movie, MovieHistoryItem, QualityProfile, RootFolder, SystemStatus, + DownloadsResponse, MinimumAvailability, Movie, MovieHistoryItem, QualityProfile, RootFolder, + SystemStatus, }; use crate::models::ScrollableText; use crate::network::utils::get_movie_status; @@ -491,53 +492,64 @@ impl<'a> Network<'a> { async fn add_movie(&self) { info!("Adding new movie to Radarr"); - let root_folders = self.app.lock().await.data.radarr_data.root_folders.to_vec(); - let current_selection = self - .app - .lock() - .await - .data - .radarr_data - .add_searched_movies - .current_selection_clone(); - let quality_profile_map = self - .app - .lock() - .await - .data - .radarr_data - .quality_profile_map - .clone(); + let body = { + let app = self.app.lock().await; + let root_folders = app.data.radarr_data.root_folders.to_vec(); + let current_selection = app + .data + .radarr_data + .add_searched_movies + .current_selection_clone(); + let quality_profile_map = app.data.radarr_data.quality_profile_map.clone(); - let RootFolder { path, .. } = root_folders - .iter() - .filter(|folder| folder.accessible) - .reduce(|a, b| { - if a.free_space.as_u64().unwrap() > b.free_space.as_u64().unwrap() { - a - } else { - b - } - }) - .unwrap(); - let AddMovieSearchResult { tmdb_id, title, .. } = current_selection; - let quality_profile_id = quality_profile_map - .iter() - .filter(|(_, value)| value.to_lowercase() == "any") - .map(|(key, _)| key) - .next() - .unwrap(); + let RootFolder { path, .. } = root_folders + .iter() + .filter(|folder| folder.accessible) + .reduce(|a, b| { + if a.free_space.as_u64().unwrap() > b.free_space.as_u64().unwrap() { + a + } else { + b + } + }) + .unwrap(); + let monitor = app + .data + .radarr_data + .add_movie_monitor_list + .current_selection() + .to_string(); + let minimum_availability = app + .data + .radarr_data + .add_movie_minimum_availability_list + .current_selection() + .to_string(); + let quality_profile = app + .data + .radarr_data + .add_movie_quality_profile_list + .current_selection_clone(); + let AddMovieSearchResult { tmdb_id, title, .. } = current_selection; + let quality_profile_id = quality_profile_map + .iter() + .filter(|(_, value)| **value == quality_profile) + .map(|(key, _)| key) + .next() + .unwrap(); - let body = AddMovieBody { - tmdb_id: tmdb_id.as_u64().unwrap(), - title: title.to_owned(), - root_folder_path: path.to_owned(), - minimum_availability: "released".to_owned(), - monitored: true, - quality_profile_id: *quality_profile_id, - add_options: AddOptions { - search_for_movie: true, - }, + AddMovieBody { + tmdb_id: tmdb_id.as_u64().unwrap(), + title, + root_folder_path: path.to_owned(), + minimum_availability, + monitored: true, + quality_profile_id: *quality_profile_id, + add_options: AddOptions { + monitor, + search_for_movie: true, + }, + } }; debug!("Add movie body: {:?}", body); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index c404f09..a93ac3c 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -299,3 +299,30 @@ pub fn draw_button(f: &mut Frame<'_, B>, area: Rect, label: &str, is f.render_widget(label_paragraph, area); } + +pub fn draw_drop_down_menu( + f: &mut Frame<'_, B>, + area: Rect, + description: &str, + selection: &str, + is_selected: bool, +) { + let horizontal_chunks = horizontal_chunks( + vec![Constraint::Percentage(50), Constraint::Percentage(50)], + area, + ); + + let description_paragraph = Paragraph::new(Text::from(format!("\n{}: ", description))) + .block(borderless_block()) + .alignment(Alignment::Right) + .style(style_primary()); + + f.render_widget(description_paragraph, horizontal_chunks[0]); + + draw_button( + f, + horizontal_chunks[1], + format!("{} ▼", selection).as_str(), + is_selected, + ); +} diff --git a/src/ui/radarr_ui/add_movie_ui.rs b/src/ui/radarr_ui/add_movie_ui.rs index 188d515..41285ba 100644 --- a/src/ui/radarr_ui/add_movie_ui.rs +++ b/src/ui/radarr_ui/add_movie_ui.rs @@ -1,16 +1,21 @@ use tui::backend::Backend; use tui::layout::{Alignment, Constraint, Rect}; +use tui::style::Modifier; use tui::text::Text; -use tui::widgets::{Cell, Paragraph, Row}; +use tui::widgets::{Cell, Paragraph, Row, Wrap}; use tui::Frame; use crate::app::radarr::ActiveRadarrBlock; +use crate::models::radarr_models::Monitor; use crate::models::Route; use crate::ui::utils::{ - borderless_block, layout_block, show_cursor, style_default, style_help, style_primary, - title_block_centered, vertical_chunks_with_margin, + borderless_block, horizontal_chunks, layout_block, show_cursor, style_default, style_help, + style_primary, title_block_centered, vertical_chunks_with_margin, +}; +use crate::ui::{ + draw_button, draw_drop_down_menu, draw_medium_popup_over, draw_prompt_box, draw_small_popup_over, + draw_table, TableProps, }; -use crate::ui::{draw_medium_popup_over, draw_prompt_box, draw_table, TableProps}; use crate::utils::convert_runtime; use crate::App; @@ -147,31 +152,129 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App, area: f.render_widget(search_paragraph, chunks[0]); } +fn draw_add_movie_confirmation_popup( + f: &mut Frame<'_, B>, + app: &mut App, + prompt_area: Rect, +) { + if let Route::Radarr(active_radarr_block) = app.get_current_route().clone() { + match active_radarr_block { + ActiveRadarrBlock::AddMovieSelectMonitor => { + // draw_small_popup_over(f, app, prompt_area, draw_add_movie_confirmation_prompt, draw_add_movie_select_monitor); + } + ActiveRadarrBlock::AddMovieSelectMinimumAvailability => { + // draw_small_popup_over(f, app, prompt_area, draw_add_movie_confirmation_prompt, draw_add_movie_select_minimum_availability); + } + ActiveRadarrBlock::AddMovieSelectQualityProfile => { + // draw_small_popup_over(f, app, prompt_area, draw_add_movie_confirmation_prompt, draw_add_movie_select_quality_profile); + } + ActiveRadarrBlock::AddMoviePrompt => draw_add_movie_confirmation_prompt(f, app, prompt_area), + _ => (), + } + } +} + fn draw_add_movie_confirmation_prompt( f: &mut Frame<'_, B>, app: &mut App, prompt_area: Rect, ) { - draw_prompt_box( - f, + let title = " Confirm Add Movie? "; + let prompt = format!( + "{}:\n\n{}", + app + .data + .radarr_data + .add_searched_movies + .current_selection() + .title, + app + .data + .radarr_data + .add_searched_movies + .current_selection() + .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; + + let selected_monitor = app + .data + .radarr_data + .add_movie_monitor_list + .current_selection(); + let selected_minimum_availability = app + .data + .radarr_data + .add_movie_minimum_availability_list + .current_selection(); + let selected_quality_profile = app + .data + .radarr_data + .add_movie_quality_profile_list + .current_selection(); + + f.render_widget(title_block_centered(title), prompt_area); + + let chunks = vertical_chunks_with_margin( + vec![ + Constraint::Percentage(40), + Constraint::Length(3), + Constraint::Length(3), + Constraint::Length(3), + Constraint::Min(5), + Constraint::Length(3), + ], prompt_area, - " Confirm Add Movie? ", - format!( - "{}:\n\n{}", - app - .data - .radarr_data - .add_searched_movies - .current_selection() - .title, - app - .data - .radarr_data - .add_searched_movies - .current_selection() - .overview - ) - .as_str(), - &app.data.radarr_data.prompt_confirm, + 1, + ); + + let prompt_paragraph = Paragraph::new(Text::from(prompt)) + .block(borderless_block()) + .style(style_primary().add_modifier(Modifier::BOLD)) + .wrap(Wrap { trim: false }) + .alignment(Alignment::Center); + f.render_widget(prompt_paragraph, chunks[0]); + + let horizontal_chunks = horizontal_chunks( + vec![Constraint::Percentage(50), Constraint::Percentage(50)], + chunks[5], + ); + + draw_drop_down_menu( + f, + chunks[1], + "Monitor", + selected_monitor.to_display_str(), + *selected_block == ActiveRadarrBlock::AddMovieSelectMonitor, + ); + + draw_drop_down_menu( + f, + chunks[2], + "Minimum Availability", + selected_minimum_availability.to_display_str(), + *selected_block == ActiveRadarrBlock::AddMovieSelectMinimumAvailability, + ); + draw_drop_down_menu( + f, + chunks[3], + "Quality Profile", + selected_quality_profile, + *selected_block == ActiveRadarrBlock::AddMovieSelectQualityProfile, + ); + + draw_button( + f, + horizontal_chunks[0], + "Yes", + *yes_no_value && highlight_yes_no, + ); + draw_button( + f, + horizontal_chunks[1], + "No", + !*yes_no_value && highlight_yes_no, ); } diff --git a/src/ui/radarr_ui/mod.rs b/src/ui/radarr_ui/mod.rs index a32664f..d34458f 100644 --- a/src/ui/radarr_ui/mod.rs +++ b/src/ui/radarr_ui/mod.rs @@ -6,14 +6,14 @@ use tui::backend::Backend; use tui::layout::{Alignment, Constraint, Rect}; use tui::style::{Color, Style}; use tui::text::Text; -use tui::widgets::{Block, Cell, Paragraph, Row}; +use tui::widgets::{Cell, Paragraph, Row}; use tui::Frame; use crate::app::radarr::{ActiveRadarrBlock, RadarrData}; use crate::app::App; use crate::logos::RADARR_LOGO; -use crate::models::radarr_models::{AddMovieSearchResult, DiskSpace, DownloadRecord, Movie}; -use crate::models::{Route, StatefulTable}; +use crate::models::radarr_models::{DiskSpace, DownloadRecord, Movie}; +use crate::models::Route; 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; @@ -64,7 +64,10 @@ pub(super) fn draw_radarr_ui(f: &mut Frame<'_, B>, app: &mut App, ar } ActiveRadarrBlock::AddMovieSearchInput | ActiveRadarrBlock::AddMovieSearchResults - | ActiveRadarrBlock::AddMoviePrompt => draw_large_popup_over( + | ActiveRadarrBlock::AddMoviePrompt + | ActiveRadarrBlock::AddMovieSelectMonitor + | ActiveRadarrBlock::AddMovieSelectMinimumAvailability + | ActiveRadarrBlock::AddMovieSelectQualityProfile => draw_large_popup_over( f, app, content_rect, @@ -168,7 +171,7 @@ fn draw_delete_movie_prompt(f: &mut Frame<'_, B>, app: &mut App, pro prompt_area, " Confirm Delete Movie? ", format!( - "Do you really want to delete {}?", + "Do you really want to delete: {}?", app.data.radarr_data.movies.current_selection().title ) .as_str(),