Added the Root Folders tab

This commit is contained in:
2023-08-08 10:50:06 -06:00
parent d8a93efd5a
commit 9142d5ab3e
5 changed files with 175 additions and 36 deletions
+46 -6
View File
@@ -13,7 +13,7 @@ use crate::models::{
use crate::network::radarr_network::RadarrEvent;
pub struct RadarrData {
pub root_folders: Vec<RootFolder>,
pub root_folders: StatefulTable<RootFolder>,
pub disk_space_vec: Vec<DiskSpace>,
pub version: String,
pub start_time: DateTime<Utc>,
@@ -217,7 +217,7 @@ impl RadarrData {
impl Default for RadarrData {
fn default() -> RadarrData {
RadarrData {
root_folders: Vec::new(),
root_folders: StatefulTable::default(),
disk_space_vec: Vec::new(),
version: String::default(),
start_time: DateTime::default(),
@@ -259,7 +259,7 @@ impl Default for RadarrData {
title: "Library".to_owned(),
route: ActiveRadarrBlock::Movies.into(),
help: String::default(),
contextual_help: Some("<a> add | <e> edit | <s> search | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter | <del> delete"
contextual_help: Some("<a> add | <e> edit | <del> delete | <s> search | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter"
.to_owned()),
},
TabRoute {
@@ -275,6 +275,12 @@ impl Default for RadarrData {
contextual_help: Some("<s> search | <e> edit | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter"
.to_owned()),
},
TabRoute {
title: "Root Folders".to_owned(),
route: ActiveRadarrBlock::RootFolders.into(),
help: String::default(),
contextual_help: Some("<a> add | <del> delete | <r> refresh".to_owned()),
},
]),
movie_info_tabs: TabState::new(vec![
TabRoute {
@@ -361,6 +367,7 @@ pub enum ActiveRadarrBlock {
MovieDetails,
MovieHistory,
Movies,
RootFolders,
UpdateAndScanPrompt,
UpdateAllCollectionsPrompt,
UpdateAllMoviesPrompt,
@@ -561,6 +568,11 @@ impl App {
.dispatch_network_event(RadarrEvent::GetDownloads.into())
.await;
}
ActiveRadarrBlock::RootFolders => {
self
.dispatch_network_event(RadarrEvent::GetRootFolders.into())
.await;
}
ActiveRadarrBlock::Movies => {
self
.dispatch_network_event(RadarrEvent::GetMovies.into())
@@ -1046,7 +1058,7 @@ mod tests {
fn test_radarr_data_defaults() {
let radarr_data = RadarrData::default();
assert_eq!(radarr_data.root_folders, Vec::new());
assert!(radarr_data.root_folders.items.is_empty());
assert_eq!(radarr_data.disk_space_vec, Vec::new());
assert!(radarr_data.version.is_empty());
assert_eq!(radarr_data.start_time, <DateTime<Utc>>::default());
@@ -1087,7 +1099,7 @@ mod tests {
assert!(!radarr_data.is_filtering);
assert!(!radarr_data.prompt_confirm);
assert_eq!(radarr_data.main_tabs.tabs.len(), 3);
assert_eq!(radarr_data.main_tabs.tabs.len(), 4);
assert_str_eq!(radarr_data.main_tabs.tabs[0].title, "Library");
assert_eq!(
@@ -1096,7 +1108,7 @@ mod tests {
);
assert!(radarr_data.main_tabs.tabs[0].help.is_empty());
assert_eq!(radarr_data.main_tabs.tabs[0].contextual_help,
Some("<a> add | <e> edit | <s> search | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter | <del> delete".to_owned()));
Some("<a> add | <e> edit | <del> delete | <s> search | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter".to_owned()));
assert_str_eq!(radarr_data.main_tabs.tabs[1].title, "Downloads");
assert_eq!(
@@ -1118,6 +1130,17 @@ mod tests {
assert_eq!(radarr_data.main_tabs.tabs[2].contextual_help,
Some("<s> search | <e> edit | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter".to_owned()));
assert_str_eq!(radarr_data.main_tabs.tabs[3].title, "Root Folders");
assert_eq!(
radarr_data.main_tabs.tabs[3].route,
ActiveRadarrBlock::RootFolders.into()
);
assert!(radarr_data.main_tabs.tabs[3].help.is_empty());
assert_eq!(
radarr_data.main_tabs.tabs[3].contextual_help,
Some("<a> add | <del> delete | <r> refresh".to_owned())
);
assert_eq!(radarr_data.movie_info_tabs.tabs.len(), 6);
assert_str_eq!(radarr_data.movie_info_tabs.tabs[0].title, "Details");
@@ -1506,6 +1529,23 @@ mod tests {
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_root_folders_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_radarr_block(&ActiveRadarrBlock::RootFolders)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetRootFolders.into()
);
assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_movies_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
+57 -23
View File
@@ -94,6 +94,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
}
}
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_up(),
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_up(),
_ => (),
}
}
@@ -122,6 +123,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
}
}
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_down(),
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_down(),
_ => (),
}
}
@@ -155,6 +157,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
}
}
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_to_top(),
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_top(),
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
self.app.data.radarr_data.search.scroll_home()
}
@@ -194,6 +197,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
}
}
ActiveRadarrBlock::Downloads => self.app.data.radarr_data.downloads.scroll_to_bottom(),
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_bottom(),
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
self.app.data.radarr_data.search.reset_offset()
}
@@ -218,23 +222,24 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
fn handle_left_right_action(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Movies | ActiveRadarrBlock::Downloads | ActiveRadarrBlock::Collections => {
match self.key {
_ if *self.key == DEFAULT_KEYBINDINGS.left.key => {
self.app.data.radarr_data.main_tabs.previous();
self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.main_tabs.get_active_route(),
);
}
_ if *self.key == DEFAULT_KEYBINDINGS.right.key => {
self.app.data.radarr_data.main_tabs.next();
self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.main_tabs.get_active_route(),
);
}
_ => (),
ActiveRadarrBlock::Movies
| ActiveRadarrBlock::Downloads
| ActiveRadarrBlock::Collections
| ActiveRadarrBlock::RootFolders => match self.key {
_ if *self.key == DEFAULT_KEYBINDINGS.left.key => {
self.app.data.radarr_data.main_tabs.previous();
self
.app
.pop_and_push_navigation_stack(*self.app.data.radarr_data.main_tabs.get_active_route());
}
}
_ if *self.key == DEFAULT_KEYBINDINGS.right.key => {
self.app.data.radarr_data.main_tabs.next();
self
.app
.pop_and_push_navigation_stack(*self.app.data.radarr_data.main_tabs.get_active_route());
}
_ => (),
},
ActiveRadarrBlock::DeleteMoviePrompt
| ActiveRadarrBlock::DeleteDownloadPrompt
| ActiveRadarrBlock::UpdateAllMoviesPrompt
@@ -505,6 +510,12 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
}
_ => (),
},
ActiveRadarrBlock::RootFolders => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ => (),
},
_ if SEARCH_BLOCKS.contains(self.active_radarr_block) => {
handle_text_box_keys!(self, key, self.app.data.radarr_data.search)
}
@@ -714,7 +725,7 @@ mod tests {
mod test_handle_scroll_up_and_down {
use rstest::rstest;
use crate::models::radarr_models::DownloadRecord;
use crate::models::radarr_models::{DownloadRecord, RootFolder};
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
use super::*;
@@ -772,12 +783,22 @@ mod tests {
None,
title
);
test_iterable_scroll!(
test_root_folders_scroll,
RadarrHandler,
root_folders,
simple_stateful_iterable_vec!(RootFolder, String, path),
ActiveRadarrBlock::RootFolders,
None,
path
);
}
mod test_handle_home_end {
use pretty_assertions::assert_eq;
use crate::models::radarr_models::DownloadRecord;
use crate::models::radarr_models::{DownloadRecord, RootFolder};
use crate::{
extended_stateful_iterable_vec, test_iterable_home_and_end, test_text_box_home_end_keys,
};
@@ -838,6 +859,16 @@ mod tests {
title
);
test_iterable_home_and_end!(
test_root_folders_home_end,
RadarrHandler,
root_folders,
extended_stateful_iterable_vec!(RootFolder, String, path),
ActiveRadarrBlock::RootFolders,
None,
path
);
#[rstest]
fn test_search_boxes_home_end_keys(
#[values(ActiveRadarrBlock::SearchMovie, ActiveRadarrBlock::SearchCollection)]
@@ -896,10 +927,11 @@ mod tests {
use super::*;
#[rstest]
#[case(ActiveRadarrBlock::Movies, 0, ActiveRadarrBlock::Collections)]
#[case(ActiveRadarrBlock::Movies, 0, ActiveRadarrBlock::RootFolders)]
#[case(ActiveRadarrBlock::Downloads, 1, ActiveRadarrBlock::Movies)]
#[case(ActiveRadarrBlock::Collections, 2, ActiveRadarrBlock::Downloads)]
fn test_movies_downloads_collections_left(
#[case(ActiveRadarrBlock::RootFolders, 3, ActiveRadarrBlock::Collections)]
fn test_radarr_tab_left(
#[case] active_radarr_block: ActiveRadarrBlock,
#[case] index: usize,
#[case] expected_radarr_block: ActiveRadarrBlock,
@@ -925,8 +957,9 @@ mod tests {
#[rstest]
#[case(ActiveRadarrBlock::Movies, 0, ActiveRadarrBlock::Downloads)]
#[case(ActiveRadarrBlock::Downloads, 1, ActiveRadarrBlock::Collections)]
#[case(ActiveRadarrBlock::Collections, 2, ActiveRadarrBlock::Movies)]
fn test_movie_downloads_collections_right(
#[case(ActiveRadarrBlock::Collections, 2, ActiveRadarrBlock::RootFolders)]
#[case(ActiveRadarrBlock::RootFolders, 3, ActiveRadarrBlock::Movies)]
fn test_radarr_tab_right(
#[case] active_radarr_block: ActiveRadarrBlock,
#[case] index: usize,
#[case] expected_radarr_block: ActiveRadarrBlock,
@@ -1499,7 +1532,8 @@ mod tests {
#[values(
ActiveRadarrBlock::Movies,
ActiveRadarrBlock::Collections,
ActiveRadarrBlock::Downloads
ActiveRadarrBlock::Downloads,
ActiveRadarrBlock::RootFolders
)]
active_radarr_block: ActiveRadarrBlock,
) {
+12 -1
View File
@@ -22,12 +22,23 @@ pub struct SystemStatus {
pub start_time: DateTime<Utc>,
}
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
#[derive(Derivative, Deserialize, Debug, Clone, Eq, PartialEq)]
#[derivative(Default)]
#[serde(rename_all = "camelCase")]
pub struct RootFolder {
#[derivative(Default(value = "Number::from(0)"))]
pub id: Number,
pub path: String,
pub accessible: bool,
#[derivative(Default(value = "Number::from(0)"))]
pub free_space: Number,
pub unmapped_folders: Option<Vec<UnmappedFolder>>,
}
#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
pub struct UnmappedFolder {
pub name: String,
pub path: String,
}
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
+12 -5
View File
@@ -639,7 +639,7 @@ impl<'a> Network<'a> {
self
.handle_request::<(), Vec<RootFolder>>(request_props, |root_folders, mut app| {
app.data.radarr_data.root_folders = root_folders;
app.data.radarr_data.root_folders.set_items(root_folders);
})
.await;
}
@@ -732,7 +732,7 @@ impl<'a> Network<'a> {
let quality_profile_id = self.extract_quality_profile_id().await;
let tag_ids_vec = self.extract_and_add_tag_ids_vec().await;
let app = self.app.lock().await;
let root_folders = app.data.radarr_data.root_folders.to_vec();
let root_folders = app.data.radarr_data.root_folders.items.to_vec();
let (tmdb_id, title) = if let Route::Radarr(active_radarr_block, _) = app.get_current_route()
{
if *active_radarr_block == ActiveRadarrBlock::CollectionDetails {
@@ -2059,6 +2059,7 @@ mod test {
#[tokio::test]
async fn test_handle_get_root_folders_event() {
let root_folder_json = json!([{
"id": 1,
"path": "/nfs",
"accessible": true,
"freeSpace": 219902325555200u64,
@@ -2078,7 +2079,7 @@ mod test {
async_server.assert_async().await;
assert_eq!(
app_arc.lock().await.data.radarr_data.root_folders,
app_arc.lock().await.data.radarr_data.root_folders.items,
vec![root_folder()]
);
}
@@ -2202,18 +2203,22 @@ mod test {
{
let mut app = app_arc.lock().await;
app.data.radarr_data.root_folders = vec![
app.data.radarr_data.root_folders.set_items(vec![
RootFolder {
id: Number::from(1),
path: "/nfs".to_owned(),
accessible: true,
free_space: Number::from(219902325555200u64),
unmapped_folders: None,
},
RootFolder {
id: Number::from(2),
path: "/nfs2".to_owned(),
accessible: true,
free_space: Number::from(21990232555520u64),
unmapped_folders: None,
},
];
]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(2222, "HD - 1080p".to_owned())]);
app.data.radarr_data.tags_map =
@@ -2926,9 +2931,11 @@ mod test {
fn root_folder() -> RootFolder {
RootFolder {
id: Number::from(1),
path: "/nfs".to_owned(),
accessible: true,
free_space: Number::from(219902325555200u64),
unmapped_folders: None,
}
}
+48 -1
View File
@@ -15,7 +15,7 @@ use crate::app::radarr::{
};
use crate::app::App;
use crate::logos::RADARR_LOGO;
use crate::models::radarr_models::{Collection, DiskSpace, DownloadRecord, Movie};
use crate::models::radarr_models::{Collection, DiskSpace, DownloadRecord, Movie, RootFolder};
use crate::models::{HorizontallyScrollableText, 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;
@@ -71,6 +71,7 @@ pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, ar
11,
),
ActiveRadarrBlock::Downloads => draw_downloads(f, app, content_rect),
ActiveRadarrBlock::RootFolders => draw_root_folders(f, app, content_rect),
ActiveRadarrBlock::Collections => draw_collections(f, app, content_rect),
_ if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) => {
draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info_popup)
@@ -720,6 +721,52 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
);
}
fn draw_root_folders<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
draw_table(
f,
area,
layout_block_top_border(),
TableProps {
content: &mut app.data.radarr_data.root_folders,
table_headers: vec!["Path", "Free Space", "Unmapped Folders"],
constraints: vec![
Constraint::Percentage(60),
Constraint::Percentage(20),
Constraint::Percentage(20),
],
help: app
.data
.radarr_data
.main_tabs
.get_active_tab_contextual_help(),
},
|root_folders| {
let RootFolder {
path,
free_space,
unmapped_folders,
..
} = root_folders;
let space: f64 = convert_to_gb(free_space.as_u64().unwrap());
Row::new(vec![
Cell::from(path.to_owned()),
Cell::from(format!("{:.2} GB", space)),
Cell::from(
unmapped_folders
.as_ref()
.unwrap_or(&Vec::new())
.len()
.to_string(),
),
])
.style(style_primary())
},
app.is_loading,
);
}
fn determine_row_style(downloads_vec: &[DownloadRecord], movie: &Movie) -> Style {
if !movie.has_file {
if let Some(download) = downloads_vec