Added the ability to add and delete root folders, and added a body to the error message logs and dialog box if a body is returned from the Servarr
This commit is contained in:
@@ -336,6 +336,7 @@ pub enum ActiveRadarrBlock {
|
||||
AddMovieConfirmPrompt,
|
||||
AddMovieTagsInput,
|
||||
AddMovieEmptySearchResults,
|
||||
AddRootFolderPrompt,
|
||||
AutomaticallySearchMoviePrompt,
|
||||
Collections,
|
||||
CollectionDetails,
|
||||
@@ -343,6 +344,7 @@ pub enum ActiveRadarrBlock {
|
||||
Crew,
|
||||
DeleteMoviePrompt,
|
||||
DeleteDownloadPrompt,
|
||||
DeleteRootFolderPrompt,
|
||||
Downloads,
|
||||
EditCollectionPrompt,
|
||||
EditCollectionConfirmPrompt,
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::handlers::radarr_handlers::edit_collection_handler::EditCollectionHan
|
||||
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};
|
||||
use crate::models::Scrollable;
|
||||
use crate::models::{HorizontallyScrollableText, Scrollable};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::utils::strip_non_alphanumeric_characters;
|
||||
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
|
||||
@@ -164,6 +164,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterCollections => {
|
||||
self.app.data.radarr_data.filter.scroll_home()
|
||||
}
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => self.app.data.radarr_data.edit_path.scroll_home(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -204,6 +205,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterCollections => {
|
||||
self.app.data.radarr_data.filter.reset_offset()
|
||||
}
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => self.app.data.radarr_data.edit_path.reset_offset(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -216,6 +218,9 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
ActiveRadarrBlock::Downloads => self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::DeleteDownloadPrompt.into()),
|
||||
ActiveRadarrBlock::RootFolders => self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -242,9 +247,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
},
|
||||
ActiveRadarrBlock::DeleteMoviePrompt
|
||||
| ActiveRadarrBlock::DeleteDownloadPrompt
|
||||
| ActiveRadarrBlock::DeleteRootFolderPrompt
|
||||
| ActiveRadarrBlock::UpdateAllMoviesPrompt
|
||||
| ActiveRadarrBlock::UpdateAllCollectionsPrompt
|
||||
| ActiveRadarrBlock::UpdateDownloadsPrompt => handle_prompt_toggle(self.app, self.key),
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => {
|
||||
handle_text_box_left_right_keys!(self, self.key, self.app.data.radarr_data.edit_path)
|
||||
}
|
||||
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
|
||||
handle_text_box_left_right_keys!(self, self.key, self.app.data.radarr_data.search)
|
||||
}
|
||||
@@ -364,6 +373,13 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveRadarrBlock::DeleteRootFolderPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteRootFolder);
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveRadarrBlock::UpdateAllMoviesPrompt => {
|
||||
if self.app.data.radarr_data.prompt_confirm {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies);
|
||||
@@ -385,6 +401,12 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => {
|
||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddRootFolder);
|
||||
self.app.data.radarr_data.prompt_confirm = true;
|
||||
self.app.should_ignore_quit_key = false;
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -401,8 +423,15 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
self.app.data.radarr_data.reset_search();
|
||||
self.app.should_ignore_quit_key = false;
|
||||
}
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.edit_path = HorizontallyScrollableText::default();
|
||||
self.app.data.radarr_data.prompt_confirm = false;
|
||||
self.app.should_ignore_quit_key = false;
|
||||
}
|
||||
ActiveRadarrBlock::DeleteMoviePrompt
|
||||
| ActiveRadarrBlock::DeleteDownloadPrompt
|
||||
| ActiveRadarrBlock::DeleteRootFolderPrompt
|
||||
| ActiveRadarrBlock::UpdateAllMoviesPrompt
|
||||
| ActiveRadarrBlock::UpdateAllCollectionsPrompt
|
||||
| ActiveRadarrBlock::UpdateDownloadsPrompt => {
|
||||
@@ -514,8 +543,17 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.add.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
|
||||
self.app.should_ignore_quit_key = true;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => {
|
||||
handle_text_box_keys!(self, key, self.app.data.radarr_data.edit_path)
|
||||
}
|
||||
_ if SEARCH_BLOCKS.contains(self.active_radarr_block) => {
|
||||
handle_text_box_keys!(self, key, self.app.data.radarr_data.search)
|
||||
}
|
||||
@@ -869,6 +907,15 @@ mod tests {
|
||||
path
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_prompt_home_end_keys() {
|
||||
test_text_box_home_end_keys!(
|
||||
RadarrHandler,
|
||||
ActiveRadarrBlock::AddRootFolderPrompt,
|
||||
edit_path
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_search_boxes_home_end_keys(
|
||||
#[values(ActiveRadarrBlock::SearchMovie, ActiveRadarrBlock::SearchCollection)]
|
||||
@@ -916,6 +963,24 @@ mod tests {
|
||||
&ActiveRadarrBlock::DeleteDownloadPrompt.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folder_delete() {
|
||||
let mut app = App::default();
|
||||
|
||||
RadarrHandler::with(
|
||||
&DELETE_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::RootFolders,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::DeleteRootFolderPrompt.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_left_right_action {
|
||||
@@ -987,6 +1052,7 @@ mod tests {
|
||||
#[values(
|
||||
ActiveRadarrBlock::DeleteMoviePrompt,
|
||||
ActiveRadarrBlock::DeleteDownloadPrompt,
|
||||
ActiveRadarrBlock::DeleteRootFolderPrompt,
|
||||
ActiveRadarrBlock::UpdateAllMoviesPrompt,
|
||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt,
|
||||
ActiveRadarrBlock::UpdateDownloadsPrompt
|
||||
@@ -1005,6 +1071,15 @@ mod tests {
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_prompt_left_right_keys() {
|
||||
test_text_box_left_right_keys!(
|
||||
RadarrHandler,
|
||||
ActiveRadarrBlock::AddRootFolderPrompt,
|
||||
edit_path
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_search_boxes_left_right_keys(
|
||||
#[values(ActiveRadarrBlock::SearchMovie, ActiveRadarrBlock::SearchCollection)]
|
||||
@@ -1240,6 +1315,34 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_prompt_confirm_submit() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.prompt_confirm = true;
|
||||
app.should_ignore_quit_key = true;
|
||||
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
|
||||
|
||||
RadarrHandler::with(
|
||||
&SUBMIT_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AddRootFolderPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.radarr_data.prompt_confirm);
|
||||
assert!(!app.should_ignore_quit_key);
|
||||
assert_eq!(
|
||||
app.data.radarr_data.prompt_confirm_action,
|
||||
Some(RadarrEvent::AddRootFolder)
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Movies,
|
||||
@@ -1251,6 +1354,11 @@ mod tests {
|
||||
ActiveRadarrBlock::DeleteDownloadPrompt,
|
||||
RadarrEvent::DeleteDownload
|
||||
)]
|
||||
#[case(
|
||||
ActiveRadarrBlock::RootFolders,
|
||||
ActiveRadarrBlock::DeleteRootFolderPrompt,
|
||||
RadarrEvent::DeleteRootFolder
|
||||
)]
|
||||
#[case(
|
||||
ActiveRadarrBlock::Movies,
|
||||
ActiveRadarrBlock::UpdateAllMoviesPrompt,
|
||||
@@ -1365,6 +1473,10 @@ mod tests {
|
||||
#[rstest]
|
||||
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::DeleteMoviePrompt)]
|
||||
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::UpdateAllMoviesPrompt)]
|
||||
#[case(
|
||||
ActiveRadarrBlock::RootFolders,
|
||||
ActiveRadarrBlock::DeleteRootFolderPrompt
|
||||
)]
|
||||
#[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::DeleteDownloadPrompt)]
|
||||
#[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::UpdateDownloadsPrompt)]
|
||||
#[case(
|
||||
@@ -1386,6 +1498,32 @@ mod tests {
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_prompt_esc() {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
|
||||
app.data.radarr_data.edit_path = HorizontallyScrollableText::from("/nfs/test".to_owned());
|
||||
app.should_ignore_quit_key = true;
|
||||
|
||||
RadarrHandler::with(
|
||||
&ESC_KEY,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AddRootFolderPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
|
||||
assert!(app.data.radarr_data.edit_path.text.is_empty());
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert!(!app.should_ignore_quit_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_esc() {
|
||||
let mut app = App::default();
|
||||
@@ -1485,6 +1623,25 @@ mod tests {
|
||||
assert!(app.should_ignore_quit_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folder_add() {
|
||||
let mut app = App::default();
|
||||
|
||||
RadarrHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.add.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::RootFolders,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::AddRootFolderPrompt.into()
|
||||
);
|
||||
assert!(app.should_ignore_quit_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_movie_edit_key() {
|
||||
test_edit_movie_key!(
|
||||
@@ -1552,6 +1709,22 @@ mod tests {
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_prompt_backspace_key() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.edit_path = "/nfs/test".to_owned().into();
|
||||
|
||||
RadarrHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.backspace.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AddRootFolderPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(app.data.radarr_data.edit_path.text, "/nfs/tes");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_search_boxes_backspace_key(
|
||||
#[values(ActiveRadarrBlock::SearchMovie, ActiveRadarrBlock::SearchCollection)]
|
||||
@@ -1590,6 +1763,21 @@ mod tests {
|
||||
assert_str_eq!(app.data.radarr_data.filter.text, "Tes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_prompt_char_key() {
|
||||
let mut app = App::default();
|
||||
|
||||
RadarrHandler::with(
|
||||
&Key::Char('h'),
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::AddRootFolderPrompt,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_str_eq!(app.data.radarr_data.edit_path.text, "h");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_search_boxes_char_key(
|
||||
#[values(ActiveRadarrBlock::SearchMovie, ActiveRadarrBlock::SearchCollection)]
|
||||
|
||||
@@ -324,6 +324,11 @@ pub struct AddMovieSearchResult {
|
||||
pub ratings: RatingsList,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
pub struct AddRootFolderBody {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Derivative, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MovieCommandBody {
|
||||
|
||||
+48
-7
@@ -3,6 +3,7 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use log::{debug, error};
|
||||
use regex::Regex;
|
||||
use reqwest::{Client, RequestBuilder};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
@@ -71,13 +72,21 @@ impl<'a> Network<'a> {
|
||||
RequestMethod::Delete | RequestMethod::Put => (),
|
||||
}
|
||||
} else {
|
||||
let status = response.status();
|
||||
let whitespace_regex = Regex::new(r"\s+").unwrap();
|
||||
let response_body = response.text().await.unwrap_or_default();
|
||||
let error_body = whitespace_regex
|
||||
.replace_all(&response_body.replace('\n', " "), " ")
|
||||
.to_string();
|
||||
|
||||
error!(
|
||||
"Request failed. Received {} response code",
|
||||
response.status()
|
||||
"Request failed. Received {} response code with body: {}",
|
||||
status, response_body
|
||||
);
|
||||
self.app.lock().await.handle_error(anyhow!(
|
||||
"Request failed. Received {} response code",
|
||||
response.status()
|
||||
"Request failed. Received {} response code with body: {}",
|
||||
status,
|
||||
error_body
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -337,7 +346,31 @@ mod tests {
|
||||
async_server.assert_async().await;
|
||||
assert_str_eq!(
|
||||
app_arc.lock().await.error.text,
|
||||
"Request failed. Received 404 Not Found response code"
|
||||
r#"Request failed. Received 404 Not Found response code with body: { "value": "Test" }"#
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_request_non_success_code_empty_response_body() {
|
||||
let (async_server, app_arc, server) = mock_api(RequestMethod::Post, 404, false).await;
|
||||
let network = Network::new(reqwest::Client::new(), &app_arc);
|
||||
|
||||
network
|
||||
.handle_request::<(), Test>(
|
||||
RequestProps {
|
||||
uri: format!("{}/test", server.url()),
|
||||
method: RequestMethod::Post,
|
||||
body: None,
|
||||
api_token: "test1234".to_owned(),
|
||||
},
|
||||
|response, mut app| app.error = HorizontallyScrollableText::from(response.value),
|
||||
)
|
||||
.await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
assert_str_eq!(
|
||||
app_arc.lock().await.error.text,
|
||||
r#"Request failed. Received 404 Not Found response code with body: "#
|
||||
);
|
||||
}
|
||||
|
||||
@@ -360,7 +393,11 @@ mod tests {
|
||||
let mut body = None::<Test>;
|
||||
|
||||
if request_method == RequestMethod::Post {
|
||||
async_server = async_server.with_body(r#"{ "value": "Test" }"#);
|
||||
async_server = async_server.with_body(
|
||||
r#"{
|
||||
"value": "Test"
|
||||
}"#,
|
||||
);
|
||||
body = Some(Test {
|
||||
value: "Test".to_owned(),
|
||||
});
|
||||
@@ -402,7 +439,11 @@ mod tests {
|
||||
.with_status(response_status);
|
||||
|
||||
if has_response_body {
|
||||
async_server = async_server.with_body(r#"{ "value": "Test" }"#);
|
||||
async_server = async_server.with_body(
|
||||
r#"{
|
||||
"value": "Test"
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
async_server = async_server.create_async().await;
|
||||
|
||||
@@ -9,9 +9,10 @@ use urlencoding::encode;
|
||||
use crate::app::radarr::ActiveRadarrBlock;
|
||||
use crate::app::RadarrConfig;
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieBody, AddMovieSearchResult, AddOptions, Collection, CollectionMovie, CommandBody, Credit,
|
||||
CreditType, DiskSpace, DownloadRecord, DownloadsResponse, Movie, MovieCommandBody,
|
||||
MovieHistoryItem, QualityProfile, Release, ReleaseDownloadBody, RootFolder, SystemStatus, Tag,
|
||||
AddMovieBody, AddMovieSearchResult, AddOptions, AddRootFolderBody, Collection, CollectionMovie,
|
||||
CommandBody, Credit, CreditType, DiskSpace, DownloadRecord, DownloadsResponse, Movie,
|
||||
MovieCommandBody, MovieHistoryItem, QualityProfile, Release, ReleaseDownloadBody, RootFolder,
|
||||
SystemStatus, Tag,
|
||||
};
|
||||
use crate::models::{Route, ScrollableText};
|
||||
use crate::network::{Network, NetworkEvent, RequestMethod, RequestProps};
|
||||
@@ -20,8 +21,10 @@ use crate::utils::{convert_runtime, convert_to_gb};
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum RadarrEvent {
|
||||
AddMovie,
|
||||
AddRootFolder,
|
||||
DeleteDownload,
|
||||
DeleteMovie,
|
||||
DeleteRootFolder,
|
||||
DownloadRelease,
|
||||
EditMovie,
|
||||
EditCollection,
|
||||
@@ -62,7 +65,9 @@ impl RadarrEvent {
|
||||
RadarrEvent::GetOverview => "/diskspace",
|
||||
RadarrEvent::GetQualityProfiles => "/qualityprofile",
|
||||
RadarrEvent::GetReleases | RadarrEvent::DownloadRelease => "/release",
|
||||
RadarrEvent::GetRootFolders => "/rootfolder",
|
||||
RadarrEvent::AddRootFolder | RadarrEvent::GetRootFolders | RadarrEvent::DeleteRootFolder => {
|
||||
"/rootfolder"
|
||||
}
|
||||
RadarrEvent::GetStatus => "/system/status",
|
||||
RadarrEvent::GetTags => "/tag",
|
||||
RadarrEvent::TriggerAutomaticSearch
|
||||
@@ -85,8 +90,10 @@ impl<'a> Network<'a> {
|
||||
pub async fn handle_radarr_event(&self, radarr_event: RadarrEvent) {
|
||||
match radarr_event {
|
||||
RadarrEvent::AddMovie => self.add_movie().await,
|
||||
RadarrEvent::AddRootFolder => self.add_root_folder().await,
|
||||
RadarrEvent::DeleteMovie => self.delete_movie().await,
|
||||
RadarrEvent::DeleteDownload => self.delete_download().await,
|
||||
RadarrEvent::DeleteRootFolder => self.delete_root_folder().await,
|
||||
RadarrEvent::DownloadRelease => self.download_release().await,
|
||||
RadarrEvent::EditMovie => self.edit_movie().await,
|
||||
RadarrEvent::EditCollection => self.edit_collection().await,
|
||||
@@ -726,6 +733,42 @@ impl<'a> Network<'a> {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn delete_root_folder(&self) {
|
||||
let root_folder_id = self
|
||||
.app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.root_folders
|
||||
.current_selection()
|
||||
.id
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
|
||||
info!(
|
||||
"Deleting Radarr root folder for folder with id: {}",
|
||||
root_folder_id
|
||||
);
|
||||
|
||||
let request_props = self
|
||||
.radarr_request_props_from(
|
||||
format!(
|
||||
"{}/{}",
|
||||
RadarrEvent::DeleteRootFolder.resource(),
|
||||
root_folder_id
|
||||
)
|
||||
.as_str(),
|
||||
RequestMethod::Delete,
|
||||
None::<()>,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), ()>(request_props, |_, _| ())
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn add_movie(&self) {
|
||||
info!("Adding new movie to Radarr");
|
||||
let body = {
|
||||
@@ -804,6 +847,27 @@ impl<'a> Network<'a> {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn add_root_folder(&self) {
|
||||
info!("Adding new root folder to Radarr");
|
||||
let body = AddRootFolderBody {
|
||||
path: self.app.lock().await.data.radarr_data.edit_path.drain(),
|
||||
};
|
||||
|
||||
debug!("Add root folder body: {:?}", body);
|
||||
|
||||
let request_props = self
|
||||
.radarr_request_props_from(
|
||||
RadarrEvent::AddRootFolder.resource(),
|
||||
RequestMethod::Post,
|
||||
Some(body),
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<AddRootFolderBody, Value>(request_props, |_, _| ())
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn edit_movie(&self) {
|
||||
info!("Editing Radarr movie");
|
||||
|
||||
@@ -2178,9 +2242,34 @@ mod test {
|
||||
async_server.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_root_folder_event() {
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Delete,
|
||||
None,
|
||||
None,
|
||||
format!("{}/1", RadarrEvent::DeleteRootFolder.resource()).as_str(),
|
||||
)
|
||||
.await;
|
||||
app_arc
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.root_folders
|
||||
.set_items(vec![root_folder()]);
|
||||
let network = Network::new(reqwest::Client::new(), &app_arc);
|
||||
|
||||
network
|
||||
.handle_radarr_event(RadarrEvent::DeleteRootFolder)
|
||||
.await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_movie_event(#[values(true, false)] collection_details_context: bool) {
|
||||
async fn test_handle_add_movie_event(#[values(true, false)] movie_details_context: bool) {
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Post,
|
||||
Some(json!({
|
||||
@@ -2239,7 +2328,7 @@ mod test {
|
||||
.radarr_data
|
||||
.minimum_availability_list
|
||||
.set_items(Vec::from_iter(MinimumAvailability::iter()));
|
||||
if collection_details_context {
|
||||
if movie_details_context {
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
@@ -2261,6 +2350,37 @@ mod test {
|
||||
async_server.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_root_folder_event() {
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Post,
|
||||
Some(json!({
|
||||
"path": "/nfs/test"
|
||||
})),
|
||||
None,
|
||||
RadarrEvent::AddRootFolder.resource(),
|
||||
)
|
||||
.await;
|
||||
|
||||
app_arc.lock().await.data.radarr_data.edit_path =
|
||||
HorizontallyScrollableText::from("/nfs/test".to_owned());
|
||||
let network = Network::new(reqwest::Client::new(), &app_arc);
|
||||
|
||||
network
|
||||
.handle_radarr_event(RadarrEvent::AddRootFolder)
|
||||
.await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
assert!(app_arc
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.edit_path
|
||||
.text
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_edit_movie_event() {
|
||||
let mut expected_body: Value = serde_json::from_str(MOVIE_JSON).unwrap();
|
||||
|
||||
+3
-5
@@ -6,7 +6,7 @@ use tui::widgets::Paragraph;
|
||||
use tui::widgets::Row;
|
||||
use tui::widgets::Table;
|
||||
use tui::widgets::Tabs;
|
||||
use tui::widgets::{Block, Borders, Wrap};
|
||||
use tui::widgets::{Block, Wrap};
|
||||
use tui::widgets::{Clear, List, ListItem};
|
||||
use tui::Frame;
|
||||
|
||||
@@ -87,10 +87,8 @@ fn draw_header_row<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|
||||
}
|
||||
|
||||
fn draw_error<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
||||
let block = borderless_block()
|
||||
.title("Error | <esc> to close")
|
||||
.style(style_failure())
|
||||
.borders(Borders::ALL);
|
||||
let block =
|
||||
title_block("Error | <esc> to close").style(style_failure().add_modifier(Modifier::BOLD));
|
||||
|
||||
app.error.scroll_left_or_reset(
|
||||
area.width as usize,
|
||||
|
||||
+64
-2
@@ -25,8 +25,9 @@ use crate::ui::radarr_ui::movie_details_ui::draw_movie_info_popup;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, horizontal_chunks, layout_block,
|
||||
layout_block_top_border, line_gauge_with_label, line_gauge_with_title, show_cursor,
|
||||
style_awaiting_import, style_bold, style_default, style_failure, style_primary, style_success,
|
||||
style_unmonitored, style_warning, title_block, title_block_centered, vertical_chunks_with_margin,
|
||||
style_awaiting_import, style_bold, style_default, style_failure, style_help, style_primary,
|
||||
style_success, style_unmonitored, style_warning, title_block, title_block_centered,
|
||||
vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::{
|
||||
draw_drop_down_list, draw_large_popup_over, draw_medium_popup_over, draw_popup, draw_popup_over,
|
||||
@@ -72,6 +73,15 @@ pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, ar
|
||||
),
|
||||
ActiveRadarrBlock::Downloads => draw_downloads(f, app, content_rect),
|
||||
ActiveRadarrBlock::RootFolders => draw_root_folders(f, app, content_rect),
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => draw_popup_over(
|
||||
f,
|
||||
app,
|
||||
content_rect,
|
||||
draw_root_folders,
|
||||
draw_add_root_folder_prompt_box,
|
||||
30,
|
||||
15,
|
||||
),
|
||||
ActiveRadarrBlock::Collections => draw_collections(f, app, content_rect),
|
||||
_ if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) => {
|
||||
draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info_popup)
|
||||
@@ -150,6 +160,13 @@ pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, ar
|
||||
draw_downloads,
|
||||
draw_delete_download_prompt,
|
||||
),
|
||||
ActiveRadarrBlock::DeleteRootFolderPrompt => draw_prompt_popup_over(
|
||||
f,
|
||||
app,
|
||||
content_rect,
|
||||
draw_root_folders,
|
||||
draw_delete_root_folder_prompt,
|
||||
),
|
||||
ActiveRadarrBlock::UpdateDownloadsPrompt => draw_prompt_popup_over(
|
||||
f,
|
||||
app,
|
||||
@@ -363,6 +380,51 @@ fn draw_delete_download_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App,
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_delete_root_folder_prompt<B: Backend>(
|
||||
f: &mut Frame<'_, B>,
|
||||
app: &mut App,
|
||||
prompt_area: Rect,
|
||||
) {
|
||||
draw_prompt_box(
|
||||
f,
|
||||
prompt_area,
|
||||
"Delete Root Folder",
|
||||
format!(
|
||||
"Do you really want to delete this root folder: {}?",
|
||||
app.data.radarr_data.root_folders.current_selection().path
|
||||
)
|
||||
.as_str(),
|
||||
&app.data.radarr_data.prompt_confirm,
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_add_root_folder_prompt_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
||||
let chunks = vertical_chunks_with_margin(
|
||||
vec![
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(1),
|
||||
Constraint::Min(0),
|
||||
],
|
||||
area,
|
||||
1,
|
||||
);
|
||||
let block_title = "Add Root Folder";
|
||||
let offset = *app.data.radarr_data.edit_path.offset.borrow();
|
||||
let block_content = &app.data.radarr_data.edit_path.text;
|
||||
|
||||
let input = Paragraph::new(block_content.as_str())
|
||||
.style(style_default())
|
||||
.block(title_block_centered(block_title));
|
||||
let help = Paragraph::new("<esc> cancel")
|
||||
.style(style_help())
|
||||
.alignment(Alignment::Center)
|
||||
.block(borderless_block());
|
||||
show_cursor(f, chunks[0], offset, block_content);
|
||||
|
||||
f.render_widget(input, chunks[0]);
|
||||
f.render_widget(help, chunks[1]);
|
||||
}
|
||||
|
||||
fn draw_search_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
||||
let chunks =
|
||||
vertical_chunks_with_margin(vec![Constraint::Length(3), Constraint::Min(0)], area, 1);
|
||||
|
||||
Reference in New Issue
Block a user