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]
name = "managarr"
version = "0.0.13"
version = "0.0.14"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "A TUI for managing *arr servers"
keywords = ["managarr", "tui-rs", "dashboard", "servarr"]
+2 -2
View File
@@ -84,10 +84,10 @@ tautulli:
- [x] Trigger automatic searches for movies
- [x] Trigger refresh and disk scan for movies, downloads, and collections
- [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 definitions
- [x] Manage your tags
- [ ] Manage your indexers
### Sonarr
+2 -2
View File
@@ -194,7 +194,7 @@ mod tests {
assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]);
assert!(app.network_tx.is_none());
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.tabs,
@@ -289,7 +289,7 @@ mod tests {
assert_eq!(app.tick_count, 0);
assert_eq!(app.error, HorizontallyScrollableText::default());
assert_eq!(app.data.radarr_data.version, String::default());
assert!(app.data.radarr_data.version.is_empty());
}
#[test]
+521 -90
View File
@@ -20,9 +20,9 @@ pub struct RadarrData {
pub movies: StatefulTable<Movie>,
pub filtered_movies: StatefulTable<Movie>,
pub add_searched_movies: StatefulTable<AddMovieSearchResult>,
pub movie_monitor_list: StatefulList<Monitor>,
pub movie_minimum_availability_list: StatefulList<MinimumAvailability>,
pub movie_quality_profile_list: StatefulList<String>,
pub monitor_list: StatefulList<Monitor>,
pub minimum_availability_list: StatefulList<MinimumAvailability>,
pub quality_profile_list: StatefulList<String>,
pub selected_block: ActiveRadarrBlock,
pub downloads: StatefulTable<DownloadRecord>,
pub quality_profile_map: BiMap<u64, String>,
@@ -47,6 +47,7 @@ pub struct RadarrData {
pub edit_path: HorizontallyScrollableText,
pub edit_tags: HorizontallyScrollableText,
pub edit_monitored: Option<bool>,
pub edit_search_on_add: Option<bool>,
pub sort_ascending: Option<bool>,
pub prompt_confirm: bool,
pub is_searching: bool,
@@ -74,11 +75,12 @@ impl RadarrData {
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_search_on_add = None;
self.edit_path = HorizontallyScrollableText::default();
self.edit_tags = HorizontallyScrollableText::default();
self.reset_movie_preferences_selections();
self.reset_preferences_selections();
}
pub fn reset_movie_info_tabs(&mut self) {
@@ -95,29 +97,25 @@ impl RadarrData {
self.movie_info_tabs.index = 0;
}
pub fn reset_movie_preferences_selections(&mut self) {
self.movie_monitor_list = StatefulList::default();
self.movie_minimum_availability_list = StatefulList::default();
self.movie_quality_profile_list = StatefulList::default();
pub fn reset_preferences_selections(&mut self) {
self.monitor_list = StatefulList::default();
self.minimum_availability_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
.movie_monitor_list
.set_items(Vec::from_iter(Monitor::iter()));
self
.movie_minimum_availability_list
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
let mut quality_profile_names: Vec<String> =
self.quality_profile_map.right_values().cloned().collect();
quality_profile_names.sort();
self
.movie_quality_profile_list
.set_items(quality_profile_names);
self.quality_profile_list.set_items(quality_profile_names);
}
pub fn populate_edit_movie_fields(&mut self) {
self.populate_movie_preferences_lists();
self.populate_preferences_lists();
let Movie {
path,
tags,
@@ -147,12 +145,12 @@ impl RadarrData {
self.edit_monitored = Some(*monitored);
let minimum_availability_index = self
.movie_minimum_availability_list
.minimum_availability_list
.items
.iter()
.position(|ma| ma == minimum_availability);
self
.movie_minimum_availability_list
.minimum_availability_list
.state
.select(minimum_availability_index);
@@ -161,12 +159,56 @@ impl RadarrData {
.get_by_left(&quality_profile_id.as_u64().unwrap())
.unwrap();
let quality_profile_index = self
.movie_quality_profile_list
.quality_profile_list
.items
.iter()
.position(|profile| profile == quality_profile_name);
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
.select(quality_profile_index);
}
@@ -181,9 +223,9 @@ impl Default for RadarrData {
start_time: DateTime::default(),
movies: StatefulTable::default(),
add_searched_movies: StatefulTable::default(),
movie_monitor_list: StatefulList::default(),
movie_minimum_availability_list: StatefulList::default(),
movie_quality_profile_list: StatefulList::default(),
monitor_list: StatefulList::default(),
minimum_availability_list: StatefulList::default(),
quality_profile_list: StatefulList::default(),
selected_block: ActiveRadarrBlock::AddMovieSelectMonitor,
filtered_movies: StatefulTable::default(),
downloads: StatefulTable::default(),
@@ -207,6 +249,7 @@ impl Default for RadarrData {
edit_path: HorizontallyScrollableText::default(),
edit_tags: HorizontallyScrollableText::default(),
edit_monitored: None,
edit_search_on_add: None,
sort_ascending: None,
is_searching: false,
is_filtering: false,
@@ -229,7 +272,7 @@ impl Default for RadarrData {
title: "Collections".to_owned(),
route: ActiveRadarrBlock::Collections.into(),
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()),
},
]),
@@ -295,6 +338,13 @@ pub enum ActiveRadarrBlock {
DeleteMoviePrompt,
DeleteDownloadPrompt,
Downloads,
EditCollectionPrompt,
EditCollectionConfirmPrompt,
EditCollectionRootFolderPathInput,
EditCollectionSelectMinimumAvailability,
EditCollectionSelectQualityProfile,
EditCollectionToggleSearchOnAdd,
EditCollectionToggleMonitored,
EditMoviePrompt,
EditMovieConfirmPrompt,
EditMoviePathInput,
@@ -331,6 +381,15 @@ pub const ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 9] = [
ActiveRadarrBlock::AddMovieAlreadyInLibrary,
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] = [
ActiveRadarrBlock::EditMoviePrompt,
ActiveRadarrBlock::EditMovieConfirmPrompt,
@@ -366,7 +425,7 @@ pub const FILTER_BLOCKS: [ActiveRadarrBlock; 2] = [
];
impl ActiveRadarrBlock {
pub fn next_add_prompt_block(&self) -> Self {
pub fn next_add_movie_prompt_block(&self) -> Self {
match self {
ActiveRadarrBlock::AddMovieSelectMonitor => {
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 {
ActiveRadarrBlock::EditMovieToggleMonitored => {
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 {
ActiveRadarrBlock::AddMovieSelectMonitor => ActiveRadarrBlock::AddMovieConfirmPrompt,
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 {
ActiveRadarrBlock::EditMovieToggleMonitored => ActiveRadarrBlock::EditMovieConfirmPrompt,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability => {
@@ -425,6 +505,30 @@ impl ActiveRadarrBlock {
_ => 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 {
@@ -538,10 +642,14 @@ impl App {
self.dispatch_by_radarr_block(&active_radarr_block).await;
}
if self.is_routing || self.tick_count % self.tick_until_poll == 0 {
self.refresh_metadata().await;
if self.should_refresh {
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) {
@@ -603,6 +711,7 @@ pub mod radarr_test_utils {
edit_path: "test path".to_owned().into(),
edit_tags: "usenet, test".to_owned().into(),
edit_monitored: Some(true),
edit_search_on_add: Some(true),
file_details: "test file details".to_owned(),
audio_details: "test audio details".to_owned(),
video_details: "test video details".to_owned(),
@@ -618,14 +727,12 @@ pub mod radarr_test_utils {
.movie_releases
.set_items(vec![Release::default()]);
radarr_data.movie_info_tabs.index = 1;
radarr_data.monitor_list.set_items(vec![Monitor::default()]);
radarr_data
.movie_monitor_list
.set_items(vec![Monitor::default()]);
radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.set_items(vec![MinimumAvailability::default()]);
radarr_data
.movie_quality_profile_list
.quality_profile_list
.set_items(vec![String::default()]);
radarr_data
.movie_releases_sort
@@ -667,9 +774,10 @@ pub mod radarr_test_utils {
}
#[macro_export]
macro_rules! assert_edit_movie_reset {
macro_rules! assert_edit_media_reset {
($radarr_data:expr) => {
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_tags.text.is_empty());
};
@@ -703,14 +811,11 @@ pub mod radarr_test_utils {
}
#[macro_export]
macro_rules! assert_movie_preferences_selections_reset {
macro_rules! assert_preferences_selections_reset {
($radarr_data:expr) => {
assert!($radarr_data.movie_monitor_list.items.is_empty());
assert!($radarr_data
.movie_minimum_availability_list
.items
.is_empty());
assert!($radarr_data.movie_quality_profile_list.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());
};
}
}
@@ -719,6 +824,7 @@ pub mod radarr_test_utils {
mod tests {
mod radarr_data_tests {
use bimap::BiMap;
use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest;
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::{ActiveRadarrBlock, RadarrData};
use crate::models::radarr_models::{MinimumAvailability, Monitor, Movie};
use crate::models::{HorizontallyScrollableText, Route, StatefulTable};
use crate::models::radarr_models::{Collection, MinimumAvailability, Monitor, Movie};
use crate::models::HorizontallyScrollableText;
use crate::models::Route;
use crate::models::StatefulTable;
#[test]
fn test_from_tuple_to_route_with_context() {
@@ -780,30 +888,31 @@ mod tests {
}
#[test]
fn test_reset_edit_movie() {
fn test_reset_add_edit_media_fields() {
let mut radarr_data = RadarrData {
edit_monitored: Some(true),
edit_search_on_add: Some(true),
edit_path: "test path".to_owned().into(),
edit_tags: "test tag".to_owned().into(),
..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]
fn test_reset_movie_preferences_selections() {
fn test_reset_preferences_selections() {
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]
fn test_populate_movie_preferences_lists() {
fn test_populate_preferences_lists() {
let mut radarr_data = RadarrData {
quality_profile_map: BiMap::from_iter([
(2222, "HD - 1080p".to_owned()),
@@ -812,18 +921,18 @@ mod tests {
..RadarrData::default()
};
radarr_data.populate_movie_preferences_lists();
radarr_data.populate_preferences_lists();
assert_eq!(
radarr_data.movie_monitor_list.items,
radarr_data.monitor_list.items,
Vec::from_iter(Monitor::iter())
);
assert_eq!(
radarr_data.movie_minimum_availability_list.items,
radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter())
);
assert_eq!(
radarr_data.movie_quality_profile_list.items,
radarr_data.quality_profile_list.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()]
);
}
@@ -860,27 +969,236 @@ mod tests {
radarr_data.populate_edit_movie_fields();
assert_eq!(
radarr_data.movie_minimum_availability_list.items,
radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter())
);
assert_eq!(
radarr_data
.movie_minimum_availability_list
.current_selection(),
radarr_data.minimum_availability_list.current_selection(),
&MinimumAvailability::Released
);
assert_eq!(
radarr_data.movie_quality_profile_list.items,
radarr_data.quality_profile_list.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()]
);
assert_str_eq!(
radarr_data.movie_quality_profile_list.current_selection(),
radarr_data.quality_profile_list.current_selection(),
"HD - 1080p"
);
assert_str_eq!(radarr_data.edit_path.text, "/nfs/movies/Test");
assert_str_eq!(radarr_data.edit_tags.text, "usenet, test");
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 {
@@ -889,128 +1207,215 @@ mod tests {
use crate::app::radarr::ActiveRadarrBlock;
#[test]
fn test_next_add_prompt_block() {
let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.next_add_prompt_block();
fn test_next_add_movie_prompt_block() {
let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.next_add_movie_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability
);
let active_block = active_block.next_add_prompt_block();
let active_block = active_block.next_add_movie_prompt_block();
assert_eq!(
active_block,
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);
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);
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);
}
#[test]
fn test_next_edit_prompt_block() {
let active_block = ActiveRadarrBlock::EditMovieToggleMonitored.next_edit_prompt_block();
fn test_next_edit_movie_prompt_block() {
let active_block = ActiveRadarrBlock::EditMovieToggleMonitored.next_edit_movie_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability
);
let active_block = active_block.next_edit_prompt_block();
let active_block = active_block.next_edit_movie_prompt_block();
assert_eq!(
active_block,
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);
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);
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);
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);
}
#[test]
fn test_previous_add_prompt_block() {
let active_block = ActiveRadarrBlock::AddMovieSelectMonitor.previous_add_prompt_block();
fn test_next_edit_collection_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);
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);
let active_block = active_block.previous_add_prompt_block();
let active_block = active_block.previous_add_movie_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::AddMovieSelectQualityProfile
);
let active_block = active_block.previous_add_prompt_block();
let active_block = active_block.previous_add_movie_prompt_block();
assert_eq!(
active_block,
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);
}
#[test]
fn test_previous_edit_prompt_block() {
let active_block = ActiveRadarrBlock::EditMovieToggleMonitored.previous_edit_prompt_block();
fn test_previous_edit_movie_prompt_block() {
let active_block =
ActiveRadarrBlock::EditMovieToggleMonitored.previous_edit_movie_prompt_block();
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);
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);
let active_block = active_block.previous_edit_prompt_block();
let active_block = active_block.previous_edit_movie_prompt_block();
assert_eq!(
active_block,
ActiveRadarrBlock::EditMovieSelectQualityProfile
);
let active_block = active_block.previous_edit_prompt_block();
let active_block = active_block.previous_edit_movie_prompt_block();
assert_eq!(
active_block,
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);
}
#[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 {
@@ -1430,6 +1835,10 @@ mod tests {
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetQualityProfiles.into()
@@ -1446,6 +1855,24 @@ mod tests {
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]
async fn test_radarr_on_tick_network_tick_frequency() {
let (mut app, mut sync_network_rx) = construct_app_unit();
@@ -1456,6 +1883,10 @@ mod tests {
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
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()
}
ActiveRadarrBlock::AddMovieSelectMonitor => {
self.app.data.radarr_data.movie_monitor_list.scroll_up()
self.app.data.radarr_data.monitor_list.scroll_up()
}
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self
.app
.data
.radarr_data
.movie_minimum_availability_list
.scroll_up(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.minimum_availability_list
.scroll_up(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_up()
}
ActiveRadarrBlock::AddMoviePrompt => {
self.app.data.radarr_data.selected_block = self
.app
@@ -58,7 +55,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
.radarr_data
.selected_block
.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()
}
ActiveRadarrBlock::AddMovieSelectMonitor => {
self.app.data.radarr_data.movie_monitor_list.scroll_down()
self.app.data.radarr_data.monitor_list.scroll_down()
}
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self
.app
.data
.radarr_data
.movie_minimum_availability_list
.scroll_down(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.minimum_availability_list
.scroll_down(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_down()
}
ActiveRadarrBlock::AddMoviePrompt => {
self.app.data.radarr_data.selected_block = self
.app
.data
.radarr_data
.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
.scroll_to_top(),
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
.app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.scroll_to_top(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.scroll_to_top(),
ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.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
.add_searched_movies
.scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSelectMonitor => self
.app
.data
.radarr_data
.movie_monitor_list
.scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSelectMonitor => {
self.app.data.radarr_data.monitor_list.scroll_to_bottom()
}
ActiveRadarrBlock::AddMovieSelectMinimumAvailability => self
.app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.scroll_to_bottom(),
ActiveRadarrBlock::AddMovieSearchInput => self.app.data.radarr_data.search.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
.app
.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;
}
}
@@ -265,7 +256,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
}
ActiveRadarrBlock::AddMoviePrompt => {
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;
}
ActiveRadarrBlock::AddMovieSelectMonitor
@@ -331,16 +322,16 @@ mod tests {
test_add_movie_select_monitor_scroll,
AddMovieHandler,
Monitor,
movie_monitor_list,
monitor_list,
ActiveRadarrBlock::AddMovieSelectMonitor,
None
);
test_enum_scroll!(
test_add_movie_select_minimuum_availability_scroll,
test_add_movie_select_minimum_availability_scroll,
AddMovieHandler,
MinimumAvailability,
movie_minimum_availability_list,
minimum_availability_list,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None
);
@@ -348,7 +339,7 @@ mod tests {
test_iterable_scroll!(
test_add_movie_select_quality_profile_scroll,
AddMovieHandler,
movie_quality_profile_list,
quality_profile_list,
ActiveRadarrBlock::AddMovieSelectQualityProfile,
None
);
@@ -399,16 +390,16 @@ mod tests {
test_add_movie_select_monitor_home_end,
AddMovieHandler,
Monitor,
movie_monitor_list,
monitor_list,
ActiveRadarrBlock::AddMovieSelectMonitor,
None
);
test_enum_home_and_end!(
test_add_movie_select_minimuum_availability_home_end,
test_add_movie_select_minimum_availability_home_end,
AddMovieHandler,
MinimumAvailability,
movie_minimum_availability_list,
minimum_availability_list,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None
);
@@ -416,7 +407,7 @@ mod tests {
test_iterable_home_and_end!(
test_add_movie_select_quality_profile_scroll,
AddMovieHandler,
movie_quality_profile_list,
quality_profile_list,
ActiveRadarrBlock::AddMovieSelectQualityProfile,
None
);
@@ -539,24 +530,19 @@ mod tests {
app.data.radarr_data.selected_block,
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
.data
.radarr_data
.movie_minimum_availability_list
.items
.is_empty());
assert!(!app
.data
.radarr_data
.movie_quality_profile_list
.minimum_availability_list
.items
.is_empty());
assert!(!app.data.radarr_data.quality_profile_list.items.is_empty());
assert_str_eq!(
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.current_selection(),
"A - Test 1"
);
@@ -728,7 +714,7 @@ mod tests {
use crate::app::radarr::radarr_test_utils::create_test_radarr_data;
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,
};
@@ -850,8 +836,8 @@ mod tests {
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into()
);
assert_movie_preferences_selections_reset!(app.data.radarr_data);
assert_edit_movie_reset!(app.data.radarr_data);
assert_preferences_selections_reset!(app.data.radarr_data);
assert_edit_media_reset!(app.data.radarr_data);
}
#[test]
@@ -1,3 +1,4 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App;
use crate::event::Key;
@@ -95,7 +96,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for CollectionDetailsHandler<'a>
.into(),
);
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)]
@@ -201,7 +216,7 @@ mod tests {
)
.into()
);
assert!(!app.data.radarr_data.movie_monitor_list.items.is_empty());
assert!(!app.data.radarr_data.monitor_list.items.is_empty());
assert_eq!(
app.data.radarr_data.selected_block,
ActiveRadarrBlock::EditMovieToggleMonitored
@@ -209,20 +224,15 @@ mod tests {
assert!(!app
.data
.radarr_data
.movie_minimum_availability_list
.items
.is_empty());
assert!(!app
.data
.radarr_data
.movie_quality_profile_list
.minimum_availability_list
.items
.is_empty());
assert!(!app.data.radarr_data.quality_profile_list.items.is_empty());
assert_str_eq!(
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.current_selection(),
"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
.data
.radarr_data
.movie_minimum_availability_list
.scroll_up(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.minimum_availability_list
.scroll_up(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_up()
}
ActiveRadarrBlock::EditMoviePrompt => {
self.app.data.radarr_data.selected_block = self
.app
@@ -54,7 +51,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.radarr_data
.selected_block
.clone()
.previous_edit_prompt_block()
.previous_edit_movie_prompt_block()
}
_ => (),
}
@@ -66,21 +63,18 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.app
.data
.radarr_data
.movie_minimum_availability_list
.scroll_down(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.minimum_availability_list
.scroll_down(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => {
self.app.data.radarr_data.quality_profile_list.scroll_down()
}
ActiveRadarrBlock::EditMoviePrompt => {
self.app.data.radarr_data.selected_block = self
.app
.data
.radarr_data
.selected_block
.next_edit_prompt_block()
.next_edit_movie_prompt_block()
}
_ => (),
}
@@ -92,13 +86,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
.app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.scroll_to_top(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.scroll_to_top(),
ActiveRadarrBlock::EditMoviePathInput => self.app.data.radarr_data.edit_path.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
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.scroll_to_bottom(),
ActiveRadarrBlock::EditMovieSelectQualityProfile => self
.app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.scroll_to_bottom(),
ActiveRadarrBlock::EditMoviePathInput => self.app.data.radarr_data.edit_path.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 {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie);
self.app.pop_navigation_stack();
self.app.should_refresh = true;
} else {
self.app.pop_navigation_stack();
}
@@ -186,7 +181,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for EditMovieHandler<'a> {
}
ActiveRadarrBlock::EditMoviePrompt => {
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;
}
ActiveRadarrBlock::EditMovieToggleMonitored
@@ -232,10 +227,10 @@ mod tests {
use super::*;
test_enum_scroll!(
test_edit_movie_select_minimuum_availability_scroll,
test_edit_movie_select_minimum_availability_scroll,
EditMovieHandler,
MinimumAvailability,
movie_minimum_availability_list,
minimum_availability_list,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None
);
@@ -243,7 +238,7 @@ mod tests {
test_iterable_scroll!(
test_edit_movie_select_quality_profile_scroll,
EditMovieHandler,
movie_quality_profile_list,
quality_profile_list,
ActiveRadarrBlock::EditMovieSelectQualityProfile,
None
);
@@ -277,10 +272,10 @@ mod tests {
use super::*;
test_enum_home_and_end!(
test_edit_movie_select_minimuum_availability_home_end,
test_edit_movie_select_minimum_availability_home_end,
EditMovieHandler,
MinimumAvailability,
movie_minimum_availability_list,
minimum_availability_list,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None
);
@@ -288,7 +283,7 @@ mod tests {
test_iterable_home_and_end!(
test_edit_movie_select_quality_profile_scroll,
EditMovieHandler,
movie_quality_profile_list,
quality_profile_list,
ActiveRadarrBlock::EditMovieSelectQualityProfile,
None
);
@@ -451,6 +446,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie)
);
assert!(app.should_refresh);
}
#[test]
@@ -567,7 +563,7 @@ mod tests {
use rstest::rstest;
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::*;
@@ -611,13 +607,11 @@ mod tests {
.handle();
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_edit_movie_reset!(radarr_data);
assert!(!radarr_data.prompt_confirm);
}
assert_preferences_selections_reset!(radarr_data);
assert_edit_media_reset!(radarr_data);
assert!(!radarr_data.prompt_confirm);
}
#[rstest]
+120 -16
View File
@@ -1,10 +1,11 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::{
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, EDIT_MOVIE_BLOCKS, FILTER_BLOCKS,
MOVIE_DETAILS_BLOCKS, SEARCH_BLOCKS,
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, EDIT_COLLECTION_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::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::movie_details_handler::MovieDetailsHandler;
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 collection_details_handler;
mod edit_collection_handler;
mod edit_movie_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) => {
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(),
}
}
@@ -447,9 +453,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
self.app.should_refresh = true;
}
_ => (),
},
@@ -460,9 +464,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
.push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
self.app.should_refresh = true;
}
_ => (),
},
@@ -481,15 +483,25 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
self.app.data.radarr_data.is_filtering = 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 => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
self.app.should_refresh = true;
}
_ => (),
},
@@ -558,6 +570,7 @@ mod radarr_handler_test_utils {
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()),
@@ -587,26 +600,26 @@ mod radarr_handler_test_utils {
ActiveRadarrBlock::EditMovieToggleMonitored
);
assert_eq!(
app.data.radarr_data.movie_minimum_availability_list.items,
app.data.radarr_data.minimum_availability_list.items,
Vec::from_iter(MinimumAvailability::iter())
);
assert_eq!(
app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.current_selection(),
&MinimumAvailability::Released
);
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()]
);
assert_str_eq!(
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.current_selection(),
"HD - 1080p"
);
@@ -615,6 +628,72 @@ mod radarr_handler_test_utils {
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)]
@@ -1382,6 +1461,15 @@ mod tests {
);
}
#[test]
fn test_collection_edit_key() {
test_edit_collection_key!(
RadarrHandler,
ActiveRadarrBlock::Collections,
ActiveRadarrBlock::Collections
);
}
#[rstest]
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::UpdateAllMoviesPrompt)]
#[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::UpdateDownloadsPrompt)]
@@ -1416,6 +1504,7 @@ mod tests {
active_radarr_block: ActiveRadarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(active_radarr_block.into());
RadarrHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
@@ -1426,7 +1515,7 @@ mod tests {
.handle();
assert_eq!(app.get_current_route(), &active_radarr_block.into());
assert!(app.is_routing);
assert!(app.should_refresh);
}
#[rstest]
@@ -1691,4 +1780,19 @@ mod tests {
) {
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)]
#[serde(rename_all = "camelCase")]
pub struct Collection {
#[derivative(Default(value = "Number::from(0)"))]
pub id: Number,
#[serde(default)]
pub title: HorizontallyScrollableText,
pub root_folder_path: Option<String>,
pub search_on_add: bool,
pub monitored: bool,
pub overview: Option<String>,
pub minimum_availability: MinimumAvailability,
#[derivative(Default(value = "Number::from(0)"))]
pub quality_profile_id: Number,
pub movies: Option<Vec<CollectionMovie>>,
+273 -17
View File
@@ -24,6 +24,7 @@ pub enum RadarrEvent {
DeleteMovie,
DownloadRelease,
EditMovie,
EditCollection,
GetCollections,
GetDownloads,
GetMovieCredits,
@@ -48,7 +49,7 @@ pub enum RadarrEvent {
impl RadarrEvent {
const fn resource(self) -> &'static str {
match self {
RadarrEvent::GetCollections => "/collection",
RadarrEvent::GetCollections | RadarrEvent::EditCollection => "/collection",
RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload => "/queue",
RadarrEvent::AddMovie
| RadarrEvent::EditMovie
@@ -88,6 +89,7 @@ impl<'a> Network<'a> {
RadarrEvent::DeleteDownload => self.delete_download().await,
RadarrEvent::DownloadRelease => self.download_release().await,
RadarrEvent::EditMovie => self.edit_movie().await,
RadarrEvent::EditCollection => self.edit_collection().await,
RadarrEvent::GetCollections => self.get_collections().await,
RadarrEvent::GetDownloads => self.get_downloads().await,
RadarrEvent::GetMovieCredits => self.get_credits().await,
@@ -762,13 +764,13 @@ impl<'a> Network<'a> {
let monitor = app
.data
.radarr_data
.movie_monitor_list
.monitor_list
.current_selection()
.to_string();
let minimum_availability = app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.current_selection()
.to_string();
@@ -827,8 +829,8 @@ impl<'a> Network<'a> {
let quality_profile_id = self.extract_quality_profile_id().await;
let tag_ids_vec = self.extract_and_add_tag_ids_vec().await;
let mut app = self.app.lock().await;
let mut detailed_movie_body: Value = serde_json::from_str(&app.response).unwrap();
app.response = String::default();
let response = app.response.drain(..).collect::<String>();
let mut detailed_movie_body: Value = serde_json::from_str(&response).unwrap();
let path: String = app.data.radarr_data.edit_path.drain();
@@ -836,7 +838,7 @@ impl<'a> Network<'a> {
let minimum_availability = app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.current_selection()
.to_string();
@@ -864,6 +866,82 @@ impl<'a> Network<'a> {
.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) {
let (guid, title, indexer_id) = {
let app = self.app.lock().await;
@@ -899,7 +977,7 @@ impl<'a> Network<'a> {
let quality_profile = app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.current_selection();
*app
.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 {
let movie_id = self.extract_movie_id().await;
format!("{}?movieId={}", resource, movie_id)
@@ -1117,8 +1232,12 @@ mod test {
}
},
"collection": {
"id": 123,
"title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah",
"qualityProfileId": 2222,
"movies": [
@@ -1791,8 +1910,12 @@ mod test {
#[tokio::test]
async fn test_handle_get_collections_event() {
let collection_json = json!([{
"id": 123,
"title": "Test Collection",
"rootFolderPath": "/nfs/movies",
"searchOnAdd": true,
"monitored": true,
"minimumAvailability": "released",
"overview": "Collection blah blah blah",
"qualityProfileId": 2222,
"movies": [{
@@ -2110,17 +2233,17 @@ mod test {
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
app
.data
.radarr_data
.movie_monitor_list
.monitor_list
.set_items(Vec::from_iter(Monitor::iter()));
app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
if collection_details_context {
app
@@ -2180,12 +2303,12 @@ mod test {
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.movies.set_items(vec![Movie {
monitored: false,
@@ -2201,11 +2324,105 @@ mod test {
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]
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;
assert!(app.data.radarr_data.edit_path.text.is_empty());
assert!(app.data.radarr_data.movie_details.items.is_empty());
let mut app = app_arc.lock().await;
app.data.radarr_data.edit_path = "/nfs/Test Path".to_owned().into();
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]
@@ -2244,7 +2461,7 @@ mod test {
app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
app.data.radarr_data.quality_profile_map =
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);
}
#[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]
async fn test_append_movie_id_param() {
let app_arc = Arc::new(Mutex::new(App::default()));
@@ -2562,9 +2815,12 @@ mod test {
fn collection() -> Collection {
Collection {
id: Number::from(123),
title: "Test Collection".to_owned().into(),
root_folder_path: None,
root_folder_path: Some("/nfs/movies".to_owned()),
search_on_add: true,
monitored: true,
minimum_availability: MinimumAvailability::Released,
overview: Some("Collection blah blah blah".to_owned()),
quality_profile_id: Number::from(2222),
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(
f,
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()),
);
}
@@ -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 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
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.current_selection();
let selected_quality_profile = app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.current_selection();
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,
content_area: Rect,
) {
if let Route::Radarr(active_radarr_block, _) = app.get_current_route() {
match active_radarr_block {
if let Route::Radarr(active_radarr_block, context_option) = app.get_current_route() {
match context_option.as_ref().unwrap_or(active_radarr_block) {
ActiveRadarrBlock::ViewMovieOverview => {
draw_small_popup_over(
f,
@@ -45,8 +45,8 @@ pub(super) fn draw_collection_details<B: Backend>(
) {
let chunks = vertical_chunks_with_margin(
vec![
Constraint::Percentage(20),
Constraint::Percentage(75),
Constraint::Percentage(25),
Constraint::Percentage(70),
Constraint::Percentage(5),
],
content_area,
@@ -81,6 +81,17 @@ pub(super) fn draw_collection_details<B: Backend>(
let mut help_text =
Text::from("<↑↓> scroll table | <enter> show overview/add movie | <esc> close");
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![
spans_info_primary(
@@ -94,11 +105,13 @@ pub(super) fn draw_collection_details<B: Backend>(
.clone()
.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(
"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)
+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,
app,
prompt_area,
draw_edit_confirmation_prompt,
draw_edit_movie_confirmation_prompt,
draw_select_minimum_availability_popup,
);
}
@@ -37,40 +37,61 @@ pub(super) fn draw_edit_movie_prompt<B: Backend>(
f,
app,
prompt_area,
draw_edit_confirmation_prompt,
draw_edit_movie_confirmation_prompt,
draw_select_quality_profile_popup,
);
}
ActiveRadarrBlock::EditMoviePrompt
| ActiveRadarrBlock::EditMovieToggleMonitored
| 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>,
app: &mut App,
prompt_area: Rect,
) {
let (movie_title, movie_overview) = (
app
.data
.radarr_data
.movies
.current_selection()
.title
.to_string(),
app
.data
.radarr_data
.movies
.current_selection()
.overview
.clone(),
);
let (movie_title, movie_overview) = if app.data.radarr_data.filtered_movies.items.is_empty() {
(
app
.data
.radarr_data
.movies
.current_selection()
.title
.to_string(),
app
.data
.radarr_data
.movies
.current_selection()
.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 yes_no_value = &app.data.radarr_data.prompt_confirm;
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
.data
.radarr_data
.movie_minimum_availability_list
.minimum_availability_list
.current_selection();
let selected_quality_profile = app
.data
.radarr_data
.movie_quality_profile_list
.quality_profile_list
.current_selection();
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 crate::app::radarr::{
ActiveRadarrBlock, RadarrData, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS, EDIT_MOVIE_BLOCKS,
FILTER_BLOCKS, MOVIE_DETAILS_BLOCKS, SEARCH_BLOCKS,
ActiveRadarrBlock, RadarrData, ADD_MOVIE_BLOCKS, COLLECTION_DETAILS_BLOCKS,
EDIT_COLLECTION_BLOCKS, EDIT_MOVIE_BLOCKS, FILTER_BLOCKS, MOVIE_DETAILS_BLOCKS, SEARCH_BLOCKS,
};
use crate::app::App;
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::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::edit_collection_ui::draw_edit_collection_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::utils::{
@@ -35,6 +36,7 @@ use crate::utils::{convert_runtime, convert_to_gb};
mod add_movie_ui;
mod collection_details_ui;
mod edit_collection_ui;
mod edit_movie_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 => {
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,
table_headers: vec![
"Collection",
"Search on Add?",
"Number of Movies",
"Root Folder Path",
"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
.data
.radarr_data
@@ -562,14 +596,19 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|collection| {
let number_of_movies = collection.movies.clone().unwrap_or_default().len();
collection.title.scroll_left_or_reset(
get_width_from_percentage(area, 100 / 5),
get_width_from_percentage(area, 25),
*collection == current_selection,
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![
Cell::from(collection.title.to_string()),
Cell::from(collection.search_on_add.to_string()),
Cell::from(number_of_movies.to_string()),
Cell::from(collection.root_folder_path.clone().unwrap_or_default()),
Cell::from(
@@ -578,6 +617,8 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
.unwrap()
.to_owned(),
),
Cell::from(search_on_add),
Cell::from(monitored),
])
.style(style_primary())
},
@@ -704,7 +745,7 @@ fn draw_select_minimum_availability_popup<B: Backend>(
draw_drop_down_list(
f,
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()),
);
}
@@ -717,7 +758,7 @@ fn draw_select_quality_profile_popup<B: Backend>(
draw_drop_down_list(
f,
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()),
);
}
+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);
if let Route::Radarr(active_radarr_block, context_option) = app.get_current_route() {
let match_block = if let Some(context) = context_option {
context
} else {
active_radarr_block
};
match match_block {
match context_option.as_ref().unwrap_or(active_radarr_block) {
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => draw_prompt_popup_over(
f,
app,
+1 -1
View File
@@ -62,7 +62,7 @@ mod tests {
}
#[test]
fn test_strop_non_alphanumeric_characters() {
fn test_strip_non_alphanumeric_characters() {
assert_eq!(
strip_non_alphanumeric_characters("Te$t S7r!ng::'~-_}"),
"tet s7rng".to_owned()