diff --git a/src/ui/readarr_ui/downloads/downloads_ui_tests.rs b/src/ui/readarr_ui/downloads/downloads_ui_tests.rs new file mode 100644 index 00000000..565b470a --- /dev/null +++ b/src/ui/readarr_ui/downloads/downloads_ui_tests.rs @@ -0,0 +1,127 @@ +#[cfg(test)] +mod tests { + use strum::IntoEnumIterator; + + use crate::app::App; + use crate::models::servarr_data::readarr::readarr_data::{ActiveReadarrBlock, DOWNLOADS_BLOCKS}; + use crate::ui::DrawUi; + use crate::ui::readarr_ui::downloads::DownloadsUi; + use crate::ui::ui_test_utils::test_utils::render_to_string_with_app; + + #[test] + fn test_downloads_ui_accepts() { + ActiveReadarrBlock::iter().for_each(|active_readarr_block| { + if DOWNLOADS_BLOCKS.contains(&active_readarr_block) { + assert!(DownloadsUi::accepts(active_readarr_block.into())); + } else { + assert!(!DownloadsUi::accepts(active_readarr_block.into())); + } + }); + } + + mod snapshot_tests { + use rstest::rstest; + use serde_json::Number; + + use crate::models::readarr_models::{DownloadRecord, DownloadStatus}; + use crate::ui::ui_test_utils::test_utils::TerminalSize; + + use super::*; + + #[test] + fn test_downloads_ui_renders_loading() { + let mut app = App::test_default(); + app.is_loading = true; + app.push_navigation_stack(ActiveReadarrBlock::Downloads.into()); + + let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| { + DownloadsUi::draw(f, app, f.area()); + }); + + insta::assert_snapshot!(output); + } + + #[test] + fn test_downloads_ui_renders_empty() { + let mut app = App::test_default(); + app.push_navigation_stack(ActiveReadarrBlock::Downloads.into()); + + let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| { + DownloadsUi::draw(f, app, f.area()); + }); + + insta::assert_snapshot!(output); + } + + #[rstest] + fn test_downloads_ui_renders( + #[values( + ActiveReadarrBlock::Downloads, + ActiveReadarrBlock::DeleteDownloadPrompt, + ActiveReadarrBlock::UpdateDownloadsPrompt + )] + active_readarr_block: ActiveReadarrBlock, + ) { + let mut app = App::test_default_fully_populated(); + app.push_navigation_stack(active_readarr_block.into()); + + let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| { + DownloadsUi::draw(f, app, f.area()); + }); + + insta::assert_snapshot!(format!("downloads_ui_{active_readarr_block}"), output); + } + + #[rstest] + fn test_downloads_ui_renders_second_item_selected( + #[values( + ActiveReadarrBlock::Downloads, + ActiveReadarrBlock::DeleteDownloadPrompt + )] + active_readarr_block: ActiveReadarrBlock, + ) { + let mut app = App::test_default(); + app.data.readarr_data.downloads.set_items(downloads_vec()); + app.data.readarr_data.downloads.select_index(Some(1)); + app.push_navigation_stack(active_readarr_block.into()); + + let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| { + DownloadsUi::draw(f, app, f.area()); + }); + + insta::assert_snapshot!( + format!("downloads_second_item_selected_{active_readarr_block}"), + output + ); + } + + fn downloads_vec() -> Vec { + vec![ + DownloadRecord { + id: 1, + title: "First Download Title".to_owned(), + status: DownloadStatus::Downloading, + book_id: Some(Number::from(1)), + author_id: Some(Number::from(1)), + size: 3543348019f64, + sizeleft: 1771674009f64, + output_path: Some("/nfs/books/First Author".into()), + indexer: "DrunkenSlug".to_owned(), + download_client: Some("sabnzbd".to_owned()), + }, + DownloadRecord { + id: 2, + title: "Second Download Title".to_owned(), + status: DownloadStatus::Paused, + book_id: Some(Number::from(2)), + author_id: Some(Number::from(2)), + size: 1073741824f64, + sizeleft: 268435456f64, + output_path: Some("/nfs/books/Second Author".into()), + indexer: "Nyaa".to_owned(), + download_client: Some("transmission".to_owned()), + }, + ] + } + } +} diff --git a/src/ui/readarr_ui/downloads/mod.rs b/src/ui/readarr_ui/downloads/mod.rs new file mode 100644 index 00000000..ac1a7c6e --- /dev/null +++ b/src/ui/readarr_ui/downloads/mod.rs @@ -0,0 +1,146 @@ +use ratatui::Frame; +use ratatui::layout::{Constraint, Rect}; +use ratatui::widgets::{Cell, Row}; + +use crate::app::App; +use crate::models::readarr_models::DownloadRecord; +use crate::models::servarr_data::readarr::readarr_data::{ActiveReadarrBlock, DOWNLOADS_BLOCKS}; +use crate::models::{HorizontallyScrollableText, Route}; +use crate::ui::DrawUi; +use crate::ui::styles::ManagarrStyle; +use crate::ui::utils::{get_width_from_percentage, 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::utils::convert_f64_to_gb; + +#[cfg(test)] +#[path = "downloads_ui_tests.rs"] +mod downloads_ui_tests; + +pub(super) struct DownloadsUi; + +impl DrawUi for DownloadsUi { + fn accepts(route: Route) -> bool { + if let Route::Readarr(active_readarr_block, _) = route { + return DOWNLOADS_BLOCKS.contains(&active_readarr_block); + } + + false + } + + fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { + if let Route::Readarr(active_readarr_block, _) = app.get_current_route() { + draw_downloads(f, app, area); + + match active_readarr_block { + ActiveReadarrBlock::DeleteDownloadPrompt => { + let prompt = format!( + "Do you really want to delete this download: \n{}?", + app.data.readarr_data.downloads.current_selection().title + ); + let confirmation_prompt = ConfirmationPrompt::new() + .title("Cancel Download") + .prompt(&prompt) + .yes_no_value(app.data.readarr_data.prompt_confirm); + + f.render_widget( + Popup::new(confirmation_prompt).size(Size::MediumPrompt), + f.area(), + ); + } + ActiveReadarrBlock::UpdateDownloadsPrompt => { + let confirmation_prompt = ConfirmationPrompt::new() + .title("Update Downloads") + .prompt("Do you want to update your downloads?") + .yes_no_value(app.data.readarr_data.prompt_confirm); + + f.render_widget( + Popup::new(confirmation_prompt).size(Size::MediumPrompt), + f.area(), + ); + } + _ => (), + } + } + } +} + +fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { + let current_selection = if app.data.readarr_data.downloads.items.is_empty() { + DownloadRecord::default() + } else { + app.data.readarr_data.downloads.current_selection().clone() + }; + + let downloads_row_mapping = |download_record: &DownloadRecord| { + let DownloadRecord { + title, + size, + sizeleft, + download_client, + indexer, + output_path, + .. + } = download_record; + + if output_path.is_some() { + output_path.as_ref().unwrap().scroll_left_or_reset( + get_width_from_percentage(area, 18), + current_selection == *download_record, + app.should_text_scroll, + ); + } + + let percent = if *size == 0.0 { + 0.0 + } else { + 1f64 - (*sizeleft / *size) + }; + let file_size: f64 = convert_f64_to_gb(*size); + + Row::new(vec![ + Cell::from(title.to_owned()), + Cell::from(format!("{:.0}%", percent * 100.0)), + Cell::from(format!("{file_size:.2} GB")), + Cell::from( + output_path + .as_ref() + .unwrap_or(&HorizontallyScrollableText::default()) + .to_string(), + ), + Cell::from(indexer.to_owned()), + Cell::from( + download_client + .as_ref() + .unwrap_or(&String::new()) + .to_owned(), + ), + ]) + .primary() + }; + let downloads_table = ManagarrTable::new( + Some(&mut app.data.readarr_data.downloads), + downloads_row_mapping, + ) + .block(layout_block_top_border()) + .loading(app.is_loading) + .headers([ + "Title", + "Percent Complete", + "Size", + "Output Path", + "Indexer", + "Download Client", + ]) + .constraints([ + Constraint::Percentage(30), + Constraint::Percentage(11), + Constraint::Percentage(11), + Constraint::Percentage(18), + Constraint::Percentage(17), + Constraint::Percentage(13), + ]); + + f.render_widget(downloads_table, area); +} diff --git a/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_second_item_selected_DeleteDownloadPrompt.snap b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_second_item_selected_DeleteDownloadPrompt.snap new file mode 100644 index 00000000..f0c11f76 --- /dev/null +++ b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_second_item_selected_DeleteDownloadPrompt.snap @@ -0,0 +1,38 @@ +--- +source: src/ui/readarr_ui/downloads/downloads_ui_tests.rs +expression: output +--- +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + Title Percent Complete Size Output Path Indexer Download Client + First Download Title 50% 3.30 GB /nfs/books/First Author DrunkenSlug sabnzbd +=> Second Download Title 75% 1.00 GB /nfs/books/Second Author Nyaa transmission + + + + + + + + + + + + + ╭──────────────────── Cancel Download ────────────────────╮ + │ Do you really want to delete this download: │ + │ Second Download Title? │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │╭────────────────────────────╮╭───────────────────────────╮│ + ││ Yes ││ No ││ + │╰────────────────────────────╯╰───────────────────────────╯│ + ╰───────────────────────────────────────────────────────────╯ diff --git a/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_second_item_selected_Downloads.snap b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_second_item_selected_Downloads.snap new file mode 100644 index 00000000..4c7f4950 --- /dev/null +++ b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_second_item_selected_Downloads.snap @@ -0,0 +1,8 @@ +--- +source: src/ui/readarr_ui/downloads/downloads_ui_tests.rs +expression: output +--- +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + Title Percent Complete Size Output Path Indexer Download Client + First Download Title 50% 3.30 GB /nfs/books/First Author DrunkenSlug sabnzbd +=> Second Download Title 75% 1.00 GB /nfs/books/Second Author Nyaa transmission diff --git a/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_DeleteDownloadPrompt.snap b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_DeleteDownloadPrompt.snap new file mode 100644 index 00000000..697b474e --- /dev/null +++ b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_DeleteDownloadPrompt.snap @@ -0,0 +1,38 @@ +--- +source: src/ui/readarr_ui/downloads/downloads_ui_tests.rs +expression: output +--- +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + Title Percent Complete Size Output Path Indexer Download Client +=> Test Download Title 50% 3.30 GB /nfs/books/Test Author kickass torrents transmission + + + + + + + + + + + + + + ╭──────────────────── Cancel Download ────────────────────╮ + │ Do you really want to delete this download: │ + │ Test Download Title? │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │╭────────────────────────────╮╭───────────────────────────╮│ + ││ Yes ││ No ││ + │╰────────────────────────────╯╰───────────────────────────╯│ + ╰───────────────────────────────────────────────────────────╯ diff --git a/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_Downloads.snap b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_Downloads.snap new file mode 100644 index 00000000..fb117252 --- /dev/null +++ b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_Downloads.snap @@ -0,0 +1,7 @@ +--- +source: src/ui/readarr_ui/downloads/downloads_ui_tests.rs +expression: output +--- +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + Title Percent Complete Size Output Path Indexer Download Client +=> Test Download Title 50% 3.30 GB /nfs/books/Test Author kickass torrents transmission diff --git a/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_UpdateDownloadsPrompt.snap b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_UpdateDownloadsPrompt.snap new file mode 100644 index 00000000..7c0ecc17 --- /dev/null +++ b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_UpdateDownloadsPrompt.snap @@ -0,0 +1,38 @@ +--- +source: src/ui/readarr_ui/downloads/downloads_ui_tests.rs +expression: output +--- +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + Title Percent Complete Size Output Path Indexer Download Client +=> Test Download Title 50% 3.30 GB /nfs/books/Test Author kickass torrents transmission + + + + + + + + + + + + + + ╭─────────────────── Update Downloads ────────────────────╮ + │ Do you want to update your downloads? │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │╭────────────────────────────╮╭───────────────────────────╮│ + ││ Yes ││ No ││ + │╰────────────────────────────╯╰───────────────────────────╯│ + ╰───────────────────────────────────────────────────────────╯ diff --git a/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_renders_empty.snap b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_renders_empty.snap new file mode 100644 index 00000000..982f0817 --- /dev/null +++ b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_renders_empty.snap @@ -0,0 +1,5 @@ +--- +source: src/ui/readarr_ui/downloads/downloads_ui_tests.rs +expression: output +--- +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── diff --git a/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_renders_loading.snap b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_renders_loading.snap new file mode 100644 index 00000000..c74ac942 --- /dev/null +++ b/src/ui/readarr_ui/downloads/snapshots/managarr__ui__readarr_ui__downloads__downloads_ui_tests__tests__snapshot_tests__downloads_ui_renders_loading.snap @@ -0,0 +1,8 @@ +--- +source: src/ui/readarr_ui/downloads/downloads_ui_tests.rs +expression: output +--- +───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + + + Loading ... diff --git a/src/ui/readarr_ui/mod.rs b/src/ui/readarr_ui/mod.rs index e9922aea..5477ab33 100644 --- a/src/ui/readarr_ui/mod.rs +++ b/src/ui/readarr_ui/mod.rs @@ -22,6 +22,7 @@ use super::{ widgets::loading_block::LoadingBlock, }; use crate::ui::readarr_ui::blocklist::BlocklistUi; +use crate::ui::readarr_ui::downloads::DownloadsUi; use crate::ui::utils::{extract_monitored_disk_space_vec, extract_monitored_root_folders}; use crate::{ app::App, @@ -36,6 +37,7 @@ use crate::{ }; mod blocklist; +mod downloads; mod library; mod readarr_ui_utils; @@ -56,6 +58,7 @@ impl DrawUi for ReadarrUi { match route { _ if LibraryUi::accepts(route) => LibraryUi::draw(f, app, content_area), + _ if DownloadsUi::accepts(route) => DownloadsUi::draw(f, app, content_area), _ if BlocklistUi::accepts(route) => BlocklistUi::draw(f, app, content_area), _ => (), } diff --git a/src/ui/readarr_ui/snapshots/managarr__ui__readarr_ui__readarr_ui_tests__tests__snapshot_tests__readarr_tabs_Downloads.snap b/src/ui/readarr_ui/snapshots/managarr__ui__readarr_ui__readarr_ui_tests__tests__snapshot_tests__readarr_tabs_Downloads.snap index d3f01721..86d9b348 100644 --- a/src/ui/readarr_ui/snapshots/managarr__ui__readarr_ui__readarr_ui_tests__tests__snapshot_tests__readarr_tabs_Downloads.snap +++ b/src/ui/readarr_ui/snapshots/managarr__ui__readarr_ui__readarr_ui_tests__tests__snapshot_tests__readarr_tabs_Downloads.snap @@ -4,9 +4,9 @@ expression: output --- ╭ Authors ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Library │ Downloads │ Blocklist │ History │ Root Folders │ Indexers │ System │ -│ │ -│ │ -│ │ +│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│ +│ Title Percent Complete Size Output Path Indexer Download Client │ +│=> Test Download Title 50% 3.30 GB /nfs/books/Test Author kickass torrents transmission │ │ │ │ │ │ │