From 0db57fbff1ed414b95e4b49c40b2b763c365e0e3 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Mon, 2 Dec 2024 11:45:13 -0700 Subject: [PATCH] feat(ui): Delete a series --- .../library/library_handler_tests.rs | 7 ++- src/handlers/sonarr_handlers/library/mod.rs | 4 +- src/models/servarr_data/sonarr/sonarr_data.rs | 2 +- .../servarr_data/sonarr/sonarr_data_tests.rs | 20 +++---- src/ui/sonarr_ui/library/delete_series_ui.rs | 57 +++++++++++++++++++ .../library/delete_series_ui_tests.rs | 19 +++++++ src/ui/sonarr_ui/library/library_ui_tests.rs | 7 ++- src/ui/sonarr_ui/library/mod.rs | 28 +++++---- 8 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 src/ui/sonarr_ui/library/delete_series_ui.rs create mode 100644 src/ui/sonarr_ui/library/delete_series_ui_tests.rs diff --git a/src/handlers/sonarr_handlers/library/library_handler_tests.rs b/src/handlers/sonarr_handlers/library/library_handler_tests.rs index 752be77..35ce42f 100644 --- a/src/handlers/sonarr_handlers/library/library_handler_tests.rs +++ b/src/handlers/sonarr_handlers/library/library_handler_tests.rs @@ -11,7 +11,9 @@ mod tests { use crate::event::Key; use crate::handlers::sonarr_handlers::library::{series_sorting_options, LibraryHandler}; use crate::handlers::KeyEventHandler; - use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SERIES_BLOCKS}; + use crate::models::servarr_data::sonarr::sonarr_data::{ + ActiveSonarrBlock, DELETE_SERIES_BLOCKS, LIBRARY_BLOCKS, + }; use crate::models::sonarr_models::{Series, SeriesStatus, SeriesType}; use crate::models::stateful_table::SortOption; use crate::models::HorizontallyScrollableText; @@ -1702,7 +1704,8 @@ mod tests { #[test] fn test_library_handler_accepts() { let mut library_handler_blocks = Vec::new(); - library_handler_blocks.extend(SERIES_BLOCKS); + library_handler_blocks.extend(LIBRARY_BLOCKS); + library_handler_blocks.extend(DELETE_SERIES_BLOCKS); ActiveSonarrBlock::iter().for_each(|active_sonarr_block| { if library_handler_blocks.contains(&active_sonarr_block) { diff --git a/src/handlers/sonarr_handlers/library/mod.rs b/src/handlers/sonarr_handlers/library/mod.rs index ee61a54..3a1a05e 100644 --- a/src/handlers/sonarr_handlers/library/mod.rs +++ b/src/handlers/sonarr_handlers/library/mod.rs @@ -8,7 +8,7 @@ use crate::{ models::{ servarr_data::sonarr::sonarr_data::{ ActiveSonarrBlock, DELETE_SERIES_SELECTION_BLOCKS, EDIT_SERIES_SELECTION_BLOCKS, - SERIES_BLOCKS, + LIBRARY_BLOCKS, }, sonarr_models::Series, stateful_table::SortOption, @@ -45,7 +45,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, ' } fn accepts(active_block: ActiveSonarrBlock) -> bool { - SERIES_BLOCKS.contains(&active_block) + DeleteSeriesHandler::accepts(active_block) || LIBRARY_BLOCKS.contains(&active_block) } fn with( diff --git a/src/models/servarr_data/sonarr/sonarr_data.rs b/src/models/servarr_data/sonarr/sonarr_data.rs index f40807a..7709042 100644 --- a/src/models/servarr_data/sonarr/sonarr_data.rs +++ b/src/models/servarr_data/sonarr/sonarr_data.rs @@ -285,7 +285,7 @@ pub enum ActiveSonarrBlock { UpdateDownloadsPrompt, } -pub static SERIES_BLOCKS: [ActiveSonarrBlock; 7] = [ +pub static LIBRARY_BLOCKS: [ActiveSonarrBlock; 7] = [ ActiveSonarrBlock::Series, ActiveSonarrBlock::SeriesSortPrompt, ActiveSonarrBlock::SearchSeries, diff --git a/src/models/servarr_data/sonarr/sonarr_data_tests.rs b/src/models/servarr_data/sonarr/sonarr_data_tests.rs index bf07cda..7af3986 100644 --- a/src/models/servarr_data/sonarr/sonarr_data_tests.rs +++ b/src/models/servarr_data/sonarr/sonarr_data_tests.rs @@ -203,19 +203,19 @@ mod tests { mod active_sonarr_block_tests { use crate::models::servarr_data::sonarr::sonarr_data::{ ActiveSonarrBlock, DELETE_SERIES_BLOCKS, DELETE_SERIES_SELECTION_BLOCKS, DOWNLOADS_BLOCKS, - EDIT_SERIES_BLOCKS, EDIT_SERIES_SELECTION_BLOCKS, SERIES_BLOCKS, + EDIT_SERIES_BLOCKS, EDIT_SERIES_SELECTION_BLOCKS, LIBRARY_BLOCKS, }; #[test] - fn test_series_blocks_contents() { - assert_eq!(SERIES_BLOCKS.len(), 7); - assert!(SERIES_BLOCKS.contains(&ActiveSonarrBlock::Series)); - assert!(SERIES_BLOCKS.contains(&ActiveSonarrBlock::SeriesSortPrompt)); - assert!(SERIES_BLOCKS.contains(&ActiveSonarrBlock::SearchSeries)); - assert!(SERIES_BLOCKS.contains(&ActiveSonarrBlock::SearchSeriesError)); - assert!(SERIES_BLOCKS.contains(&ActiveSonarrBlock::FilterSeries)); - assert!(SERIES_BLOCKS.contains(&ActiveSonarrBlock::FilterSeriesError)); - assert!(SERIES_BLOCKS.contains(&ActiveSonarrBlock::UpdateAllSeriesPrompt)); + fn test_library_blocks_contents() { + assert_eq!(LIBRARY_BLOCKS.len(), 7); + assert!(LIBRARY_BLOCKS.contains(&ActiveSonarrBlock::Series)); + assert!(LIBRARY_BLOCKS.contains(&ActiveSonarrBlock::SeriesSortPrompt)); + assert!(LIBRARY_BLOCKS.contains(&ActiveSonarrBlock::SearchSeries)); + assert!(LIBRARY_BLOCKS.contains(&ActiveSonarrBlock::SearchSeriesError)); + assert!(LIBRARY_BLOCKS.contains(&ActiveSonarrBlock::FilterSeries)); + assert!(LIBRARY_BLOCKS.contains(&ActiveSonarrBlock::FilterSeriesError)); + assert!(LIBRARY_BLOCKS.contains(&ActiveSonarrBlock::UpdateAllSeriesPrompt)); } #[test] diff --git a/src/ui/sonarr_ui/library/delete_series_ui.rs b/src/ui/sonarr_ui/library/delete_series_ui.rs new file mode 100644 index 0000000..eb7278e --- /dev/null +++ b/src/ui/sonarr_ui/library/delete_series_ui.rs @@ -0,0 +1,57 @@ +use ratatui::layout::Rect; +use ratatui::Frame; + +use crate::app::App; +use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS}; +use crate::models::Route; +use crate::ui::sonarr_ui::library::draw_library; +use crate::ui::widgets::checkbox::Checkbox; +use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt; +use crate::ui::widgets::popup::{Popup, Size}; +use crate::ui::DrawUi; + +#[cfg(test)] +#[path = "delete_series_ui_tests.rs"] +mod delete_series_ui_tests; + +pub(super) struct DeleteSeriesUi; + +impl DrawUi for DeleteSeriesUi { + fn accepts(route: Route) -> bool { + if let Route::Sonarr(active_sonarr_block, _) = route { + return DELETE_SERIES_BLOCKS.contains(&active_sonarr_block); + } + + false + } + + fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { + if matches!( + app.get_current_route(), + Route::Sonarr(ActiveSonarrBlock::DeleteSeriesPrompt, _) + ) { + let selected_block = app.data.sonarr_data.selected_block.get_active_block(); + let prompt = format!( + "Do you really want to delete: \n{}?", + app.data.sonarr_data.series.current_selection().title.text + ); + let checkboxes = vec![ + Checkbox::new("Delete Series File") + .checked(app.data.sonarr_data.delete_series_files) + .highlighted(selected_block == ActiveSonarrBlock::DeleteSeriesToggleDeleteFile), + Checkbox::new("Add List Exclusion") + .checked(app.data.sonarr_data.add_list_exclusion) + .highlighted(selected_block == ActiveSonarrBlock::DeleteSeriesToggleAddListExclusion), + ]; + let confirmation_prompt = ConfirmationPrompt::new() + .title("Delete Series") + .prompt(&prompt) + .checkboxes(checkboxes) + .yes_no_highlighted(selected_block == ActiveSonarrBlock::DeleteSeriesConfirmPrompt) + .yes_no_value(app.data.sonarr_data.prompt_confirm); + + draw_library(f, app, area); + f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area()); + } + } +} diff --git a/src/ui/sonarr_ui/library/delete_series_ui_tests.rs b/src/ui/sonarr_ui/library/delete_series_ui_tests.rs new file mode 100644 index 0000000..17c61b1 --- /dev/null +++ b/src/ui/sonarr_ui/library/delete_series_ui_tests.rs @@ -0,0 +1,19 @@ +#[cfg(test)] +mod tests { + use strum::IntoEnumIterator; + + use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS}; + use crate::ui::sonarr_ui::library::delete_series_ui::DeleteSeriesUi; + use crate::ui::DrawUi; + + #[test] + fn test_delete_series_ui_accepts() { + ActiveSonarrBlock::iter().for_each(|active_sonarr_block| { + if DELETE_SERIES_BLOCKS.contains(&active_sonarr_block) { + assert!(DeleteSeriesUi::accepts(active_sonarr_block.into())); + } else { + assert!(!DeleteSeriesUi::accepts(active_sonarr_block.into())); + } + }); + } +} diff --git a/src/ui/sonarr_ui/library/library_ui_tests.rs b/src/ui/sonarr_ui/library/library_ui_tests.rs index 31c40fc..c94ea65 100644 --- a/src/ui/sonarr_ui/library/library_ui_tests.rs +++ b/src/ui/sonarr_ui/library/library_ui_tests.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests { - use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; + use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS}; use crate::models::{ - servarr_data::sonarr::sonarr_data::SERIES_BLOCKS, sonarr_models::SeriesStatus, + servarr_data::sonarr::sonarr_data::LIBRARY_BLOCKS, sonarr_models::SeriesStatus, }; use crate::ui::sonarr_ui::library::LibraryUi; use crate::ui::styles::ManagarrStyle; @@ -20,7 +20,8 @@ mod tests { #[test] fn test_library_ui_accepts() { let mut library_ui_blocks = Vec::new(); - library_ui_blocks.extend(SERIES_BLOCKS); + library_ui_blocks.extend(LIBRARY_BLOCKS); + library_ui_blocks.extend(DELETE_SERIES_BLOCKS); ActiveSonarrBlock::iter().for_each(|active_radarr_block| { if library_ui_blocks.contains(&active_radarr_block) { diff --git a/src/ui/sonarr_ui/library/mod.rs b/src/ui/sonarr_ui/library/mod.rs index a9661b3..e25027f 100644 --- a/src/ui/sonarr_ui/library/mod.rs +++ b/src/ui/sonarr_ui/library/mod.rs @@ -1,3 +1,4 @@ +use delete_series_ui::DeleteSeriesUi; use ratatui::{ layout::{Constraint, Rect}, widgets::{Cell, Row}, @@ -12,7 +13,7 @@ use crate::ui::widgets::{ use crate::{ app::App, models::{ - servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SERIES_BLOCKS}, + servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, LIBRARY_BLOCKS}, sonarr_models::{Series, SeriesStatus}, EnumDisplayStyle, Route, }, @@ -25,6 +26,8 @@ use crate::{ }, }; +mod delete_series_ui; + #[cfg(test)] #[path = "library_ui_tests.rs"] mod library_ui_tests; @@ -34,7 +37,7 @@ pub(super) struct LibraryUi; impl DrawUi for LibraryUi { fn accepts(route: Route) -> bool { if let Route::Sonarr(active_sonarr_block, _) = route { - return SERIES_BLOCKS.contains(&active_sonarr_block); + return DeleteSeriesUi::accepts(route) || LIBRARY_BLOCKS.contains(&active_sonarr_block); } false @@ -44,26 +47,26 @@ impl DrawUi for LibraryUi { let route = app.get_current_route(); let mut series_ui_matchers = |active_sonarr_block: ActiveSonarrBlock| match active_sonarr_block { - ActiveSonarrBlock::Series | ActiveSonarrBlock::SeriesSortPrompt => draw_series(f, app, area), + ActiveSonarrBlock::Series | ActiveSonarrBlock::SeriesSortPrompt => draw_library(f, app, area), ActiveSonarrBlock::SearchSeries => draw_popup_over( f, app, area, - draw_series, - draw_series_search_box, + draw_library, + draw_library_search_box, Size::InputBox, ), ActiveSonarrBlock::SearchSeriesError => { let popup = Popup::new(Message::new("Series not found!")).size(Size::Message); - draw_series(f, app, area); + draw_library(f, app, area); f.render_widget(popup, f.area()); } ActiveSonarrBlock::FilterSeries => draw_popup_over( f, app, area, - draw_series, + draw_library, draw_filter_series_box, Size::InputBox, ), @@ -71,7 +74,7 @@ impl DrawUi for LibraryUi { let popup = Popup::new(Message::new("No series found matching the given filter!")) .size(Size::Message); - draw_series(f, app, area); + draw_library(f, app, area); f.render_widget(popup, f.area()); } ActiveSonarrBlock::UpdateAllSeriesPrompt => { @@ -80,14 +83,15 @@ impl DrawUi for LibraryUi { .prompt("Do you want to update info and scan your disks for all of your series?") .yes_no_value(app.data.sonarr_data.prompt_confirm); - draw_series(f, app, area); + draw_library(f, app, area); f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area()); } _ => (), }; match route { - Route::Sonarr(active_sonarr_block, _) if SERIES_BLOCKS.contains(&active_sonarr_block) => { + _ if DeleteSeriesUi::accepts(route) => DeleteSeriesUi::draw(f, app, area), + Route::Sonarr(active_sonarr_block, _) if LIBRARY_BLOCKS.contains(&active_sonarr_block) => { series_ui_matchers(active_sonarr_block) } _ => (), @@ -95,7 +99,7 @@ impl DrawUi for LibraryUi { } } -pub(super) fn draw_series(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { +pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() { let current_selection = if !app.data.sonarr_data.series.items.is_empty() { app.data.sonarr_data.series.current_selection().clone() @@ -220,7 +224,7 @@ fn decorate_series_row_with_style<'a>(series: &Series, row: Row<'a>) -> Row<'a> } } -fn draw_series_search_box(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { +fn draw_library_search_box(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { draw_input_box_popup( f, area,