Added the Root Folders tab
This commit is contained in:
+46
-6
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
) {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user