Added the ability to edit existing indexers with basic options, added a tags column to the indexers table, and fixed a bug in the counter fields that displayed the cursor next to the integer instead of on it to make understanding the counter easier. Also upgraded to confy v0.60.0 and rust version to 1.75
This commit is contained in:
Generated
+16
-9
@@ -246,13 +246,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "confy"
|
name = "confy"
|
||||||
version = "0.5.1"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c"
|
checksum = "15d296c475c6ed4093824c28e222420831d27577aaaf0a1163a3b7fc35b248a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"directories",
|
"directories",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml 0.8.26",
|
"serde_yaml 0.9.29",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -331,22 +331,23 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "directories"
|
name = "directories"
|
||||||
version = "4.0.1"
|
version = "5.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
|
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-sys",
|
"dirs-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-sys"
|
name = "dirs-sys"
|
||||||
version = "0.3.7"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"winapi",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -860,7 +861,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "managarr"
|
name = "managarr"
|
||||||
version = "0.0.30"
|
version = "0.0.31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
@@ -1047,6 +1048,12 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "2.10.1"
|
version = "2.10.1"
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "managarr"
|
name = "managarr"
|
||||||
version = "0.0.30"
|
version = "0.0.31"
|
||||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||||
description = "A TUI to manage your Servarrs"
|
description = "A TUI to manage your Servarrs"
|
||||||
keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"]
|
keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"]
|
||||||
@@ -9,14 +9,14 @@ repository = "https://github.com/Dark-Alex-17/managarr"
|
|||||||
homepage = "https://github.com/Dark-Alex-17/managarr"
|
homepage = "https://github.com/Dark-Alex-17/managarr"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.72.0"
|
rust-version = "1.75.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.68"
|
anyhow = "1.0.68"
|
||||||
backtrace = "0.3.67"
|
backtrace = "0.3.67"
|
||||||
bimap = "0.6.3"
|
bimap = "0.6.3"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
confy = { version = "0.5.1", default_features = false, features = ["yaml_conf"] }
|
confy = { version = "0.6.0", default_features = false, features = ["yaml_conf"] }
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
derivative = "2.2.0"
|
derivative = "2.2.0"
|
||||||
human-panic = "1.1.3"
|
human-panic = "1.1.3"
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ Managarr is a TUI to help you manage your HTPC (Home Theater PC). Built with lov
|
|||||||
|
|
||||||
## NOTE: Managarr is not yet stable (Pre-Alpha)
|
## NOTE: Managarr is not yet stable (Pre-Alpha)
|
||||||
I'm regularly making changes to get Managarr to an alpha release. As such, I'm regularly refactoring the code to be cleaner
|
I'm regularly making changes to get Managarr to an alpha release. As such, I'm regularly refactoring the code to be cleaner
|
||||||
and more easily extensible. This makes contributing difficult and until I get Managarr across the alpha-release finish line,
|
and more easily extensible. Until I get Managarr across the alpha-release finish line, this regular refactoring will make
|
||||||
contributions will likely be difficult. Thus, stability is not guaranteed (yet!).
|
contributions difficult. Thus, stability is not guaranteed (yet!).
|
||||||
|
|
||||||
This means that while all tests will pass, there may be certain menus or keymappings that are no-ops, or produce empty
|
This means that while all tests will pass, there may be certain menus or keymappings that are no-ops, or produce empty
|
||||||
screens, or things of this sort.
|
screens, or things of this sort.
|
||||||
@@ -40,16 +40,15 @@ pleasant as possible!
|
|||||||
- [x] View details of a specific movie including description, history, downloaded file info, or the credits
|
- [x] View details of a specific movie including description, history, downloaded file info, or the credits
|
||||||
- [x] View details of any collection and the movies in them
|
- [x] View details of any collection and the movies in them
|
||||||
- [x] Search your library or collections
|
- [x] Search your library or collections
|
||||||
- [x] Add or delete movies and downloads
|
- [x] Add or delete movies, downloads, and indexers
|
||||||
- [x] Trigger automatic searches for movies
|
- [x] Trigger automatic searches for movies
|
||||||
- [x] Trigger refresh and disk scan for movies, downloads, and collections
|
- [x] Trigger refresh and disk scan for movies, downloads, and collections
|
||||||
- [x] Manually search for movies
|
- [x] Manually search for movies
|
||||||
- [x] Edit your movies and collections
|
- [x] Edit your movies, collections, and indexers
|
||||||
- [x] Manage your tags
|
- [x] Manage your tags
|
||||||
- [x] Manage your root folders
|
- [x] Manage your root folders
|
||||||
- [ ] Manage your quality profiles
|
- [ ] Manage your quality profiles
|
||||||
- [ ] Manage your quality definitions
|
- [ ] Manage your quality definitions
|
||||||
- [ ] Manage your indexers
|
|
||||||
- [x] View and browse logs, tasks, events queues, and updates
|
- [x] View and browse logs, tasks, events queues, and updates
|
||||||
- [x] Manually trigger scheduled tasks
|
- [x] Manually trigger scheduled tasks
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pub static ROOT_FOLDERS_CONTEXT_CLUES: [ContextClue; 3] = [
|
|||||||
|
|
||||||
pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [
|
pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [
|
||||||
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
|
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
|
||||||
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
(DEFAULT_KEYBINDINGS.submit, "edit indexer"),
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.settings,
|
DEFAULT_KEYBINDINGS.settings,
|
||||||
DEFAULT_KEYBINDINGS.settings.desc,
|
DEFAULT_KEYBINDINGS.settings.desc,
|
||||||
|
|||||||
@@ -151,8 +151,8 @@ mod tests {
|
|||||||
|
|
||||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
|
assert_str_eq!(*description, "edit indexer");
|
||||||
|
|
||||||
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -1,7 +1,4 @@
|
|||||||
pub use self::{
|
pub use self::key::Key;
|
||||||
input_event::{Events, InputEvent},
|
|
||||||
key::Key,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod input_event;
|
pub mod input_event;
|
||||||
mod key;
|
mod key;
|
||||||
|
|||||||
@@ -0,0 +1,431 @@
|
|||||||
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
|
use crate::network::radarr_network::RadarrEvent;
|
||||||
|
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "edit_indexer_handler_tests.rs"]
|
||||||
|
mod edit_indexer_handler_tests;
|
||||||
|
|
||||||
|
pub(super) struct EditIndexerHandler<'a, 'b> {
|
||||||
|
key: &'a Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_radarr_block: &'a ActiveRadarrBlock,
|
||||||
|
_context: &'a Option<ActiveRadarrBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'a, 'b> {
|
||||||
|
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||||
|
EDIT_INDEXER_BLOCKS.contains(active_block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with(
|
||||||
|
key: &'a Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_block: &'a ActiveRadarrBlock,
|
||||||
|
_context: &'a Option<ActiveRadarrBlock>,
|
||||||
|
) -> EditIndexerHandler<'a, 'b> {
|
||||||
|
EditIndexerHandler {
|
||||||
|
key,
|
||||||
|
app,
|
||||||
|
active_radarr_block: active_block,
|
||||||
|
_context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> &Key {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_up(&mut self) {
|
||||||
|
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt {
|
||||||
|
self.app.data.radarr_data.selected_block.previous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_down(&mut self) {
|
||||||
|
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt {
|
||||||
|
self.app.data.radarr_data.selected_block.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_home(&mut self) {
|
||||||
|
match self.active_radarr_block {
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.name
|
||||||
|
.scroll_home();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.url
|
||||||
|
.scroll_home();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.api_key
|
||||||
|
.scroll_home();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.seed_ratio
|
||||||
|
.scroll_home();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.tags
|
||||||
|
.scroll_home();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_end(&mut self) {
|
||||||
|
match self.active_radarr_block {
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.name
|
||||||
|
.reset_offset();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.url
|
||||||
|
.reset_offset();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.api_key
|
||||||
|
.reset_offset();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.seed_ratio
|
||||||
|
.reset_offset();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.tags
|
||||||
|
.reset_offset();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_delete(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_left_right_action(&mut self) {
|
||||||
|
match self.active_radarr_block {
|
||||||
|
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||||
|
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||||
|
== &ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||||
|
{
|
||||||
|
handle_prompt_toggle(self.app, self.key);
|
||||||
|
} else {
|
||||||
|
let len = self.app.data.radarr_data.selected_block.blocks.len();
|
||||||
|
let idx = self.app.data.radarr_data.selected_block.index;
|
||||||
|
self.app.data.radarr_data.selected_block.index = (idx + 5) % len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput => {
|
||||||
|
handle_text_box_left_right_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput => {
|
||||||
|
handle_text_box_left_right_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput => {
|
||||||
|
handle_text_box_left_right_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.api_key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput => {
|
||||||
|
handle_text_box_left_right_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.seed_ratio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||||
|
handle_text_box_left_right_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.tags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_submit(&mut self) {
|
||||||
|
match self.active_radarr_block {
|
||||||
|
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||||
|
let selected_block = *self.app.data.radarr_data.selected_block.get_active_block();
|
||||||
|
match selected_block {
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt => {
|
||||||
|
let radarr_data = &mut self.app.data.radarr_data;
|
||||||
|
if radarr_data.prompt_confirm {
|
||||||
|
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer);
|
||||||
|
self.app.should_refresh = true;
|
||||||
|
} else {
|
||||||
|
radarr_data.edit_indexer_modal = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerUrlInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerApiKeyInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerSeedRatioInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||||
|
self.app.push_navigation_stack(selected_block.into());
|
||||||
|
self.app.should_ignore_quit_key = true;
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableRss => {
|
||||||
|
let indexer = self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap();
|
||||||
|
indexer.enable_rss = Some(!indexer.enable_rss.unwrap_or_default());
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch => {
|
||||||
|
let indexer = self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap();
|
||||||
|
indexer.enable_automatic_search =
|
||||||
|
Some(!indexer.enable_automatic_search.unwrap_or_default());
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch => {
|
||||||
|
let indexer = self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap();
|
||||||
|
indexer.enable_interactive_search =
|
||||||
|
Some(!indexer.enable_interactive_search.unwrap_or_default());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerUrlInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerApiKeyInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerSeedRatioInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_esc(&mut self) {
|
||||||
|
match self.active_radarr_block {
|
||||||
|
ActiveRadarrBlock::EditIndexerPrompt => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.data.radarr_data.prompt_confirm = false;
|
||||||
|
self.app.data.radarr_data.edit_indexer_modal = None;
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerUrlInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerApiKeyInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerSeedRatioInput
|
||||||
|
| ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
}
|
||||||
|
_ => self.app.pop_navigation_stack(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_char_key_event(&mut self) {
|
||||||
|
match self.active_radarr_block {
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.api_key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.seed_ratio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
self.key,
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.tags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -46,7 +46,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
|||||||
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt => {
|
ActiveRadarrBlock::IndexerSettingsPrompt => {
|
||||||
self.app.data.radarr_data.selected_block.previous()
|
self.app.data.radarr_data.selected_block.previous();
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
||||||
indexer_settings.minimum_age += 1;
|
indexer_settings.minimum_age += 1;
|
||||||
@@ -166,7 +166,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
|||||||
ActiveRadarrBlock::IndexerSettingsConfirmPrompt => {
|
ActiveRadarrBlock::IndexerSettingsConfirmPrompt => {
|
||||||
let radarr_data = &mut self.app.data.radarr_data;
|
let radarr_data = &mut self.app.data.radarr_data;
|
||||||
if radarr_data.prompt_confirm {
|
if radarr_data.prompt_confirm {
|
||||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateIndexerSettings);
|
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditAllIndexerSettings);
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
} else {
|
} else {
|
||||||
radarr_data.indexer_settings = None;
|
radarr_data.indexer_settings = None;
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ mod tests {
|
|||||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::UpdateIndexerSettings)
|
Some(RadarrEvent::EditAllIndexerSettings)
|
||||||
);
|
);
|
||||||
assert!(app.data.radarr_data.indexer_settings.is_some());
|
assert!(app.data.radarr_data.indexer_settings.is_some());
|
||||||
assert!(app.should_refresh);
|
assert!(app.should_refresh);
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ mod tests {
|
|||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
use crate::handlers::radarr_handlers::indexers::{IndexersHandler, TestAllIndexersHandler};
|
use crate::handlers::radarr_handlers::indexers::IndexersHandler;
|
||||||
use crate::handlers::KeyEventHandler;
|
use crate::handlers::KeyEventHandler;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
ActiveRadarrBlock, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
ActiveRadarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::test_handler_delegation;
|
use crate::test_handler_delegation;
|
||||||
|
|
||||||
@@ -147,7 +147,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_submit {
|
mod test_handle_submit {
|
||||||
|
use crate::models::radarr_models::{Indexer, IndexerField};
|
||||||
|
use crate::models::servarr_data::radarr::modals::EditIndexerModal;
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
|
RadarrData, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
|
||||||
|
};
|
||||||
|
use bimap::BiMap;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
use serde_json::{Number, Value};
|
||||||
|
|
||||||
use crate::network::radarr_network::RadarrEvent;
|
use crate::network::radarr_network::RadarrEvent;
|
||||||
|
|
||||||
@@ -155,16 +162,85 @@ mod tests {
|
|||||||
|
|
||||||
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||||
|
|
||||||
#[test]
|
#[rstest]
|
||||||
fn test_indexer_submit_aka_edit() {
|
fn test_edit_indexer_submit(#[values(true, false)] torrent_protocol: bool) {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
let protocol = if torrent_protocol {
|
||||||
|
"torrent".to_owned()
|
||||||
|
} else {
|
||||||
|
"usenet".to_owned()
|
||||||
|
};
|
||||||
|
let mut expected_edit_indexer_modal = EditIndexerModal {
|
||||||
|
name: "Test".into(),
|
||||||
|
enable_rss: Some(true),
|
||||||
|
enable_automatic_search: Some(true),
|
||||||
|
enable_interactive_search: Some(true),
|
||||||
|
url: "https://test.com".into(),
|
||||||
|
api_key: "1234".into(),
|
||||||
|
tags: "usenet, test".into(),
|
||||||
|
..EditIndexerModal::default()
|
||||||
|
};
|
||||||
|
let mut radarr_data = RadarrData {
|
||||||
|
tags_map: BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]),
|
||||||
|
..RadarrData::default()
|
||||||
|
};
|
||||||
|
let mut fields = vec![
|
||||||
|
IndexerField {
|
||||||
|
name: Some("baseUrl".to_owned()),
|
||||||
|
value: Some(Value::String("https://test.com".to_owned())),
|
||||||
|
},
|
||||||
|
IndexerField {
|
||||||
|
name: Some("apiKey".to_owned()),
|
||||||
|
value: Some(Value::String("1234".to_owned())),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if torrent_protocol {
|
||||||
|
fields.push(IndexerField {
|
||||||
|
name: Some("seedCriteria.seedRatio".to_owned()),
|
||||||
|
value: Some(Value::from(1.2f64)),
|
||||||
|
});
|
||||||
|
expected_edit_indexer_modal.seed_ratio = "1.2".into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let indexer = Indexer {
|
||||||
|
name: Some("Test".to_owned()),
|
||||||
|
enable_rss: true,
|
||||||
|
enable_automatic_search: true,
|
||||||
|
enable_interactive_search: true,
|
||||||
|
protocol,
|
||||||
|
tags: vec![Number::from(1), Number::from(2)],
|
||||||
|
fields: Some(fields),
|
||||||
|
..Indexer::default()
|
||||||
|
};
|
||||||
|
radarr_data.indexers.set_items(vec![indexer]);
|
||||||
|
app.data.radarr_data = radarr_data;
|
||||||
|
|
||||||
IndexersHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
IndexersHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::EditIndexer.into()
|
&ActiveRadarrBlock::EditIndexerPrompt.into()
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app.data.radarr_data.edit_indexer_modal,
|
||||||
|
Some((&app.data.radarr_data).into())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app.data.radarr_data.edit_indexer_modal,
|
||||||
|
Some(expected_edit_indexer_modal)
|
||||||
|
);
|
||||||
|
if torrent_protocol {
|
||||||
|
assert_eq!(
|
||||||
|
app.data.radarr_data.selected_block.blocks,
|
||||||
|
&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert_eq!(
|
||||||
|
app.data.radarr_data.selected_block.blocks,
|
||||||
|
&EDIT_INDEXER_NZB_SELECTION_BLOCKS
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -322,6 +398,29 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_delegates_edit_indexer_blocks_to_edit_indexer_handler(
|
||||||
|
#[values(
|
||||||
|
ActiveRadarrBlock::EditIndexerPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput
|
||||||
|
)]
|
||||||
|
active_radarr_block: ActiveRadarrBlock,
|
||||||
|
) {
|
||||||
|
test_handler_delegation!(
|
||||||
|
IndexersHandler,
|
||||||
|
ActiveRadarrBlock::Indexers,
|
||||||
|
active_radarr_block
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_delegates_indexer_settings_blocks_to_indexer_settings_handler(
|
fn test_delegates_indexer_settings_blocks_to_indexer_settings_handler(
|
||||||
#[values(
|
#[values(
|
||||||
@@ -348,7 +447,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_delegates_test_all_indexers_block_to_test_all_indexers_handler() {
|
fn test_delegates_test_all_indexers_block_to_test_all_indexers_handler() {
|
||||||
test_handler_delegation!(
|
test_handler_delegation!(
|
||||||
TestAllIndexersHandler,
|
IndexersHandler,
|
||||||
ActiveRadarrBlock::Indexers,
|
ActiveRadarrBlock::Indexers,
|
||||||
ActiveRadarrBlock::TestAllIndexers
|
ActiveRadarrBlock::TestAllIndexers
|
||||||
);
|
);
|
||||||
@@ -359,6 +458,8 @@ mod tests {
|
|||||||
let mut indexers_blocks = Vec::new();
|
let mut indexers_blocks = Vec::new();
|
||||||
indexers_blocks.extend(INDEXERS_BLOCKS);
|
indexers_blocks.extend(INDEXERS_BLOCKS);
|
||||||
indexers_blocks.extend(INDEXER_SETTINGS_BLOCKS);
|
indexers_blocks.extend(INDEXER_SETTINGS_BLOCKS);
|
||||||
|
indexers_blocks.extend(EDIT_INDEXER_BLOCKS);
|
||||||
|
indexers_blocks.push(ActiveRadarrBlock::TestAllIndexers);
|
||||||
|
|
||||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||||
if indexers_blocks.contains(&active_radarr_block) {
|
if indexers_blocks.contains(&active_radarr_block) {
|
||||||
|
|||||||
@@ -2,15 +2,18 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
|
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
|
||||||
|
use crate::handlers::radarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler;
|
||||||
use crate::handlers::radarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
|
use crate::handlers::radarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
|
||||||
use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
|
use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
|
||||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
ActiveRadarrBlock, INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS,
|
ActiveRadarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
|
||||||
|
INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::models::{BlockSelectionState, Scrollable};
|
use crate::models::{BlockSelectionState, Scrollable};
|
||||||
use crate::network::radarr_network::RadarrEvent;
|
use crate::network::radarr_network::RadarrEvent;
|
||||||
|
|
||||||
|
mod edit_indexer_handler;
|
||||||
mod edit_indexer_settings_handler;
|
mod edit_indexer_settings_handler;
|
||||||
mod test_all_indexers_handler;
|
mod test_all_indexers_handler;
|
||||||
|
|
||||||
@@ -28,6 +31,10 @@ pub(super) struct IndexersHandler<'a, 'b> {
|
|||||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, 'b> {
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, 'b> {
|
||||||
fn handle(&mut self) {
|
fn handle(&mut self) {
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
|
_ if EditIndexerHandler::accepts(self.active_radarr_block) => {
|
||||||
|
EditIndexerHandler::with(self.key, self.app, self.active_radarr_block, self.context)
|
||||||
|
.handle()
|
||||||
|
}
|
||||||
_ if IndexerSettingsHandler::accepts(self.active_radarr_block) => {
|
_ if IndexerSettingsHandler::accepts(self.active_radarr_block) => {
|
||||||
IndexerSettingsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
|
IndexerSettingsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
|
||||||
.handle()
|
.handle()
|
||||||
@@ -41,7 +48,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
|
||||||
IndexerSettingsHandler::accepts(active_block) || INDEXERS_BLOCKS.contains(active_block)
|
EditIndexerHandler::accepts(active_block)
|
||||||
|
|| IndexerSettingsHandler::accepts(active_block)
|
||||||
|
|| TestAllIndexersHandler::accepts(active_block)
|
||||||
|
|| INDEXERS_BLOCKS.contains(active_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with(
|
fn with(
|
||||||
@@ -115,7 +125,22 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
|||||||
ActiveRadarrBlock::Indexers => {
|
ActiveRadarrBlock::Indexers => {
|
||||||
self
|
self
|
||||||
.app
|
.app
|
||||||
.push_navigation_stack(ActiveRadarrBlock::EditIndexer.into());
|
.push_navigation_stack(ActiveRadarrBlock::EditIndexerPrompt.into());
|
||||||
|
self.app.data.radarr_data.edit_indexer_modal = Some((&self.app.data.radarr_data).into());
|
||||||
|
let protocol = &self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.indexers
|
||||||
|
.current_selection()
|
||||||
|
.protocol;
|
||||||
|
if protocol == "torrent" {
|
||||||
|
self.app.data.radarr_data.selected_block =
|
||||||
|
BlockSelectionState::new(&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
|
||||||
|
} else {
|
||||||
|
self.app.data.radarr_data.selected_block =
|
||||||
|
BlockSelectionState::new(&EDIT_INDEXER_NZB_SELECTION_BLOCKS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,30 +164,13 @@ pub struct Indexer {
|
|||||||
pub priority: i64,
|
pub priority: i64,
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub download_client_id: i64,
|
pub download_client_id: i64,
|
||||||
pub tags: Option<Vec<String>>,
|
pub tags: Vec<Number>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct IndexerField {
|
pub struct IndexerField {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
|
||||||
pub order: i64,
|
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub label: Option<String>,
|
|
||||||
pub value: Option<Value>,
|
pub value: Option<Value>,
|
||||||
#[serde(rename(deserialize = "type"))]
|
|
||||||
pub field_type: Option<String>,
|
|
||||||
pub select_options: Option<Vec<IndexerSelectOption>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct IndexerSelectOption {
|
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
|
||||||
pub value: i64,
|
|
||||||
pub name: Option<String>,
|
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
|
||||||
pub order: i64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::models::radarr_models::{
|
use crate::models::radarr_models::{
|
||||||
Collection, Credit, MinimumAvailability, Monitor, Movie, MovieHistoryItem, Release, ReleaseField,
|
Collection, Credit, Indexer, MinimumAvailability, Monitor, Movie, MovieHistoryItem, Release,
|
||||||
RootFolder,
|
ReleaseField, RootFolder,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::RadarrData;
|
use crate::models::servarr_data::radarr::radarr_data::RadarrData;
|
||||||
use crate::models::{HorizontallyScrollableText, ScrollableText, StatefulList, StatefulTable};
|
use crate::models::{HorizontallyScrollableText, ScrollableText, StatefulList, StatefulTable};
|
||||||
@@ -24,6 +24,96 @@ pub struct MovieDetailsModal {
|
|||||||
pub sort_ascending: Option<bool>,
|
pub sort_ascending: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
|
pub struct EditIndexerModal {
|
||||||
|
pub name: HorizontallyScrollableText,
|
||||||
|
pub enable_rss: Option<bool>,
|
||||||
|
pub enable_automatic_search: Option<bool>,
|
||||||
|
pub enable_interactive_search: Option<bool>,
|
||||||
|
pub url: HorizontallyScrollableText,
|
||||||
|
pub api_key: HorizontallyScrollableText,
|
||||||
|
pub seed_ratio: HorizontallyScrollableText,
|
||||||
|
pub tags: HorizontallyScrollableText,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&RadarrData<'_>> for EditIndexerModal {
|
||||||
|
fn from(radarr_data: &RadarrData<'_>) -> EditIndexerModal {
|
||||||
|
let mut edit_indexer_modal = EditIndexerModal::default();
|
||||||
|
let Indexer {
|
||||||
|
name,
|
||||||
|
enable_rss,
|
||||||
|
enable_automatic_search,
|
||||||
|
enable_interactive_search,
|
||||||
|
tags,
|
||||||
|
fields,
|
||||||
|
..
|
||||||
|
} = radarr_data.indexers.current_selection();
|
||||||
|
let seed_ratio_field_option = fields
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|field| field.name.as_ref().unwrap() == "seedCriteria.seedRatio");
|
||||||
|
let seed_ratio_value_option = if let Some(seed_ratio_field) = seed_ratio_field_option {
|
||||||
|
seed_ratio_field.value.clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
edit_indexer_modal.name = name.clone().unwrap().into();
|
||||||
|
edit_indexer_modal.enable_rss = Some(*enable_rss);
|
||||||
|
edit_indexer_modal.enable_automatic_search = Some(*enable_automatic_search);
|
||||||
|
edit_indexer_modal.enable_interactive_search = Some(*enable_interactive_search);
|
||||||
|
edit_indexer_modal.url = fields
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|field| field.name.as_ref().unwrap() == "baseUrl")
|
||||||
|
.unwrap()
|
||||||
|
.value
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
edit_indexer_modal.api_key = fields
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|field| field.name.as_ref().unwrap() == "apiKey")
|
||||||
|
.unwrap()
|
||||||
|
.value
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
if seed_ratio_value_option.is_some() {
|
||||||
|
edit_indexer_modal.seed_ratio = seed_ratio_value_option
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_indexer_modal.tags = tags
|
||||||
|
.iter()
|
||||||
|
.map(|tag_id| {
|
||||||
|
radarr_data
|
||||||
|
.tags_map
|
||||||
|
.get_by_left(&tag_id.as_i64().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
.into();
|
||||||
|
|
||||||
|
edit_indexer_modal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EditMovieModal {
|
pub struct EditMovieModal {
|
||||||
pub minimum_availability_list: StatefulList<MinimumAvailability>,
|
pub minimum_availability_list: StatefulList<MinimumAvailability>,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::models::radarr_models::{Collection, MinimumAvailability, Monitor, Movie, RootFolder};
|
use crate::models::radarr_models::{
|
||||||
|
Collection, Indexer, IndexerField, MinimumAvailability, Monitor, Movie, RootFolder,
|
||||||
|
};
|
||||||
use crate::models::servarr_data::radarr::modals::{
|
use crate::models::servarr_data::radarr::modals::{
|
||||||
AddMovieModal, EditCollectionModal, EditMovieModal,
|
AddMovieModal, EditCollectionModal, EditIndexerModal, EditMovieModal,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::radarr_test_utils::utils::create_test_radarr_data;
|
use crate::models::servarr_data::radarr::radarr_data::radarr_test_utils::utils::create_test_radarr_data;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::RadarrData;
|
use crate::models::servarr_data::radarr::radarr_data::RadarrData;
|
||||||
@@ -10,9 +12,103 @@ mod test {
|
|||||||
use bimap::BiMap;
|
use bimap::BiMap;
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use serde_json::Number;
|
use serde_json::{Number, Value};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_edit_indexer_modal_from_radarr_data(#[values(true, false)] seed_ratio_present: bool) {
|
||||||
|
let mut radarr_data = RadarrData {
|
||||||
|
tags_map: BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]),
|
||||||
|
..RadarrData::default()
|
||||||
|
};
|
||||||
|
let mut fields = vec![
|
||||||
|
IndexerField {
|
||||||
|
name: Some("baseUrl".to_owned()),
|
||||||
|
value: Some(Value::String("https://test.com".to_owned())),
|
||||||
|
},
|
||||||
|
IndexerField {
|
||||||
|
name: Some("apiKey".to_owned()),
|
||||||
|
value: Some(Value::String("1234".to_owned())),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if seed_ratio_present {
|
||||||
|
fields.push(IndexerField {
|
||||||
|
name: Some("seedCriteria.seedRatio".to_owned()),
|
||||||
|
value: Some(Value::from(1.2f64)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let indexer = Indexer {
|
||||||
|
name: Some("Test".to_owned()),
|
||||||
|
enable_rss: true,
|
||||||
|
enable_automatic_search: true,
|
||||||
|
enable_interactive_search: true,
|
||||||
|
tags: vec![Number::from(1), Number::from(2)],
|
||||||
|
fields: Some(fields),
|
||||||
|
..Indexer::default()
|
||||||
|
};
|
||||||
|
radarr_data.indexers.set_items(vec![indexer]);
|
||||||
|
|
||||||
|
let edit_indexer_modal = EditIndexerModal::from(&radarr_data);
|
||||||
|
|
||||||
|
assert_str_eq!(edit_indexer_modal.name.text, "Test");
|
||||||
|
assert_eq!(edit_indexer_modal.enable_rss, Some(true));
|
||||||
|
assert_eq!(edit_indexer_modal.enable_automatic_search, Some(true));
|
||||||
|
assert_eq!(edit_indexer_modal.enable_interactive_search, Some(true));
|
||||||
|
assert_str_eq!(edit_indexer_modal.url.text, "https://test.com");
|
||||||
|
assert_str_eq!(edit_indexer_modal.api_key.text, "1234");
|
||||||
|
|
||||||
|
if seed_ratio_present {
|
||||||
|
assert_str_eq!(edit_indexer_modal.seed_ratio.text, "1.2");
|
||||||
|
} else {
|
||||||
|
assert!(edit_indexer_modal.seed_ratio.text.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edit_indexer_modal_from_radarr_data_seed_ratio_value_is_none() {
|
||||||
|
let mut radarr_data = RadarrData {
|
||||||
|
tags_map: BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]),
|
||||||
|
..RadarrData::default()
|
||||||
|
};
|
||||||
|
let fields = vec![
|
||||||
|
IndexerField {
|
||||||
|
name: Some("baseUrl".to_owned()),
|
||||||
|
value: Some(Value::String("https://test.com".to_owned())),
|
||||||
|
},
|
||||||
|
IndexerField {
|
||||||
|
name: Some("apiKey".to_owned()),
|
||||||
|
value: Some(Value::String("1234".to_owned())),
|
||||||
|
},
|
||||||
|
IndexerField {
|
||||||
|
name: Some("seedCriteria.seedRatio".to_owned()),
|
||||||
|
value: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let indexer = Indexer {
|
||||||
|
name: Some("Test".to_owned()),
|
||||||
|
enable_rss: true,
|
||||||
|
enable_automatic_search: true,
|
||||||
|
enable_interactive_search: true,
|
||||||
|
tags: vec![Number::from(1), Number::from(2)],
|
||||||
|
fields: Some(fields),
|
||||||
|
..Indexer::default()
|
||||||
|
};
|
||||||
|
radarr_data.indexers.set_items(vec![indexer]);
|
||||||
|
|
||||||
|
let edit_indexer_modal = EditIndexerModal::from(&radarr_data);
|
||||||
|
|
||||||
|
assert_str_eq!(edit_indexer_modal.name.text, "Test");
|
||||||
|
assert_eq!(edit_indexer_modal.enable_rss, Some(true));
|
||||||
|
assert_eq!(edit_indexer_modal.enable_automatic_search, Some(true));
|
||||||
|
assert_eq!(edit_indexer_modal.enable_interactive_search, Some(true));
|
||||||
|
assert_str_eq!(edit_indexer_modal.url.text, "https://test.com");
|
||||||
|
assert_str_eq!(edit_indexer_modal.api_key.text, "1234");
|
||||||
|
assert!(edit_indexer_modal.seed_ratio.text.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_edit_movie_modal_from_radarr_data(#[values(true, false)] test_filtered_movies: bool) {
|
fn test_edit_movie_modal_from_radarr_data(#[values(true, false)] test_filtered_movies: bool) {
|
||||||
let mut radarr_data = RadarrData {
|
let mut radarr_data = RadarrData {
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ use crate::models::radarr_models::{
|
|||||||
IndexerSettings, Movie, QueueEvent, RootFolder, Task,
|
IndexerSettings, Movie, QueueEvent, RootFolder, Task,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::modals::{
|
use crate::models::servarr_data::radarr::modals::{
|
||||||
AddMovieModal, EditCollectionModal, EditMovieModal, IndexerTestResultModalItem, MovieDetailsModal,
|
AddMovieModal, EditCollectionModal, EditIndexerModal, EditMovieModal, IndexerTestResultModalItem,
|
||||||
|
MovieDetailsModal,
|
||||||
};
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
BlockSelectionState, HorizontallyScrollableText, Route, ScrollableText, StatefulList,
|
BlockSelectionState, HorizontallyScrollableText, Route, ScrollableText, StatefulList,
|
||||||
@@ -55,6 +56,7 @@ pub struct RadarrData<'a> {
|
|||||||
pub add_searched_movies: Option<StatefulTable<AddMovieSearchResult>>,
|
pub add_searched_movies: Option<StatefulTable<AddMovieSearchResult>>,
|
||||||
pub edit_movie_modal: Option<EditMovieModal>,
|
pub edit_movie_modal: Option<EditMovieModal>,
|
||||||
pub edit_collection_modal: Option<EditCollectionModal>,
|
pub edit_collection_modal: Option<EditCollectionModal>,
|
||||||
|
pub edit_indexer_modal: Option<EditIndexerModal>,
|
||||||
pub edit_root_folder: Option<HorizontallyScrollableText>,
|
pub edit_root_folder: Option<HorizontallyScrollableText>,
|
||||||
pub filtered_collections: Option<StatefulTable<Collection>>,
|
pub filtered_collections: Option<StatefulTable<Collection>>,
|
||||||
pub filtered_movies: Option<StatefulTable<Movie>>,
|
pub filtered_movies: Option<StatefulTable<Movie>>,
|
||||||
@@ -123,6 +125,7 @@ impl<'a> Default for RadarrData<'a> {
|
|||||||
add_searched_movies: None,
|
add_searched_movies: None,
|
||||||
edit_movie_modal: None,
|
edit_movie_modal: None,
|
||||||
edit_collection_modal: None,
|
edit_collection_modal: None,
|
||||||
|
edit_indexer_modal: None,
|
||||||
edit_root_folder: None,
|
edit_root_folder: None,
|
||||||
filtered_collections: None,
|
filtered_collections: None,
|
||||||
filtered_movies: None,
|
filtered_movies: None,
|
||||||
@@ -252,7 +255,16 @@ pub enum ActiveRadarrBlock {
|
|||||||
EditCollectionSelectQualityProfile,
|
EditCollectionSelectQualityProfile,
|
||||||
EditCollectionToggleSearchOnAdd,
|
EditCollectionToggleSearchOnAdd,
|
||||||
EditCollectionToggleMonitored,
|
EditCollectionToggleMonitored,
|
||||||
EditIndexer,
|
EditIndexerPrompt,
|
||||||
|
EditIndexerConfirmPrompt,
|
||||||
|
EditIndexerApiKeyInput,
|
||||||
|
EditIndexerNameInput,
|
||||||
|
EditIndexerSeedRatioInput,
|
||||||
|
EditIndexerToggleEnableRss,
|
||||||
|
EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
EditIndexerUrlInput,
|
||||||
|
EditIndexerTagsInput,
|
||||||
EditMoviePrompt,
|
EditMoviePrompt,
|
||||||
EditMovieConfirmPrompt,
|
EditMovieConfirmPrompt,
|
||||||
EditMoviePathInput,
|
EditMoviePathInput,
|
||||||
@@ -318,12 +330,10 @@ pub static COLLECTIONS_BLOCKS: [ActiveRadarrBlock; 6] = [
|
|||||||
ActiveRadarrBlock::FilterCollectionsError,
|
ActiveRadarrBlock::FilterCollectionsError,
|
||||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt,
|
ActiveRadarrBlock::UpdateAllCollectionsPrompt,
|
||||||
];
|
];
|
||||||
pub static INDEXERS_BLOCKS: [ActiveRadarrBlock; 5] = [
|
pub static INDEXERS_BLOCKS: [ActiveRadarrBlock; 3] = [
|
||||||
ActiveRadarrBlock::AddIndexer,
|
ActiveRadarrBlock::AddIndexer,
|
||||||
ActiveRadarrBlock::EditIndexer,
|
|
||||||
ActiveRadarrBlock::DeleteIndexerPrompt,
|
ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||||
ActiveRadarrBlock::Indexers,
|
ActiveRadarrBlock::Indexers,
|
||||||
ActiveRadarrBlock::TestAllIndexers,
|
|
||||||
];
|
];
|
||||||
pub static ROOT_FOLDERS_BLOCKS: [ActiveRadarrBlock; 3] = [
|
pub static ROOT_FOLDERS_BLOCKS: [ActiveRadarrBlock; 3] = [
|
||||||
ActiveRadarrBlock::RootFolders,
|
ActiveRadarrBlock::RootFolders,
|
||||||
@@ -416,6 +426,42 @@ pub static DELETE_MOVIE_SELECTION_BLOCKS: [ActiveRadarrBlock; 3] = [
|
|||||||
ActiveRadarrBlock::DeleteMovieToggleAddListExclusion,
|
ActiveRadarrBlock::DeleteMovieToggleAddListExclusion,
|
||||||
ActiveRadarrBlock::DeleteMovieConfirmPrompt,
|
ActiveRadarrBlock::DeleteMovieConfirmPrompt,
|
||||||
];
|
];
|
||||||
|
pub static EDIT_INDEXER_BLOCKS: [ActiveRadarrBlock; 10] = [
|
||||||
|
ActiveRadarrBlock::EditIndexerPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput,
|
||||||
|
];
|
||||||
|
pub static EDIT_INDEXER_TORRENT_SELECTION_BLOCKS: [ActiveRadarrBlock; 10] = [
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
];
|
||||||
|
pub static EDIT_INDEXER_NZB_SELECTION_BLOCKS: [ActiveRadarrBlock; 10] = [
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
];
|
||||||
pub static INDEXER_SETTINGS_BLOCKS: [ActiveRadarrBlock; 10] = [
|
pub static INDEXER_SETTINGS_BLOCKS: [ActiveRadarrBlock; 10] = [
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt,
|
ActiveRadarrBlock::IndexerSettingsPrompt,
|
||||||
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ mod tests {
|
|||||||
assert!(radarr_data.edit_movie_modal.is_none());
|
assert!(radarr_data.edit_movie_modal.is_none());
|
||||||
assert!(radarr_data.edit_collection_modal.is_none());
|
assert!(radarr_data.edit_collection_modal.is_none());
|
||||||
assert!(radarr_data.edit_root_folder.is_none());
|
assert!(radarr_data.edit_root_folder.is_none());
|
||||||
|
assert!(radarr_data.edit_indexer_modal.is_none());
|
||||||
assert!(radarr_data.filtered_collections.is_none());
|
assert!(radarr_data.filtered_collections.is_none());
|
||||||
assert!(radarr_data.filtered_movies.is_none());
|
assert!(radarr_data.filtered_movies.is_none());
|
||||||
assert!(radarr_data.indexer_settings.is_none());
|
assert!(radarr_data.indexer_settings.is_none());
|
||||||
@@ -270,9 +271,10 @@ mod tests {
|
|||||||
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, COLLECTIONS_BLOCKS,
|
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, COLLECTIONS_BLOCKS,
|
||||||
COLLECTION_DETAILS_BLOCKS, DELETE_MOVIE_BLOCKS, DELETE_MOVIE_SELECTION_BLOCKS,
|
COLLECTION_DETAILS_BLOCKS, DELETE_MOVIE_BLOCKS, DELETE_MOVIE_SELECTION_BLOCKS,
|
||||||
DOWNLOADS_BLOCKS, EDIT_COLLECTION_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
|
DOWNLOADS_BLOCKS, EDIT_COLLECTION_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
|
||||||
EDIT_MOVIE_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
EDIT_INDEXER_BLOCKS, EDIT_INDEXER_NZB_SELECTION_BLOCKS,
|
||||||
INDEXER_SETTINGS_SELECTION_BLOCKS, LIBRARY_BLOCKS, MOVIE_DETAILS_BLOCKS, ROOT_FOLDERS_BLOCKS,
|
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, EDIT_MOVIE_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS,
|
||||||
SYSTEM_DETAILS_BLOCKS,
|
INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS, LIBRARY_BLOCKS,
|
||||||
|
MOVIE_DETAILS_BLOCKS, ROOT_FOLDERS_BLOCKS, SYSTEM_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -299,12 +301,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_indexers_blocks_contents() {
|
fn test_indexers_blocks_contents() {
|
||||||
assert_eq!(INDEXERS_BLOCKS.len(), 5);
|
assert_eq!(INDEXERS_BLOCKS.len(), 3);
|
||||||
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::AddIndexer));
|
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::AddIndexer));
|
||||||
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::EditIndexer));
|
|
||||||
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::DeleteIndexerPrompt));
|
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::DeleteIndexerPrompt));
|
||||||
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::Indexers));
|
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::Indexers));
|
||||||
assert!(INDEXERS_BLOCKS.contains(&ActiveRadarrBlock::TestAllIndexers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -398,6 +398,25 @@ mod tests {
|
|||||||
assert!(DELETE_MOVIE_BLOCKS.contains(&ActiveRadarrBlock::DeleteMovieToggleAddListExclusion));
|
assert!(DELETE_MOVIE_BLOCKS.contains(&ActiveRadarrBlock::DeleteMovieToggleAddListExclusion));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edit_indexer_blocks_contents() {
|
||||||
|
assert_eq!(EDIT_INDEXER_BLOCKS.len(), 10);
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerPrompt));
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerConfirmPrompt));
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerApiKeyInput));
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerNameInput));
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerSeedRatioInput));
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerToggleEnableRss));
|
||||||
|
assert!(
|
||||||
|
EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch)
|
||||||
|
);
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerUrlInput));
|
||||||
|
assert!(EDIT_INDEXER_BLOCKS.contains(&ActiveRadarrBlock::EditIndexerTagsInput));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_indexer_settings_blocks_contents() {
|
fn test_indexer_settings_blocks_contents() {
|
||||||
assert_eq!(INDEXER_SETTINGS_BLOCKS.len(), 10);
|
assert_eq!(INDEXER_SETTINGS_BLOCKS.len(), 10);
|
||||||
@@ -542,6 +561,101 @@ mod tests {
|
|||||||
assert_eq!(delete_movie_block_iter.next(), None);
|
assert_eq!(delete_movie_block_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edit_indexer_torrent_selection_blocks_ordering() {
|
||||||
|
let mut edit_indexer_torrent_selection_block_iter =
|
||||||
|
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS.iter();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerNameInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerToggleEnableRss
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerUrlInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerApiKeyInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerSeedRatioInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerTagsInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_torrent_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||||
|
);
|
||||||
|
assert_eq!(edit_indexer_torrent_selection_block_iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edit_indexer_nzb_selection_blocks_ordering() {
|
||||||
|
let mut edit_indexer_nzb_selection_block_iter = EDIT_INDEXER_NZB_SELECTION_BLOCKS.iter();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerNameInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerToggleEnableRss
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerUrlInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerApiKeyInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerTagsInput
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
edit_indexer_nzb_selection_block_iter.next().unwrap(),
|
||||||
|
&ActiveRadarrBlock::EditIndexerConfirmPrompt
|
||||||
|
);
|
||||||
|
assert_eq!(edit_indexer_nzb_selection_block_iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_indexer_settings_selection_blocks_ordering() {
|
fn test_indexer_settings_selection_blocks_ordering() {
|
||||||
let mut indexer_settings_block_iter = INDEXER_SETTINGS_SELECTION_BLOCKS.iter();
|
let mut indexer_settings_block_iter = INDEXER_SETTINGS_SELECTION_BLOCKS.iter();
|
||||||
|
|||||||
+166
-38
@@ -16,7 +16,8 @@ use crate::models::radarr_models::{
|
|||||||
Update,
|
Update,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::modals::{
|
use crate::models::servarr_data::radarr::modals::{
|
||||||
AddMovieModal, EditCollectionModal, EditMovieModal, IndexerTestResultModalItem, MovieDetailsModal,
|
AddMovieModal, EditCollectionModal, EditIndexerModal, EditMovieModal, IndexerTestResultModalItem,
|
||||||
|
MovieDetailsModal,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText, StatefulTable};
|
use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText, StatefulTable};
|
||||||
@@ -36,7 +37,9 @@ pub enum RadarrEvent {
|
|||||||
DeleteMovie,
|
DeleteMovie,
|
||||||
DeleteRootFolder,
|
DeleteRootFolder,
|
||||||
DownloadRelease,
|
DownloadRelease,
|
||||||
|
EditAllIndexerSettings,
|
||||||
EditCollection,
|
EditCollection,
|
||||||
|
EditIndexer,
|
||||||
EditMovie,
|
EditMovie,
|
||||||
GetCollections,
|
GetCollections,
|
||||||
GetDownloads,
|
GetDownloads,
|
||||||
@@ -65,7 +68,6 @@ pub enum RadarrEvent {
|
|||||||
UpdateAndScan,
|
UpdateAndScan,
|
||||||
UpdateCollections,
|
UpdateCollections,
|
||||||
UpdateDownloads,
|
UpdateDownloads,
|
||||||
UpdateIndexerSettings,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RadarrEvent {
|
impl RadarrEvent {
|
||||||
@@ -73,8 +75,10 @@ impl RadarrEvent {
|
|||||||
match self {
|
match self {
|
||||||
RadarrEvent::GetCollections | RadarrEvent::EditCollection => "/collection",
|
RadarrEvent::GetCollections | RadarrEvent::EditCollection => "/collection",
|
||||||
RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload => "/queue",
|
RadarrEvent::GetDownloads | RadarrEvent::DeleteDownload => "/queue",
|
||||||
RadarrEvent::GetIndexers | RadarrEvent::DeleteIndexer => "/indexer",
|
RadarrEvent::GetIndexers | RadarrEvent::EditIndexer | RadarrEvent::DeleteIndexer => {
|
||||||
RadarrEvent::GetIndexerSettings | RadarrEvent::UpdateIndexerSettings => "/config/indexer",
|
"/indexer"
|
||||||
|
}
|
||||||
|
RadarrEvent::GetIndexerSettings | RadarrEvent::EditAllIndexerSettings => "/config/indexer",
|
||||||
RadarrEvent::GetLogs => "/log",
|
RadarrEvent::GetLogs => "/log",
|
||||||
RadarrEvent::AddMovie
|
RadarrEvent::AddMovie
|
||||||
| RadarrEvent::EditMovie
|
| RadarrEvent::EditMovie
|
||||||
@@ -123,7 +127,9 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
RadarrEvent::DeleteMovie => self.delete_movie().await,
|
RadarrEvent::DeleteMovie => self.delete_movie().await,
|
||||||
RadarrEvent::DeleteRootFolder => self.delete_root_folder().await,
|
RadarrEvent::DeleteRootFolder => self.delete_root_folder().await,
|
||||||
RadarrEvent::DownloadRelease => self.download_release().await,
|
RadarrEvent::DownloadRelease => self.download_release().await,
|
||||||
|
RadarrEvent::EditAllIndexerSettings => self.edit_all_indexer_settings().await,
|
||||||
RadarrEvent::EditCollection => self.edit_collection().await,
|
RadarrEvent::EditCollection => self.edit_collection().await,
|
||||||
|
RadarrEvent::EditIndexer => self.edit_indexer().await,
|
||||||
RadarrEvent::EditMovie => self.edit_movie().await,
|
RadarrEvent::EditMovie => self.edit_movie().await,
|
||||||
RadarrEvent::GetCollections => self.get_collections().await,
|
RadarrEvent::GetCollections => self.get_collections().await,
|
||||||
RadarrEvent::GetDownloads => self.get_downloads().await,
|
RadarrEvent::GetDownloads => self.get_downloads().await,
|
||||||
@@ -152,7 +158,6 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
RadarrEvent::UpdateAndScan => self.update_and_scan().await,
|
RadarrEvent::UpdateAndScan => self.update_and_scan().await,
|
||||||
RadarrEvent::UpdateCollections => self.update_collections().await,
|
RadarrEvent::UpdateCollections => self.update_collections().await,
|
||||||
RadarrEvent::UpdateDownloads => self.update_downloads().await,
|
RadarrEvent::UpdateDownloads => self.update_downloads().await,
|
||||||
RadarrEvent::UpdateIndexerSettings => self.update_indexer_settings().await,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,6 +471,37 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn edit_all_indexer_settings(&mut self) {
|
||||||
|
info!("Updating Radarr indexer settings");
|
||||||
|
|
||||||
|
let body = self
|
||||||
|
.app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.indexer_settings
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
debug!("Indexer settings body: {body:?}");
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.radarr_request_props_from(
|
||||||
|
RadarrEvent::EditAllIndexerSettings.resource(),
|
||||||
|
RequestMethod::Put,
|
||||||
|
Some(body),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<IndexerSettings, Value>(request_props, |_, _| {})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self.app.lock().await.data.radarr_data.indexer_settings = None;
|
||||||
|
}
|
||||||
|
|
||||||
async fn edit_collection(&mut self) {
|
async fn edit_collection(&mut self) {
|
||||||
info!("Editing Radarr collection");
|
info!("Editing Radarr collection");
|
||||||
|
|
||||||
@@ -545,6 +581,129 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn edit_indexer(&mut self) {
|
||||||
|
let id = self
|
||||||
|
.app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.indexers
|
||||||
|
.current_selection()
|
||||||
|
.id;
|
||||||
|
info!("Updating Radarr indexer with ID: {id}");
|
||||||
|
|
||||||
|
info!("Fetching indexer details for indexer with ID: {id}");
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.radarr_request_props_from(
|
||||||
|
format!("{}/{id}", RadarrEvent::GetIndexers.resource()).as_str(),
|
||||||
|
RequestMethod::Get,
|
||||||
|
None::<()>,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut response = String::new();
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Value>(request_props, |detailed_indexer_body, _| {
|
||||||
|
response = detailed_indexer_body.to_string()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
info!("Constructing edit indexer body");
|
||||||
|
|
||||||
|
let body = {
|
||||||
|
let tags = self
|
||||||
|
.app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.edit_indexer_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.tags
|
||||||
|
.text
|
||||||
|
.clone();
|
||||||
|
let tag_ids_vec = self.extract_and_add_tag_ids_vec(tags).await;
|
||||||
|
let mut app = self.app.lock().await;
|
||||||
|
let mut detailed_indexer_body: Value = serde_json::from_str(&response).unwrap();
|
||||||
|
|
||||||
|
let EditIndexerModal {
|
||||||
|
name,
|
||||||
|
enable_rss,
|
||||||
|
enable_automatic_search,
|
||||||
|
enable_interactive_search,
|
||||||
|
url,
|
||||||
|
api_key,
|
||||||
|
seed_ratio,
|
||||||
|
..
|
||||||
|
} = app.data.radarr_data.edit_indexer_modal.as_ref().unwrap();
|
||||||
|
|
||||||
|
*detailed_indexer_body.get_mut("name").unwrap() = json!(name.text.clone());
|
||||||
|
*detailed_indexer_body.get_mut("enableRss").unwrap() = json!(enable_rss.unwrap_or_default());
|
||||||
|
*detailed_indexer_body
|
||||||
|
.get_mut("enableAutomaticSearch")
|
||||||
|
.unwrap() = json!(enable_automatic_search.unwrap_or_default());
|
||||||
|
*detailed_indexer_body
|
||||||
|
.get_mut("enableInteractiveSearch")
|
||||||
|
.unwrap() = json!(enable_interactive_search.unwrap_or_default());
|
||||||
|
*detailed_indexer_body
|
||||||
|
.get_mut("fields")
|
||||||
|
.unwrap()
|
||||||
|
.as_array_mut()
|
||||||
|
.unwrap()
|
||||||
|
.iter_mut()
|
||||||
|
.find(|field| field["name"] == "baseUrl")
|
||||||
|
.unwrap()
|
||||||
|
.get_mut("value")
|
||||||
|
.unwrap() = json!(url.text.clone());
|
||||||
|
*detailed_indexer_body
|
||||||
|
.get_mut("fields")
|
||||||
|
.unwrap()
|
||||||
|
.as_array_mut()
|
||||||
|
.unwrap()
|
||||||
|
.iter_mut()
|
||||||
|
.find(|field| field["name"] == "apiKey")
|
||||||
|
.unwrap()
|
||||||
|
.get_mut("value")
|
||||||
|
.unwrap() = json!(api_key.text.clone());
|
||||||
|
*detailed_indexer_body.get_mut("tags").unwrap() = json!(tag_ids_vec);
|
||||||
|
let seed_ratio_field_option = detailed_indexer_body
|
||||||
|
.get_mut("fields")
|
||||||
|
.unwrap()
|
||||||
|
.as_array_mut()
|
||||||
|
.unwrap()
|
||||||
|
.iter_mut()
|
||||||
|
.find(|field| field["name"] == "seedCriteria.seedRatio");
|
||||||
|
if let Some(seed_ratio_field) = seed_ratio_field_option {
|
||||||
|
seed_ratio_field
|
||||||
|
.as_object_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert("value".to_string(), json!(seed_ratio.text.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
app.data.radarr_data.edit_indexer_modal = None;
|
||||||
|
|
||||||
|
detailed_indexer_body
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Edit indexer body: {body:?}");
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.radarr_request_props_from(
|
||||||
|
format!("{}/{id}", RadarrEvent::EditIndexer.resource()).as_str(),
|
||||||
|
RequestMethod::Put,
|
||||||
|
Some(body),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<Value, ()>(request_props, |_, _| ())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
async fn edit_movie(&mut self) {
|
async fn edit_movie(&mut self) {
|
||||||
info!("Editing Radarr movie");
|
info!("Editing Radarr movie");
|
||||||
|
|
||||||
@@ -663,13 +822,13 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
.handle_request::<(), Vec<Credit>>(request_props, |credit_vec, mut app| {
|
.handle_request::<(), Vec<Credit>>(request_props, |credit_vec, mut app| {
|
||||||
let cast_vec: Vec<Credit> = credit_vec
|
let cast_vec: Vec<Credit> = credit_vec
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|&credit| credit.credit_type == CreditType::Cast)
|
||||||
.cloned()
|
.cloned()
|
||||||
.filter(|credit| credit.credit_type == CreditType::Cast)
|
|
||||||
.collect();
|
.collect();
|
||||||
let crew_vec: Vec<Credit> = credit_vec
|
let crew_vec: Vec<Credit> = credit_vec
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|&credit| credit.credit_type == CreditType::Crew)
|
||||||
.cloned()
|
.cloned()
|
||||||
.filter(|credit| credit.credit_type == CreditType::Crew)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if app.data.radarr_data.movie_details_modal.is_none() {
|
if app.data.radarr_data.movie_details_modal.is_none() {
|
||||||
@@ -1500,37 +1659,6 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_indexer_settings(&mut self) {
|
|
||||||
info!("Updating Radarr indexer settings");
|
|
||||||
|
|
||||||
let body = self
|
|
||||||
.app
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.indexer_settings
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
debug!("Indexer settings body: {body:?}");
|
|
||||||
|
|
||||||
let request_props = self
|
|
||||||
.radarr_request_props_from(
|
|
||||||
RadarrEvent::UpdateIndexerSettings.resource(),
|
|
||||||
RequestMethod::Put,
|
|
||||||
Some(body),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
self
|
|
||||||
.handle_request::<IndexerSettings, Value>(request_props, |_, _| {})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
self.app.lock().await.data.radarr_data.indexer_settings = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn radarr_request_props_from<T: Serialize + Debug>(
|
async fn radarr_request_props_from<T: Serialize + Debug>(
|
||||||
&self,
|
&self,
|
||||||
resource: &str,
|
resource: &str,
|
||||||
|
|||||||
+409
-140
@@ -13,8 +13,8 @@ mod test {
|
|||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::models::radarr_models::{
|
use crate::models::radarr_models::{
|
||||||
CollectionMovie, IndexerField, IndexerSelectOption, Language, MediaInfo, MinimumAvailability,
|
CollectionMovie, IndexerField, Language, MediaInfo, MinimumAvailability, Monitor, MovieFile,
|
||||||
Monitor, MovieFile, Quality, QualityWrapper, Rating, RatingsList,
|
Quality, QualityWrapper, Rating, RatingsList,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
use crate::models::{HorizontallyScrollableText, StatefulTable};
|
use crate::models::{HorizontallyScrollableText, StatefulTable};
|
||||||
@@ -136,7 +136,7 @@ mod test {
|
|||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_resource_indexer_settings(
|
fn test_resource_indexer_settings(
|
||||||
#[values(RadarrEvent::GetIndexerSettings, RadarrEvent::UpdateIndexerSettings)]
|
#[values(RadarrEvent::GetIndexerSettings, RadarrEvent::EditAllIndexerSettings)]
|
||||||
event: RadarrEvent,
|
event: RadarrEvent,
|
||||||
) {
|
) {
|
||||||
assert_str_eq!(event.resource(), "/config/indexer");
|
assert_str_eq!(event.resource(), "/config/indexer");
|
||||||
@@ -782,45 +782,6 @@ mod test {
|
|||||||
async_server.assert_async().await;
|
async_server.assert_async().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_handle_update_indexer_settings_event() {
|
|
||||||
let indexer_settings_json = json!({
|
|
||||||
"minimumAge": 0,
|
|
||||||
"maximumSize": 0,
|
|
||||||
"retention": 0,
|
|
||||||
"rssSyncInterval": 60,
|
|
||||||
"preferIndexerFlags": false,
|
|
||||||
"availabilityDelay": 0,
|
|
||||||
"allowHardcodedSubs": true,
|
|
||||||
"whitelistedHardcodedSubs": "",
|
|
||||||
"id": 1
|
|
||||||
});
|
|
||||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
|
||||||
RequestMethod::Put,
|
|
||||||
Some(indexer_settings_json),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
RadarrEvent::UpdateIndexerSettings.resource(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
app_arc.lock().await.data.radarr_data.indexer_settings = Some(indexer_settings());
|
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
|
||||||
|
|
||||||
network
|
|
||||||
.handle_radarr_event(RadarrEvent::UpdateIndexerSettings)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
async_server.assert_async().await;
|
|
||||||
assert!(app_arc
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.indexer_settings
|
|
||||||
.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_update_collections_event() {
|
async fn test_handle_update_collections_event() {
|
||||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||||
@@ -1211,37 +1172,22 @@ mod test {
|
|||||||
"name": "Test Indexer",
|
"name": "Test Indexer",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"order": 0,
|
"name": "baseUrl",
|
||||||
"name": "valueIsString",
|
"value": "https://test.com",
|
||||||
"label": "Value Is String",
|
|
||||||
"value": "hello",
|
|
||||||
"type": "textbox",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 1,
|
"name": "apiKey",
|
||||||
"name": "emptyValueWithSelectOptions",
|
"value": "",
|
||||||
"label": "Empty Value With Select Options",
|
|
||||||
"type": "select",
|
|
||||||
"selectOptions": [
|
|
||||||
{
|
|
||||||
"value": -2,
|
|
||||||
"name": "Original",
|
|
||||||
"order": 0,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 2,
|
"name": "seedCriteria.seedRatio",
|
||||||
"name": "valueIsAnArray",
|
"value": "1.2",
|
||||||
"label": "Value is an array",
|
|
||||||
"value": [1, 2],
|
|
||||||
"type": "select",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"implementationName": "Torznab",
|
"implementationName": "Torznab",
|
||||||
"implementation": "Torznab",
|
"implementation": "Torznab",
|
||||||
"configContract": "TorznabSettings",
|
"configContract": "TorznabSettings",
|
||||||
"tags": ["test_tag"],
|
"tags": [1],
|
||||||
"id": 1
|
"id": 1
|
||||||
}]);
|
}]);
|
||||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||||
@@ -2083,66 +2029,42 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_edit_movie_event() {
|
async fn test_handle_edit_all_indexer_settings_event() {
|
||||||
let mut expected_body: Value = serde_json::from_str(MOVIE_JSON).unwrap();
|
let indexer_settings_json = json!({
|
||||||
*expected_body.get_mut("monitored").unwrap() = json!(false);
|
"minimumAge": 0,
|
||||||
*expected_body.get_mut("minimumAvailability").unwrap() = json!("announced");
|
"maximumSize": 0,
|
||||||
*expected_body.get_mut("qualityProfileId").unwrap() = json!(1111);
|
"retention": 0,
|
||||||
*expected_body.get_mut("path").unwrap() = json!("/nfs/Test Path");
|
"rssSyncInterval": 60,
|
||||||
*expected_body.get_mut("tags").unwrap() = json!([1, 2]);
|
"preferIndexerFlags": false,
|
||||||
|
"availabilityDelay": 0,
|
||||||
let resource = format!("{}/1", RadarrEvent::GetMovieDetails.resource());
|
"allowHardcodedSubs": true,
|
||||||
let (async_details_server, app_arc, mut server) = mock_radarr_api(
|
"whitelistedHardcodedSubs": "",
|
||||||
RequestMethod::Get,
|
"id": 1
|
||||||
|
});
|
||||||
|
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||||
|
RequestMethod::Put,
|
||||||
|
Some(indexer_settings_json),
|
||||||
None,
|
None,
|
||||||
Some(serde_json::from_str(MOVIE_JSON).unwrap()),
|
|
||||||
None,
|
None,
|
||||||
&resource,
|
RadarrEvent::EditAllIndexerSettings.resource(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let async_edit_server = server
|
|
||||||
.mock(
|
app_arc.lock().await.data.radarr_data.indexer_settings = Some(indexer_settings());
|
||||||
"PUT",
|
|
||||||
format!("/api/v3{}/1", RadarrEvent::EditMovie.resource()).as_str(),
|
|
||||||
)
|
|
||||||
.with_status(202)
|
|
||||||
.match_header("X-Api-Key", "test1234")
|
|
||||||
.match_body(Matcher::Json(expected_body))
|
|
||||||
.create_async()
|
|
||||||
.await;
|
|
||||||
{
|
|
||||||
let mut app = app_arc.lock().await;
|
|
||||||
app.data.radarr_data.tags_map =
|
|
||||||
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
|
|
||||||
let mut edit_movie = EditMovieModal {
|
|
||||||
tags: "usenet, testing".to_owned().into(),
|
|
||||||
path: "/nfs/Test Path".to_owned().into(),
|
|
||||||
monitored: Some(false),
|
|
||||||
..EditMovieModal::default()
|
|
||||||
};
|
|
||||||
edit_movie
|
|
||||||
.quality_profile_list
|
|
||||||
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
|
|
||||||
edit_movie
|
|
||||||
.minimum_availability_list
|
|
||||||
.set_items(Vec::from_iter(MinimumAvailability::iter()));
|
|
||||||
app.data.radarr_data.edit_movie_modal = Some(edit_movie);
|
|
||||||
app.data.radarr_data.movies.set_items(vec![Movie {
|
|
||||||
monitored: false,
|
|
||||||
..movie()
|
|
||||||
}]);
|
|
||||||
app.data.radarr_data.quality_profile_map =
|
|
||||||
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
|
|
||||||
}
|
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network.handle_radarr_event(RadarrEvent::EditMovie).await;
|
network
|
||||||
|
.handle_radarr_event(RadarrEvent::EditAllIndexerSettings)
|
||||||
|
.await;
|
||||||
|
|
||||||
async_details_server.assert_async().await;
|
async_server.assert_async().await;
|
||||||
async_edit_server.assert_async().await;
|
assert!(app_arc
|
||||||
|
.lock()
|
||||||
let app = app_arc.lock().await;
|
.await
|
||||||
assert!(app.data.radarr_data.edit_movie_modal.is_none());
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.indexer_settings
|
||||||
|
.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -2240,6 +2162,369 @@ mod test {
|
|||||||
assert!(app.data.radarr_data.edit_collection_modal.is_none());
|
assert!(app.data.radarr_data.edit_collection_modal.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_edit_indexer_event() {
|
||||||
|
let indexer_details_json = json!({
|
||||||
|
"enableRss": true,
|
||||||
|
"enableAutomaticSearch": true,
|
||||||
|
"enableInteractiveSearch": true,
|
||||||
|
"name": "Test Indexer",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "baseUrl",
|
||||||
|
"value": "https://test.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"value": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "seedCriteria.seedRatio",
|
||||||
|
"value": "1.2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"tags": [1],
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected_indexer_edit_body_json = json!({
|
||||||
|
"enableRss": false,
|
||||||
|
"enableAutomaticSearch": false,
|
||||||
|
"enableInteractiveSearch": false,
|
||||||
|
"name": "Test Update",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "baseUrl",
|
||||||
|
"value": "https://localhost:9696/1/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"value": "test1234",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "seedCriteria.seedRatio",
|
||||||
|
"value": "1.3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"tags": [1, 2],
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
|
||||||
|
let resource = format!("{}/1", RadarrEvent::GetIndexers.resource());
|
||||||
|
let (async_details_server, app_arc, mut server) = mock_radarr_api(
|
||||||
|
RequestMethod::Get,
|
||||||
|
None,
|
||||||
|
Some(indexer_details_json),
|
||||||
|
None,
|
||||||
|
&resource,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let async_edit_server = server
|
||||||
|
.mock(
|
||||||
|
"PUT",
|
||||||
|
format!("/api/v3{}/1", RadarrEvent::EditIndexer.resource()).as_str(),
|
||||||
|
)
|
||||||
|
.with_status(202)
|
||||||
|
.match_header("X-Api-Key", "test1234")
|
||||||
|
.match_body(Matcher::Json(expected_indexer_edit_body_json))
|
||||||
|
.create_async()
|
||||||
|
.await;
|
||||||
|
{
|
||||||
|
let mut app = app_arc.lock().await;
|
||||||
|
app.data.radarr_data.tags_map =
|
||||||
|
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
|
||||||
|
let edit_indexer_modal = EditIndexerModal {
|
||||||
|
name: "Test Update".into(),
|
||||||
|
enable_rss: Some(false),
|
||||||
|
enable_automatic_search: Some(false),
|
||||||
|
enable_interactive_search: Some(false),
|
||||||
|
url: "https://localhost:9696/1/".into(),
|
||||||
|
api_key: "test1234".into(),
|
||||||
|
seed_ratio: "1.3".into(),
|
||||||
|
tags: "usenet, testing".into(),
|
||||||
|
};
|
||||||
|
app.data.radarr_data.edit_indexer_modal = Some(edit_indexer_modal);
|
||||||
|
app.data.radarr_data.indexers.set_items(vec![indexer()]);
|
||||||
|
}
|
||||||
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
|
network.handle_radarr_event(RadarrEvent::EditIndexer).await;
|
||||||
|
|
||||||
|
async_details_server.assert_async().await;
|
||||||
|
async_edit_server.assert_async().await;
|
||||||
|
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert!(app.data.radarr_data.edit_indexer_modal.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_edit_indexer_event_does_not_add_seed_ratio_when_seed_ratio_field_is_none_in_details(
|
||||||
|
) {
|
||||||
|
let indexer_details_json = json!({
|
||||||
|
"enableRss": true,
|
||||||
|
"enableAutomaticSearch": true,
|
||||||
|
"enableInteractiveSearch": true,
|
||||||
|
"name": "Test Indexer",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "baseUrl",
|
||||||
|
"value": "https://test.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"value": "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"tags": [1],
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected_indexer_edit_body_json = json!({
|
||||||
|
"enableRss": false,
|
||||||
|
"enableAutomaticSearch": false,
|
||||||
|
"enableInteractiveSearch": false,
|
||||||
|
"name": "Test Update",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "baseUrl",
|
||||||
|
"value": "https://localhost:9696/1/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"value": "test1234",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"tags": [1, 2],
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
|
||||||
|
let resource = format!("{}/1", RadarrEvent::GetIndexers.resource());
|
||||||
|
let (async_details_server, app_arc, mut server) = mock_radarr_api(
|
||||||
|
RequestMethod::Get,
|
||||||
|
None,
|
||||||
|
Some(indexer_details_json),
|
||||||
|
None,
|
||||||
|
&resource,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let async_edit_server = server
|
||||||
|
.mock(
|
||||||
|
"PUT",
|
||||||
|
format!("/api/v3{}/1", RadarrEvent::EditIndexer.resource()).as_str(),
|
||||||
|
)
|
||||||
|
.with_status(202)
|
||||||
|
.match_header("X-Api-Key", "test1234")
|
||||||
|
.match_body(Matcher::Json(expected_indexer_edit_body_json))
|
||||||
|
.create_async()
|
||||||
|
.await;
|
||||||
|
{
|
||||||
|
let mut app = app_arc.lock().await;
|
||||||
|
app.data.radarr_data.tags_map =
|
||||||
|
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
|
||||||
|
let edit_indexer_modal = EditIndexerModal {
|
||||||
|
name: "Test Update".into(),
|
||||||
|
enable_rss: Some(false),
|
||||||
|
enable_automatic_search: Some(false),
|
||||||
|
enable_interactive_search: Some(false),
|
||||||
|
url: "https://localhost:9696/1/".into(),
|
||||||
|
api_key: "test1234".into(),
|
||||||
|
seed_ratio: "1.3".into(),
|
||||||
|
tags: "usenet, testing".into(),
|
||||||
|
};
|
||||||
|
app.data.radarr_data.edit_indexer_modal = Some(edit_indexer_modal);
|
||||||
|
let mut indexer = indexer();
|
||||||
|
indexer.fields = Some(
|
||||||
|
indexer
|
||||||
|
.fields
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|field| field.name != Some("seedCriteria.seedRatio".to_string()))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
app.data.radarr_data.indexers.set_items(vec![indexer]);
|
||||||
|
}
|
||||||
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
|
network.handle_radarr_event(RadarrEvent::EditIndexer).await;
|
||||||
|
|
||||||
|
async_details_server.assert_async().await;
|
||||||
|
async_edit_server.assert_async().await;
|
||||||
|
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert!(app.data.radarr_data.edit_indexer_modal.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_edit_indexer_event_populates_the_seed_ratio_value_when_seed_ratio_field_is_present_in_details(
|
||||||
|
) {
|
||||||
|
let indexer_details_json = json!({
|
||||||
|
"enableRss": true,
|
||||||
|
"enableAutomaticSearch": true,
|
||||||
|
"enableInteractiveSearch": true,
|
||||||
|
"name": "Test Indexer",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "baseUrl",
|
||||||
|
"value": "https://test.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"value": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "seedCriteria.seedRatio",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"tags": [1],
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected_indexer_edit_body_json = json!({
|
||||||
|
"enableRss": false,
|
||||||
|
"enableAutomaticSearch": false,
|
||||||
|
"enableInteractiveSearch": false,
|
||||||
|
"name": "Test Update",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "baseUrl",
|
||||||
|
"value": "https://localhost:9696/1/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"value": "test1234",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "seedCriteria.seedRatio",
|
||||||
|
"value": "1.3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"tags": [1, 2],
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
|
||||||
|
let resource = format!("{}/1", RadarrEvent::GetIndexers.resource());
|
||||||
|
let (async_details_server, app_arc, mut server) = mock_radarr_api(
|
||||||
|
RequestMethod::Get,
|
||||||
|
None,
|
||||||
|
Some(indexer_details_json),
|
||||||
|
None,
|
||||||
|
&resource,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let async_edit_server = server
|
||||||
|
.mock(
|
||||||
|
"PUT",
|
||||||
|
format!("/api/v3{}/1", RadarrEvent::EditIndexer.resource()).as_str(),
|
||||||
|
)
|
||||||
|
.with_status(202)
|
||||||
|
.match_header("X-Api-Key", "test1234")
|
||||||
|
.match_body(Matcher::Json(expected_indexer_edit_body_json))
|
||||||
|
.create_async()
|
||||||
|
.await;
|
||||||
|
{
|
||||||
|
let mut app = app_arc.lock().await;
|
||||||
|
app.data.radarr_data.tags_map =
|
||||||
|
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
|
||||||
|
let edit_indexer_modal = EditIndexerModal {
|
||||||
|
name: "Test Update".into(),
|
||||||
|
enable_rss: Some(false),
|
||||||
|
enable_automatic_search: Some(false),
|
||||||
|
enable_interactive_search: Some(false),
|
||||||
|
url: "https://localhost:9696/1/".into(),
|
||||||
|
api_key: "test1234".into(),
|
||||||
|
seed_ratio: "1.3".into(),
|
||||||
|
tags: "usenet, testing".into(),
|
||||||
|
};
|
||||||
|
app.data.radarr_data.edit_indexer_modal = Some(edit_indexer_modal);
|
||||||
|
let mut indexer = indexer();
|
||||||
|
indexer.fields = Some(
|
||||||
|
indexer
|
||||||
|
.fields
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut field| {
|
||||||
|
if field.name == Some("seedCriteria.seedRatio".to_string()) {
|
||||||
|
field.value = None;
|
||||||
|
field
|
||||||
|
} else {
|
||||||
|
field
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
app.data.radarr_data.indexers.set_items(vec![indexer]);
|
||||||
|
}
|
||||||
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
|
network.handle_radarr_event(RadarrEvent::EditIndexer).await;
|
||||||
|
|
||||||
|
async_details_server.assert_async().await;
|
||||||
|
async_edit_server.assert_async().await;
|
||||||
|
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert!(app.data.radarr_data.edit_indexer_modal.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_edit_movie_event() {
|
||||||
|
let mut expected_body: Value = serde_json::from_str(MOVIE_JSON).unwrap();
|
||||||
|
*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("path").unwrap() = json!("/nfs/Test Path");
|
||||||
|
*expected_body.get_mut("tags").unwrap() = json!([1, 2]);
|
||||||
|
|
||||||
|
let resource = format!("{}/1", RadarrEvent::GetMovieDetails.resource());
|
||||||
|
let (async_details_server, app_arc, mut server) = mock_radarr_api(
|
||||||
|
RequestMethod::Get,
|
||||||
|
None,
|
||||||
|
Some(serde_json::from_str(MOVIE_JSON).unwrap()),
|
||||||
|
None,
|
||||||
|
&resource,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let async_edit_server = server
|
||||||
|
.mock(
|
||||||
|
"PUT",
|
||||||
|
format!("/api/v3{}/1", RadarrEvent::EditMovie.resource()).as_str(),
|
||||||
|
)
|
||||||
|
.with_status(202)
|
||||||
|
.match_header("X-Api-Key", "test1234")
|
||||||
|
.match_body(Matcher::Json(expected_body))
|
||||||
|
.create_async()
|
||||||
|
.await;
|
||||||
|
{
|
||||||
|
let mut app = app_arc.lock().await;
|
||||||
|
app.data.radarr_data.tags_map =
|
||||||
|
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
|
||||||
|
let mut edit_movie = EditMovieModal {
|
||||||
|
tags: "usenet, testing".to_owned().into(),
|
||||||
|
path: "/nfs/Test Path".to_owned().into(),
|
||||||
|
monitored: Some(false),
|
||||||
|
..EditMovieModal::default()
|
||||||
|
};
|
||||||
|
edit_movie
|
||||||
|
.quality_profile_list
|
||||||
|
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
|
||||||
|
edit_movie
|
||||||
|
.minimum_availability_list
|
||||||
|
.set_items(Vec::from_iter(MinimumAvailability::iter()));
|
||||||
|
app.data.radarr_data.edit_movie_modal = Some(edit_movie);
|
||||||
|
app.data.radarr_data.movies.set_items(vec![Movie {
|
||||||
|
monitored: false,
|
||||||
|
..movie()
|
||||||
|
}]);
|
||||||
|
app.data.radarr_data.quality_profile_map =
|
||||||
|
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
|
||||||
|
}
|
||||||
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
|
network.handle_radarr_event(RadarrEvent::EditMovie).await;
|
||||||
|
|
||||||
|
async_details_server.assert_async().await;
|
||||||
|
async_edit_server.assert_async().await;
|
||||||
|
|
||||||
|
let app = app_arc.lock().await;
|
||||||
|
assert!(app.data.radarr_data.edit_movie_modal.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_download_release_event() {
|
async fn test_handle_download_release_event() {
|
||||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||||
@@ -2786,36 +3071,20 @@ mod test {
|
|||||||
implementation_name: Some("Torznab".to_owned()),
|
implementation_name: Some("Torznab".to_owned()),
|
||||||
implementation: Some("Torznab".to_owned()),
|
implementation: Some("Torznab".to_owned()),
|
||||||
config_contract: Some("TorznabSettings".to_owned()),
|
config_contract: Some("TorznabSettings".to_owned()),
|
||||||
tags: Some(vec!["test_tag".to_owned()]),
|
tags: vec![Number::from(1)],
|
||||||
id: 1,
|
id: 1,
|
||||||
fields: Some(vec![
|
fields: Some(vec![
|
||||||
IndexerField {
|
IndexerField {
|
||||||
order: 0,
|
name: Some("baseUrl".to_owned()),
|
||||||
name: Some("valueIsString".to_owned()),
|
value: Some(json!("https://test.com")),
|
||||||
label: Some("Value Is String".to_owned()),
|
|
||||||
value: Some(json!("hello")),
|
|
||||||
field_type: Some("textbox".to_owned()),
|
|
||||||
select_options: None,
|
|
||||||
},
|
},
|
||||||
IndexerField {
|
IndexerField {
|
||||||
order: 1,
|
name: Some("apiKey".to_owned()),
|
||||||
name: Some("emptyValueWithSelectOptions".to_owned()),
|
value: Some(json!("")),
|
||||||
label: Some("Empty Value With Select Options".to_owned()),
|
|
||||||
value: None,
|
|
||||||
field_type: Some("select".to_owned()),
|
|
||||||
select_options: Some(vec![IndexerSelectOption {
|
|
||||||
value: -2,
|
|
||||||
name: Some("Original".to_owned()),
|
|
||||||
order: 0,
|
|
||||||
}]),
|
|
||||||
},
|
},
|
||||||
IndexerField {
|
IndexerField {
|
||||||
order: 2,
|
name: Some("seedCriteria.seedRatio".to_owned()),
|
||||||
name: Some("valueIsAnArray".to_owned()),
|
value: Some(json!("1.2")),
|
||||||
label: Some("Value is an array".to_owned()),
|
|
||||||
value: Some(json!([1, 2])),
|
|
||||||
field_type: Some("select".to_owned()),
|
|
||||||
select_options: None,
|
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-28
@@ -676,15 +676,26 @@ fn draw_help_and_get_content_rect(f: &mut Frame<'_>, area: Rect, help: Option<St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_text_box(
|
pub struct TextBoxProps<'a> {
|
||||||
f: &mut Frame<'_>,
|
pub text_box_area: Rect,
|
||||||
text_box_area: Rect,
|
pub block_title: Option<&'a str>,
|
||||||
block_title: Option<&str>,
|
pub block_content: &'a str,
|
||||||
block_content: &str,
|
pub offset: usize,
|
||||||
offset: usize,
|
pub should_show_cursor: bool,
|
||||||
should_show_cursor: bool,
|
pub is_selected: bool,
|
||||||
is_selected: bool,
|
pub cursor_after_string: bool,
|
||||||
) {
|
}
|
||||||
|
|
||||||
|
pub fn draw_text_box(f: &mut Frame<'_>, text_box_props: TextBoxProps<'_>) {
|
||||||
|
let TextBoxProps {
|
||||||
|
text_box_area,
|
||||||
|
block_title,
|
||||||
|
block_content,
|
||||||
|
offset,
|
||||||
|
should_show_cursor,
|
||||||
|
is_selected,
|
||||||
|
cursor_after_string,
|
||||||
|
} = text_box_props;
|
||||||
let (block, style) = if let Some(title) = block_title {
|
let (block, style) = if let Some(title) = block_title {
|
||||||
(title_block_centered(title), style_default())
|
(title_block_centered(title), style_default())
|
||||||
} else {
|
} else {
|
||||||
@@ -703,19 +714,33 @@ pub fn draw_text_box(
|
|||||||
f.render_widget(paragraph, text_box_area);
|
f.render_widget(paragraph, text_box_area);
|
||||||
|
|
||||||
if should_show_cursor {
|
if should_show_cursor {
|
||||||
show_cursor(f, text_box_area, offset, block_content);
|
show_cursor(f, text_box_area, offset, block_content, cursor_after_string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct LabeledTextBoxProps<'a> {
|
||||||
|
pub area: Rect,
|
||||||
|
pub label: &'a str,
|
||||||
|
pub text: &'a str,
|
||||||
|
pub offset: usize,
|
||||||
|
pub is_selected: bool,
|
||||||
|
pub should_show_cursor: bool,
|
||||||
|
pub cursor_after_string: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw_text_box_with_label(
|
pub fn draw_text_box_with_label(
|
||||||
f: &mut Frame<'_>,
|
f: &mut Frame<'_>,
|
||||||
area: Rect,
|
labeled_text_box_props: LabeledTextBoxProps<'_>,
|
||||||
label: &str,
|
|
||||||
text: &str,
|
|
||||||
offset: usize,
|
|
||||||
is_selected: bool,
|
|
||||||
should_show_cursor: bool,
|
|
||||||
) {
|
) {
|
||||||
|
let LabeledTextBoxProps {
|
||||||
|
area,
|
||||||
|
label,
|
||||||
|
text,
|
||||||
|
offset,
|
||||||
|
is_selected,
|
||||||
|
should_show_cursor,
|
||||||
|
cursor_after_string,
|
||||||
|
} = labeled_text_box_props;
|
||||||
let horizontal_chunks = horizontal_chunks(
|
let horizontal_chunks = horizontal_chunks(
|
||||||
vec![
|
vec![
|
||||||
Constraint::Percentage(48),
|
Constraint::Percentage(48),
|
||||||
@@ -734,12 +759,15 @@ pub fn draw_text_box_with_label(
|
|||||||
|
|
||||||
draw_text_box(
|
draw_text_box(
|
||||||
f,
|
f,
|
||||||
horizontal_chunks[1],
|
TextBoxProps {
|
||||||
None,
|
text_box_area: horizontal_chunks[1],
|
||||||
text,
|
block_title: None,
|
||||||
offset,
|
block_content: text,
|
||||||
should_show_cursor,
|
offset,
|
||||||
is_selected,
|
should_show_cursor,
|
||||||
|
is_selected,
|
||||||
|
cursor_after_string,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -761,12 +789,15 @@ pub fn draw_input_box_popup(
|
|||||||
|
|
||||||
draw_text_box(
|
draw_text_box(
|
||||||
f,
|
f,
|
||||||
chunks[0],
|
TextBoxProps {
|
||||||
Some(box_title),
|
text_box_area: chunks[0],
|
||||||
&box_content.text,
|
block_title: Some(box_title),
|
||||||
*box_content.offset.borrow(),
|
block_content: &box_content.text,
|
||||||
true,
|
offset: *box_content.offset.borrow(),
|
||||||
false,
|
should_show_cursor: true,
|
||||||
|
is_selected: false,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let help = Paragraph::new("<esc> cancel")
|
let help = Paragraph::new("<esc> cancel")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use crate::ui::utils::{
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
draw_button, draw_checkbox_with_label, draw_drop_down_menu_button, draw_drop_down_popup,
|
draw_button, draw_checkbox_with_label, draw_drop_down_menu_button, draw_drop_down_popup,
|
||||||
draw_large_popup_over_background_fn_with_ui, draw_medium_popup_over, draw_popup,
|
draw_large_popup_over_background_fn_with_ui, draw_medium_popup_over, draw_popup,
|
||||||
draw_selectable_list, draw_text_box_with_label, DrawUi,
|
draw_selectable_list, draw_text_box_with_label, DrawUi, LabeledTextBoxProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -191,12 +191,16 @@ fn draw_edit_collection_confirmation_prompt(
|
|||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
chunks[4],
|
LabeledTextBoxProps {
|
||||||
"Root Folder",
|
area: chunks[4],
|
||||||
&path.text,
|
label: "Root Folder",
|
||||||
*path.offset.borrow(),
|
text: &path.text,
|
||||||
selected_block == &ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
offset: *path.offset.borrow(),
|
||||||
active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
is_selected: selected_block == &ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||||
|
should_show_cursor: active_radarr_block
|
||||||
|
== ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::ui::radarr_ui::indexers::draw_indexers;
|
||||||
|
use crate::ui::utils::{
|
||||||
|
horizontal_chunks, horizontal_chunks_with_margin, title_block_centered, vertical_chunks,
|
||||||
|
vertical_chunks_with_margin,
|
||||||
|
};
|
||||||
|
use crate::ui::{
|
||||||
|
draw_button, draw_checkbox_with_label, draw_popup_over, draw_text_box_with_label, loading,
|
||||||
|
DrawUi, LabeledTextBoxProps,
|
||||||
|
};
|
||||||
|
use ratatui::layout::{Constraint, Rect};
|
||||||
|
use ratatui::Frame;
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "edit_indexer_ui_tests.rs"]
|
||||||
|
mod edit_indexer_ui_tests;
|
||||||
|
|
||||||
|
pub(super) struct EditIndexerUi;
|
||||||
|
|
||||||
|
impl DrawUi for EditIndexerUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
if let Route::Radarr(active_radarr_block, _) = route {
|
||||||
|
return EDIT_INDEXER_BLOCKS.contains(&active_radarr_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, content_rect: Rect) {
|
||||||
|
draw_popup_over(
|
||||||
|
f,
|
||||||
|
app,
|
||||||
|
content_rect,
|
||||||
|
draw_indexers,
|
||||||
|
draw_edit_indexer_prompt,
|
||||||
|
70,
|
||||||
|
45,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: Rect) {
|
||||||
|
let block = title_block_centered("Edit Indexer");
|
||||||
|
let yes_no_value = app.data.radarr_data.prompt_confirm;
|
||||||
|
let selected_block = app.data.radarr_data.selected_block.get_active_block();
|
||||||
|
let highlight_yes_no = selected_block == &ActiveRadarrBlock::EditIndexerConfirmPrompt;
|
||||||
|
let edit_indexer_modal_option = &app.data.radarr_data.edit_indexer_modal;
|
||||||
|
let protocol = &app.data.radarr_data.indexers.current_selection().protocol;
|
||||||
|
|
||||||
|
if edit_indexer_modal_option.is_some() {
|
||||||
|
let edit_indexer_modal = edit_indexer_modal_option.as_ref().unwrap();
|
||||||
|
f.render_widget(block, prompt_area);
|
||||||
|
|
||||||
|
let chunks = vertical_chunks_with_margin(
|
||||||
|
vec![Constraint::Min(0), Constraint::Length(3)],
|
||||||
|
prompt_area,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
let split_chunks = horizontal_chunks_with_margin(
|
||||||
|
vec![Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)],
|
||||||
|
chunks[0],
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
let left_chunks = vertical_chunks(
|
||||||
|
vec![
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Min(0),
|
||||||
|
],
|
||||||
|
split_chunks[0],
|
||||||
|
);
|
||||||
|
let right_chunks = vertical_chunks(
|
||||||
|
vec![
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Min(0),
|
||||||
|
],
|
||||||
|
split_chunks[1],
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
|
draw_text_box_with_label(
|
||||||
|
f,
|
||||||
|
LabeledTextBoxProps {
|
||||||
|
area: left_chunks[0],
|
||||||
|
label: "Name",
|
||||||
|
text: &edit_indexer_modal.name.text,
|
||||||
|
offset: *edit_indexer_modal.name.offset.borrow(),
|
||||||
|
is_selected: selected_block == &ActiveRadarrBlock::EditIndexerNameInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditIndexerNameInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
draw_text_box_with_label(
|
||||||
|
f,
|
||||||
|
LabeledTextBoxProps {
|
||||||
|
area: right_chunks[0],
|
||||||
|
label: "URL",
|
||||||
|
text: &edit_indexer_modal.url.text,
|
||||||
|
offset: *edit_indexer_modal.url.offset.borrow(),
|
||||||
|
is_selected: selected_block == &ActiveRadarrBlock::EditIndexerUrlInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditIndexerUrlInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
draw_text_box_with_label(
|
||||||
|
f,
|
||||||
|
LabeledTextBoxProps {
|
||||||
|
area: right_chunks[1],
|
||||||
|
label: "API Key",
|
||||||
|
text: &edit_indexer_modal.api_key.text,
|
||||||
|
offset: *edit_indexer_modal.api_key.offset.borrow(),
|
||||||
|
is_selected: selected_block == &ActiveRadarrBlock::EditIndexerApiKeyInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditIndexerApiKeyInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if protocol == "torrent" {
|
||||||
|
draw_text_box_with_label(
|
||||||
|
f,
|
||||||
|
LabeledTextBoxProps {
|
||||||
|
area: right_chunks[2],
|
||||||
|
label: "Seed Ratio",
|
||||||
|
text: &edit_indexer_modal.seed_ratio.text,
|
||||||
|
offset: *edit_indexer_modal.seed_ratio.offset.borrow(),
|
||||||
|
is_selected: selected_block == &ActiveRadarrBlock::EditIndexerSeedRatioInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditIndexerSeedRatioInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
draw_text_box_with_label(
|
||||||
|
f,
|
||||||
|
LabeledTextBoxProps {
|
||||||
|
area: right_chunks[3],
|
||||||
|
label: "Tags",
|
||||||
|
text: &edit_indexer_modal.tags.text,
|
||||||
|
offset: *edit_indexer_modal.tags.offset.borrow(),
|
||||||
|
is_selected: selected_block == &ActiveRadarrBlock::EditIndexerTagsInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditIndexerTagsInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draw_text_box_with_label(
|
||||||
|
f,
|
||||||
|
LabeledTextBoxProps {
|
||||||
|
area: right_chunks[2],
|
||||||
|
label: "Tags",
|
||||||
|
text: &edit_indexer_modal.tags.text,
|
||||||
|
offset: *edit_indexer_modal.tags.offset.borrow(),
|
||||||
|
is_selected: selected_block == &ActiveRadarrBlock::EditIndexerTagsInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditIndexerTagsInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_checkbox_with_label(
|
||||||
|
f,
|
||||||
|
left_chunks[1],
|
||||||
|
"Enable RSS",
|
||||||
|
edit_indexer_modal.enable_rss.unwrap_or_default(),
|
||||||
|
selected_block == &ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||||
|
);
|
||||||
|
draw_checkbox_with_label(
|
||||||
|
f,
|
||||||
|
left_chunks[2],
|
||||||
|
"Enable Automatic Search",
|
||||||
|
edit_indexer_modal
|
||||||
|
.enable_automatic_search
|
||||||
|
.unwrap_or_default(),
|
||||||
|
selected_block == &ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
);
|
||||||
|
draw_checkbox_with_label(
|
||||||
|
f,
|
||||||
|
left_chunks[3],
|
||||||
|
"Enable Interactive Search",
|
||||||
|
edit_indexer_modal
|
||||||
|
.enable_interactive_search
|
||||||
|
.unwrap_or_default(),
|
||||||
|
selected_block == &ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
);
|
||||||
|
|
||||||
|
let button_chunks = horizontal_chunks(
|
||||||
|
iter::repeat(Constraint::Ratio(1, 4)).take(4).collect(),
|
||||||
|
chunks[1],
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_button(
|
||||||
|
f,
|
||||||
|
button_chunks[1],
|
||||||
|
"Save",
|
||||||
|
yes_no_value && highlight_yes_no,
|
||||||
|
);
|
||||||
|
draw_button(
|
||||||
|
f,
|
||||||
|
button_chunks[2],
|
||||||
|
"Cancel",
|
||||||
|
!yes_no_value && highlight_yes_no,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loading(f, block, prompt_area, app.is_loading);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
|
use crate::ui::radarr_ui::indexers::edit_indexer_ui::EditIndexerUi;
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edit_indexer_ui_accepts() {
|
||||||
|
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||||
|
if EDIT_INDEXER_BLOCKS.contains(&active_radarr_block) {
|
||||||
|
assert!(EditIndexerUi::accepts(active_radarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!EditIndexerUi::accepts(active_radarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,8 @@ use crate::ui::utils::{
|
|||||||
vertical_chunks_with_margin,
|
vertical_chunks_with_margin,
|
||||||
};
|
};
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
draw_button, draw_checkbox_with_label, draw_popup_over, draw_text_box_with_label, loading, DrawUi,
|
draw_button, draw_checkbox_with_label, draw_popup_over, draw_text_box_with_label, loading,
|
||||||
|
DrawUi, LabeledTextBoxProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -91,57 +92,82 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, promp
|
|||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
left_chunks[0],
|
LabeledTextBoxProps {
|
||||||
"Minimum Age (minutes) ▴▾",
|
area: left_chunks[0],
|
||||||
&indexer_settings.minimum_age.to_string(),
|
label: "Minimum Age (minutes) ▴▾",
|
||||||
0,
|
text: &indexer_settings.minimum_age.to_string(),
|
||||||
selected_block == &ActiveRadarrBlock::IndexerSettingsMinimumAgeInput,
|
offset: 0,
|
||||||
active_radarr_block == ActiveRadarrBlock::IndexerSettingsMinimumAgeInput,
|
is_selected: selected_block == &ActiveRadarrBlock::IndexerSettingsMinimumAgeInput,
|
||||||
|
should_show_cursor: active_radarr_block
|
||||||
|
== ActiveRadarrBlock::IndexerSettingsMinimumAgeInput,
|
||||||
|
cursor_after_string: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
left_chunks[1],
|
LabeledTextBoxProps {
|
||||||
"Retention (days) ▴▾",
|
area: left_chunks[1],
|
||||||
&indexer_settings.retention.to_string(),
|
label: "Retention (days) ▴▾",
|
||||||
0,
|
text: &indexer_settings.retention.to_string(),
|
||||||
selected_block == &ActiveRadarrBlock::IndexerSettingsRetentionInput,
|
offset: 0,
|
||||||
active_radarr_block == ActiveRadarrBlock::IndexerSettingsRetentionInput,
|
is_selected: selected_block == &ActiveRadarrBlock::IndexerSettingsRetentionInput,
|
||||||
|
should_show_cursor: active_radarr_block
|
||||||
|
== ActiveRadarrBlock::IndexerSettingsRetentionInput,
|
||||||
|
cursor_after_string: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
left_chunks[2],
|
LabeledTextBoxProps {
|
||||||
"Maximum Size (MB) ▴▾",
|
area: left_chunks[2],
|
||||||
&indexer_settings.maximum_size.to_string(),
|
label: "Maximum Size (MB) ▴▾",
|
||||||
0,
|
text: &indexer_settings.maximum_size.to_string(),
|
||||||
selected_block == &ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
offset: 0,
|
||||||
active_radarr_block == ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
is_selected: selected_block == &ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
||||||
|
should_show_cursor: active_radarr_block
|
||||||
|
== ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
||||||
|
cursor_after_string: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
right_chunks[0],
|
LabeledTextBoxProps {
|
||||||
"Availability Delay (days) ▴▾",
|
area: right_chunks[0],
|
||||||
&indexer_settings.availability_delay.to_string(),
|
label: "Availability Delay (days) ▴▾",
|
||||||
0,
|
text: &indexer_settings.availability_delay.to_string(),
|
||||||
selected_block == &ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
offset: 0,
|
||||||
active_radarr_block == ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
is_selected: selected_block == &ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
||||||
|
should_show_cursor: active_radarr_block
|
||||||
|
== ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
||||||
|
cursor_after_string: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
right_chunks[1],
|
LabeledTextBoxProps {
|
||||||
"RSS Sync Interval (minutes) ▴▾",
|
area: right_chunks[1],
|
||||||
&indexer_settings.rss_sync_interval.to_string(),
|
label: "RSS Sync Interval (minutes) ▴▾",
|
||||||
0,
|
text: &indexer_settings.rss_sync_interval.to_string(),
|
||||||
selected_block == &ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput,
|
offset: 0,
|
||||||
active_radarr_block == ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput,
|
is_selected: selected_block == &ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput,
|
||||||
|
should_show_cursor: active_radarr_block
|
||||||
|
== ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput,
|
||||||
|
cursor_after_string: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
right_chunks[2],
|
LabeledTextBoxProps {
|
||||||
"Whitelisted Subtitle Tags",
|
area: right_chunks[2],
|
||||||
&indexer_settings.whitelisted_hardcoded_subs.text,
|
label: "Whitelisted Subtitle Tags",
|
||||||
*indexer_settings.whitelisted_hardcoded_subs.offset.borrow(),
|
text: &indexer_settings.whitelisted_hardcoded_subs.text,
|
||||||
selected_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
offset: *indexer_settings.whitelisted_hardcoded_subs.offset.borrow(),
|
||||||
active_radarr_block == ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
is_selected: selected_block
|
||||||
|
== &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||||
|
should_show_cursor: active_radarr_block
|
||||||
|
== ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod tests {
|
|||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
ActiveRadarrBlock, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
ActiveRadarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::ui::radarr_ui::indexers::IndexersUi;
|
use crate::ui::radarr_ui::indexers::IndexersUi;
|
||||||
use crate::ui::DrawUi;
|
use crate::ui::DrawUi;
|
||||||
@@ -13,6 +13,8 @@ mod tests {
|
|||||||
let mut indexers_blocks = Vec::new();
|
let mut indexers_blocks = Vec::new();
|
||||||
indexers_blocks.extend(INDEXERS_BLOCKS);
|
indexers_blocks.extend(INDEXERS_BLOCKS);
|
||||||
indexers_blocks.extend(INDEXER_SETTINGS_BLOCKS);
|
indexers_blocks.extend(INDEXER_SETTINGS_BLOCKS);
|
||||||
|
indexers_blocks.extend(EDIT_INDEXER_BLOCKS);
|
||||||
|
indexers_blocks.push(ActiveRadarrBlock::TestAllIndexers);
|
||||||
|
|
||||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||||
if indexers_blocks.contains(&active_radarr_block) {
|
if indexers_blocks.contains(&active_radarr_block) {
|
||||||
|
|||||||
@@ -7,24 +7,29 @@ use crate::app::App;
|
|||||||
use crate::models::radarr_models::Indexer;
|
use crate::models::radarr_models::Indexer;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, INDEXERS_BLOCKS};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, INDEXERS_BLOCKS};
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
|
use crate::ui::radarr_ui::indexers::edit_indexer_ui::EditIndexerUi;
|
||||||
use crate::ui::radarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi;
|
use crate::ui::radarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi;
|
||||||
use crate::ui::radarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi;
|
use crate::ui::radarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi;
|
||||||
use crate::ui::utils::{layout_block_top_border, style_failure, style_primary, style_success};
|
use crate::ui::utils::{layout_block_top_border, style_failure, style_primary, style_success};
|
||||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
||||||
|
|
||||||
|
mod edit_indexer_ui;
|
||||||
mod indexer_settings_ui;
|
mod indexer_settings_ui;
|
||||||
|
mod test_all_indexers_ui;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "indexers_ui_tests.rs"]
|
#[path = "indexers_ui_tests.rs"]
|
||||||
mod indexers_ui_tests;
|
mod indexers_ui_tests;
|
||||||
mod test_all_indexers_ui;
|
|
||||||
|
|
||||||
pub(super) struct IndexersUi;
|
pub(super) struct IndexersUi;
|
||||||
|
|
||||||
impl DrawUi for IndexersUi {
|
impl DrawUi for IndexersUi {
|
||||||
fn accepts(route: Route) -> bool {
|
fn accepts(route: Route) -> bool {
|
||||||
if let Route::Radarr(active_radarr_block, _) = route {
|
if let Route::Radarr(active_radarr_block, _) = route {
|
||||||
return IndexerSettingsUi::accepts(route) || INDEXERS_BLOCKS.contains(&active_radarr_block);
|
return EditIndexerUi::accepts(route)
|
||||||
|
|| IndexerSettingsUi::accepts(route)
|
||||||
|
|| TestAllIndexersUi::accepts(route)
|
||||||
|
|| INDEXERS_BLOCKS.contains(&active_radarr_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
@@ -45,6 +50,7 @@ impl DrawUi for IndexersUi {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match route {
|
match route {
|
||||||
|
_ if EditIndexerUi::accepts(route) => EditIndexerUi::draw(f, app, content_rect),
|
||||||
_ if IndexerSettingsUi::accepts(route) => IndexerSettingsUi::draw(f, app, content_rect),
|
_ if IndexerSettingsUi::accepts(route) => IndexerSettingsUi::draw(f, app, content_rect),
|
||||||
_ if TestAllIndexersUi::accepts(route) => TestAllIndexersUi::draw(f, app, content_rect),
|
_ if TestAllIndexersUi::accepts(route) => TestAllIndexersUi::draw(f, app, content_rect),
|
||||||
Route::Radarr(active_radarr_block, _) if INDEXERS_BLOCKS.contains(&active_radarr_block) => {
|
Route::Radarr(active_radarr_block, _) if INDEXERS_BLOCKS.contains(&active_radarr_block) => {
|
||||||
@@ -69,13 +75,15 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
"Automatic Search",
|
"Automatic Search",
|
||||||
"Interactive Search",
|
"Interactive Search",
|
||||||
"Priority",
|
"Priority",
|
||||||
|
"Tags",
|
||||||
],
|
],
|
||||||
constraints: vec![
|
constraints: vec![
|
||||||
Constraint::Ratio(1, 5),
|
Constraint::Percentage(25),
|
||||||
Constraint::Ratio(1, 5),
|
Constraint::Percentage(13),
|
||||||
Constraint::Ratio(1, 5),
|
Constraint::Percentage(13),
|
||||||
Constraint::Ratio(1, 5),
|
Constraint::Percentage(13),
|
||||||
Constraint::Ratio(1, 5),
|
Constraint::Percentage(13),
|
||||||
|
Constraint::Percentage(23),
|
||||||
],
|
],
|
||||||
help: app
|
help: app
|
||||||
.data
|
.data
|
||||||
@@ -90,6 +98,7 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
enable_automatic_search,
|
enable_automatic_search,
|
||||||
enable_interactive_search,
|
enable_interactive_search,
|
||||||
priority,
|
priority,
|
||||||
|
tags,
|
||||||
..
|
..
|
||||||
} = indexer;
|
} = indexer;
|
||||||
let bool_to_text = |flag: bool| {
|
let bool_to_text = |flag: bool| {
|
||||||
@@ -112,6 +121,19 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
bool_to_text(*enable_interactive_search);
|
bool_to_text(*enable_interactive_search);
|
||||||
let mut interactive_search = Text::from(interactive_search_text);
|
let mut interactive_search = Text::from(interactive_search_text);
|
||||||
interactive_search.patch_style(interactive_search_style);
|
interactive_search.patch_style(interactive_search_style);
|
||||||
|
let tags: String = tags
|
||||||
|
.iter()
|
||||||
|
.map(|tag_id| {
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.tags_map
|
||||||
|
.get_by_left(&tag_id.as_i64().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
Cell::from(name.clone().unwrap_or_default()),
|
Cell::from(name.clone().unwrap_or_default()),
|
||||||
@@ -119,6 +141,7 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
Cell::from(automatic_search),
|
Cell::from(automatic_search),
|
||||||
Cell::from(interactive_search),
|
Cell::from(interactive_search),
|
||||||
Cell::from(priority.to_string()),
|
Cell::from(priority.to_string()),
|
||||||
|
Cell::from(tags),
|
||||||
])
|
])
|
||||||
.style(style_primary())
|
.style(style_primary())
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ use crate::ui::utils::{
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
draw_button, draw_drop_down_menu_button, draw_drop_down_popup, draw_error_popup,
|
draw_button, draw_drop_down_menu_button, draw_drop_down_popup, draw_error_popup,
|
||||||
draw_error_popup_over, draw_large_popup_over, draw_medium_popup_over, draw_selectable_list,
|
draw_error_popup_over, draw_large_popup_over, draw_medium_popup_over, draw_selectable_list,
|
||||||
draw_table, draw_text_box, draw_text_box_with_label, DrawUi, TableProps,
|
draw_table, draw_text_box, draw_text_box_with_label, DrawUi, LabeledTextBoxProps, TableProps,
|
||||||
|
TextBoxProps,
|
||||||
};
|
};
|
||||||
use crate::utils::convert_runtime;
|
use crate::utils::convert_runtime;
|
||||||
use crate::App;
|
use crate::App;
|
||||||
@@ -135,12 +136,15 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
ActiveRadarrBlock::AddMovieSearchInput => {
|
ActiveRadarrBlock::AddMovieSearchInput => {
|
||||||
draw_text_box(
|
draw_text_box(
|
||||||
f,
|
f,
|
||||||
chunks[0],
|
TextBoxProps {
|
||||||
Some("Add Movie"),
|
text_box_area: chunks[0],
|
||||||
block_content,
|
block_title: Some("Add Movie"),
|
||||||
offset,
|
block_content,
|
||||||
true,
|
offset,
|
||||||
false,
|
should_show_cursor: true,
|
||||||
|
is_selected: false,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
f.render_widget(layout_block(), chunks[1]);
|
f.render_widget(layout_block(), chunks[1]);
|
||||||
|
|
||||||
@@ -267,12 +271,15 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
draw_text_box(
|
draw_text_box(
|
||||||
f,
|
f,
|
||||||
chunks[0],
|
TextBoxProps {
|
||||||
Some("Add Movie"),
|
text_box_area: chunks[0],
|
||||||
block_content,
|
block_title: Some("Add Movie"),
|
||||||
offset,
|
block_content,
|
||||||
false,
|
offset,
|
||||||
false,
|
should_show_cursor: false,
|
||||||
|
is_selected: false,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,12 +448,15 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
|||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
chunks[5],
|
LabeledTextBoxProps {
|
||||||
"Tags",
|
area: chunks[5],
|
||||||
&tags.text,
|
label: "Tags",
|
||||||
*tags.offset.borrow(),
|
text: &tags.text,
|
||||||
selected_block == &ActiveRadarrBlock::AddMovieTagsInput,
|
offset: *tags.offset.borrow(),
|
||||||
active_radarr_block == ActiveRadarrBlock::AddMovieTagsInput,
|
is_selected: selected_block == &ActiveRadarrBlock::AddMovieTagsInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::AddMovieTagsInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::ui::utils::{
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
draw_button, draw_checkbox_with_label, draw_drop_down_menu_button, draw_drop_down_popup,
|
draw_button, draw_checkbox_with_label, draw_drop_down_menu_button, draw_drop_down_popup,
|
||||||
draw_large_popup_over_background_fn_with_ui, draw_medium_popup_over, draw_popup,
|
draw_large_popup_over_background_fn_with_ui, draw_medium_popup_over, draw_popup,
|
||||||
draw_selectable_list, draw_text_box_with_label, DrawUi,
|
draw_selectable_list, draw_text_box_with_label, DrawUi, LabeledTextBoxProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -179,21 +179,27 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, pro
|
|||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
chunks[4],
|
LabeledTextBoxProps {
|
||||||
"Path",
|
area: chunks[4],
|
||||||
&path.text,
|
label: "Path",
|
||||||
*path.offset.borrow(),
|
text: &path.text,
|
||||||
selected_block == &ActiveRadarrBlock::EditMoviePathInput,
|
offset: *path.offset.borrow(),
|
||||||
active_radarr_block == ActiveRadarrBlock::EditMoviePathInput,
|
is_selected: selected_block == &ActiveRadarrBlock::EditMoviePathInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditMoviePathInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
draw_text_box_with_label(
|
draw_text_box_with_label(
|
||||||
f,
|
f,
|
||||||
chunks[5],
|
LabeledTextBoxProps {
|
||||||
"Tags",
|
area: chunks[5],
|
||||||
&tags.text,
|
label: "Tags",
|
||||||
*tags.offset.borrow(),
|
text: &tags.text,
|
||||||
selected_block == &ActiveRadarrBlock::EditMovieTagsInput,
|
offset: *tags.offset.borrow(),
|
||||||
active_radarr_block == ActiveRadarrBlock::EditMovieTagsInput,
|
is_selected: selected_block == &ActiveRadarrBlock::EditMovieTagsInput,
|
||||||
|
should_show_cursor: active_radarr_block == ActiveRadarrBlock::EditMovieTagsInput,
|
||||||
|
cursor_after_string: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -267,8 +267,18 @@ pub fn line_gauge_with_label(title: &str, ratio: f64) -> LineGauge<'_> {
|
|||||||
.label(Line::from(format!("{title}: {:.0}%", ratio * 100.0)))
|
.label(Line::from(format!("{title}: {:.0}%", ratio * 100.0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_cursor(f: &mut Frame<'_>, area: Rect, offset: usize, string: &str) {
|
pub fn show_cursor(
|
||||||
f.set_cursor(area.x + (string.len() - offset) as u16 + 1, area.y + 1);
|
f: &mut Frame<'_>,
|
||||||
|
area: Rect,
|
||||||
|
offset: usize,
|
||||||
|
string: &str,
|
||||||
|
cursor_after_string: bool,
|
||||||
|
) {
|
||||||
|
if cursor_after_string {
|
||||||
|
f.set_cursor(area.x + (string.len() - offset) as u16 + 1, area.y + 1);
|
||||||
|
} else {
|
||||||
|
f.set_cursor(area.x + 1u16, area.y + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_width_from_percentage(area: Rect, percentage: u16) -> usize {
|
pub fn get_width_from_percentage(area: Rect, percentage: u16) -> usize {
|
||||||
|
|||||||
Reference in New Issue
Block a user