Implemented the ability to edit collections and fixed a refresh bug so screens will automatically refresh when users edit movies or collections

This commit is contained in:
2023-08-08 10:50:06 -06:00
parent 173e81fc17
commit bf5ecbd5a6
18 changed files with 2040 additions and 273 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "managarr" name = "managarr"
version = "0.0.13" version = "0.0.14"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"] authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "A TUI for managing *arr servers" description = "A TUI for managing *arr servers"
keywords = ["managarr", "tui-rs", "dashboard", "servarr"] keywords = ["managarr", "tui-rs", "dashboard", "servarr"]
+2 -2
View File
@@ -84,10 +84,10 @@ tautulli:
- [x] Trigger automatic searches for movies - [x] Trigger automatic searches for movies
- [x] Trigger refresh and disk scan for movies, downloads, and collections - [x] Trigger refresh and disk scan for movies, downloads, and collections
- [x] Manually search for movies - [x] Manually search for movies
- [x] Edit movies - [x] Edit your movies and collections
- [x] Manage your tags
- [ ] Manage your quality profiles - [ ] Manage your quality profiles
- [ ] Manage your quality definitions - [ ] Manage your quality definitions
- [x] Manage your tags
- [ ] Manage your indexers - [ ] Manage your indexers
### Sonarr ### Sonarr
+2 -2
View File
@@ -194,7 +194,7 @@ mod tests {
assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]); assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]);
assert!(app.network_tx.is_none()); assert!(app.network_tx.is_none());
assert_eq!(app.error, HorizontallyScrollableText::default()); assert_eq!(app.error, HorizontallyScrollableText::default());
assert_eq!(app.response, String::default()); assert!(app.response.is_empty());
assert_eq!(app.server_tabs.index, 0); assert_eq!(app.server_tabs.index, 0);
assert_eq!( assert_eq!(
app.server_tabs.tabs, app.server_tabs.tabs,
@@ -289,7 +289,7 @@ mod tests {
assert_eq!(app.tick_count, 0); assert_eq!(app.tick_count, 0);
assert_eq!(app.error, HorizontallyScrollableText::default()); assert_eq!(app.error, HorizontallyScrollableText::default());
assert_eq!(app.data.radarr_data.version, String::default()); assert!(app.data.radarr_data.version.is_empty());
} }
#[test] #[test]
+521 -90
View File
@@ -20,9 +20,9 @@ pub struct RadarrData {
pub movies: StatefulTable<Movie>, pub movies: StatefulTable<Movie>,
pub filtered_movies: StatefulTable<Movie>, pub filtered_movies: StatefulTable<Movie>,
pub add_searched_movies: StatefulTable<AddMovieSearchResult>, pub add_searched_movies: StatefulTable<AddMovieSearchResult>,
pub movie_monitor_list: StatefulList<Monitor>, pub monitor_list: StatefulList<Monitor>,
pub movie_minimum_availability_list: StatefulList<MinimumAvailability>, pub minimum_availability_list: StatefulList<MinimumAvailability>,
pub movie_quality_profile_list: StatefulList<String>, pub quality_profile_list: StatefulList<String>,
pub selected_block: ActiveRadarrBlock, pub selected_block: ActiveRadarrBlock,
pub downloads: StatefulTable<DownloadRecord>, pub downloads: StatefulTable<DownloadRecord>,
pub quality_profile_map: BiMap<u64, String>, pub quality_profile_map: BiMap<u64, String>,
@@ -47,6 +47,7 @@ pub struct RadarrData {
pub edit_path: HorizontallyScrollableText, pub edit_path: HorizontallyScrollableText,
pub edit_tags: HorizontallyScrollableText, pub edit_tags: HorizontallyScrollableText,
pub edit_monitored: Option<bool>, pub edit_monitored: Option<bool>,
pub edit_search_on_add: Option<bool>,
pub sort_ascending: Option<bool>, pub sort_ascending: Option<bool>,
pub prompt_confirm: bool, pub prompt_confirm: bool,
pub is_searching: bool, pub is_searching: bool,
@@ -74,11 +75,12 @@ impl RadarrData {
self.filtered_collections = StatefulTable::default(); self.filtered_collections = StatefulTable::default();
} }
pub fn reset_add_edit_movie_fields(&mut self) { pub fn reset_add_edit_media_fields(&mut self) {
self.edit_monitored = None; self.edit_monitored = None;
self.edit_search_on_add = None;
self.edit_path = HorizontallyScrollableText::default(); self.edit_path = HorizontallyScrollableText::default();
self.edit_tags = HorizontallyScrollableText::default(); self.edit_tags = HorizontallyScrollableText::default();
self.reset_movie_preferences_selections(); self.reset_preferences_selections();
} }
pub fn reset_movie_info_tabs(&mut self) { pub fn reset_movie_info_tabs(&mut self) {
@@ -95,29 +97,25 @@ impl RadarrData {
self.movie_info_tabs.index = 0; self.movie_info_tabs.index = 0;
} }
pub fn reset_movie_preferences_selections(&mut self) { pub fn reset_preferences_selections(&mut self) {
self.movie_monitor_list = StatefulList::default(); self.monitor_list = StatefulList::default();
self.movie_minimum_availability_list = StatefulList::default(); self.minimum_availability_list = StatefulList::default();
self.movie_quality_profile_list = StatefulList::default(); self.quality_profile_list = StatefulList::default();
} }
pub fn populate_movie_preferences_lists(&mut self) { pub fn populate_preferences_lists(&mut self) {
self.monitor_list.set_items(Vec::from_iter(Monitor::iter()));
self self
.movie_monitor_list .minimum_availability_list
.set_items(Vec::from_iter(Monitor::iter()));
self
.movie_minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter())); .set_items(Vec::from_iter(MinimumAvailability::iter()));
let mut quality_profile_names: Vec<String> = let mut quality_profile_names: Vec<String> =
self.quality_profile_map.right_values().cloned().collect(); self.quality_profile_map.right_values().cloned().collect();
quality_profile_names.sort(); quality_profile_names.sort();
self self.quality_profile_list.set_items(quality_profile_names);
.movie_quality_profile_list
.set_items(quality_profile_names);
} }
pub fn populate_edit_movie_fields(&mut self) { pub fn populate_edit_movie_fields(&mut self) {
self.populate_movie_preferences_lists(); self.populate_preferences_lists();
let Movie { let Movie {
path, path,
tags, tags,
@@ -147,12 +145,12 @@ impl RadarrData {
self.edit_monitored = Some(*monitored); self.edit_monitored = Some(*monitored);
let minimum_availability_index = self let minimum_availability_index = self
.movie_minimum_availability_list .minimum_availability_list
.items .items
.iter() .iter()
.position(|ma| ma == minimum_availability); .position(|ma| ma == minimum_availability);
self self
.movie_minimum_availability_list .minimum_availability_list
.state .state
.select(minimum_availability_index); .select(minimum_availability_index);
@@ -161,12 +159,56 @@ impl RadarrData {
.get_by_left(&quality_profile_id.as_u64().unwrap()) .get_by_left(&quality_profile_id.as_u64().unwrap())
.unwrap(); .unwrap();
let quality_profile_index = self let quality_profile_index = self
.movie_quality_profile_list .quality_profile_list
.items .items
.iter() .iter()
.position(|profile| profile == quality_profile_name); .position(|profile| profile == quality_profile_name);
self self
.movie_quality_profile_list .quality_profile_list
.state
.select(quality_profile_index);
}
pub fn populate_edit_collection_fields(&mut self) {
self.populate_preferences_lists();
let Collection {
root_folder_path,
monitored,
search_on_add,
minimum_availability,
quality_profile_id,
..
} = if self.filtered_collections.items.is_empty() {
self.collections.current_selection()
} else {
self.filtered_collections.current_selection()
};
self.edit_path = root_folder_path.clone().unwrap_or_default().into();
self.edit_monitored = Some(*monitored);
self.edit_search_on_add = Some(*search_on_add);
let minimum_availability_index = self
.minimum_availability_list
.items
.iter()
.position(|ma| ma == minimum_availability);
self
.minimum_availability_list
.state
.select(minimum_availability_index);
let quality_profile_name = self
.quality_profile_map
.get_by_left(&quality_profile_id.as_u64().unwrap())
.unwrap();
let quality_profile_index = self
.quality_profile_list
.items
.iter()
.position(|profile| profile == quality_profile_name);
self
.quality_profile_list
.state .state
.select(quality_profile_index); .select(quality_profile_index);
} }
@@ -181,9 +223,9 @@ impl Default for RadarrData {
start_time: DateTime::default(), start_time: DateTime::default(),
movies: StatefulTable::default(), movies: StatefulTable::default(),
add_searched_movies: StatefulTable::default(), add_searched_movies: StatefulTable::default(),
movie_monitor_list: StatefulList::default(), monitor_list: StatefulList::default(),
movie_minimum_availability_list: StatefulList::default(), minimum_availability_list: StatefulList::default(),
movie_quality_profile_list: StatefulList::default(), quality_profile_list: StatefulList::default(),
selected_block: ActiveRadarrBlock::AddMovieSelectMonitor, selected_block: ActiveRadarrBlock::AddMovieSelectMonitor,
filtered_movies: StatefulTable::default(), filtered_movies: StatefulTable::default(),
downloads: StatefulTable::default(), downloads: StatefulTable::default(),
@@ -207,6 +249,7 @@ impl Default for RadarrData {
edit_path: HorizontallyScrollableText::default(), edit_path: HorizontallyScrollableText::default(),
edit_tags: HorizontallyScrollableText::default(), edit_tags: HorizontallyScrollableText::default(),
edit_monitored: None, edit_monitored: None,
edit_search_on_add: None,
sort_ascending: None, sort_ascending: None,
is_searching: false, is_searching: false,
is_filtering: false, is_filtering: false,
@@ -229,7 +272,7 @@ impl Default for RadarrData {
title: "Collections".to_owned(), title: "Collections".to_owned(),
route: ActiveRadarrBlock::Collections.into(), route: ActiveRadarrBlock::Collections.into(),
help: String::default(), help: String::default(),
contextual_help: Some("<s> search | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter" contextual_help: Some("<s> search | <e> edit | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter"
.to_owned()), .to_owned()),
}, },
]), ]),
@@ -295,6 +338,13 @@ pub enum ActiveRadarrBlock {
DeleteMoviePrompt, DeleteMoviePrompt,
DeleteDownloadPrompt, DeleteDownloadPrompt,
Downloads, Downloads,
EditCollectionPrompt,
EditCollectionConfirmPrompt,
EditCollectionRootFolderPathInput,
EditCollectionSelectMinimumAvailability,
EditCollectionSelectQualityProfile,
EditCollectionToggleSearchOnAdd,
EditCollectionToggleMonitored,
EditMoviePrompt, EditMoviePrompt,
EditMovieConfirmPrompt, EditMovieConfirmPrompt,
EditMoviePathInput, EditMoviePathInput,
@@ -331,6 +381,15 @@ pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 9] = [
ActiveRadarrBlock::AddMovieAlreadyInLibrary, ActiveRadarrBlock::AddMovieAlreadyInLibrary,
ActiveRadarrBlock::AddMovieTagsInput, ActiveRadarrBlock::AddMovieTagsInput,
]; ];
pub const EDIT_COLLECTION_BLOCKS: [ActiveRadarrBlock; 7] = [
ActiveRadarrBlock::EditCollectionPrompt,
ActiveRadarrBlock::EditCollectionConfirmPrompt,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd,
ActiveRadarrBlock::EditCollectionToggleMonitored,
];
pub const EDIT_MOVIE_BLOCKS: [ActiveRadarrBlock; 7] = [ pub const EDIT_MOVIE_BLOCKS: [ActiveRadarrBlock; 7] = [
ActiveRadarrBlock::EditMoviePrompt, ActiveRadarrBlock::EditMoviePrompt,
ActiveRadarrBlock::EditMovieConfirmPrompt, ActiveRadarrBlock::EditMovieConfirmPrompt,
@@ -366,7 +425,7 @@ pub const FILTER_BLOCKS: [ActiveRadarrBlock; 2] = [
]; ];
impl ActiveRadarrBlock { impl ActiveRadarrBlock {
pub fn next_add_prompt_block(&self) -> Self { pub fn next_add_movie_prompt_block(&self) -> Self {
match self { match self {
ActiveRadarrBlock::AddMovieSelectMonitor => { ActiveRadarrBlock::AddMovieSelectMonitor => {
ActiveRadarrBlock::AddMovieSelectMinimumAvailability ActiveRadarrBlock::AddMovieSelectMinimumAvailability
@@ -380,7 +439,7 @@ impl ActiveRadarrBlock {
} }
} }
pub fn next_edit_prompt_block(&self) -> Self { pub fn next_edit_movie_prompt_block(&self) -> Self {
match self { match self {
ActiveRadarrBlock::EditMovieToggleMonitored => { ActiveRadarrBlock::EditMovieToggleMonitored => {
ActiveRadarrBlock::EditMovieSelectMinimumAvailability ActiveRadarrBlock::EditMovieSelectMinimumAvailability
@@ -395,7 +454,28 @@ impl ActiveRadarrBlock {
} }
} }
pub fn previous_add_prompt_block(&self) -> Self { pub fn next_edit_collection_prompt_block(&self) -> Self {
match self {
ActiveRadarrBlock::EditCollectionToggleMonitored => {
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
}
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => {
ActiveRadarrBlock::EditCollectionSelectQualityProfile
}
ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
ActiveRadarrBlock::EditCollectionRootFolderPathInput
}
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd
}
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd => {
ActiveRadarrBlock::EditCollectionConfirmPrompt
}
_ => ActiveRadarrBlock::EditCollectionToggleMonitored,
}
}
pub fn previous_add_movie_prompt_block(&self) -> Self {
match self { match self {
ActiveRadarrBlock::AddMovieSelectMonitor => ActiveRadarrBlock::AddMovieConfirmPrompt, ActiveRadarrBlock::AddMovieSelectMonitor => ActiveRadarrBlock::AddMovieConfirmPrompt,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => { ActiveRadarrBlock::AddMovieSelectMinimumAvailability => {
@@ -410,7 +490,7 @@ impl ActiveRadarrBlock {
} }
} }
pub fn previous_edit_prompt_block(&self) -> Self { pub fn previous_edit_movie_prompt_block(&self) -> Self {
match self { match self {
ActiveRadarrBlock::EditMovieToggleMonitored => ActiveRadarrBlock::EditMovieConfirmPrompt, ActiveRadarrBlock::EditMovieToggleMonitored => ActiveRadarrBlock::EditMovieConfirmPrompt,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability => { ActiveRadarrBlock::EditMovieSelectMinimumAvailability => {
@@ -425,6 +505,30 @@ impl ActiveRadarrBlock {
_ => ActiveRadarrBlock::EditMovieToggleMonitored, _ => ActiveRadarrBlock::EditMovieToggleMonitored,
} }
} }
pub fn previous_edit_collection_prompt_block(&self) -> Self {
match self {
ActiveRadarrBlock::EditCollectionToggleMonitored => {
ActiveRadarrBlock::EditCollectionConfirmPrompt
}
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => {
ActiveRadarrBlock::EditCollectionToggleMonitored
}
ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
}
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
ActiveRadarrBlock::EditCollectionSelectQualityProfile
}
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd => {
ActiveRadarrBlock::EditCollectionRootFolderPathInput
}
ActiveRadarrBlock::EditCollectionConfirmPrompt => {
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd
}
_ => ActiveRadarrBlock::EditCollectionToggleMonitored,
}
}
} }
impl From<ActiveRadarrBlock> for Route { impl From<ActiveRadarrBlock> for Route {
@@ -538,10 +642,14 @@ impl App {
self.dispatch_by_radarr_block(&active_radarr_block).await; self.dispatch_by_radarr_block(&active_radarr_block).await;
} }
if self.is_routing || self.tick_count % self.tick_until_poll == 0 { if self.should_refresh {
self.refresh_metadata().await;
self.dispatch_by_radarr_block(&active_radarr_block).await; self.dispatch_by_radarr_block(&active_radarr_block).await;
} }
if self.is_routing || self.tick_count % self.tick_until_poll == 0 {
self.dispatch_by_radarr_block(&active_radarr_block).await;
self.refresh_metadata().await;
}
} }
async fn refresh_metadata(&mut self) { async fn refresh_metadata(&mut self) {
@@ -603,6 +711,7 @@ pub mod radarr_test_utils {
edit_path: "test path".to_owned().into(), edit_path: "test path".to_owned().into(),
edit_tags: "usenet, test".to_owned().into(), edit_tags: "usenet, test".to_owned().into(),
edit_monitored: Some(true), edit_monitored: Some(true),
edit_search_on_add: Some(true),
file_details: "test file details".to_owned(), file_details: "test file details".to_owned(),
audio_details: "test audio details".to_owned(), audio_details: "test audio details".to_owned(),
video_details: "test video details".to_owned(), video_details: "test video details".to_owned(),
@@ -618,14 +727,12 @@ pub mod radarr_test_utils {
.movie_releases .movie_releases
.set_items(vec![Release::default()]); .set_items(vec![Release::default()]);
radarr_data.movie_info_tabs.index = 1; radarr_data.movie_info_tabs.index = 1;
radarr_data.monitor_list.set_items(vec![Monitor::default()]);
radarr_data radarr_data
.movie_monitor_list .minimum_availability_list
.set_items(vec![Monitor::default()]);
radarr_data
.movie_minimum_availability_list
.set_items(vec![MinimumAvailability::default()]); .set_items(vec![MinimumAvailability::default()]);
radarr_data radarr_data
.movie_quality_profile_list .quality_profile_list
.set_items(vec![String::default()]); .set_items(vec![String::default()]);
radarr_data radarr_data
.movie_releases_sort .movie_releases_sort
@@ -667,9 +774,10 @@ pub mod radarr_test_utils {
} }
#[macro_export] #[macro_export]
macro_rules! assert_edit_movie_reset { macro_rules! assert_edit_media_reset {
($radarr_data:expr) => { ($radarr_data:expr) => {
assert!($radarr_data.edit_monitored.is_none()); assert!($radarr_data.edit_monitored.is_none());
assert!($radarr_data.edit_search_on_add.is_none());
assert!($radarr_data.edit_path.text.is_empty()); assert!($radarr_data.edit_path.text.is_empty());
assert!($radarr_data.edit_tags.text.is_empty()); assert!($radarr_data.edit_tags.text.is_empty());
}; };
@@ -703,14 +811,11 @@ pub mod radarr_test_utils {
} }
#[macro_export] #[macro_export]
macro_rules! assert_movie_preferences_selections_reset { macro_rules! assert_preferences_selections_reset {
($radarr_data:expr) => { ($radarr_data:expr) => {
assert!($radarr_data.movie_monitor_list.items.is_empty()); assert!($radarr_data.monitor_list.items.is_empty());
assert!($radarr_data assert!($radarr_data.minimum_availability_list.items.is_empty());
.movie_minimum_availability_list assert!($radarr_data.quality_profile_list.items.is_empty());
.items
.is_empty());
assert!($radarr_data.movie_quality_profile_list.items.is_empty());
}; };
} }
} }
@@ -719,6 +824,7 @@ pub mod radarr_test_utils {
mod tests { mod tests {
mod radarr_data_tests { mod radarr_data_tests {
use bimap::BiMap; use bimap::BiMap;
use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq}; use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest; use rstest::rstest;
use serde_json::Number; use serde_json::Number;
@@ -726,8 +832,10 @@ mod tests {
use crate::app::radarr::radarr_test_utils::create_test_radarr_data; use crate::app::radarr::radarr_test_utils::create_test_radarr_data;
use crate::app::radarr::{ActiveRadarrBlock, RadarrData}; use crate::app::radarr::{ActiveRadarrBlock, RadarrData};
use crate::models::radarr_models::{MinimumAvailability, Monitor, Movie}; use crate::models::radarr_models::{Collection, MinimumAvailability, Monitor, Movie};
use crate::models::{HorizontallyScrollableText, Route, StatefulTable}; use crate::models::HorizontallyScrollableText;
use crate::models::Route;
use crate::models::StatefulTable;
#[test] #[test]
fn test_from_tuple_to_route_with_context() { fn test_from_tuple_to_route_with_context() {
@@ -780,30 +888,31 @@ mod tests {
} }
#[test] #[test]
fn test_reset_edit_movie() { fn test_reset_add_edit_media_fields() {
let mut radarr_data = RadarrData { let mut radarr_data = RadarrData {
edit_monitored: Some(true), edit_monitored: Some(true),
edit_search_on_add: Some(true),
edit_path: "test path".to_owned().into(), edit_path: "test path".to_owned().into(),
edit_tags: "test tag".to_owned().into(), edit_tags: "test tag".to_owned().into(),
..RadarrData::default() ..RadarrData::default()
}; };
radarr_data.reset_add_edit_movie_fields(); radarr_data.reset_add_edit_media_fields();
assert_edit_movie_reset!(radarr_data); assert_edit_media_reset!(radarr_data);
} }
#[test] #[test]
fn test_reset_movie_preferences_selections() { fn test_reset_preferences_selections() {
let mut radarr_data = create_test_radarr_data(); let mut radarr_data = create_test_radarr_data();
radarr_data.reset_movie_preferences_selections(); radarr_data.reset_preferences_selections();
assert_movie_preferences_selections_reset!(radarr_data); assert_preferences_selections_reset!(radarr_data);
} }
#[test] #[test]
fn test_populate_movie_preferences_lists() { fn test_populate_preferences_lists() {
let mut radarr_data = RadarrData { let mut radarr_data = RadarrData {
quality_profile_map: BiMap::from_iter([ quality_profile_map: BiMap::from_iter([
(2222, "HD - 1080p".to_owned()), (2222, "HD - 1080p".to_owned()),
@@ -812,18 +921,18 @@ mod tests {
..RadarrData::default() ..RadarrData::default()
}; };
radarr_data.populate_movie_preferences_lists(); radarr_data.populate_preferences_lists();
assert_eq!( assert_eq!(
radarr_data.movie_monitor_list.items, radarr_data.monitor_list.items,
Vec::from_iter(Monitor::iter()) Vec::from_iter(Monitor::iter())
); );
assert_eq!( assert_eq!(
radarr_data.movie_minimum_availability_list.items, radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter()) Vec::from_iter(MinimumAvailability::iter())
); );
assert_eq!( assert_eq!(
radarr_data.movie_quality_profile_list.items, radarr_data.quality_profile_list.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()] vec!["Any".to_owned(), "HD - 1080p".to_owned()]
); );
} }
@@ -860,27 +969,236 @@ mod tests {
radarr_data.populate_edit_movie_fields(); radarr_data.populate_edit_movie_fields();
assert_eq!( assert_eq!(
radarr_data.movie_minimum_availability_list.items, radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter()) Vec::from_iter(MinimumAvailability::iter())
); );
assert_eq!( assert_eq!(
radarr_data radarr_data.minimum_availability_list.current_selection(),
.movie_minimum_availability_list
.current_selection(),
&MinimumAvailability::Released &MinimumAvailability::Released
); );
assert_eq!( assert_eq!(
radarr_data.movie_quality_profile_list.items, radarr_data.quality_profile_list.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()] vec!["Any".to_owned(), "HD - 1080p".to_owned()]
); );
assert_str_eq!( assert_str_eq!(
radarr_data.movie_quality_profile_list.current_selection(), radarr_data.quality_profile_list.current_selection(),
"HD - 1080p" "HD - 1080p"
); );
assert_str_eq!(radarr_data.edit_path.text, "/nfs/movies/Test"); assert_str_eq!(radarr_data.edit_path.text, "/nfs/movies/Test");
assert_str_eq!(radarr_data.edit_tags.text, "usenet, test"); assert_str_eq!(radarr_data.edit_tags.text, "usenet, test");
assert_eq!(radarr_data.edit_monitored, Some(true)); assert_eq!(radarr_data.edit_monitored, Some(true));
} }
#[rstest]
fn test_populate_edit_collection_fields(
#[values(true, false)] test_filtered_collections: bool,
) {
let mut radarr_data = RadarrData {
edit_path: HorizontallyScrollableText::default(),
edit_monitored: None,
edit_search_on_add: None,
quality_profile_map: BiMap::from_iter([
(2222, "HD - 1080p".to_owned()),
(1111, "Any".to_owned()),
]),
filtered_collections: StatefulTable::default(),
..create_test_radarr_data()
};
let collection = Collection {
root_folder_path: Some("/nfs/movies/Test".to_owned()),
monitored: true,
search_on_add: true,
quality_profile_id: Number::from(2222),
minimum_availability: MinimumAvailability::Released,
..Collection::default()
};
if test_filtered_collections {
radarr_data.filtered_collections.set_items(vec![collection]);
} else {
radarr_data.collections.set_items(vec![collection]);
}
radarr_data.populate_edit_collection_fields();
assert_eq!(
radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter())
);
assert_eq!(
radarr_data.minimum_availability_list.current_selection(),
&MinimumAvailability::Released
);
assert_eq!(
radarr_data.quality_profile_list.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()]
);
assert_str_eq!(
radarr_data.quality_profile_list.current_selection(),
"HD - 1080p"
);
assert_str_eq!(radarr_data.edit_path.text, "/nfs/movies/Test");
assert_eq!(radarr_data.edit_monitored, Some(true));
assert_eq!(radarr_data.edit_search_on_add, Some(true));
}
#[test]
fn test_radarr_data_defaults() {
let radarr_data = RadarrData::default();
assert_eq!(radarr_data.root_folders, Vec::new());
assert_eq!(radarr_data.disk_space_vec, Vec::new());
assert!(radarr_data.version.is_empty());
assert_eq!(radarr_data.start_time, <DateTime<Utc>>::default());
assert!(radarr_data.movies.items.is_empty());
assert!(radarr_data.add_searched_movies.items.is_empty());
assert!(radarr_data.monitor_list.items.is_empty());
assert!(radarr_data.minimum_availability_list.items.is_empty());
assert!(radarr_data.quality_profile_list.items.is_empty());
assert_eq!(
radarr_data.selected_block,
ActiveRadarrBlock::AddMovieSelectMonitor
);
assert!(radarr_data.filtered_movies.items.is_empty());
assert!(radarr_data.downloads.items.is_empty());
assert!(radarr_data.quality_profile_map.is_empty());
assert!(radarr_data.tags_map.is_empty());
assert!(radarr_data.file_details.is_empty());
assert!(radarr_data.audio_details.is_empty());
assert!(radarr_data.video_details.is_empty());
assert!(radarr_data.movie_details.get_text().is_empty());
assert!(radarr_data.movie_history.items.is_empty());
assert!(radarr_data.movie_cast.items.is_empty());
assert!(radarr_data.movie_crew.items.is_empty());
assert!(radarr_data.movie_releases.items.is_empty());
assert!(radarr_data.movie_releases_sort.items.is_empty());
assert!(radarr_data.collections.items.is_empty());
assert!(radarr_data.filtered_collections.items.is_empty());
assert!(radarr_data.collection_movies.items.is_empty());
assert!(radarr_data.prompt_confirm_action.is_none());
assert!(radarr_data.search.text.is_empty());
assert!(radarr_data.filter.text.is_empty());
assert!(radarr_data.edit_path.text.is_empty());
assert!(radarr_data.edit_tags.text.is_empty());
assert!(radarr_data.edit_monitored.is_none());
assert!(radarr_data.edit_search_on_add.is_none());
assert!(radarr_data.sort_ascending.is_none());
assert!(!radarr_data.is_searching);
assert!(!radarr_data.is_filtering);
assert!(!radarr_data.prompt_confirm);
assert_eq!(radarr_data.main_tabs.tabs.len(), 3);
assert_str_eq!(radarr_data.main_tabs.tabs[0].title, "Library");
assert_eq!(
radarr_data.main_tabs.tabs[0].route,
ActiveRadarrBlock::Movies.into()
);
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()));
assert_str_eq!(radarr_data.main_tabs.tabs[1].title, "Downloads");
assert_eq!(
radarr_data.main_tabs.tabs[1].route,
ActiveRadarrBlock::Downloads.into()
);
assert!(radarr_data.main_tabs.tabs[1].help.is_empty());
assert_eq!(
radarr_data.main_tabs.tabs[1].contextual_help,
Some("<r> refresh | <del> delete".to_owned())
);
assert_str_eq!(radarr_data.main_tabs.tabs[2].title, "Collections");
assert_eq!(
radarr_data.main_tabs.tabs[2].route,
ActiveRadarrBlock::Collections.into()
);
assert!(radarr_data.main_tabs.tabs[2].help.is_empty());
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_eq!(radarr_data.movie_info_tabs.tabs.len(), 6);
assert_str_eq!(radarr_data.movie_info_tabs.tabs[0].title, "Details");
assert_eq!(
radarr_data.movie_info_tabs.tabs[0].route,
ActiveRadarrBlock::MovieDetails.into()
);
assert_str_eq!(
radarr_data.movie_info_tabs.tabs[0].help,
"<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close"
);
assert!(radarr_data.movie_info_tabs.tabs[0]
.contextual_help
.is_none());
assert_str_eq!(radarr_data.movie_info_tabs.tabs[1].title, "History");
assert_eq!(
radarr_data.movie_info_tabs.tabs[1].route,
ActiveRadarrBlock::MovieHistory.into()
);
assert_str_eq!(
radarr_data.movie_info_tabs.tabs[1].help,
"<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close"
);
assert!(radarr_data.movie_info_tabs.tabs[1]
.contextual_help
.is_none());
assert_str_eq!(radarr_data.movie_info_tabs.tabs[2].title, "File");
assert_eq!(
radarr_data.movie_info_tabs.tabs[2].route,
ActiveRadarrBlock::FileInfo.into()
);
assert_str_eq!(
radarr_data.movie_info_tabs.tabs[2].help,
"<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close"
);
assert!(radarr_data.movie_info_tabs.tabs[2]
.contextual_help
.is_none());
assert_str_eq!(radarr_data.movie_info_tabs.tabs[3].title, "Cast");
assert_eq!(
radarr_data.movie_info_tabs.tabs[3].route,
ActiveRadarrBlock::Cast.into()
);
assert_str_eq!(
radarr_data.movie_info_tabs.tabs[3].help,
"<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close"
);
assert!(radarr_data.movie_info_tabs.tabs[3]
.contextual_help
.is_none());
assert_str_eq!(radarr_data.movie_info_tabs.tabs[4].title, "Crew");
assert_eq!(
radarr_data.movie_info_tabs.tabs[4].route,
ActiveRadarrBlock::Crew.into()
);
assert_str_eq!(
radarr_data.movie_info_tabs.tabs[4].help,
"<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close"
);
assert!(radarr_data.movie_info_tabs.tabs[4]
.contextual_help
.is_none());
assert_str_eq!(radarr_data.movie_info_tabs.tabs[5].title, "Manual Search");
assert_eq!(
radarr_data.movie_info_tabs.tabs[5].route,
ActiveRadarrBlock::ManualSearch.into()
);
assert_str_eq!(
radarr_data.movie_info_tabs.tabs[5].help,
"<r> refresh | <u> update | <e> edit | <o> sort | <s> auto search | <esc> close"
);
assert_eq!(
radarr_data.movie_info_tabs.tabs[5].contextual_help,
Some("<enter> details".to_owned())
);
}
} }
mod active_radarr_block_tests { mod active_radarr_block_tests {
@@ -889,128 +1207,215 @@ mod tests {
use crate::app::radarr::ActiveRadarrBlock; use crate::app::radarr::ActiveRadarrBlock;
#[test] #[test]
fn test_next_add_prompt_block() { fn test_next_add_movie_prompt_block() {
let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.next_add_prompt_block(); let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.next_add_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability ActiveRadarrBlock::AddMovieSelectMinimumAvailability
); );
let active_block = active_block.next_add_prompt_block(); let active_block = active_block.next_add_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::AddMovieSelectQualityProfile ActiveRadarrBlock::AddMovieSelectQualityProfile
); );
let active_block = active_block.next_add_prompt_block(); let active_block = active_block.next_add_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::AddMovieTagsInput); assert_eq!(active_block, ActiveRadarrBlock::AddMovieTagsInput);
let active_block = active_block.next_add_prompt_block(); let active_block = active_block.next_add_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::AddMovieConfirmPrompt); assert_eq!(active_block, ActiveRadarrBlock::AddMovieConfirmPrompt);
let active_block = active_block.next_add_prompt_block(); let active_block = active_block.next_add_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectMonitor); assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectMonitor);
} }
#[test] #[test]
fn test_next_edit_prompt_block() { fn test_next_edit_movie_prompt_block() {
let active_block = ActiveRadarrBlock::EditMovieToggleMonitored.next_edit_prompt_block(); let active_block = ActiveRadarrBlock::EditMovieToggleMonitored.next_edit_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability ActiveRadarrBlock::EditMovieSelectMinimumAvailability
); );
let active_block = active_block.next_edit_prompt_block(); let active_block = active_block.next_edit_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::EditMovieSelectQualityProfile ActiveRadarrBlock::EditMovieSelectQualityProfile
); );
let active_block = active_block.next_edit_prompt_block(); let active_block = active_block.next_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMoviePathInput); assert_eq!(active_block, ActiveRadarrBlock::EditMoviePathInput);
let active_block = active_block.next_edit_prompt_block(); let active_block = active_block.next_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMovieTagsInput); assert_eq!(active_block, ActiveRadarrBlock::EditMovieTagsInput);
let active_block = active_block.next_edit_prompt_block(); let active_block = active_block.next_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMovieConfirmPrompt); assert_eq!(active_block, ActiveRadarrBlock::EditMovieConfirmPrompt);
let active_block = active_block.next_edit_prompt_block(); let active_block = active_block.next_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMovieToggleMonitored); assert_eq!(active_block, ActiveRadarrBlock::EditMovieToggleMonitored);
} }
#[test] #[test]
fn test_previous_add_prompt_block() { fn test_next_edit_collection_prompt_block() {
let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.previous_add_prompt_block(); let active_block =
ActiveRadarrBlock::EditCollectionToggleMonitored.next_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
);
let active_block = active_block.next_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionSelectQualityProfile
);
let active_block = active_block.next_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionRootFolderPathInput
);
let active_block = active_block.next_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd
);
let active_block = active_block.next_edit_collection_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditCollectionConfirmPrompt);
let active_block = active_block.next_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionToggleMonitored
);
}
#[test]
fn test_previous_add_movie_prompt_block() {
let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.previous_add_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::AddMovieConfirmPrompt); assert_eq!(active_block, ActiveRadarrBlock::AddMovieConfirmPrompt);
let active_block = active_block.previous_add_prompt_block(); let active_block = active_block.previous_add_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::AddMovieTagsInput); assert_eq!(active_block, ActiveRadarrBlock::AddMovieTagsInput);
let active_block = active_block.previous_add_prompt_block(); let active_block = active_block.previous_add_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::AddMovieSelectQualityProfile ActiveRadarrBlock::AddMovieSelectQualityProfile
); );
let active_block = active_block.previous_add_prompt_block(); let active_block = active_block.previous_add_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability ActiveRadarrBlock::AddMovieSelectMinimumAvailability
); );
let active_block = active_block.previous_add_prompt_block(); let active_block = active_block.previous_add_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectMonitor); assert_eq!(active_block, ActiveRadarrBlock::AddMovieSelectMonitor);
} }
#[test] #[test]
fn test_previous_edit_prompt_block() { fn test_previous_edit_movie_prompt_block() {
let active_block = ActiveRadarrBlock::EditMovieToggleMonitored.previous_edit_prompt_block(); let active_block =
ActiveRadarrBlock::EditMovieToggleMonitored.previous_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMovieConfirmPrompt); assert_eq!(active_block, ActiveRadarrBlock::EditMovieConfirmPrompt);
let active_block = active_block.previous_edit_prompt_block(); let active_block = active_block.previous_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMovieTagsInput); assert_eq!(active_block, ActiveRadarrBlock::EditMovieTagsInput);
let active_block = active_block.previous_edit_prompt_block(); let active_block = active_block.previous_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMoviePathInput); assert_eq!(active_block, ActiveRadarrBlock::EditMoviePathInput);
let active_block = active_block.previous_edit_prompt_block(); let active_block = active_block.previous_edit_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::EditMovieSelectQualityProfile ActiveRadarrBlock::EditMovieSelectQualityProfile
); );
let active_block = active_block.previous_edit_prompt_block(); let active_block = active_block.previous_edit_movie_prompt_block();
assert_eq!( assert_eq!(
active_block, active_block,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability ActiveRadarrBlock::EditMovieSelectMinimumAvailability
); );
let active_block = active_block.previous_edit_prompt_block(); let active_block = active_block.previous_edit_movie_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditMovieToggleMonitored); assert_eq!(active_block, ActiveRadarrBlock::EditMovieToggleMonitored);
} }
#[test]
fn test_previous_edit_collection_prompt_block() {
let active_block =
ActiveRadarrBlock::EditCollectionToggleMonitored.previous_edit_collection_prompt_block();
assert_eq!(active_block, ActiveRadarrBlock::EditCollectionConfirmPrompt);
let active_block = active_block.previous_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd
);
let active_block = active_block.previous_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionRootFolderPathInput
);
let active_block = active_block.previous_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionSelectQualityProfile
);
let active_block = active_block.previous_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
);
let active_block = active_block.previous_edit_collection_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditCollectionToggleMonitored
);
}
} }
mod radarr_tests { mod radarr_tests {
@@ -1430,6 +1835,10 @@ mod tests {
.radarr_on_tick(ActiveRadarrBlock::Downloads, false) .radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await; .await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into()
);
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetQualityProfiles.into() RadarrEvent::GetQualityProfiles.into()
@@ -1446,6 +1855,24 @@ mod tests {
assert!(!app.data.radarr_data.prompt_confirm); assert!(!app.data.radarr_data.prompt_confirm);
} }
#[tokio::test]
async fn test_radarr_on_tick_should_refresh() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.should_refresh = true;
app
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into()
);
assert!(app.is_loading);
assert!(app.should_refresh);
assert!(!app.data.radarr_data.prompt_confirm);
}
#[tokio::test] #[tokio::test]
async fn test_radarr_on_tick_network_tick_frequency() { async fn test_radarr_on_tick_network_tick_frequency() {
let (mut app, mut sync_network_rx) = construct_app_unit(); let (mut app, mut sync_network_rx) = construct_app_unit();
@@ -1456,6 +1883,10 @@ mod tests {
.radarr_on_tick(ActiveRadarrBlock::Downloads, false) .radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await; .await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into()
);
assert_eq!( assert_eq!(
sync_network_rx.recv().await.unwrap(), sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetQualityProfiles.into() RadarrEvent::GetQualityProfiles.into()
@@ -37,20 +37,17 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
self.app.data.radarr_data.add_searched_movies.scroll_up() self.app.data.radarr_data.add_searched_movies.scroll_up()
} }
ActiveRadarrBlock::AddMovieSelectMonitor => { ActiveRadarrBlock::AddMovieSelectMonitor => {
self.app.data.radarr_data.movie_monitor_list.scroll_up() self.app.data.radarr_data.monitor_list.scroll_up()
} }
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_up(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.scroll_up(), .scroll_up(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_up()
}
ActiveRadarrBlock::AddMoviePrompt => { ActiveRadarrBlock::AddMoviePrompt => {
self.app.data.radarr_data.selected_block = self self.app.data.radarr_data.selected_block = self
.app .app
@@ -58,7 +55,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
.radarr_data .radarr_data
.selected_block .selected_block
.clone() .clone()
.previous_add_prompt_block() .previous_add_movie_prompt_block()
} }
_ => (), _ => (),
} }
@@ -70,27 +67,24 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
self.app.data.radarr_data.add_searched_movies.scroll_down() self.app.data.radarr_data.add_searched_movies.scroll_down()
} }
ActiveRadarrBlock::AddMovieSelectMonitor => { ActiveRadarrBlock::AddMovieSelectMonitor => {
self.app.data.radarr_data.movie_monitor_list.scroll_down() self.app.data.radarr_data.monitor_list.scroll_down()
} }
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_down(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.scroll_down(), .scroll_down(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_down()
}
ActiveRadarrBlock::AddMoviePrompt => { ActiveRadarrBlock::AddMoviePrompt => {
self.app.data.radarr_data.selected_block = self self.app.data.radarr_data.selected_block = self
.app .app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.next_add_prompt_block() .next_add_movie_prompt_block()
} }
_ => (), _ => (),
} }
@@ -105,19 +99,19 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
.add_searched_movies .add_searched_movies
.scroll_to_top(), .scroll_to_top(),
ActiveRadarrBlock::AddMovieSelectMonitor => { ActiveRadarrBlock::AddMovieSelectMonitor => {
self.app.data.radarr_data.movie_monitor_list.scroll_to_top() self.app.data.radarr_data.monitor_list.scroll_to_top()
} }
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_to_top(), .scroll_to_top(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.scroll_to_top(), .scroll_to_top(),
ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.scroll_home(), ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.scroll_home(),
ActiveRadarrBlock::AddMovieTagsInput => self.app.data.radarr_data.edit_tags.scroll_home(), ActiveRadarrBlock::AddMovieTagsInput => self.app.data.radarr_data.edit_tags.scroll_home(),
@@ -133,23 +127,20 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
.radarr_data .radarr_data
.add_searched_movies .add_searched_movies
.scroll_to_bottom(), .scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSelectMonitor => self ActiveRadarrBlock::AddMovieSelectMonitor => {
.app self.app.data.radarr_data.monitor_list.scroll_to_bottom()
.data }
.radarr_data
.movie_monitor_list
.scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_to_bottom(), .scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.scroll_to_bottom(), .scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.reset_offset(), ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.reset_offset(),
ActiveRadarrBlock::AddMovieTagsInput => self.app.data.radarr_data.edit_tags.reset_offset(), ActiveRadarrBlock::AddMovieTagsInput => self.app.data.radarr_data.edit_tags.reset_offset(),
@@ -214,7 +205,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into()); .push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
self.app.data.radarr_data.populate_movie_preferences_lists(); self.app.data.radarr_data.populate_preferences_lists();
self.app.data.radarr_data.selected_block = ActiveRadarrBlock::AddMovieSelectMonitor; self.app.data.radarr_data.selected_block = ActiveRadarrBlock::AddMovieSelectMonitor;
} }
} }
@@ -265,7 +256,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
} }
ActiveRadarrBlock::AddMoviePrompt => { ActiveRadarrBlock::AddMoviePrompt => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.data.radarr_data.reset_add_edit_movie_fields(); self.app.data.radarr_data.reset_add_edit_media_fields();
self.app.data.radarr_data.prompt_confirm = false; self.app.data.radarr_data.prompt_confirm = false;
} }
ActiveRadarrBlock::AddMovieSelectMonitor ActiveRadarrBlock::AddMovieSelectMonitor
@@ -331,16 +322,16 @@ mod tests {
test_add_movie_select_monitor_scroll, test_add_movie_select_monitor_scroll,
AddMovieHandler, AddMovieHandler,
Monitor, Monitor,
movie_monitor_list, monitor_list,
ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMonitor,
None None
); );
test_enum_scroll!( test_enum_scroll!(
test_add_movie_select_minimuum_availability_scroll, test_add_movie_select_minimum_availability_scroll,
AddMovieHandler, AddMovieHandler,
MinimumAvailability, MinimumAvailability,
movie_minimum_availability_list, minimum_availability_list,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None None
); );
@@ -348,7 +339,7 @@ mod tests {
test_iterable_scroll!( test_iterable_scroll!(
test_add_movie_select_quality_profile_scroll, test_add_movie_select_quality_profile_scroll,
AddMovieHandler, AddMovieHandler,
movie_quality_profile_list, quality_profile_list,
ActiveRadarrBlock::AddMovieSelectQualityProfile, ActiveRadarrBlock::AddMovieSelectQualityProfile,
None None
); );
@@ -399,16 +390,16 @@ mod tests {
test_add_movie_select_monitor_home_end, test_add_movie_select_monitor_home_end,
AddMovieHandler, AddMovieHandler,
Monitor, Monitor,
movie_monitor_list, monitor_list,
ActiveRadarrBlock::AddMovieSelectMonitor, ActiveRadarrBlock::AddMovieSelectMonitor,
None None
); );
test_enum_home_and_end!( test_enum_home_and_end!(
test_add_movie_select_minimuum_availability_home_end, test_add_movie_select_minimum_availability_home_end,
AddMovieHandler, AddMovieHandler,
MinimumAvailability, MinimumAvailability,
movie_minimum_availability_list, minimum_availability_list,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability, ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None None
); );
@@ -416,7 +407,7 @@ mod tests {
test_iterable_home_and_end!( test_iterable_home_and_end!(
test_add_movie_select_quality_profile_scroll, test_add_movie_select_quality_profile_scroll,
AddMovieHandler, AddMovieHandler,
movie_quality_profile_list, quality_profile_list,
ActiveRadarrBlock::AddMovieSelectQualityProfile, ActiveRadarrBlock::AddMovieSelectQualityProfile,
None None
); );
@@ -539,24 +530,19 @@ mod tests {
app.data.radarr_data.selected_block, app.data.radarr_data.selected_block,
ActiveRadarrBlock::AddMovieSelectMonitor ActiveRadarrBlock::AddMovieSelectMonitor
); );
assert!(!app.data.radarr_data.movie_monitor_list.items.is_empty()); assert!(!app.data.radarr_data.monitor_list.items.is_empty());
assert!(!app assert!(!app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.items
.is_empty());
assert!(!app
.data
.radarr_data
.movie_quality_profile_list
.items .items
.is_empty()); .is_empty());
assert!(!app.data.radarr_data.quality_profile_list.items.is_empty());
assert_str_eq!( assert_str_eq!(
app app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.current_selection(), .current_selection(),
"A - Test 1" "A - Test 1"
); );
@@ -728,7 +714,7 @@ mod tests {
use crate::app::radarr::radarr_test_utils::create_test_radarr_data; use crate::app::radarr::radarr_test_utils::create_test_radarr_data;
use crate::{ use crate::{
assert_edit_movie_reset, assert_movie_preferences_selections_reset, assert_search_reset, assert_edit_media_reset, assert_preferences_selections_reset, assert_search_reset,
simple_stateful_iterable_vec, simple_stateful_iterable_vec,
}; };
@@ -850,8 +836,8 @@ mod tests {
app.get_current_route(), app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into() &ActiveRadarrBlock::AddMovieSearchResults.into()
); );
assert_movie_preferences_selections_reset!(app.data.radarr_data); assert_preferences_selections_reset!(app.data.radarr_data);
assert_edit_movie_reset!(app.data.radarr_data); assert_edit_media_reset!(app.data.radarr_data);
} }
#[test] #[test]
@@ -1,3 +1,4 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::ActiveRadarrBlock; use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
@@ -95,7 +96,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for CollectionDetailsHandler<'a>
.into(), .into(),
); );
self.app.data.radarr_data.selected_block = ActiveRadarrBlock::EditMovieToggleMonitored; self.app.data.radarr_data.selected_block = ActiveRadarrBlock::EditMovieToggleMonitored;
self.app.data.radarr_data.populate_movie_preferences_lists(); self.app.data.radarr_data.populate_preferences_lists();
} }
} }
} }
@@ -111,7 +112,21 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for CollectionDetailsHandler<'a>
} }
} }
fn handle_char_key_event(&mut self) {} fn handle_char_key_event(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::CollectionDetails
&& *self.key == DEFAULT_KEYBINDINGS.edit.key
{
self.app.push_navigation_stack(
(
ActiveRadarrBlock::EditCollectionPrompt,
Some(*self.active_radarr_block),
)
.into(),
);
self.app.data.radarr_data.populate_edit_collection_fields();
self.app.data.radarr_data.selected_block = ActiveRadarrBlock::EditCollectionToggleMonitored;
}
}
} }
#[cfg(test)] #[cfg(test)]
@@ -201,7 +216,7 @@ mod tests {
) )
.into() .into()
); );
assert!(!app.data.radarr_data.movie_monitor_list.items.is_empty()); assert!(!app.data.radarr_data.monitor_list.items.is_empty());
assert_eq!( assert_eq!(
app.data.radarr_data.selected_block, app.data.radarr_data.selected_block,
ActiveRadarrBlock::EditMovieToggleMonitored ActiveRadarrBlock::EditMovieToggleMonitored
@@ -209,20 +224,15 @@ mod tests {
assert!(!app assert!(!app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.items
.is_empty());
assert!(!app
.data
.radarr_data
.movie_quality_profile_list
.items .items
.is_empty()); .is_empty());
assert!(!app.data.radarr_data.quality_profile_list.items.is_empty());
assert_str_eq!( assert_str_eq!(
app app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.current_selection(), .current_selection(),
"A - Test 1" "A - Test 1"
); );
@@ -310,4 +320,29 @@ mod tests {
); );
} }
} }
mod test_handle_key_char {
use bimap::BiMap;
use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::Number;
use strum::IntoEnumIterator;
use crate::app::radarr::radarr_test_utils::create_test_radarr_data;
use crate::app::radarr::RadarrData;
use crate::models::radarr_models::{Collection, MinimumAvailability};
use crate::models::HorizontallyScrollableText;
use crate::models::StatefulTable;
use crate::test_edit_collection_key;
use super::*;
#[test]
fn test_edit_key() {
test_edit_collection_key!(
CollectionDetailsHandler,
ActiveRadarrBlock::CollectionDetails,
ActiveRadarrBlock::CollectionDetails
);
}
}
} }
@@ -0,0 +1,693 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
pub(super) struct EditCollectionHandler<'a> {
key: &'a Key,
app: &'a mut App,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
}
impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditCollectionHandler<'a> {
fn with(
key: &'a Key,
app: &'a mut App,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
) -> EditCollectionHandler<'a> {
EditCollectionHandler {
key,
app,
active_radarr_block: active_block,
context,
}
}
fn get_key(&self) -> &Key {
self.key
}
fn handle_scroll_up(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => self
.app
.data
.radarr_data
.minimum_availability_list
.scroll_up(),
ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_up()
}
ActiveRadarrBlock::EditCollectionPrompt => {
self.app.data.radarr_data.selected_block = self
.app
.data
.radarr_data
.selected_block
.clone()
.previous_edit_collection_prompt_block()
}
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => self
.app
.data
.radarr_data
.minimum_availability_list
.scroll_down(),
ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_down()
}
ActiveRadarrBlock::EditCollectionPrompt => {
self.app.data.radarr_data.selected_block = self
.app
.data
.radarr_data
.selected_block
.next_edit_collection_prompt_block()
}
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => self
.app
.data
.radarr_data
.minimum_availability_list
.scroll_to_top(),
ActiveRadarrBlock::EditCollectionSelectQualityProfile => self
.app
.data
.radarr_data
.quality_profile_list
.scroll_to_top(),
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
self.app.data.radarr_data.edit_path.scroll_home()
}
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => self
.app
.data
.radarr_data
.minimum_availability_list
.scroll_to_bottom(),
ActiveRadarrBlock::EditCollectionSelectQualityProfile => self
.app
.data
.radarr_data
.quality_profile_list
.scroll_to_bottom(),
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
self.app.data.radarr_data.edit_path.reset_offset()
}
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::EditCollectionPrompt => handle_prompt_toggle(self.app, self.key),
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
handle_text_box_left_right_keys!(self, self.key, self.app.data.radarr_data.edit_path)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::EditCollectionPrompt => match self.app.data.radarr_data.selected_block {
ActiveRadarrBlock::EditCollectionConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection);
self.app.pop_navigation_stack();
self.app.should_refresh = true;
} else {
self.app.pop_navigation_stack();
}
}
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
| ActiveRadarrBlock::EditCollectionSelectQualityProfile => self
.app
.push_navigation_stack((self.app.data.radarr_data.selected_block, *self.context).into()),
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
self.app.push_navigation_stack(
(self.app.data.radarr_data.selected_block, *self.context).into(),
);
self.app.should_ignore_quit_key = true;
}
ActiveRadarrBlock::EditCollectionToggleMonitored => {
self.app.data.radarr_data.edit_monitored =
Some(!self.app.data.radarr_data.edit_monitored.unwrap_or_default())
}
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd => {
self.app.data.radarr_data.edit_search_on_add = Some(
!self
.app
.data
.radarr_data
.edit_search_on_add
.unwrap_or_default(),
)
}
_ => (),
},
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
| ActiveRadarrBlock::EditCollectionSelectQualityProfile => self.app.pop_navigation_stack(),
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::EditCollectionPrompt => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.reset_add_edit_media_fields();
self.app.data.radarr_data.prompt_confirm = false;
}
ActiveRadarrBlock::EditCollectionToggleMonitored
| ActiveRadarrBlock::EditCollectionToggleSearchOnAdd
| ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
| ActiveRadarrBlock::EditCollectionSelectQualityProfile => self.app.pop_navigation_stack(),
_ => (),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
if self.active_radarr_block == &ActiveRadarrBlock::EditCollectionRootFolderPathInput {
handle_text_box_keys!(self, key, self.app.data.radarr_data.edit_path)
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::edit_collection_handler::EditCollectionHandler;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::MinimumAvailability;
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
use rstest::rstest;
use strum::IntoEnumIterator;
use crate::{test_enum_scroll, test_iterable_scroll};
use super::*;
test_enum_scroll!(
test_edit_collection_select_minimum_availability_scroll,
EditCollectionHandler,
MinimumAvailability,
minimum_availability_list,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
None
);
test_iterable_scroll!(
test_edit_collection_select_quality_profile_scroll,
EditCollectionHandler,
quality_profile_list,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
None
);
#[rstest]
fn test_edit_collection_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.data.radarr_data.selected_block =
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability;
EditCollectionHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
if key == Key::Up {
assert_eq!(
app.data.radarr_data.selected_block,
ActiveRadarrBlock::EditCollectionToggleMonitored
);
} else {
assert_eq!(
app.data.radarr_data.selected_block,
ActiveRadarrBlock::EditCollectionSelectQualityProfile
);
}
}
}
mod test_handle_home_end {
use strum::IntoEnumIterator;
use crate::{test_enum_home_and_end, test_iterable_home_and_end, test_text_box_home_end_keys};
use super::*;
test_enum_home_and_end!(
test_edit_collection_select_minimum_availability_home_end,
EditCollectionHandler,
MinimumAvailability,
minimum_availability_list,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
None
);
test_iterable_home_and_end!(
test_edit_collection_select_quality_profile_scroll,
EditCollectionHandler,
quality_profile_list,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
None
);
#[test]
fn test_edit_collection_root_folder_path_input_home_end_keys() {
test_text_box_home_end_keys!(
EditCollectionHandler,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
edit_path
);
}
}
mod test_handle_left_right_action {
use rstest::rstest;
use crate::test_text_box_left_right_keys;
use super::*;
#[rstest]
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
EditCollectionHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert!(app.data.radarr_data.prompt_confirm);
EditCollectionHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
#[test]
fn test_edit_collection_root_folder_path_input_left_right_keys() {
test_text_box_left_right_keys!(
EditCollectionHandler,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
edit_path
);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::models::Route;
use crate::network::radarr_network::RadarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_edit_collection_root_folder_path_input_submit() {
let mut app = App::default();
app.should_ignore_quit_key = true;
app.data.radarr_data.edit_path = "Test Path".to_owned().into();
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
)
.handle();
assert!(!app.should_ignore_quit_key);
assert!(!app.data.radarr_data.edit_path.text.is_empty());
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into()
);
}
#[test]
fn test_edit_collection_prompt_prompt_decline_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.selected_block = ActiveRadarrBlock::EditCollectionConfirmPrompt;
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
}
#[test]
fn test_edit_collection_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block = ActiveRadarrBlock::EditCollectionConfirmPrompt;
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditCollection)
);
assert!(app.should_refresh);
}
#[test]
fn test_edit_collection_toggle_monitored_submit() {
let current_route = Route::from((
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
));
let mut app = App::default();
app.data.radarr_data.selected_block = ActiveRadarrBlock::EditCollectionToggleMonitored;
app.push_navigation_stack(current_route);
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.data.radarr_data.edit_monitored, Some(true));
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.data.radarr_data.edit_monitored, Some(false));
}
#[test]
fn test_edit_collection_toggle_search_on_add_submit() {
let current_route = Route::from((
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
));
let mut app = App::default();
app.data.radarr_data.selected_block = ActiveRadarrBlock::EditCollectionToggleSearchOnAdd;
app.push_navigation_stack(current_route);
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.data.radarr_data.edit_search_on_add, Some(true));
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.data.radarr_data.edit_search_on_add, Some(false));
}
#[rstest]
fn test_edit_collection_prompt_selected_block_submit(
#[values(
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
ActiveRadarrBlock::EditCollectionRootFolderPathInput
)]
selected_block: ActiveRadarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(
(
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
)
.into(),
);
app.data.radarr_data.selected_block = selected_block;
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(
app.get_current_route(),
&(selected_block, Some(ActiveRadarrBlock::Collections)).into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
if selected_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput {
assert!(app.should_ignore_quit_key);
}
}
#[rstest]
fn test_edit_collection_prompt_selecting_preferences_blocks_submit(
#[values(
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
ActiveRadarrBlock::EditCollectionRootFolderPathInput
)]
active_radarr_block: ActiveRadarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.push_navigation_stack(active_radarr_block.into());
EditCollectionHandler::with(
&SUBMIT_KEY,
&mut app,
&active_radarr_block,
&Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into()
);
if active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput {
assert!(!app.should_ignore_quit_key);
}
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::app::radarr::radarr_test_utils::create_test_radarr_data;
use crate::{assert_edit_media_reset, assert_preferences_selections_reset};
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[test]
fn test_edit_collection_root_folder_path_input_esc() {
let mut app = App::default();
app.data.radarr_data = create_test_radarr_data();
app.should_ignore_quit_key = true;
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
EditCollectionHandler::with(
&ESC_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
)
.handle();
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into()
);
}
#[test]
fn test_edit_collection_prompt_esc() {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data = create_test_radarr_data();
EditCollectionHandler::with(
&ESC_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
);
let radarr_data = &app.data.radarr_data;
assert_preferences_selections_reset!(radarr_data);
assert_edit_media_reset!(radarr_data);
assert!(!radarr_data.prompt_confirm);
}
#[rstest]
fn test_edit_collection_esc(
#[values(
ActiveRadarrBlock::EditCollectionToggleMonitored,
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
ActiveRadarrBlock::EditCollectionSelectQualityProfile
)]
active_radarr_block: ActiveRadarrBlock,
) {
let mut app = App::default();
app.data.radarr_data = create_test_radarr_data();
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(active_radarr_block.into());
EditCollectionHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
);
}
}
mod test_handle_key_char {
use super::*;
#[test]
fn test_edit_collection_root_folder_path_input_backspace() {
let mut app = App::default();
app.data.radarr_data.edit_path = "Test".to_owned().into();
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
)
.handle();
assert_str_eq!(app.data.radarr_data.edit_path.text, "Tes");
}
#[test]
fn test_edit_collection_root_folder_path_input_char_key() {
let mut app = App::default();
EditCollectionHandler::with(
&Key::Char('h'),
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
)
.handle();
assert_str_eq!(app.data.radarr_data.edit_path.text, "h");
}
}
}
@@ -39,14 +39,11 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_up(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.scroll_up(), .scroll_up(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_up()
}
ActiveRadarrBlock::EditMoviePrompt => { ActiveRadarrBlock::EditMoviePrompt => {
self.app.data.radarr_data.selected_block = self self.app.data.radarr_data.selected_block = self
.app .app
@@ -54,7 +51,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.radarr_data .radarr_data
.selected_block .selected_block
.clone() .clone()
.previous_edit_prompt_block() .previous_edit_movie_prompt_block()
} }
_ => (), _ => (),
} }
@@ -66,21 +63,18 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_down(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.scroll_down(), .scroll_down(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_down()
}
ActiveRadarrBlock::EditMoviePrompt => { ActiveRadarrBlock::EditMoviePrompt => {
self.app.data.radarr_data.selected_block = self self.app.data.radarr_data.selected_block = self
.app .app
.data .data
.radarr_data .radarr_data
.selected_block .selected_block
.next_edit_prompt_block() .next_edit_movie_prompt_block()
} }
_ => (), _ => (),
} }
@@ -92,13 +86,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_to_top(), .scroll_to_top(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.scroll_to_top(), .scroll_to_top(),
ActiveRadarrBlock::EditMoviePathInput => self.app.data.radarr_data.edit_path.scroll_home(), ActiveRadarrBlock::EditMoviePathInput => self.app.data.radarr_data.edit_path.scroll_home(),
ActiveRadarrBlock::EditMovieTagsInput => self.app.data.radarr_data.edit_tags.scroll_home(), ActiveRadarrBlock::EditMovieTagsInput => self.app.data.radarr_data.edit_tags.scroll_home(),
@@ -112,13 +106,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.app .app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.scroll_to_bottom(), .scroll_to_bottom(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app .app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.scroll_to_bottom(), .scroll_to_bottom(),
ActiveRadarrBlock::EditMoviePathInput => self.app.data.radarr_data.edit_path.reset_offset(), ActiveRadarrBlock::EditMoviePathInput => self.app.data.radarr_data.edit_path.reset_offset(),
ActiveRadarrBlock::EditMovieTagsInput => self.app.data.radarr_data.edit_tags.reset_offset(), ActiveRadarrBlock::EditMovieTagsInput => self.app.data.radarr_data.edit_tags.reset_offset(),
@@ -148,6 +142,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie);
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.should_refresh = true;
} else { } else {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
@@ -186,7 +181,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
} }
ActiveRadarrBlock::EditMoviePrompt => { ActiveRadarrBlock::EditMoviePrompt => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.data.radarr_data.reset_add_edit_movie_fields(); self.app.data.radarr_data.reset_add_edit_media_fields();
self.app.data.radarr_data.prompt_confirm = false; self.app.data.radarr_data.prompt_confirm = false;
} }
ActiveRadarrBlock::EditMovieToggleMonitored ActiveRadarrBlock::EditMovieToggleMonitored
@@ -232,10 +227,10 @@ mod tests {
use super::*; use super::*;
test_enum_scroll!( test_enum_scroll!(
test_edit_movie_select_minimuum_availability_scroll, test_edit_movie_select_minimum_availability_scroll,
EditMovieHandler, EditMovieHandler,
MinimumAvailability, MinimumAvailability,
movie_minimum_availability_list, minimum_availability_list,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability, ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None None
); );
@@ -243,7 +238,7 @@ mod tests {
test_iterable_scroll!( test_iterable_scroll!(
test_edit_movie_select_quality_profile_scroll, test_edit_movie_select_quality_profile_scroll,
EditMovieHandler, EditMovieHandler,
movie_quality_profile_list, quality_profile_list,
ActiveRadarrBlock::EditMovieSelectQualityProfile, ActiveRadarrBlock::EditMovieSelectQualityProfile,
None None
); );
@@ -277,10 +272,10 @@ mod tests {
use super::*; use super::*;
test_enum_home_and_end!( test_enum_home_and_end!(
test_edit_movie_select_minimuum_availability_home_end, test_edit_movie_select_minimum_availability_home_end,
EditMovieHandler, EditMovieHandler,
MinimumAvailability, MinimumAvailability,
movie_minimum_availability_list, minimum_availability_list,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability, ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None None
); );
@@ -288,7 +283,7 @@ mod tests {
test_iterable_home_and_end!( test_iterable_home_and_end!(
test_edit_movie_select_quality_profile_scroll, test_edit_movie_select_quality_profile_scroll,
EditMovieHandler, EditMovieHandler,
movie_quality_profile_list, quality_profile_list,
ActiveRadarrBlock::EditMovieSelectQualityProfile, ActiveRadarrBlock::EditMovieSelectQualityProfile,
None None
); );
@@ -451,6 +446,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action, app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie) Some(RadarrEvent::EditMovie)
); );
assert!(app.should_refresh);
} }
#[test] #[test]
@@ -567,7 +563,7 @@ mod tests {
use rstest::rstest; use rstest::rstest;
use crate::app::radarr::radarr_test_utils::create_test_radarr_data; use crate::app::radarr::radarr_test_utils::create_test_radarr_data;
use crate::{assert_edit_movie_reset, assert_movie_preferences_selections_reset}; use crate::{assert_edit_media_reset, assert_preferences_selections_reset};
use super::*; use super::*;
@@ -611,13 +607,11 @@ mod tests {
.handle(); .handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
{ let radarr_data = &app.data.radarr_data;
let radarr_data = &app.data.radarr_data;
assert_movie_preferences_selections_reset!(radarr_data); assert_preferences_selections_reset!(radarr_data);
assert_edit_movie_reset!(radarr_data); assert_edit_media_reset!(radarr_data);
assert!(!radarr_data.prompt_confirm); assert!(!radarr_data.prompt_confirm);
}
} }
#[rstest] #[rstest]
+120 -16
View File
@@ -1,10 +1,11 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::{ use crate::app::radarr::{
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, EDIT_MOVIE_BLOCKS, FILTER_BLOCKS, ActiveRadarrBlock, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, EDIT_COLLECTION_BLOCKS,
MOVIE_DETAILS_BLOCKS, SEARCH_BLOCKS, EDIT_MOVIE_BLOCKS, FILTER_BLOCKS, MOVIE_DETAILS_BLOCKS, SEARCH_BLOCKS,
}; };
use crate::handlers::radarr_handlers::add_movie_handler::AddMovieHandler; use crate::handlers::radarr_handlers::add_movie_handler::AddMovieHandler;
use crate::handlers::radarr_handlers::collection_details_handler::CollectionDetailsHandler; use crate::handlers::radarr_handlers::collection_details_handler::CollectionDetailsHandler;
use crate::handlers::radarr_handlers::edit_collection_handler::EditCollectionHandler;
use crate::handlers::radarr_handlers::edit_movie_handler::EditMovieHandler; use crate::handlers::radarr_handlers::edit_movie_handler::EditMovieHandler;
use crate::handlers::radarr_handlers::movie_details_handler::MovieDetailsHandler; use crate::handlers::radarr_handlers::movie_details_handler::MovieDetailsHandler;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
@@ -15,6 +16,7 @@ use crate::{handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
mod add_movie_handler; mod add_movie_handler;
mod collection_details_handler; mod collection_details_handler;
mod edit_collection_handler;
mod edit_movie_handler; mod edit_movie_handler;
mod movie_details_handler; mod movie_details_handler;
@@ -42,6 +44,10 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
_ if EDIT_MOVIE_BLOCKS.contains(self.active_radarr_block) => { _ if EDIT_MOVIE_BLOCKS.contains(self.active_radarr_block) => {
EditMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context).handle() EditMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context).handle()
} }
_ if EDIT_COLLECTION_BLOCKS.contains(self.active_radarr_block) => {
EditCollectionHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ => self.handle_key_event(), _ => self.handle_key_event(),
} }
} }
@@ -447,9 +453,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self self.app.should_refresh = true;
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
} }
_ => (), _ => (),
}, },
@@ -460,9 +464,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
.push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self self.app.should_refresh = true;
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
} }
_ => (), _ => (),
}, },
@@ -481,15 +483,25 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
self.app.data.radarr_data.is_filtering = true; self.app.data.radarr_data.is_filtering = true;
self.app.should_ignore_quit_key = true; self.app.should_ignore_quit_key = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
)
.into(),
);
self.app.data.radarr_data.populate_edit_collection_fields();
self.app.data.radarr_data.selected_block =
ActiveRadarrBlock::EditCollectionToggleMonitored;
}
_ if *key == DEFAULT_KEYBINDINGS.update.key => { _ if *key == DEFAULT_KEYBINDINGS.update.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into()); .push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into());
} }
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self self.app.should_refresh = true;
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
} }
_ => (), _ => (),
}, },
@@ -558,6 +570,7 @@ mod radarr_handler_test_utils {
edit_path: HorizontallyScrollableText::default(), edit_path: HorizontallyScrollableText::default(),
edit_tags: HorizontallyScrollableText::default(), edit_tags: HorizontallyScrollableText::default(),
edit_monitored: None, edit_monitored: None,
edit_search_on_add: None,
quality_profile_map: BiMap::from_iter([ quality_profile_map: BiMap::from_iter([
(2222, "HD - 1080p".to_owned()), (2222, "HD - 1080p".to_owned()),
(1111, "Any".to_owned()), (1111, "Any".to_owned()),
@@ -587,26 +600,26 @@ mod radarr_handler_test_utils {
ActiveRadarrBlock::EditMovieToggleMonitored ActiveRadarrBlock::EditMovieToggleMonitored
); );
assert_eq!( assert_eq!(
app.data.radarr_data.movie_minimum_availability_list.items, app.data.radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter()) Vec::from_iter(MinimumAvailability::iter())
); );
assert_eq!( assert_eq!(
app app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.current_selection(), .current_selection(),
&MinimumAvailability::Released &MinimumAvailability::Released
); );
assert_eq!( assert_eq!(
app.data.radarr_data.movie_quality_profile_list.items, app.data.radarr_data.quality_profile_list.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()] vec!["Any".to_owned(), "HD - 1080p".to_owned()]
); );
assert_str_eq!( assert_str_eq!(
app app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.current_selection(), .current_selection(),
"HD - 1080p" "HD - 1080p"
); );
@@ -615,6 +628,72 @@ mod radarr_handler_test_utils {
assert_eq!(app.data.radarr_data.edit_monitored, Some(true)); assert_eq!(app.data.radarr_data.edit_monitored, Some(true));
}; };
} }
#[macro_export]
macro_rules! test_edit_collection_key {
($handler:ident, $block:expr, $context:expr) => {
let mut app = App::default();
let mut radarr_data = RadarrData {
edit_path: HorizontallyScrollableText::default(),
edit_tags: HorizontallyScrollableText::default(),
edit_monitored: None,
edit_search_on_add: None,
quality_profile_map: BiMap::from_iter([
(2222, "HD - 1080p".to_owned()),
(1111, "Any".to_owned()),
]),
filtered_collections: StatefulTable::default(),
..create_test_radarr_data()
};
radarr_data.collections.set_items(vec![Collection {
root_folder_path: "/nfs/movies/Test".to_owned().into(),
monitored: true,
search_on_add: true,
quality_profile_id: Number::from(2222),
minimum_availability: MinimumAvailability::Released,
..Collection::default()
}]);
app.data.radarr_data = radarr_data;
$handler::with(&DEFAULT_KEYBINDINGS.edit.key, &mut app, &$block, &None).handle();
assert_eq!(
app.get_current_route(),
&(ActiveRadarrBlock::EditCollectionPrompt, Some($context)).into()
);
assert_eq!(
app.data.radarr_data.selected_block,
ActiveRadarrBlock::EditCollectionToggleMonitored
);
assert_eq!(
app.data.radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter())
);
assert_eq!(
app
.data
.radarr_data
.minimum_availability_list
.current_selection(),
&MinimumAvailability::Released
);
assert_eq!(
app.data.radarr_data.quality_profile_list.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()]
);
assert_str_eq!(
app
.data
.radarr_data
.quality_profile_list
.current_selection(),
"HD - 1080p"
);
assert_str_eq!(app.data.radarr_data.edit_path.text, "/nfs/movies/Test");
assert_eq!(app.data.radarr_data.edit_monitored, Some(true));
assert_eq!(app.data.radarr_data.edit_search_on_add, Some(true));
};
}
} }
#[cfg(test)] #[cfg(test)]
@@ -1382,6 +1461,15 @@ mod tests {
); );
} }
#[test]
fn test_collection_edit_key() {
test_edit_collection_key!(
RadarrHandler,
ActiveRadarrBlock::Collections,
ActiveRadarrBlock::Collections
);
}
#[rstest] #[rstest]
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::UpdateAllMoviesPrompt)] #[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::UpdateAllMoviesPrompt)]
#[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::UpdateDownloadsPrompt)] #[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::UpdateDownloadsPrompt)]
@@ -1416,6 +1504,7 @@ mod tests {
active_radarr_block: ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
) { ) {
let mut app = App::default(); let mut app = App::default();
app.push_navigation_stack(active_radarr_block.into());
RadarrHandler::with( RadarrHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, &DEFAULT_KEYBINDINGS.refresh.key,
@@ -1426,7 +1515,7 @@ mod tests {
.handle(); .handle();
assert_eq!(app.get_current_route(), &active_radarr_block.into()); assert_eq!(app.get_current_route(), &active_radarr_block.into());
assert!(app.is_routing); assert!(app.should_refresh);
} }
#[rstest] #[rstest]
@@ -1691,4 +1780,19 @@ mod tests {
) { ) {
test_handler_delegation!(ActiveRadarrBlock::Movies, active_radarr_block); test_handler_delegation!(ActiveRadarrBlock::Movies, active_radarr_block);
} }
#[rstest]
fn test_delegate_edit_collection_blocks_to_edit_collection_handler(
#[values(
ActiveRadarrBlock::EditCollectionPrompt,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd,
ActiveRadarrBlock::EditCollectionToggleMonitored
)]
active_radarr_block: ActiveRadarrBlock,
) {
test_handler_delegation!(ActiveRadarrBlock::Collections, active_radarr_block);
}
} }
+4
View File
@@ -83,11 +83,15 @@ pub struct CollectionMovie {
#[derivative(Default)] #[derivative(Default)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Collection { pub struct Collection {
#[derivative(Default(value = "Number::from(0)"))]
pub id: Number,
#[serde(default)] #[serde(default)]
pub title: HorizontallyScrollableText, pub title: HorizontallyScrollableText,
pub root_folder_path: Option<String>, pub root_folder_path: Option<String>,
pub search_on_add: bool, pub search_on_add: bool,
pub monitored: bool,
pub overview: Option<String>, pub overview: Option<String>,
pub minimum_availability: MinimumAvailability,
#[derivative(Default(value = "Number::from(0)"))] #[derivative(Default(value = "Number::from(0)"))]
pub quality_profile_id: Number, pub quality_profile_id: Number,
pub movies: Option<Vec<CollectionMovie>>, pub movies: Option<Vec<CollectionMovie>>,
+273 -17
View File
@@ -24,6 +24,7 @@ pub enum RadarrEvent {
DeleteMovie, DeleteMovie,
DownloadRelease, DownloadRelease,
EditMovie, EditMovie,
EditCollection,
GetCollections, GetCollections,
GetDownloads, GetDownloads,
GetMovieCredits, GetMovieCredits,
@@ -48,7 +49,7 @@ pub enum RadarrEvent {
impl RadarrEvent { impl RadarrEvent {
const fn resource(self) -> &'static str { const fn resource(self) -> &'static str {
match self { match self {
RadarrEvent::GetCollections => "/collection", RadarrEvent::GetCollections | RadarrEvent::EditCollection => "/collection",
RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload => "/queue", RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload => "/queue",
RadarrEvent::AddMovie RadarrEvent::AddMovie
| RadarrEvent::EditMovie | RadarrEvent::EditMovie
@@ -88,6 +89,7 @@ impl<'a> Network<'a> {
RadarrEvent::DeleteDownload => self.delete_download().await, RadarrEvent::DeleteDownload => self.delete_download().await,
RadarrEvent::DownloadRelease => self.download_release().await, RadarrEvent::DownloadRelease => self.download_release().await,
RadarrEvent::EditMovie => self.edit_movie().await, RadarrEvent::EditMovie => self.edit_movie().await,
RadarrEvent::EditCollection => self.edit_collection().await,
RadarrEvent::GetCollections => self.get_collections().await, RadarrEvent::GetCollections => self.get_collections().await,
RadarrEvent::GetDownloads => self.get_downloads().await, RadarrEvent::GetDownloads => self.get_downloads().await,
RadarrEvent::GetMovieCredits => self.get_credits().await, RadarrEvent::GetMovieCredits => self.get_credits().await,
@@ -762,13 +764,13 @@ impl<'a> Network<'a> {
let monitor = app let monitor = app
.data .data
.radarr_data .radarr_data
.movie_monitor_list .monitor_list
.current_selection() .current_selection()
.to_string(); .to_string();
let minimum_availability = app let minimum_availability = app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.current_selection() .current_selection()
.to_string(); .to_string();
@@ -827,8 +829,8 @@ impl<'a> Network<'a> {
let quality_profile_id = self.extract_quality_profile_id().await; let quality_profile_id = self.extract_quality_profile_id().await;
let tag_ids_vec = self.extract_and_add_tag_ids_vec().await; let tag_ids_vec = self.extract_and_add_tag_ids_vec().await;
let mut app = self.app.lock().await; let mut app = self.app.lock().await;
let mut detailed_movie_body: Value = serde_json::from_str(&app.response).unwrap(); let response = app.response.drain(..).collect::<String>();
app.response = String::default(); let mut detailed_movie_body: Value = serde_json::from_str(&response).unwrap();
let path: String = app.data.radarr_data.edit_path.drain(); let path: String = app.data.radarr_data.edit_path.drain();
@@ -836,7 +838,7 @@ impl<'a> Network<'a> {
let minimum_availability = app let minimum_availability = app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.current_selection() .current_selection()
.to_string(); .to_string();
@@ -864,6 +866,82 @@ impl<'a> Network<'a> {
.await; .await;
} }
async fn edit_collection(&self) {
info!("Editing Radarr collection");
info!("Fetching collection details");
let collection_id = self.extract_collection_id().await;
let request_props = self
.radarr_request_props_from(
format!(
"{}/{}",
RadarrEvent::GetCollections.resource(),
collection_id
)
.as_str(),
RequestMethod::Get,
None::<()>,
)
.await;
self
.handle_request::<(), Value>(request_props, |detailed_collection_body, mut app| {
app.response = detailed_collection_body.to_string()
})
.await;
info!("Constructing edit collection body");
let body = {
let quality_profile_id = self.extract_quality_profile_id().await;
let mut app = self.app.lock().await;
let response = app.response.drain(..).collect::<String>();
let mut detailed_collection_body: Value = serde_json::from_str(&response).unwrap();
let root_folder_path: String = app.data.radarr_data.edit_path.drain();
let monitored = app.data.radarr_data.edit_monitored.unwrap_or_default();
let search_on_add = app.data.radarr_data.edit_search_on_add.unwrap_or_default();
let minimum_availability = app
.data
.radarr_data
.minimum_availability_list
.current_selection()
.to_string();
*detailed_collection_body.get_mut("monitored").unwrap() = json!(monitored);
*detailed_collection_body
.get_mut("minimumAvailability")
.unwrap() = json!(minimum_availability);
*detailed_collection_body
.get_mut("qualityProfileId")
.unwrap() = json!(quality_profile_id);
*detailed_collection_body.get_mut("rootFolderPath").unwrap() = json!(root_folder_path);
*detailed_collection_body.get_mut("searchOnAdd").unwrap() = json!(search_on_add);
detailed_collection_body
};
debug!("Edit collection body: {:?}", body);
let request_props = self
.radarr_request_props_from(
format!(
"{}/{}",
RadarrEvent::EditCollection.resource(),
collection_id
)
.as_str(),
RequestMethod::Put,
Some(body),
)
.await;
self
.handle_request::<Value, ()>(request_props, |_, _| ())
.await;
}
async fn download_release(&self) { async fn download_release(&self) {
let (guid, title, indexer_id) = { let (guid, title, indexer_id) = {
let app = self.app.lock().await; let app = self.app.lock().await;
@@ -899,7 +977,7 @@ impl<'a> Network<'a> {
let quality_profile = app let quality_profile = app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.current_selection(); .current_selection();
*app *app
.data .data
@@ -988,6 +1066,43 @@ impl<'a> Network<'a> {
} }
} }
async fn extract_collection_id(&self) -> u64 {
if !self
.app
.lock()
.await
.data
.radarr_data
.filtered_collections
.items
.is_empty()
{
self
.app
.lock()
.await
.data
.radarr_data
.filtered_collections
.current_selection()
.id
.as_u64()
.unwrap()
} else {
self
.app
.lock()
.await
.data
.radarr_data
.collections
.current_selection()
.id
.as_u64()
.unwrap()
}
}
async fn append_movie_id_param(&self, resource: &str) -> String { async fn append_movie_id_param(&self, resource: &str) -> String {
let movie_id = self.extract_movie_id().await; let movie_id = self.extract_movie_id().await;
format!("{}?movieId={}", resource, movie_id) format!("{}?movieId={}", resource, movie_id)
@@ -1117,8 +1232,12 @@ mod test {
} }
}, },
"collection": { "collection": {
"id": 123,
"title": "Test Collection", "title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true, "searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah", "overview": "Collection blah blah blah",
"qualityProfileId": 2222, "qualityProfileId": 2222,
"movies": [ "movies": [
@@ -1791,8 +1910,12 @@ mod test {
#[tokio::test] #[tokio::test]
async fn test_handle_get_collections_event() { async fn test_handle_get_collections_event() {
let collection_json = json!([{ let collection_json = json!([{
"id": 123,
"title": "Test Collection", "title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true, "searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah", "overview": "Collection blah blah blah",
"qualityProfileId": 2222, "qualityProfileId": 2222,
"movies": [{ "movies": [{
@@ -2110,17 +2233,17 @@ mod test {
app app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]); .set_items(vec!["HD - 1080p".to_owned()]);
app app
.data .data
.radarr_data .radarr_data
.movie_monitor_list .monitor_list
.set_items(Vec::from_iter(Monitor::iter())); .set_items(Vec::from_iter(Monitor::iter()));
app app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter())); .set_items(Vec::from_iter(MinimumAvailability::iter()));
if collection_details_context { if collection_details_context {
app app
@@ -2180,12 +2303,12 @@ mod test {
app app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]); .set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter())); .set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.movies.set_items(vec![Movie { app.data.radarr_data.movies.set_items(vec![Movie {
monitored: false, monitored: false,
@@ -2201,11 +2324,105 @@ mod test {
async_details_server.assert_async().await; async_details_server.assert_async().await;
async_edit_server.assert_async().await; async_edit_server.assert_async().await;
let app = app_arc.lock().await;
assert!(app.response.is_empty());
assert!(app.data.radarr_data.edit_path.text.is_empty());
assert!(app.data.radarr_data.movie_details.items.is_empty());
}
#[tokio::test]
async fn test_handle_edit_collection_event() {
let detailed_collection_body = json!({
"id": 123,
"title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah",
"qualityProfileId": 2222,
"movies": [
{
"title": "Test",
"overview": "Collection blah blah blah",
"year": 2023,
"runtime": 120,
"tmdbId": 1234,
"genres": ["cool", "family", "fun"],
"ratings": {
"imdb": {
"value": 9.9
},
"tmdb": {
"value": 9.9
},
"rottenTomatoes": {
"value": 9.9
}
}
}
]
});
let mut expected_body = detailed_collection_body.clone();
*expected_body.get_mut("monitored").unwrap() = json!(false);
*expected_body.get_mut("minimumAvailability").unwrap() = json!("announced");
*expected_body.get_mut("qualityProfileId").unwrap() = json!(1111);
*expected_body.get_mut("rootFolderPath").unwrap() = json!("/nfs/Test Path");
*expected_body.get_mut("searchOnAdd").unwrap() = json!(false);
let (async_details_server, app_arc, mut server) = mock_radarr_api(
RequestMethod::Get,
None,
Some(detailed_collection_body),
format!("{}/123", RadarrEvent::GetCollections.resource()).as_str(),
)
.await;
let async_edit_server = server
.mock(
"PUT",
format!("/api/v3{}/123", RadarrEvent::EditCollection.resource()).as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_body))
.create_async()
.await;
{ {
let app = app_arc.lock().await; let mut app = app_arc.lock().await;
assert!(app.data.radarr_data.edit_path.text.is_empty()); app.data.radarr_data.edit_path = "/nfs/Test Path".to_owned().into();
assert!(app.data.radarr_data.movie_details.items.is_empty()); app.data.radarr_data.edit_monitored = Some(false);
app.data.radarr_data.edit_search_on_add = Some(false);
app
.data
.radarr_data
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app
.data
.radarr_data
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.collections.set_items(vec![Collection {
monitored: false,
search_on_add: false,
..collection()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
} }
let network = Network::new(reqwest::Client::new(), &app_arc);
network
.handle_radarr_event(RadarrEvent::EditCollection)
.await;
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
let app = app_arc.lock().await;
assert!(app.response.is_empty());
assert!(app.data.radarr_data.edit_path.text.is_empty());
assert!(app.data.radarr_data.movie_details.items.is_empty());
} }
#[tokio::test] #[tokio::test]
@@ -2244,7 +2461,7 @@ mod test {
app app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]); .set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app.data.radarr_data.quality_profile_map = app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1, "Any".to_owned()), (2, "HD - 1080p".to_owned())]); BiMap::from_iter([(1, "Any".to_owned()), (2, "HD - 1080p".to_owned())]);
@@ -2338,6 +2555,42 @@ mod test {
assert_eq!(network.extract_movie_id().await, 1); assert_eq!(network.extract_movie_id().await, 1);
} }
#[tokio::test]
async fn test_extract_collection_id() {
let app_arc = Arc::new(Mutex::new(App::default()));
app_arc
.lock()
.await
.data
.radarr_data
.collections
.set_items(vec![Collection {
id: Number::from(1),
..Collection::default()
}]);
let network = Network::new(reqwest::Client::new(), &app_arc);
assert_eq!(network.extract_collection_id().await, 1);
}
#[tokio::test]
async fn test_extract_collection_id_filtered_collection() {
let app_arc = Arc::new(Mutex::new(App::default()));
app_arc
.lock()
.await
.data
.radarr_data
.filtered_collections
.set_items(vec![Collection {
id: Number::from(1),
..Collection::default()
}]);
let network = Network::new(reqwest::Client::new(), &app_arc);
assert_eq!(network.extract_collection_id().await, 1);
}
#[tokio::test] #[tokio::test]
async fn test_append_movie_id_param() { async fn test_append_movie_id_param() {
let app_arc = Arc::new(Mutex::new(App::default())); let app_arc = Arc::new(Mutex::new(App::default()));
@@ -2562,9 +2815,12 @@ mod test {
fn collection() -> Collection { fn collection() -> Collection {
Collection { Collection {
id: Number::from(123),
title: "Test Collection".to_owned().into(), title: "Test Collection".to_owned().into(),
root_folder_path: None, root_folder_path: Some("/nfs/movies".to_owned()),
search_on_add: true, search_on_add: true,
monitored: true,
minimum_availability: MinimumAvailability::Released,
overview: Some("Collection blah blah blah".to_owned()), overview: Some("Collection blah blah blah".to_owned()),
quality_profile_id: Number::from(2222), quality_profile_id: Number::from(2222),
movies: Some(vec![collection_movie()]), movies: Some(vec![collection_movie()]),
+4 -4
View File
@@ -272,7 +272,7 @@ fn draw_select_monitor_popup<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, po
draw_drop_down_list( draw_drop_down_list(
f, f,
popup_area, popup_area,
&mut app.data.radarr_data.movie_monitor_list, &mut app.data.radarr_data.monitor_list,
|monitor| ListItem::new(monitor.to_display_str().to_owned()), |monitor| ListItem::new(monitor.to_display_str().to_owned()),
); );
} }
@@ -319,16 +319,16 @@ fn draw_confirmation_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, pro
let selected_block = &app.data.radarr_data.selected_block; let selected_block = &app.data.radarr_data.selected_block;
let highlight_yes_no = *selected_block == ActiveRadarrBlock::AddMovieConfirmPrompt; let highlight_yes_no = *selected_block == ActiveRadarrBlock::AddMovieConfirmPrompt;
let selected_monitor = app.data.radarr_data.movie_monitor_list.current_selection(); let selected_monitor = app.data.radarr_data.monitor_list.current_selection();
let selected_minimum_availability = app let selected_minimum_availability = app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.current_selection(); .current_selection();
let selected_quality_profile = app let selected_quality_profile = app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.current_selection(); .current_selection();
f.render_widget(title_block_centered(title), prompt_area); f.render_widget(title_block_centered(title), prompt_area);
+21 -8
View File
@@ -21,8 +21,8 @@ pub(super) fn draw_collection_details_popup<B: Backend>(
app: &mut App, app: &mut App,
content_area: Rect, content_area: Rect,
) { ) {
if let Route::Radarr(active_radarr_block, _) = app.get_current_route() { if let Route::Radarr(active_radarr_block, context_option) = app.get_current_route() {
match active_radarr_block { match context_option.as_ref().unwrap_or(active_radarr_block) {
ActiveRadarrBlock::ViewMovieOverview => { ActiveRadarrBlock::ViewMovieOverview => {
draw_small_popup_over( draw_small_popup_over(
f, f,
@@ -45,8 +45,8 @@ pub(super) fn draw_collection_details<B: Backend>(
) { ) {
let chunks = vertical_chunks_with_margin( let chunks = vertical_chunks_with_margin(
vec![ vec![
Constraint::Percentage(20), Constraint::Percentage(25),
Constraint::Percentage(75), Constraint::Percentage(70),
Constraint::Percentage(5), Constraint::Percentage(5),
], ],
content_area, content_area,
@@ -81,6 +81,17 @@ pub(super) fn draw_collection_details<B: Backend>(
let mut help_text = let mut help_text =
Text::from("<↑↓> scroll table | <enter> show overview/add movie | <esc> close"); Text::from("<↑↓> scroll table | <enter> show overview/add movie | <esc> close");
help_text.patch_style(style_help()); help_text.patch_style(style_help());
let monitored = if collection_selection.monitored {
"Yes"
} else {
"No"
};
let search_on_add = if collection_selection.search_on_add {
"Yes"
} else {
"No"
};
let minimum_availability = collection_selection.minimum_availability.to_display_str();
let collection_description = Text::from(vec![ let collection_description = Text::from(vec![
spans_info_primary( spans_info_primary(
@@ -94,11 +105,13 @@ pub(super) fn draw_collection_details<B: Backend>(
.clone() .clone()
.unwrap_or_default(), .unwrap_or_default(),
), ),
spans_info_primary(
"Search on Add: ".to_owned(),
collection_selection.search_on_add.to_string(),
),
spans_info_primary("Quality Profile: ".to_owned(), quality_profile), spans_info_primary("Quality Profile: ".to_owned(), quality_profile),
spans_info_primary(
"Minimum Availability: ".to_owned(),
minimum_availability.to_owned(),
),
spans_info_primary("Monitored: ".to_owned(), monitored.to_owned()),
spans_info_primary("Search on Add: ".to_owned(), search_on_add.to_owned()),
]); ]);
let description_paragraph = Paragraph::new(collection_description) let description_paragraph = Paragraph::new(collection_description)
+194
View File
@@ -0,0 +1,194 @@
use tui::backend::Backend;
use tui::layout::{Constraint, Rect};
use tui::Frame;
use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App;
use crate::models::Route;
use crate::ui::radarr_ui::{
draw_select_minimum_availability_popup, draw_select_quality_profile_popup,
};
use crate::ui::utils::{
horizontal_chunks, layout_paragraph_borderless, title_block_centered, vertical_chunks_with_margin,
};
use crate::ui::{
draw_button, draw_checkbox_with_label, draw_drop_down_menu_button, draw_drop_down_popup,
draw_text_box_with_label,
};
pub(super) fn draw_edit_collection_prompt<B: Backend>(
f: &mut Frame<'_, B>,
app: &mut App,
prompt_area: Rect,
) {
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => {
draw_drop_down_popup(
f,
app,
prompt_area,
draw_edit_collection_confirmation_prompt,
draw_select_minimum_availability_popup,
);
}
ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
draw_drop_down_popup(
f,
app,
prompt_area,
draw_edit_collection_confirmation_prompt,
draw_select_quality_profile_popup,
);
}
ActiveRadarrBlock::EditCollectionPrompt
| ActiveRadarrBlock::EditCollectionToggleMonitored
| ActiveRadarrBlock::EditCollectionRootFolderPathInput
| ActiveRadarrBlock::EditCollectionToggleSearchOnAdd => {
draw_edit_collection_confirmation_prompt(f, app, prompt_area)
}
_ => (),
}
}
}
fn draw_edit_collection_confirmation_prompt<B: Backend>(
f: &mut Frame<'_, B>,
app: &mut App,
prompt_area: Rect,
) {
let (collection_title, collection_overview) =
if app.data.radarr_data.filtered_collections.items.is_empty() {
(
app
.data
.radarr_data
.collections
.current_selection()
.title
.to_string(),
app
.data
.radarr_data
.collections
.current_selection()
.overview
.clone()
.unwrap_or_default(),
)
} else {
(
app
.data
.radarr_data
.filtered_collections
.current_selection()
.title
.to_string(),
app
.data
.radarr_data
.filtered_collections
.current_selection()
.overview
.clone()
.unwrap_or_default(),
)
};
let title = format!("Edit - {}", collection_title);
let yes_no_value = &app.data.radarr_data.prompt_confirm;
let selected_block = &app.data.radarr_data.selected_block;
let highlight_yes_no = *selected_block == ActiveRadarrBlock::EditCollectionConfirmPrompt;
let selected_minimum_availability = app
.data
.radarr_data
.minimum_availability_list
.current_selection();
let selected_quality_profile = app
.data
.radarr_data
.quality_profile_list
.current_selection();
f.render_widget(title_block_centered(&title), prompt_area);
let chunks = vertical_chunks_with_margin(
vec![
Constraint::Percentage(35),
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(3),
Constraint::Min(0),
Constraint::Length(3),
],
prompt_area,
1,
);
let prompt_paragraph = layout_paragraph_borderless(&collection_overview);
f.render_widget(prompt_paragraph, chunks[0]);
let horizontal_chunks = horizontal_chunks(
vec![Constraint::Percentage(50), Constraint::Percentage(50)],
chunks[7],
);
draw_checkbox_with_label(
f,
chunks[1],
"Monitored",
app.data.radarr_data.edit_monitored.unwrap_or_default(),
*selected_block == ActiveRadarrBlock::EditCollectionToggleMonitored,
);
draw_drop_down_menu_button(
f,
chunks[2],
"Minimum Availability",
selected_minimum_availability.to_display_str(),
*selected_block == ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
);
draw_drop_down_menu_button(
f,
chunks[3],
"Quality Profile",
selected_quality_profile,
*selected_block == ActiveRadarrBlock::EditCollectionSelectQualityProfile,
);
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
draw_text_box_with_label(
f,
chunks[4],
"Root Folder",
&app.data.radarr_data.edit_path.text,
*app.data.radarr_data.edit_path.offset.borrow(),
*selected_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput,
active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput,
);
}
draw_checkbox_with_label(
f,
chunks[5],
"Search on Add",
app.data.radarr_data.edit_search_on_add.unwrap_or_default(),
*selected_block == ActiveRadarrBlock::EditCollectionToggleSearchOnAdd,
);
draw_button(
f,
horizontal_chunks[0],
"Save",
*yes_no_value && highlight_yes_no,
);
draw_button(
f,
horizontal_chunks[1],
"Cancel",
!*yes_no_value && highlight_yes_no,
);
}
+43 -22
View File
@@ -28,7 +28,7 @@ pub(super) fn draw_edit_movie_prompt<B: Backend>(
f, f,
app, app,
prompt_area, prompt_area,
draw_edit_confirmation_prompt, draw_edit_movie_confirmation_prompt,
draw_select_minimum_availability_popup, draw_select_minimum_availability_popup,
); );
} }
@@ -37,40 +37,61 @@ pub(super) fn draw_edit_movie_prompt<B: Backend>(
f, f,
app, app,
prompt_area, prompt_area,
draw_edit_confirmation_prompt, draw_edit_movie_confirmation_prompt,
draw_select_quality_profile_popup, draw_select_quality_profile_popup,
); );
} }
ActiveRadarrBlock::EditMoviePrompt ActiveRadarrBlock::EditMoviePrompt
| ActiveRadarrBlock::EditMovieToggleMonitored | ActiveRadarrBlock::EditMovieToggleMonitored
| ActiveRadarrBlock::EditMoviePathInput | ActiveRadarrBlock::EditMoviePathInput
| ActiveRadarrBlock::EditMovieTagsInput => draw_edit_confirmation_prompt(f, app, prompt_area), | ActiveRadarrBlock::EditMovieTagsInput => {
draw_edit_movie_confirmation_prompt(f, app, prompt_area)
}
_ => (), _ => (),
} }
} }
} }
fn draw_edit_confirmation_prompt<B: Backend>( fn draw_edit_movie_confirmation_prompt<B: Backend>(
f: &mut Frame<'_, B>, f: &mut Frame<'_, B>,
app: &mut App, app: &mut App,
prompt_area: Rect, prompt_area: Rect,
) { ) {
let (movie_title, movie_overview) = ( let (movie_title, movie_overview) = if app.data.radarr_data.filtered_movies.items.is_empty() {
app (
.data app
.radarr_data .data
.movies .radarr_data
.current_selection() .movies
.title .current_selection()
.to_string(), .title
app .to_string(),
.data app
.radarr_data .data
.movies .radarr_data
.current_selection() .movies
.overview .current_selection()
.clone(), .overview
); .clone(),
)
} else {
(
app
.data
.radarr_data
.filtered_movies
.current_selection()
.title
.to_string(),
app
.data
.radarr_data
.filtered_movies
.current_selection()
.overview
.clone(),
)
};
let title = format!("Edit - {}", movie_title); let title = format!("Edit - {}", movie_title);
let yes_no_value = &app.data.radarr_data.prompt_confirm; let yes_no_value = &app.data.radarr_data.prompt_confirm;
let selected_block = &app.data.radarr_data.selected_block; let selected_block = &app.data.radarr_data.selected_block;
@@ -79,12 +100,12 @@ fn draw_edit_confirmation_prompt<B: Backend>(
let selected_minimum_availability = app let selected_minimum_availability = app
.data .data
.radarr_data .radarr_data
.movie_minimum_availability_list .minimum_availability_list
.current_selection(); .current_selection();
let selected_quality_profile = app let selected_quality_profile = app
.data .data
.radarr_data .radarr_data
.movie_quality_profile_list .quality_profile_list
.current_selection(); .current_selection();
f.render_widget(title_block_centered(&title), prompt_area); f.render_widget(title_block_centered(&title), prompt_area);
+49 -8
View File
@@ -10,8 +10,8 @@ use tui::widgets::{Cell, ListItem, Paragraph, Row};
use tui::Frame; use tui::Frame;
use crate::app::radarr::{ use crate::app::radarr::{
ActiveRadarrBlock, RadarrData, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, EDIT_MOVIE_BLOCKS, ActiveRadarrBlock, RadarrData, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS,
FILTER_BLOCKS, MOVIE_DETAILS_BLOCKS, SEARCH_BLOCKS, EDIT_COLLECTION_BLOCKS, EDIT_MOVIE_BLOCKS, FILTER_BLOCKS, MOVIE_DETAILS_BLOCKS, SEARCH_BLOCKS,
}; };
use crate::app::App; use crate::app::App;
use crate::logos::RADARR_LOGO; use crate::logos::RADARR_LOGO;
@@ -19,6 +19,7 @@ use crate::models::radarr_models::{Collection, DiskSpace, DownloadRecord, Movie}
use crate::models::{HorizontallyScrollableText, Route}; use crate::models::{HorizontallyScrollableText, Route};
use crate::ui::radarr_ui::add_movie_ui::draw_add_movie_search_popup; 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; use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup;
use crate::ui::radarr_ui::edit_collection_ui::draw_edit_collection_prompt;
use crate::ui::radarr_ui::edit_movie_ui::draw_edit_movie_prompt; use crate::ui::radarr_ui::edit_movie_ui::draw_edit_movie_prompt;
use crate::ui::radarr_ui::movie_details_ui::draw_movie_info_popup; use crate::ui::radarr_ui::movie_details_ui::draw_movie_info_popup;
use crate::ui::utils::{ use crate::ui::utils::{
@@ -35,6 +36,7 @@ use crate::utils::{convert_runtime, convert_to_gb};
mod add_movie_ui; mod add_movie_ui;
mod collection_details_ui; mod collection_details_ui;
mod edit_collection_ui;
mod edit_movie_ui; mod edit_movie_ui;
mod movie_details_ui; mod movie_details_ui;
@@ -113,6 +115,30 @@ pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, ar
} }
} }
} }
_ if EDIT_COLLECTION_BLOCKS.contains(&active_radarr_block) => {
if let Some(context) = context_option {
match context {
ActiveRadarrBlock::Collections => draw_medium_popup_over(
f,
app,
content_rect,
draw_collections,
draw_edit_collection_prompt,
),
_ if COLLECTION_DETAILS_BLOCKS.contains(&context) => {
draw_large_popup_over(
f,
app,
content_rect,
draw_collections,
draw_collection_details_popup,
);
draw_popup(f, app, draw_edit_collection_prompt, 60, 60);
}
_ => (),
}
}
}
ActiveRadarrBlock::DeleteMoviePrompt => { ActiveRadarrBlock::DeleteMoviePrompt => {
draw_prompt_popup_over(f, app, content_rect, draw_library, draw_delete_movie_prompt) draw_prompt_popup_over(f, app, content_rect, draw_library, draw_delete_movie_prompt)
} }
@@ -547,12 +573,20 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
content, content,
table_headers: vec![ table_headers: vec![
"Collection", "Collection",
"Search on Add?",
"Number of Movies", "Number of Movies",
"Root Folder Path", "Root Folder Path",
"Quality Profile", "Quality Profile",
"Search on Add",
"Monitored",
],
constraints: vec![
Constraint::Percentage(25),
Constraint::Percentage(15),
Constraint::Percentage(15),
Constraint::Percentage(15),
Constraint::Percentage(15),
Constraint::Percentage(15),
], ],
constraints: iter::repeat(Constraint::Ratio(1, 5)).take(5).collect(),
help: app help: app
.data .data
.radarr_data .radarr_data
@@ -562,14 +596,19 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|collection| { |collection| {
let number_of_movies = collection.movies.clone().unwrap_or_default().len(); let number_of_movies = collection.movies.clone().unwrap_or_default().len();
collection.title.scroll_left_or_reset( collection.title.scroll_left_or_reset(
get_width_from_percentage(area, 100 / 5), get_width_from_percentage(area, 25),
*collection == current_selection, *collection == current_selection,
app.tick_count % app.ticks_until_scroll == 0, app.tick_count % app.ticks_until_scroll == 0,
); );
let monitored = if collection.monitored { "🏷" } else { "" };
let search_on_add = if collection.search_on_add {
"Yes"
} else {
"No"
};
Row::new(vec![ Row::new(vec![
Cell::from(collection.title.to_string()), Cell::from(collection.title.to_string()),
Cell::from(collection.search_on_add.to_string()),
Cell::from(number_of_movies.to_string()), Cell::from(number_of_movies.to_string()),
Cell::from(collection.root_folder_path.clone().unwrap_or_default()), Cell::from(collection.root_folder_path.clone().unwrap_or_default()),
Cell::from( Cell::from(
@@ -578,6 +617,8 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
.unwrap() .unwrap()
.to_owned(), .to_owned(),
), ),
Cell::from(search_on_add),
Cell::from(monitored),
]) ])
.style(style_primary()) .style(style_primary())
}, },
@@ -704,7 +745,7 @@ fn draw_select_minimum_availability_popup<B: Backend>(
draw_drop_down_list( draw_drop_down_list(
f, f,
popup_area, popup_area,
&mut app.data.radarr_data.movie_minimum_availability_list, &mut app.data.radarr_data.minimum_availability_list,
|minimum_availability| ListItem::new(minimum_availability.to_display_str().to_owned()), |minimum_availability| ListItem::new(minimum_availability.to_display_str().to_owned()),
); );
} }
@@ -717,7 +758,7 @@ fn draw_select_quality_profile_popup<B: Backend>(
draw_drop_down_list( draw_drop_down_list(
f, f,
popup_area, popup_area,
&mut app.data.radarr_data.movie_quality_profile_list, &mut app.data.radarr_data.quality_profile_list,
|quality_profile| ListItem::new(quality_profile.clone()), |quality_profile| ListItem::new(quality_profile.clone()),
); );
} }
+1 -6
View File
@@ -26,12 +26,7 @@ pub(super) fn draw_movie_info_popup<B: Backend>(f: &mut Frame<'_, B>, app: &mut
let (content_area, _) = draw_tabs(f, area, "Movie Info", &app.data.radarr_data.movie_info_tabs); let (content_area, _) = draw_tabs(f, area, "Movie Info", &app.data.radarr_data.movie_info_tabs);
if let Route::Radarr(active_radarr_block, context_option) = app.get_current_route() { if let Route::Radarr(active_radarr_block, context_option) = app.get_current_route() {
let match_block = if let Some(context) = context_option { match context_option.as_ref().unwrap_or(active_radarr_block) {
context
} else {
active_radarr_block
};
match match_block {
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => draw_prompt_popup_over( ActiveRadarrBlock::AutomaticallySearchMoviePrompt => draw_prompt_popup_over(
f, f,
app, app,
+1 -1
View File
@@ -62,7 +62,7 @@ mod tests {
} }
#[test] #[test]
fn test_strop_non_alphanumeric_characters() { fn test_strip_non_alphanumeric_characters() {
assert_eq!( assert_eq!(
strip_non_alphanumeric_characters("Te$t S7r!ng::'~-_}"), strip_non_alphanumeric_characters("Te$t S7r!ng::'~-_}"),
"tet s7rng".to_owned() "tet s7rng".to_owned()