From 8660de530d3b5b5d697d3f28567ada0851ecf799 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Tue, 3 Dec 2024 16:24:23 -0700 Subject: [PATCH] feat(ui): Root folder tab support --- src/ui/sonarr_ui/mod.rs | 3 + src/ui/sonarr_ui/root_folders/mod.rs | 117 ++++++++++++++++++ .../root_folders/root_folders_ui_tests.rs | 19 +++ 3 files changed, 139 insertions(+) create mode 100644 src/ui/sonarr_ui/root_folders/mod.rs create mode 100644 src/ui/sonarr_ui/root_folders/root_folders_ui_tests.rs diff --git a/src/ui/sonarr_ui/mod.rs b/src/ui/sonarr_ui/mod.rs index 22a5656..ae7a097 100644 --- a/src/ui/sonarr_ui/mod.rs +++ b/src/ui/sonarr_ui/mod.rs @@ -12,6 +12,7 @@ use ratatui::{ widgets::Paragraph, Frame, }; +use root_folders::RootFoldersUi; use crate::{ app::App, @@ -39,6 +40,7 @@ mod blocklist; mod downloads; mod history; mod library; +mod root_folders; #[cfg(test)] #[path = "sonarr_ui_tests.rs"] @@ -60,6 +62,7 @@ impl DrawUi for SonarrUi { _ if DownloadsUi::accepts(route) => DownloadsUi::draw(f, app, content_area), _ if BlocklistUi::accepts(route) => BlocklistUi::draw(f, app, content_area), _ if HistoryUi::accepts(route) => HistoryUi::draw(f, app, content_area), + _ if RootFoldersUi::accepts(route) => RootFoldersUi::draw(f, app, content_area), _ => (), } } diff --git a/src/ui/sonarr_ui/root_folders/mod.rs b/src/ui/sonarr_ui/root_folders/mod.rs new file mode 100644 index 0000000..ee564e3 --- /dev/null +++ b/src/ui/sonarr_ui/root_folders/mod.rs @@ -0,0 +1,117 @@ +use ratatui::layout::{Constraint, Rect}; +use ratatui::widgets::{Cell, Row}; +use ratatui::Frame; + +use crate::app::App; +use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS}; +use crate::models::servarr_models::RootFolder; +use crate::models::Route; +use crate::ui::styles::ManagarrStyle; +use crate::ui::utils::layout_block_top_border; +use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt; +use crate::ui::widgets::managarr_table::ManagarrTable; +use crate::ui::widgets::popup::{Popup, Size}; +use crate::ui::{draw_input_box_popup, draw_popup_over, DrawUi}; +use crate::utils::convert_to_gb; + +#[cfg(test)] +#[path = "root_folders_ui_tests.rs"] +mod root_folders_ui_tests; + +pub(super) struct RootFoldersUi; + +impl DrawUi for RootFoldersUi { + fn accepts(route: Route) -> bool { + if let Route::Sonarr(active_sonarr_block, _) = route { + return ROOT_FOLDERS_BLOCKS.contains(&active_sonarr_block); + } + + false + } + + fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { + if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() { + match active_sonarr_block { + ActiveSonarrBlock::RootFolders => draw_root_folders(f, app, area), + ActiveSonarrBlock::AddRootFolderPrompt => draw_popup_over( + f, + app, + area, + draw_root_folders, + draw_add_root_folder_prompt_box, + Size::InputBox, + ), + ActiveSonarrBlock::DeleteRootFolderPrompt => { + let prompt = format!( + "Do you really want to delete this root folder: \n{}?", + app.data.sonarr_data.root_folders.current_selection().path + ); + let confirmation_prompt = ConfirmationPrompt::new() + .title("Delete Root Folder") + .prompt(&prompt) + .yes_no_value(app.data.sonarr_data.prompt_confirm); + + draw_root_folders(f, app, area); + f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area()); + } + _ => (), + } + } + } +} + +fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { + let help_footer = app + .data + .sonarr_data + .main_tabs + .get_active_tab_contextual_help(); + let root_folders_row_mapping = |root_folders: &RootFolder| { + let RootFolder { + path, + free_space, + unmapped_folders, + .. + } = root_folders; + + let space: f64 = convert_to_gb(*free_space); + + Row::new(vec![ + Cell::from(path.to_owned()), + Cell::from(format!("{space:.2} GB")), + Cell::from( + unmapped_folders + .as_ref() + .unwrap_or(&Vec::new()) + .len() + .to_string(), + ), + ]) + .primary() + }; + + let root_folders_table = ManagarrTable::new( + Some(&mut app.data.sonarr_data.root_folders), + root_folders_row_mapping, + ) + .block(layout_block_top_border()) + .loading(app.is_loading) + .footer(help_footer) + .headers(["Path", "Free Space", "Unmapped Folders"]) + .constraints([ + Constraint::Ratio(3, 5), + Constraint::Ratio(1, 5), + Constraint::Ratio(1, 5), + ]); + + f.render_widget(root_folders_table, area); +} + +fn draw_add_root_folder_prompt_box(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { + draw_input_box_popup( + f, + area, + "Add Root Folder", + app.data.sonarr_data.edit_root_folder.as_ref().unwrap(), + ); +} diff --git a/src/ui/sonarr_ui/root_folders/root_folders_ui_tests.rs b/src/ui/sonarr_ui/root_folders/root_folders_ui_tests.rs new file mode 100644 index 0000000..1e9e6c0 --- /dev/null +++ b/src/ui/sonarr_ui/root_folders/root_folders_ui_tests.rs @@ -0,0 +1,19 @@ +#[cfg(test)] +mod tests { + use strum::IntoEnumIterator; + + use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS}; + use crate::ui::sonarr_ui::root_folders::RootFoldersUi; + use crate::ui::DrawUi; + + #[test] + fn test_root_folders_ui_accepts() { + ActiveSonarrBlock::iter().for_each(|active_sonarr_block| { + if ROOT_FOLDERS_BLOCKS.contains(&active_sonarr_block) { + assert!(RootFoldersUi::accepts(active_sonarr_block.into())); + } else { + assert!(!RootFoldersUi::accepts(active_sonarr_block.into())); + } + }); + } +}