Compare commits

..

67 Commits

Author SHA1 Message Date
Dark-Alex-17 f3b7f155b7 feat(app): Model and modal support for the season and episode details popups 2024-12-09 15:15:09 -07:00
Dark-Alex-17 6427a80bd1 feat(cli): Sonarr support for fetching season history events 2024-12-09 14:30:07 -07:00
Dark-Alex-17 5b65e87225 feat(network): Sonarr support for fetching season history 2024-12-09 14:15:47 -07:00
Dark-Alex-17 1b8b19fde5 refactor(indexers_handler): Use the new handle_table_events macro 2024-12-08 14:42:18 -07:00
Dark-Alex-17 03d7aed258 refactor(root_folders_handler): Use the new handle_table_events macro 2024-12-08 14:38:26 -07:00
Dark-Alex-17 23d149093f refactor(blocklist_handler): Use the new handle_table_events macro 2024-12-08 14:34:47 -07:00
Dark-Alex-17 27f12716d9 refactor(downloads_handler): Use the new handle_table_events macro 2024-12-08 14:28:12 -07:00
Dark-Alex-17 048877bbb6 refactor(collection_details_handler): use the new handle_table_events macro 2024-12-08 14:22:59 -07:00
Dark-Alex-17 87a652d911 refactor(collections_handler): Use the new handle_table_events macro 2024-12-08 14:14:24 -07:00
Dark-Alex-17 d6863dc1fd refactor(movie_details_handler): Use the new handle_table_events macro 2024-12-08 14:04:34 -07:00
Dark-Alex-17 f1d934b0a6 refactor(library_handler): Radarr use the new handle_table_events macro 2024-12-08 13:43:01 -07:00
Dark-Alex-17 5850f7a621 refactor(indexers_handler): Use the new handle_table_events macro 2024-12-08 13:26:59 -07:00
Dark-Alex-17 dd23e84ccf refactor(indexers_handler): Use the new handle_table_events macro 2024-12-08 13:24:18 -07:00
Dark-Alex-17 b060518778 refactor(root_folder_handler): Use the new handle_table_events macro 2024-12-08 13:15:59 -07:00
Dark-Alex-17 de95f13feb fix(handler_tests): Fixed all delegation tests to have initial conditions set properly 2024-12-08 13:10:17 -07:00
Dark-Alex-17 0205f13e53 refactor(history_handler): Use the new handle_table_event macro 2024-12-08 13:08:43 -07:00
Dark-Alex-17 b4de97dfe2 refactor(blocklist_handler): Use the new handle_table_events macro 2024-12-08 12:39:07 -07:00
Dark-Alex-17 35bc6cf31c refactor(downloads_handler): Use the new handle_table_events macro 2024-12-08 12:35:12 -07:00
Dark-Alex-17 c58e8b1a00 refactor(series_details_handler): Use the new handle_table_events macro 2024-12-08 12:29:59 -07:00
Dark-Alex-17 accdf99503 fix(ui): Fixed a bug that requires a minimum height for all popups so all error messages and other simple popups appear 2024-12-07 19:36:43 -07:00
Dark-Alex-17 47b609369b refactor(handler): Created a macro to handle all table key events to reduce code duplication and make future implementations faster; Only refactored the Sonarr library to use it thus far 2024-12-07 19:20:13 -07:00
Dark-Alex-17 23b1ca4371 feat(ui): Sonarr support for the series details popup 2024-12-06 20:30:26 -07:00
Dark-Alex-17 73d666d1f5 feat(ui): Sonarr support for editing a series from within the series details popup 2024-12-05 19:11:54 -07:00
Dark-Alex-17 b27c13cf74 fix(handler): Fixed a bug in the history handler that wouldn't reset the filter or search if a user hit 'esc' on the History tab 2024-12-05 19:08:11 -07:00
Dark-Alex-17 bd1a4f0939 feat(ui): Sonarr Series details UI is now available 2024-12-05 19:07:45 -07:00
Dark-Alex-17 5abed23cf2 refactor(ui): all table search and filter functionality is now available directly through the ManagarrTable widget to make life easier moving forward 2024-12-05 19:07:03 -07:00
Dark-Alex-17 9d0948e124 refactor(keys): Created a auto search key instead of reusing the existing search key to make things easier 2024-12-05 12:29:09 -07:00
Dark-Alex-17 678bc77a23 fix(ui): Fix the System Details Tasks popup to be navigable in both Sonarr and Radarr 2024-12-05 11:45:46 -07:00
Dark-Alex-17 00cdeee5c6 feat(ui): Full Sonarr system tab support
Signed-off-by: Alex Clarke <alex.j.tusa@gmail.com>
2024-12-04 17:41:30 -07:00
Dark-Alex-17 2d251554ad feat(handler): System handler support for Sonarr 2024-12-04 17:04:36 -07:00
Dark-Alex-17 1b5d70ae2d perf: Improved performance by optimizing API calls to only refresh when the tick prompts a refresh. All UI is now significantly faster 2024-12-04 16:46:06 -07:00
Dark-Alex-17 2d2901f6dc feat(ui): Full Sonarr support for the indexer tab 2024-12-04 16:39:37 -07:00
Dark-Alex-17 a0b27ec105 feat(ui): Support for modifying the indexer priority in Radarr 2024-12-03 18:12:23 -07:00
Dark-Alex-17 093ef136e7 feat(handler): Full indexer tab handler support 2024-12-03 17:46:37 -07:00
Dark-Alex-17 8660de530d feat(ui): Root folder tab support 2024-12-03 16:24:23 -07:00
Dark-Alex-17 bda6f253e0 feat(handlers): Support for root folder actions 2024-12-03 16:18:39 -07:00
Dark-Alex-17 4eb974567f feat(ui): History tab support 2024-12-02 18:47:50 -07:00
Dark-Alex-17 4f5bad5874 feat(handler): History tab support 2024-12-02 18:03:59 -07:00
Dark-Alex-17 1c6e798632 feat(ui): Blocklist UI support 2024-12-02 16:54:27 -07:00
Dark-Alex-17 3186fb42e7 feat(handler): Wired in the blocklist handler to the main handlers 2024-12-02 16:39:40 -07:00
Dark-Alex-17 4b7185fbb0 feat(handler): Blocklist handler support 2024-12-02 16:37:46 -07:00
Dark-Alex-17 f0d8555a8a feat(ui): Downloads tab support 2024-12-02 15:57:48 -07:00
Dark-Alex-17 f338dfcb12 feat(handler): Download tab support 2024-12-02 15:40:11 -07:00
Dark-Alex-17 188d781b0d feat(ui): Edit series support 2024-12-02 15:31:12 -07:00
Dark-Alex-17 adb1f07fd0 feat(handler): Edit series support 2024-12-02 14:58:51 -07:00
Dark-Alex-17 82e51be096 feat(ui): Add series support Sonarr 2024-12-02 13:53:28 -07:00
Dark-Alex-17 d7f6d12f59 feat(handler): Add series support for Sonarr 2024-12-02 12:43:17 -07:00
Dark-Alex-17 0db57fbff1 feat(ui): Delete a series 2024-12-02 11:45:13 -07:00
Dark-Alex-17 b1bdc19afb feat(handler): Support for deleting a series in Sonarr 2024-12-02 11:30:34 -07:00
Dark-Alex-17 b75a95a708 feat(ui): Support for the Series table 2024-12-01 14:08:06 -07:00
Dark-Alex-17 c3fb5dcd5f feat(handlers): Sonarr key support for the Series table 2024-12-01 13:48:48 -07:00
Dark-Alex-17 21911f93d1 feat(models): Added the necessary contextual help and tabs for the Sonarr UI 2024-12-01 12:05:20 -07:00
Dark-Alex-17 f7c96d81e9 refactor(BlockSelectionState): Refactored so selection of blocks in 2x2 grids is more intuitive and added left() and right() methods to aid this effort. 2024-11-30 12:22:46 -07:00
Dark-Alex-17 9b2040059d fix(ui): Fixed a potential rare bug in the UI where the application would panic if the height of the downloads window is 0. 2024-11-29 16:31:51 -07:00
Dark-Alex-17 08f190fc6e feat(ui): Initial UI support for switching to Sonarr tabs 2024-11-29 15:58:19 -07:00
Dark-Alex-17 4d1b0fe301 docs(context): Updated the Servarr context clues to say how to switch Servarr tabs via TAB and SHIFT+TAB 2024-11-27 17:14:40 -07:00
Dark-Alex-17 f139db07d9 feat(app): Dispatch support for all relevant Sonarr blocks 2024-11-27 17:06:20 -07:00
Alex Clarke 73a4129000 Update environment variables table so it appears better in Crates.io [skip ci] 2024-11-25 20:25:04 -07:00
Dark-Alex-17 1ddf797e28 ci: Updated the release so all GitHub release names are correctly simply the version: v0.1.2 [skip ci] 2024-11-25 20:11:15 -07:00
github-actions[bot] 18280f0478 chore: Bump the version in Cargo.lock 2024-11-26 03:04:19 +00:00
github-actions[bot] 4348705a0a bump: version 0.3.6 → 0.3.7 [skip ci] 2024-11-26 03:04:17 +00:00
Dark-Alex-17 5a1b92547d fix(ci): Forgot to also pull in the most recent changes [skip ci] 2024-11-25 20:02:37 -07:00
github-actions[bot] 9e44713985 chore: Bump the version in Cargo.lock 2024-11-26 02:59:13 +00:00
github-actions[bot] e93837fef7 bump: version 0.3.5 → 0.3.6 [skip ci] 2024-11-26 02:59:11 +00:00
Dark-Alex-17 6006c9d0e8 fix(ci): Ensure the Release Crate job fetches the most recent commit before publishing the crate [skip ci] 2024-11-25 19:58:34 -07:00
github-actions[bot] c93543186a chore: Bump the version in Cargo.lock 2024-11-26 02:49:11 +00:00
github-actions[bot] e1b74d7a36 bump: version 0.3.3 → 0.3.4 [skip ci] 2024-11-26 02:49:07 +00:00
194 changed files with 33290 additions and 5437 deletions
+8 -1
View File
@@ -94,7 +94,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ env.version }}
name: "Release v${{ env.version }}"
name: "v${{ env.version }}"
body: ${{ env.changelog_body }}
draft: false
prerelease: false
@@ -120,8 +120,15 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Ensure repository is up-to-date
run: |
git fetch --all
git pull
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- uses: katyo/publish-crates@v2
with:
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
+14
View File
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.3.7 (2024-11-26)
### Fix
- **ci**: Forgot to also pull in the most recent changes [skip ci]
## v0.3.6 (2024-11-26)
### Fix
- **ci**: Ensure the Release Crate job fetches the most recent commit before publishing the crate [skip ci]
## v0.3.4 (2024-11-26)
## v0.3.3 (2024-11-26)
### Fix
Generated
+22 -1
View File
@@ -489,12 +489,30 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "derive_setters"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.89",
]
[[package]]
name = "destructure_traitobject"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
[[package]]
name = "deunicode"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
[[package]]
name = "diff"
version = "0.1.13"
@@ -1303,7 +1321,7 @@ dependencies = [
[[package]]
name = "managarr"
version = "0.3.3"
version = "0.3.7"
dependencies = [
"anyhow",
"assert_cmd",
@@ -1319,6 +1337,8 @@ dependencies = [
"crossterm",
"ctrlc",
"derivative",
"derive_setters",
"deunicode",
"dirs-next",
"human-panic",
"indicatif",
@@ -1329,6 +1349,7 @@ dependencies = [
"managarr-tree-widget",
"mockall",
"mockito",
"paste",
"pretty_assertions",
"ratatui",
"regex",
+8 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "managarr"
version = "0.3.3"
version = "0.3.7"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "A TUI and CLI to manage your Servarrs"
keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"]
@@ -36,7 +36,10 @@ strum = { version = "0.26.3", features = ["derive"] }
strum_macros = "0.26.4"
tokio = { version = "1.36.0", features = ["full"] }
tokio-util = "0.7.8"
ratatui = { version = "0.29.0", features = ["all-widgets"] }
ratatui = { version = "0.29.0", features = [
"all-widgets",
"unstable-widget-ref",
] }
urlencoding = "2.1.2"
clap = { version = "4.5.20", features = ["derive", "cargo", "env"] }
clap_complete = "4.5.33"
@@ -47,6 +50,9 @@ async-trait = "0.1.83"
dirs-next = "2.0.0"
managarr-tree-widget = "0.24.0"
indicatif = "0.17.9"
derive_setters = "0.1.6"
deunicode = "1.6.0"
paste = "1.0.15"
[dev-dependencies]
assert_cmd = "2.0.16"
+1 -2
View File
@@ -94,7 +94,7 @@ Key:
| 🕒 | ✅ | Search your library |
| 🕒 | ✅ | Add series to your library |
| 🕒 | ✅ | Delete series, downloads, indexers, root folders, and episode files |
| 🕒 | ✅ | Mark history events as failed |
| 🚫 | ✅ | Mark history events as failed |
| 🕒 | ✅ | Trigger automatic searches for series, seasons, or episodes |
| 🕒 | ✅ | Trigger refresh and disk scan for series and downloads |
| 🕒 | ✅ | Manually search for series, seasons, or episodes |
@@ -275,7 +275,6 @@ Managarr supports using environment variables on startup so you don't have to al
|-----------------------------------------|--------------------------------------------------------------------------------|----------------------------------|
| `MANAGARR_CONFIG_FILE` | Set the path to the config file | `--config` |
| `MANAGARR_DISABLE_SPINNER` | Disable the CLI spinner (this can be useful when scripting and parsing output) | `--disable-spinner` |
|-----------------------------------------|--------------------------------------------------------------------------------|----------------------------------|
## Track My Progress for the Beta release (With Sonarr Support!)
Progress for the beta release can be followed on my [Wekan Board](https://wekan.alexjclarke.com/b/dHoGjBb44MHM9HSv4/managarr)
+25 -21
View File
@@ -19,6 +19,7 @@ mod tests {
assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]);
assert!(app.network_tx.is_none());
assert!(!app.cancellation_token.is_cancelled());
assert!(app.is_first_render);
assert_eq!(app.error, HorizontallyScrollableText::default());
assert_eq!(app.server_tabs.index, 0);
assert_eq!(
@@ -55,14 +56,11 @@ mod tests {
fn test_navigation_stack_methods() {
let mut app = App::default();
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE);
assert_eq!(app.get_current_route(), DEFAULT_ROUTE);
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
assert!(app.is_routing);
app.is_routing = false;
@@ -70,20 +68,20 @@ mod tests {
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
assert!(app.is_routing);
app.is_routing = false;
app.pop_navigation_stack();
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE);
assert_eq!(app.get_current_route(), DEFAULT_ROUTE);
assert!(app.is_routing);
app.is_routing = false;
app.pop_navigation_stack();
assert_eq!(app.get_current_route(), &DEFAULT_ROUTE);
assert_eq!(app.get_current_route(), DEFAULT_ROUTE);
assert!(app.is_routing);
}
@@ -120,19 +118,23 @@ mod tests {
#[test]
fn test_reset() {
let radarr_data = RadarrData {
version: "test".into(),
..RadarrData::default()
};
let sonarr_data = SonarrData {
version: "test".into(),
..SonarrData::default()
};
let data = Data {
radarr_data,
sonarr_data,
};
let mut app = App {
tick_count: 2,
error: "Test error".to_owned().into(),
data: Data {
radarr_data: RadarrData {
version: "test".to_owned(),
..RadarrData::default()
},
sonarr_data: SonarrData {
version: "test".to_owned(),
..SonarrData::default()
},
},
is_first_render: false,
data,
..App::default()
};
@@ -140,6 +142,7 @@ mod tests {
assert_eq!(app.tick_count, 0);
assert_eq!(app.error, HorizontallyScrollableText::default());
assert!(app.is_first_render);
assert!(app.data.radarr_data.version.is_empty());
assert!(app.data.sonarr_data.version.is_empty());
}
@@ -188,12 +191,13 @@ mod tests {
let mut app = App {
tick_until_poll: 2,
network_tx: Some(sync_network_tx),
is_first_render: true,
..App::default()
};
assert_eq!(app.tick_count, 0);
app.on_tick(true).await;
app.on_tick().await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
@@ -237,7 +241,7 @@ mod tests {
..App::default()
};
app.on_tick(false).await;
app.on_tick().await;
assert!(!app.is_routing);
}
@@ -250,7 +254,7 @@ mod tests {
..App::default()
};
app.on_tick(false).await;
app.on_tick().await;
assert!(!app.should_refresh);
}
+69 -2
View File
@@ -14,10 +14,77 @@ pub fn build_context_clue_string(context_clues: &[(KeyBinding, &str)]) -> String
.join(" | ")
}
pub static SERVARR_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.tab, "change servarr"),
pub static SERVARR_CONTEXT_CLUES: [ContextClue; 3] = [
(
DEFAULT_KEYBINDINGS.next_servarr,
DEFAULT_KEYBINDINGS.next_servarr.desc,
),
(
DEFAULT_KEYBINDINGS.previous_servarr,
DEFAULT_KEYBINDINGS.previous_servarr.desc,
),
(DEFAULT_KEYBINDINGS.quit, DEFAULT_KEYBINDINGS.quit.desc),
];
pub static BARE_POPUP_CONTEXT_CLUES: [ContextClue; 1] =
[(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc)];
pub static BLOCKLIST_CONTEXT_CLUES: [ContextClue; 5] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.submit, "details"),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.clear, "clear blocklist"),
];
pub static CONFIRMATION_PROMPT_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.confirm, "submit"),
(DEFAULT_KEYBINDINGS.esc, "cancel"),
];
pub static DOWNLOADS_CONTEXT_CLUES: [ContextClue; 3] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.update, "update downloads"),
];
pub static ROOT_FOLDERS_CONTEXT_CLUES: [ContextClue; 3] = [
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
];
pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [
(DEFAULT_KEYBINDINGS.submit, "edit indexer"),
(
DEFAULT_KEYBINDINGS.settings,
DEFAULT_KEYBINDINGS.settings.desc,
),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.test, "test indexer"),
(DEFAULT_KEYBINDINGS.test_all, "test all indexers"),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
];
pub static SYSTEM_CONTEXT_CLUES: [ContextClue; 5] = [
(DEFAULT_KEYBINDINGS.tasks, "open tasks"),
(DEFAULT_KEYBINDINGS.events, "open events"),
(DEFAULT_KEYBINDINGS.logs, "open logs"),
(DEFAULT_KEYBINDINGS.update, "open updates"),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
];
+168 -3
View File
@@ -2,7 +2,11 @@
mod test {
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::app::context_clues::{BARE_POPUP_CONTEXT_CLUES, SERVARR_CONTEXT_CLUES};
use crate::app::context_clues::{
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES,
SERVARR_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
};
use crate::app::{context_clues::build_context_clue_string, key_binding::DEFAULT_KEYBINDINGS};
#[test]
@@ -24,8 +28,13 @@ mod test {
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.tab);
assert_str_eq!(*description, "change servarr");
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.next_servarr);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.next_servarr.desc);
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.previous_servarr);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.previous_servarr.desc);
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
@@ -44,4 +53,160 @@ mod test {
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(bare_popup_context_clues_iter.next(), None);
}
#[test]
fn test_downloads_context_clues() {
let mut downloads_context_clues_iter = DOWNLOADS_CONTEXT_CLUES.iter();
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
assert_str_eq!(*description, "update downloads");
assert_eq!(downloads_context_clues_iter.next(), None);
}
#[test]
fn test_blocklist_context_clues() {
let mut blocklist_context_clues_iter = BLOCKLIST_CONTEXT_CLUES.iter();
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "details");
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.clear);
assert_str_eq!(*description, "clear blocklist");
assert_eq!(blocklist_context_clues_iter.next(), None);
}
#[test]
fn test_confirmation_prompt_context_clues() {
let mut confirmation_prompt_context_clues_iter = CONFIRMATION_PROMPT_CONTEXT_CLUES.iter();
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.confirm);
assert_str_eq!(*description, "submit");
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, "cancel");
assert_eq!(confirmation_prompt_context_clues_iter.next(), None);
}
#[test]
fn test_root_folders_context_clues() {
let mut root_folders_context_clues_iter = ROOT_FOLDERS_CONTEXT_CLUES.iter();
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.add);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.add.desc);
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
assert_eq!(root_folders_context_clues_iter.next(), None);
}
#[test]
fn test_indexers_context_clues() {
let mut indexers_context_clues_iter = INDEXERS_CONTEXT_CLUES.iter();
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "edit indexer");
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.settings);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.settings.desc);
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test);
assert_str_eq!(*description, "test indexer");
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test_all);
assert_str_eq!(*description, "test all indexers");
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
assert_eq!(indexers_context_clues_iter.next(), None);
}
#[test]
fn test_system_context_clues() {
let mut system_context_clues_iter = SYSTEM_CONTEXT_CLUES.iter();
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.tasks);
assert_str_eq!(*description, "open tasks");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.events);
assert_str_eq!(*description, "open events");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.logs);
assert_str_eq!(*description, "open logs");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
assert_str_eq!(*description, "open updates");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
assert_eq!(system_context_clues_iter.next(), None);
}
}
+16 -6
View File
@@ -15,8 +15,11 @@ generate_keybindings! {
left,
right,
backspace,
next_servarr,
previous_servarr,
clear,
search,
auto_search,
settings,
filter,
sort,
@@ -30,7 +33,6 @@ generate_keybindings! {
events,
home,
end,
tab,
delete,
submit,
confirm,
@@ -69,16 +71,28 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
key: Key::Backspace,
desc: "backspace",
},
next_servarr: KeyBinding {
key: Key::Tab,
desc: "next servarr",
},
previous_servarr: KeyBinding {
key: Key::BackTab,
desc: "previous servarr",
},
clear: KeyBinding {
key: Key::Char('c'),
desc: "clear",
},
auto_search: KeyBinding {
key: Key::Char('S'),
desc: "auto search",
},
search: KeyBinding {
key: Key::Char('s'),
desc: "search",
},
settings: KeyBinding {
key: Key::Char('s'),
key: Key::Char('S'),
desc: "settings",
},
filter: KeyBinding {
@@ -129,10 +143,6 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
key: Key::End,
desc: "end",
},
tab: KeyBinding {
key: Key::Tab,
desc: "tab",
},
delete: KeyBinding {
key: Key::Delete,
desc: "delete",
+4 -2
View File
@@ -13,9 +13,12 @@ mod test {
#[case(DEFAULT_KEYBINDINGS.left, Key::Left, "left")]
#[case(DEFAULT_KEYBINDINGS.right, Key::Right, "right")]
#[case(DEFAULT_KEYBINDINGS.backspace, Key::Backspace, "backspace")]
#[case(DEFAULT_KEYBINDINGS.next_servarr, Key::Tab, "next servarr")]
#[case(DEFAULT_KEYBINDINGS.previous_servarr, Key::BackTab, "previous servarr")]
#[case(DEFAULT_KEYBINDINGS.clear, Key::Char('c'), "clear")]
#[case(DEFAULT_KEYBINDINGS.auto_search, Key::Char('S'), "auto search")]
#[case(DEFAULT_KEYBINDINGS.search, Key::Char('s'), "search")]
#[case(DEFAULT_KEYBINDINGS.settings, Key::Char('s'), "settings")]
#[case(DEFAULT_KEYBINDINGS.settings, Key::Char('S'), "settings")]
#[case(DEFAULT_KEYBINDINGS.filter, Key::Char('f'), "filter")]
#[case(DEFAULT_KEYBINDINGS.sort, Key::Char('o'), "sort")]
#[case(DEFAULT_KEYBINDINGS.edit, Key::Char('e'), "edit")]
@@ -28,7 +31,6 @@ mod test {
#[case(DEFAULT_KEYBINDINGS.update, Key::Char('u'), "update")]
#[case(DEFAULT_KEYBINDINGS.home, Key::Home, "home")]
#[case(DEFAULT_KEYBINDINGS.end, Key::End, "end")]
#[case(DEFAULT_KEYBINDINGS.tab, Key::Tab, "tab")]
#[case(DEFAULT_KEYBINDINGS.delete, Key::Delete, "delete")]
#[case(DEFAULT_KEYBINDINGS.submit, Key::Enter, "submit")]
#[case(DEFAULT_KEYBINDINGS.confirm, Key::Ctrl('s'), "submit")]
+14 -11
View File
@@ -1,6 +1,6 @@
use std::process;
use anyhow::anyhow;
use anyhow::{anyhow, Error};
use colored::Colorize;
use log::{debug, error};
use serde::{Deserialize, Serialize};
@@ -21,6 +21,7 @@ pub mod context_clues;
pub mod key_binding;
mod key_binding_tests;
pub mod radarr;
pub mod sonarr;
const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies, None);
@@ -28,6 +29,7 @@ pub struct App<'a> {
navigation_stack: Vec<Route>,
network_tx: Option<Sender<NetworkEvent>>,
cancellation_token: CancellationToken,
pub is_first_render: bool,
pub server_tabs: TabState,
pub error: HorizontallyScrollableText,
pub tick_until_poll: u64,
@@ -76,26 +78,26 @@ impl<'a> App<'a> {
self.tick_count = 0;
}
// Allowing this code for now since we'll eventually be implementing additional Servarr support and we'll need it then
#[allow(dead_code)]
pub fn reset(&mut self) {
self.reset_tick_count();
self.error = HorizontallyScrollableText::default();
self.is_first_render = true;
self.data = Data::default();
}
pub fn handle_error(&mut self, error: anyhow::Error) {
pub fn handle_error(&mut self, error: Error) {
if self.error.text.is_empty() {
self.error = error.to_string().into();
}
}
pub async fn on_tick(&mut self, is_first_render: bool) {
pub async fn on_tick(&mut self) {
if self.tick_count % self.tick_until_poll == 0 || self.is_routing || self.should_refresh {
if let Route::Radarr(active_radarr_block, _) = self.get_current_route() {
self
.radarr_on_tick(*active_radarr_block, is_first_render)
.await;
match self.get_current_route() {
Route::Radarr(active_radarr_block, _) => self.radarr_on_tick(active_radarr_block).await,
Route::Sonarr(active_sonarr_block, _) => self.sonarr_on_tick(active_sonarr_block).await,
_ => (),
}
self.is_routing = false;
@@ -130,8 +132,8 @@ impl<'a> App<'a> {
self.push_navigation_stack(route);
}
pub fn get_current_route(&self) -> &Route {
self.navigation_stack.last().unwrap_or(&DEFAULT_ROUTE)
pub fn get_current_route(&self) -> Route {
*self.navigation_stack.last().unwrap_or(&DEFAULT_ROUTE)
}
}
@@ -142,6 +144,7 @@ impl<'a> Default for App<'a> {
network_tx: None,
cancellation_token: CancellationToken::new(),
error: HorizontallyScrollableText::default(),
is_first_render: true,
server_tabs: TabState::new(vec![
TabRoute {
title: "Radarr",
@@ -176,7 +179,7 @@ impl<'a> Default for App<'a> {
#[derive(Default)]
pub struct Data<'a> {
pub radarr_data: RadarrData<'a>,
pub sonarr_data: SonarrData,
pub sonarr_data: SonarrData<'a>,
}
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
+10 -13
View File
@@ -119,11 +119,11 @@ impl<'a> App<'a> {
_ => (),
}
self.check_for_prompt_action().await;
self.check_for_radarr_prompt_action().await;
self.reset_tick_count();
}
async fn check_for_prompt_action(&mut self) {
async fn check_for_radarr_prompt_action(&mut self) {
if self.data.radarr_data.prompt_confirm {
self.data.radarr_data.prompt_confirm = false;
if let Some(radarr_event) = &self.data.radarr_data.prompt_confirm_action {
@@ -136,19 +136,17 @@ impl<'a> App<'a> {
}
}
pub(super) async fn radarr_on_tick(
&mut self,
active_radarr_block: ActiveRadarrBlock,
is_first_render: bool,
) {
if is_first_render {
self.refresh_metadata().await;
pub(super) async fn radarr_on_tick(&mut self, active_radarr_block: ActiveRadarrBlock) {
if self.is_first_render {
self.refresh_radarr_metadata().await;
self.dispatch_by_radarr_block(&active_radarr_block).await;
self.is_first_render = false;
return;
}
if self.should_refresh {
self.dispatch_by_radarr_block(&active_radarr_block).await;
self.refresh_metadata().await;
self.refresh_radarr_metadata().await;
}
if self.is_routing {
@@ -156,16 +154,15 @@ impl<'a> App<'a> {
self.cancellation_token.cancel();
} else {
self.dispatch_by_radarr_block(&active_radarr_block).await;
self.refresh_metadata().await;
}
}
if self.tick_count % self.tick_until_poll == 0 {
self.refresh_metadata().await;
self.refresh_radarr_metadata().await;
}
}
async fn refresh_metadata(&mut self) {
async fn refresh_radarr_metadata(&mut self) {
self
.dispatch_network_event(RadarrEvent::GetQualityProfiles.into())
.await;
+8 -61
View File
@@ -35,60 +35,6 @@ pub static COLLECTIONS_CONTEXT_CLUES: [ContextClue; 8] = [
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
];
pub static DOWNLOADS_CONTEXT_CLUES: [ContextClue; 2] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
];
pub static BLOCKLIST_CONTEXT_CLUES: [ContextClue; 5] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.submit, "details"),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.clear, "clear blocklist"),
];
pub static ROOT_FOLDERS_CONTEXT_CLUES: [ContextClue; 3] = [
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
];
pub static INDEXERS_CONTEXT_CLUES: [ContextClue; 6] = [
(DEFAULT_KEYBINDINGS.submit, "edit indexer"),
(
DEFAULT_KEYBINDINGS.settings,
DEFAULT_KEYBINDINGS.settings.desc,
),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.test, "test indexer"),
(DEFAULT_KEYBINDINGS.test_all, "test all indexers"),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
];
pub static SYSTEM_CONTEXT_CLUES: [ContextClue; 5] = [
(DEFAULT_KEYBINDINGS.tasks, "open tasks"),
(DEFAULT_KEYBINDINGS.events, "open events"),
(DEFAULT_KEYBINDINGS.logs, "open logs"),
(DEFAULT_KEYBINDINGS.update, "open updates"),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
];
pub static MOVIE_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
(
DEFAULT_KEYBINDINGS.refresh,
@@ -96,7 +42,10 @@ pub static MOVIE_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
),
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
(DEFAULT_KEYBINDINGS.search, "auto search"),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
@@ -108,7 +57,10 @@ pub static MANUAL_MOVIE_SEARCH_CONTEXT_CLUES: [ContextClue; 6] = [
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.search, "auto search"),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
@@ -120,11 +72,6 @@ pub static ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.esc, "edit search"),
];
pub static CONFIRMATION_PROMPT_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.confirm, "submit"),
(DEFAULT_KEYBINDINGS.esc, "cancel"),
];
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.submit, "start task"),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
+8 -160
View File
@@ -4,11 +4,10 @@ mod tests {
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::radarr_context_clues::{
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES,
COLLECTION_DETAILS_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES,
INDEXERS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES,
MANUAL_MOVIE_SEARCH_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES,
SYSTEM_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES,
COLLECTION_DETAILS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES,
MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
MOVIE_DETAILS_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
};
#[test]
@@ -113,141 +112,6 @@ mod tests {
assert_eq!(collections_context_clues.next(), None);
}
#[test]
fn test_downloads_context_clues() {
let mut downloads_context_clues_iter = DOWNLOADS_CONTEXT_CLUES.iter();
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = downloads_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
assert_eq!(downloads_context_clues_iter.next(), None);
}
#[test]
fn test_blocklist_context_clues() {
let mut blocklist_context_clues_iter = BLOCKLIST_CONTEXT_CLUES.iter();
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "details");
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = blocklist_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.clear);
assert_str_eq!(*description, "clear blocklist");
assert_eq!(blocklist_context_clues_iter.next(), None);
}
#[test]
fn test_root_folders_context_clues() {
let mut root_folders_context_clues_iter = ROOT_FOLDERS_CONTEXT_CLUES.iter();
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.add);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.add.desc);
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = root_folders_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
assert_eq!(root_folders_context_clues_iter.next(), None);
}
#[test]
fn test_indexers_context_clues() {
let mut indexers_context_clues_iter = INDEXERS_CONTEXT_CLUES.iter();
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "edit indexer");
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.settings);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.settings.desc);
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test);
assert_str_eq!(*description, "test indexer");
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.test_all);
assert_str_eq!(*description, "test all indexers");
let (key_binding, description) = indexers_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
assert_eq!(indexers_context_clues_iter.next(), None);
}
#[test]
fn test_system_context_clues() {
let mut system_context_clues_iter = SYSTEM_CONTEXT_CLUES.iter();
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.tasks);
assert_str_eq!(*description, "open tasks");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.events);
assert_str_eq!(*description, "open events");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.logs);
assert_str_eq!(*description, "open logs");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
assert_str_eq!(*description, "open updates");
let (key_binding, description) = system_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
assert_eq!(system_context_clues_iter.next(), None);
}
#[test]
fn test_movie_details_context_clues() {
let mut movie_details_context_clues_iter = MOVIE_DETAILS_CONTEXT_CLUES.iter();
@@ -269,8 +133,8 @@ mod tests {
let (key_binding, description) = movie_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, "auto search");
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = movie_details_context_clues_iter.next().unwrap();
@@ -305,8 +169,8 @@ mod tests {
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, "auto search");
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
@@ -349,22 +213,6 @@ mod tests {
assert_eq!(add_movie_search_results_context_clues_iter.next(), None);
}
#[test]
fn test_confirmation_prompt_context_clues() {
let mut confirmation_prompt_context_clues_iter = CONFIRMATION_PROMPT_CONTEXT_CLUES.iter();
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.confirm);
assert_str_eq!(*description, "submit");
let (key_binding, description) = confirmation_prompt_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, "cancel");
assert_eq!(confirmation_prompt_context_clues_iter.next(), None);
}
#[test]
fn test_system_tasks_context_clues() {
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter();
+14 -37
View File
@@ -459,22 +459,22 @@ mod tests {
}
#[tokio::test]
async fn test_check_for_prompt_action_no_prompt_confirm() {
async fn test_check_for_radarr_prompt_action_no_prompt_confirm() {
let mut app = App::default();
app.data.radarr_data.prompt_confirm = false;
app.check_for_prompt_action().await;
app.check_for_radarr_prompt_action().await;
assert!(!app.data.radarr_data.prompt_confirm);
assert!(!app.should_refresh);
}
#[tokio::test]
async fn test_check_for_prompt_action() {
async fn test_check_for_radarr_prompt_action() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::GetStatus);
app.check_for_prompt_action().await;
app.check_for_radarr_prompt_action().await;
assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(
@@ -490,7 +490,7 @@ mod tests {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.is_routing = true;
app.refresh_metadata().await;
app.refresh_radarr_metadata().await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
@@ -522,8 +522,9 @@ mod tests {
#[tokio::test]
async fn test_radarr_on_tick_first_render() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.is_first_render = true;
app.radarr_on_tick(ActiveRadarrBlock::Downloads, true).await;
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
@@ -551,6 +552,7 @@ mod tests {
);
assert!(app.is_loading);
assert!(!app.data.radarr_data.prompt_confirm);
assert!(!app.is_first_render);
}
#[tokio::test]
@@ -559,26 +561,8 @@ mod tests {
app.is_routing = true;
app.should_refresh = true;
app
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetQualityProfiles.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetTags.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetRootFolders.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
RadarrEvent::GetDownloads.into()
@@ -592,9 +576,7 @@ mod tests {
app.is_routing = true;
app.should_refresh = false;
app
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
assert!(app.cancellation_token.is_cancelled());
}
@@ -604,9 +586,7 @@ mod tests {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.should_refresh = true;
app
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
@@ -623,9 +603,7 @@ mod tests {
app.is_routing = true;
app.should_refresh = true;
app
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
@@ -643,9 +621,7 @@ mod tests {
app.tick_count = 2;
app.tick_until_poll = 2;
app
.radarr_on_tick(ActiveRadarrBlock::Downloads, false)
.await;
app.radarr_on_tick(ActiveRadarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
@@ -701,6 +677,7 @@ mod tests {
let mut app = App {
network_tx: Some(sync_network_tx),
tick_count: 1,
is_first_render: false,
..App::default()
};
app.data.radarr_data.prompt_confirm = true;
+213
View File
@@ -0,0 +1,213 @@
use crate::{
models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock,
network::sonarr_network::SonarrEvent,
};
use super::App;
pub mod sonarr_context_clues;
#[cfg(test)]
#[path = "sonarr_tests.rs"]
mod sonarr_tests;
impl<'a> App<'a> {
pub(super) async fn dispatch_by_sonarr_block(&mut self, active_sonarr_block: &ActiveSonarrBlock) {
match active_sonarr_block {
ActiveSonarrBlock::Series => {
self
.dispatch_network_event(SonarrEvent::ListSeries.into())
.await;
}
ActiveSonarrBlock::SeriesDetails => {
self.is_loading = true;
self.populate_seasons_table().await;
self.is_loading = false;
}
ActiveSonarrBlock::SeriesHistory => {
self
.dispatch_network_event(SonarrEvent::GetSeriesHistory(None).into())
.await;
}
ActiveSonarrBlock::SeasonDetails => {
self
.dispatch_network_event(SonarrEvent::GetEpisodes(None).into())
.await;
}
ActiveSonarrBlock::SeasonHistory => {
self
.dispatch_network_event(SonarrEvent::GetSeasonHistory(None).into())
.await;
}
ActiveSonarrBlock::ManualSeasonSearch => {
self
.dispatch_network_event(SonarrEvent::GetSeasonReleases(None).into())
.await;
}
ActiveSonarrBlock::EpisodeDetails | ActiveSonarrBlock::EpisodeFile => {
self
.dispatch_network_event(SonarrEvent::GetEpisodeDetails(None).into())
.await;
}
ActiveSonarrBlock::EpisodeHistory => {
self
.dispatch_network_event(SonarrEvent::GetEpisodeHistory(None).into())
.await;
}
ActiveSonarrBlock::ManualEpisodeSearch => {
self
.dispatch_network_event(SonarrEvent::GetEpisodeReleases(None).into())
.await;
}
ActiveSonarrBlock::Downloads => {
self
.dispatch_network_event(SonarrEvent::GetDownloads.into())
.await;
}
ActiveSonarrBlock::Blocklist => {
self
.dispatch_network_event(SonarrEvent::ListSeries.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetBlocklist.into())
.await;
}
ActiveSonarrBlock::History => {
self
.dispatch_network_event(SonarrEvent::GetHistory(None).into())
.await;
}
ActiveSonarrBlock::RootFolders => {
self
.dispatch_network_event(SonarrEvent::GetRootFolders.into())
.await;
}
ActiveSonarrBlock::Indexers => {
self
.dispatch_network_event(SonarrEvent::GetIndexers.into())
.await;
}
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
self
.dispatch_network_event(SonarrEvent::GetAllIndexerSettings.into())
.await;
}
ActiveSonarrBlock::TestIndexer => {
self
.dispatch_network_event(SonarrEvent::TestIndexer(None).into())
.await;
}
ActiveSonarrBlock::TestAllIndexers => {
self
.dispatch_network_event(SonarrEvent::TestAllIndexers.into())
.await;
}
ActiveSonarrBlock::System => {
self
.dispatch_network_event(SonarrEvent::GetTasks.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetQueuedEvents.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetLogs(None).into())
.await;
}
ActiveSonarrBlock::AddSeriesSearchResults => {
self
.dispatch_network_event(SonarrEvent::SearchNewSeries(None).into())
.await;
}
ActiveSonarrBlock::SystemUpdates => {
self
.dispatch_network_event(SonarrEvent::GetUpdates.into())
.await;
}
_ => (),
}
self.check_for_sonarr_prompt_action().await;
self.reset_tick_count();
}
async fn check_for_sonarr_prompt_action(&mut self) {
if self.data.sonarr_data.prompt_confirm {
self.data.sonarr_data.prompt_confirm = false;
if let Some(sonarr_event) = &self.data.sonarr_data.prompt_confirm_action {
self
.dispatch_network_event(sonarr_event.clone().into())
.await;
self.should_refresh = true;
self.data.sonarr_data.prompt_confirm_action = None;
}
}
}
pub(super) async fn sonarr_on_tick(&mut self, active_sonarr_block: ActiveSonarrBlock) {
if self.is_first_render {
self.refresh_sonarr_metadata().await;
self.dispatch_by_sonarr_block(&active_sonarr_block).await;
self.is_first_render = false;
return;
}
if self.should_refresh {
self.dispatch_by_sonarr_block(&active_sonarr_block).await;
self.refresh_sonarr_metadata().await;
}
if self.is_routing {
if !self.should_refresh {
self.cancellation_token.cancel();
} else {
self.dispatch_by_sonarr_block(&active_sonarr_block).await;
}
}
if self.tick_count % self.tick_until_poll == 0 {
self.refresh_sonarr_metadata().await;
}
}
async fn refresh_sonarr_metadata(&mut self) {
self
.dispatch_network_event(SonarrEvent::GetQualityProfiles.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetLanguageProfiles.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetTags.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetRootFolders.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetDownloads.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetDiskSpace.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetStatus.into())
.await;
}
async fn populate_seasons_table(&mut self) {
let seasons = self
.data
.sonarr_data
.series
.current_selection()
.clone()
.seasons
.unwrap_or_default()
.into_iter()
.map(|mut season| {
season.title = Some(format!("Season {}", season.season_number));
season
})
.collect();
self.data.sonarr_data.seasons.set_items(seasons);
}
}
+166
View File
@@ -0,0 +1,166 @@
use crate::app::{context_clues::ContextClue, key_binding::DEFAULT_KEYBINDINGS};
#[cfg(test)]
#[path = "sonarr_context_clues_tests.rs"]
mod sonarr_context_clues_tests;
pub static ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.submit, "details"),
(DEFAULT_KEYBINDINGS.esc, "edit search"),
];
pub static SERIES_CONTEXT_CLUES: [ContextClue; 10] = [
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.update, "update all"),
(DEFAULT_KEYBINDINGS.submit, "details"),
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
];
pub static SERIES_DETAILS_CONTEXT_CLUES: [ContextClue; 7] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
(DEFAULT_KEYBINDINGS.submit, "season details"),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
pub static HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
(DEFAULT_KEYBINDINGS.submit, "details"),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
];
pub static SERIES_HISTORY_CONTEXT_CLUES: [ContextClue; 9] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
(DEFAULT_KEYBINDINGS.submit, "details"),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
];
pub static SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.submit, "episode details"),
(DEFAULT_KEYBINDINGS.delete, "delete episode"),
];
pub static SEASON_DETAILS_CONTEXT_CLUES: [ContextClue; 4] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
pub static SEASON_HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
];
pub static MANUAL_SEASON_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
pub static MANUAL_EPISODE_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
pub static DETAILS_CONTEXTUAL_CONTEXT_CLUES: [ContextClue; 1] =
[(DEFAULT_KEYBINDINGS.submit, "details")];
pub static EPISODE_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
pub static EPISODE_HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
(
DEFAULT_KEYBINDINGS.auto_search,
DEFAULT_KEYBINDINGS.auto_search.desc,
),
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
];
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
(DEFAULT_KEYBINDINGS.submit, "start task"),
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
];
@@ -0,0 +1,392 @@
#[cfg(test)]
mod tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::app::{
key_binding::DEFAULT_KEYBINDINGS,
sonarr::sonarr_context_clues::{
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES, DETAILS_CONTEXTUAL_CONTEXT_CLUES,
EPISODE_DETAILS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXT_CLUES,
MANUAL_SEASON_SEARCH_CONTEXT_CLUES, SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES,
SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES,
SERIES_DETAILS_CONTEXT_CLUES, SERIES_HISTORY_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
},
};
#[test]
fn test_add_series_search_results_context_clues() {
let mut add_series_search_results_context_clues_iter =
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES.iter();
let (key_binding, description) = add_series_search_results_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "details");
let (key_binding, description) = add_series_search_results_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, "edit search");
assert_eq!(add_series_search_results_context_clues_iter.next(), None);
}
#[test]
fn test_series_context_clues() {
let mut series_context_clues_iter = SERIES_CONTEXT_CLUES.iter();
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.add);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.add.desc);
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.delete.desc);
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
assert_str_eq!(*description, "update all");
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "details");
let (key_binding, description) = series_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, "cancel filter");
assert_eq!(series_context_clues_iter.next(), None);
}
#[test]
fn test_series_history_context_clues() {
let mut series_history_context_clues_iter = SERIES_HISTORY_CONTEXT_CLUES.iter();
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "details");
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.update.desc);
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, "cancel filter/close");
assert_eq!(series_history_context_clues_iter.next(), None);
}
#[test]
fn test_history_context_clues() {
let mut history_context_clues_iter = HISTORY_CONTEXT_CLUES.iter();
let (key_binding, description) = history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "details");
let (key_binding, description) = history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
let (key_binding, description) = history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
let (key_binding, description) = history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, "cancel filter");
assert_eq!(history_context_clues_iter.next(), None);
}
#[test]
fn test_series_details_context_clues() {
let mut series_details_context_clues_iter = SERIES_DETAILS_CONTEXT_CLUES.iter();
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "season details");
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.update.desc);
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = series_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(series_details_context_clues_iter.next(), None);
}
#[test]
fn test_season_details_context_clues() {
let mut season_details_context_clues_iter = SEASON_DETAILS_CONTEXT_CLUES.iter();
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(season_details_context_clues_iter.next(), None);
}
#[test]
fn test_season_details_contextual_context_clues() {
let mut season_details_contextual_context_clues_iter =
SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES.iter();
let (key_binding, description) = season_details_contextual_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "episode details");
let (key_binding, description) = season_details_contextual_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
assert_str_eq!(*description, "delete episode");
assert_eq!(season_details_contextual_context_clues_iter.next(), None);
}
#[test]
fn test_season_history_context_clues() {
let mut season_history_context_clues_iter = SEASON_HISTORY_CONTEXT_CLUES.iter();
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, "cancel filter/close");
assert_eq!(season_history_context_clues_iter.next(), None);
}
#[test]
fn test_manual_season_search_context_clues() {
let mut manual_season_search_context_clues_iter = MANUAL_SEASON_SEARCH_CONTEXT_CLUES.iter();
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(manual_season_search_context_clues_iter.next(), None);
}
#[test]
fn test_manual_episode_search_context_clues() {
let mut manual_episode_search_context_clues_iter = MANUAL_EPISODE_SEARCH_CONTEXT_CLUES.iter();
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(manual_episode_search_context_clues_iter.next(), None);
}
#[test]
fn details_contextual_context_clues() {
let mut manual_search_contextual_context_clues_iter = DETAILS_CONTEXTUAL_CONTEXT_CLUES.iter();
let (key_binding, description) = manual_search_contextual_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "details");
assert_eq!(manual_search_contextual_context_clues_iter.next(), None);
}
#[test]
fn test_episode_details_context_clues() {
let mut episode_details_context_clues_iter = EPISODE_DETAILS_CONTEXT_CLUES.iter();
let (key_binding, description) = episode_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
let (key_binding, description) = episode_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
let (key_binding, description) = episode_details_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(episode_details_context_clues_iter.next(), None);
}
#[test]
fn test_system_tasks_context_clues() {
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter();
let (key_binding, description) = system_tasks_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
assert_str_eq!(*description, "start task");
let (key_binding, description) = system_tasks_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
assert_eq!(system_tasks_context_clues_iter.next(), None);
}
}
+634
View File
@@ -0,0 +1,634 @@
#[cfg(test)]
mod tests {
mod sonarr_tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use tokio::sync::mpsc;
use crate::{
app::App,
models::{
servarr_data::sonarr::sonarr_data::ActiveSonarrBlock,
sonarr_models::{Season, Series},
},
network::{sonarr_network::SonarrEvent, NetworkEvent},
};
#[tokio::test]
async fn test_dispatch_by_blocklist_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Blocklist)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::ListSeries.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetBlocklist.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_series_history_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeriesHistory)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeriesHistory(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_series_details_block() {
let (mut app, _) = construct_app_unit();
app.data.sonarr_data.series.set_items(vec![Series {
seasons: Some(vec![Season::default()]),
..Series::default()
}]);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeriesDetails)
.await;
assert!(!app.is_loading);
assert!(!app.data.sonarr_data.seasons.items.is_empty());
assert_eq!(app.tick_count, 0);
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[tokio::test]
async fn test_dispatch_by_season_details_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonDetails)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodes(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_season_history_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonHistory)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeasonHistory(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_season_search_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualSeasonSearch)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeasonReleases(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_episode_details_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeDetails)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeDetails(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_episode_file_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeFile)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeDetails(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_episode_history_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeHistory)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeHistory(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_episode_search_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualEpisodeSearch)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeReleases(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_history_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::History)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetHistory(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_downloads_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Downloads)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDownloads.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_root_folders_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::RootFolders)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetRootFolders.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_series_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Series)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::ListSeries.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_indexers_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::Indexers)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetIndexers.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_all_indexer_settings_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::AllIndexerSettingsPrompt)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetAllIndexerSettings.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_test_indexer_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::TestIndexer)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::TestIndexer(None).into()
);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_test_all_indexers_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::TestAllIndexers)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::TestAllIndexers.into()
);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_system_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::System)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetTasks.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetQueuedEvents.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetLogs(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_system_updates_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SystemUpdates)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetUpdates.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_add_movie_search_results_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::AddSeriesSearchResults)
.await;
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::SearchNewSeries(None).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_check_for_sonarr_prompt_action_no_prompt_confirm() {
let mut app = App::default();
app.data.sonarr_data.prompt_confirm = false;
app.check_for_sonarr_prompt_action().await;
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.should_refresh);
}
#[tokio::test]
async fn test_check_for_sonarr_prompt_action() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::GetStatus);
app.check_for_sonarr_prompt_action().await;
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetStatus.into()
);
assert!(app.should_refresh);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
}
#[tokio::test]
async fn test_sonarr_refresh_metadata() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.is_routing = true;
app.refresh_sonarr_metadata().await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetQualityProfiles.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetLanguageProfiles.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetTags.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetRootFolders.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDownloads.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDiskSpace.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetStatus.into()
);
assert!(app.is_loading);
}
#[tokio::test]
async fn test_sonarr_on_tick_first_render() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.is_first_render = true;
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetQualityProfiles.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetLanguageProfiles.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetTags.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetRootFolders.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDownloads.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDiskSpace.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetStatus.into()
);
assert!(app.is_loading);
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.is_first_render);
}
#[tokio::test]
async fn test_sonarr_on_tick_routing() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.is_routing = true;
app.should_refresh = true;
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDownloads.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[tokio::test]
async fn test_sonarr_on_tick_routing_while_long_request_is_running_should_cancel_request() {
let (mut app, _) = construct_app_unit();
app.is_routing = true;
app.should_refresh = false;
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
assert!(app.cancellation_token.is_cancelled());
}
#[tokio::test]
async fn test_sonarr_on_tick_should_refresh() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.should_refresh = true;
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDownloads.into()
);
assert!(app.should_refresh);
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[tokio::test]
async fn test_sonarr_on_tick_should_refresh_does_not_cancel_prompt_requests() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.is_loading = true;
app.is_routing = true;
app.should_refresh = true;
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDownloads.into()
);
assert!(app.is_loading);
assert!(app.should_refresh);
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.cancellation_token.is_cancelled());
}
#[tokio::test]
async fn test_sonarr_on_tick_network_tick_frequency() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.tick_count = 2;
app.tick_until_poll = 2;
app.sonarr_on_tick(ActiveSonarrBlock::Downloads).await;
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetQualityProfiles.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetLanguageProfiles.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetTags.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetRootFolders.into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetDownloads.into()
);
assert!(app.is_loading);
}
#[tokio::test]
async fn test_populate_seasons_table_unfiltered() {
let mut app = App::default();
app.data.sonarr_data.series.set_items(vec![Series {
seasons: Some(vec![Season::default()]),
..Series::default()
}]);
app.populate_seasons_table().await;
assert!(!app.data.sonarr_data.seasons.items.is_empty());
assert_str_eq!(
app.data.sonarr_data.seasons.items[0]
.title
.as_ref()
.unwrap(),
"Season 0"
);
}
#[tokio::test]
async fn test_populate_seasons_table_filtered() {
let mut app = App::default();
app.data.sonarr_data.series.set_filtered_items(vec![Series {
seasons: Some(vec![Season::default()]),
..Series::default()
}]);
app.populate_seasons_table().await;
assert!(!app.data.sonarr_data.seasons.items.is_empty());
assert_str_eq!(
app.data.sonarr_data.seasons.items[0]
.title
.as_ref()
.unwrap(),
"Season 0"
);
}
fn construct_app_unit<'a>() -> (App<'a>, mpsc::Receiver<NetworkEvent>) {
let (sync_network_tx, sync_network_rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App {
network_tx: Some(sync_network_tx),
tick_count: 1,
is_first_render: false,
..App::default()
};
app.data.sonarr_data.prompt_confirm = true;
(app, sync_network_rx)
}
}
}
+29
View File
@@ -67,6 +67,23 @@ pub enum SonarrListCommand {
QueuedEvents,
#[command(about = "List all root folders in Sonarr")]
RootFolders,
#[command(
about = "Fetch all history events for the given season corresponding to the series with the given ID."
)]
SeasonHistory {
#[arg(
long,
help = "The Sonarr ID of the series whose history you wish to fetch and list",
required = true
)]
series_id: i64,
#[arg(
long,
help = "The season number to fetch history events for",
required = true
)]
season_number: i64,
},
#[command(about = "List all series in your Sonarr library")]
Series,
#[command(about = "Fetch all history events for the series with the given ID")]
@@ -207,6 +224,18 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::SeasonHistory {
series_id,
season_number,
} => {
let resp = self
.network
.handle_network_event(
SonarrEvent::GetSeasonHistory(Some((series_id, season_number))).into(),
)
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::Series => {
let resp = self
.network
@@ -149,6 +149,58 @@ mod tests {
}
}
#[test]
fn test_season_history_requires_series_id() {
let result = Cli::command().try_get_matches_from([
"managarr",
"sonarr",
"list",
"season-history",
"--season-number",
"1",
]);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().kind(),
ErrorKind::MissingRequiredArgument
);
}
#[test]
fn test_season_history_requires_season_number() {
let result = Cli::command().try_get_matches_from([
"managarr",
"sonarr",
"list",
"season-history",
"--series-id",
"1",
]);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().kind(),
ErrorKind::MissingRequiredArgument
);
}
#[test]
fn test_season_history_requirements_satisfied() {
let result = Cli::command().try_get_matches_from([
"managarr",
"sonarr",
"list",
"season-history",
"--series-id",
"1",
"--season-number",
"1",
]);
assert!(result.is_ok());
}
#[test]
fn test_list_series_history_requires_series_id() {
let result =
@@ -368,5 +420,35 @@ mod tests {
assert!(result.is_ok());
}
#[tokio::test]
async fn test_list_season_history_command() {
let expected_series_id = 1;
let expected_season_number = 1;
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetSeasonHistory(Some((expected_series_id, expected_season_number))).into(),
))
.times(1)
.returning(|_| {
Ok(Serdeable::Sonarr(SonarrSerdeable::Value(
json!({"testResponse": "response"}),
)))
});
let app_arc = Arc::new(Mutex::new(App::default()));
let list_season_history_command = SonarrListCommand::SeasonHistory {
series_id: 1,
season_number: 1,
};
let result =
SonarrListCommandHandler::with(&app_arc, list_season_history_command, &mut mock_network)
.handle()
.await;
assert!(result.is_ok());
}
}
}
+7
View File
@@ -19,6 +19,7 @@ pub enum Key {
Home,
End,
Tab,
BackTab,
Delete,
Ctrl(char),
Char(char),
@@ -40,6 +41,7 @@ impl Display for Key {
Key::Home => write!(f, "<home>"),
Key::End => write!(f, "<end>"),
Key::Tab => write!(f, "<tab>"),
Key::BackTab => write!(f, "<shift-tab>"),
Key::Delete => write!(f, "<del>"),
_ => write!(f, "<{self:?}>"),
}
@@ -75,6 +77,11 @@ impl From<KeyEvent> for Key {
KeyEvent {
code: KeyCode::End, ..
} => Key::End,
KeyEvent {
code: KeyCode::BackTab,
modifiers: KeyModifiers::SHIFT,
..
} => Key::BackTab,
KeyEvent {
code: KeyCode::Tab, ..
} => Key::Tab,
+14
View File
@@ -17,6 +17,7 @@ mod tests {
#[case(Key::Home, "home")]
#[case(Key::End, "end")]
#[case(Key::Tab, "tab")]
#[case(Key::BackTab, "shift-tab")]
#[case(Key::Delete, "del")]
#[case(Key::Char('q'), "q")]
#[case(Key::Ctrl('q'), "ctrl-q")]
@@ -67,6 +68,19 @@ mod tests {
assert_eq!(Key::from(KeyEvent::from(KeyCode::Tab)), Key::Tab);
}
#[test]
fn test_key_from_back_tab() {
assert_eq!(
Key::from(KeyEvent {
code: KeyCode::BackTab,
modifiers: KeyModifiers::SHIFT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE
}),
Key::BackTab
);
}
#[test]
fn test_key_from_delete() {
assert_eq!(Key::from(KeyEvent::from(KeyCode::Delete)), Key::Delete);
+126 -56
View File
@@ -99,86 +99,96 @@ mod test_utils {
#[macro_export]
macro_rules! test_iterable_scroll {
($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $block:expr, $context:expr) => {
#[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default();
app.push_navigation_stack($block.into());
app
.data
.radarr_data
.$servarr_data
.$data_ref
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
$handler::with(&key, &mut app, &$block, &$context).handle();
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 2");
assert_str_eq!(
app.data.$servarr_data.$data_ref.current_selection(),
"Test 2"
);
$handler::with(&key, &mut app, &$block, &$context).handle();
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 1");
assert_str_eq!(
app.data.$servarr_data.$data_ref.current_selection(),
"Test 1"
);
}
};
($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
#[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default();
app.push_navigation_stack($block.into());
app
.data
.radarr_data
.$servarr_data
.$data_ref
.set_items(simple_stateful_iterable_vec!($items));
$handler::with(&key, &mut app, &$block, &$context).handle();
$handler::with(key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 2"
);
$handler::with(&key, &mut app, &$block, &$context).handle();
$handler::with(key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1"
);
}
};
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
#[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default();
app.data.radarr_data.$data_ref.set_items($items);
app.push_navigation_stack($block.into());
app.data.$servarr_data.$data_ref.set_items($items);
$handler::with(&key, &mut app, &$block, &$context).handle();
$handler::with(key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 2"
);
$handler::with(&key, &mut app, &$block, &$context).handle();
$handler::with(key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1"
);
}
};
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
#[rstest]
fn $func(#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key) {
let mut app = App::default();
app.data.radarr_data.$data_ref.set_items($items);
app.push_navigation_stack($block.into());
app.data.$servarr_data.$data_ref.set_items($items);
$handler::with(&key, &mut app, &$block, &$context).handle();
$handler::with(key, &mut app, $block, $context).handle();
assert_str_eq!(
app
.data
.radarr_data
.$servarr_data
.$data_ref
.current_selection()
.$field
@@ -186,12 +196,12 @@ mod test_utils {
"Test 2"
);
$handler::with(&key, &mut app, &$block, &$context).handle();
$handler::with(key, &mut app, $block, $context).handle();
assert_str_eq!(
app
.data
.radarr_data
.$servarr_data
.$data_ref
.current_selection()
.$field
@@ -204,86 +214,96 @@ mod test_utils {
#[macro_export]
macro_rules! test_iterable_home_and_end {
($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $block:expr, $context:expr) => {
#[test]
fn $func() {
let mut app = App::default();
app.data.radarr_data.$data_ref.set_items(vec![
app.push_navigation_stack($block.into());
app.data.$servarr_data.$data_ref.set_items(vec![
"Test 1".to_owned(),
"Test 2".to_owned(),
"Test 3".to_owned(),
]);
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 3");
assert_str_eq!(
app.data.$servarr_data.$data_ref.current_selection(),
"Test 3"
);
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
assert_str_eq!(app.data.radarr_data.$data_ref.current_selection(), "Test 1");
assert_str_eq!(
app.data.$servarr_data.$data_ref.current_selection(),
"Test 1"
);
}
};
($func:ident, $handler:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:ident, $block:expr, $context:expr, $field:ident) => {
#[test]
fn $func() {
let mut app = App::default();
app.push_navigation_stack($block.into());
app
.data
.radarr_data
.$servarr_data
.$data_ref
.set_items(extended_stateful_iterable_vec!($items));
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 3"
);
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1"
);
}
};
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident) => {
#[test]
fn $func() {
let mut app = App::default();
app.data.radarr_data.$data_ref.set_items($items);
app.push_navigation_stack($block.into());
app.data.$servarr_data.$data_ref.set_items($items);
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 3"
);
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
assert_str_eq!(
app.data.radarr_data.$data_ref.current_selection().$field,
app.data.$servarr_data.$data_ref.current_selection().$field,
"Test 1"
);
}
};
($func:ident, $handler:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
($func:ident, $handler:ident, $servarr_data:ident, $data_ref:ident, $items:expr, $block:expr, $context:expr, $field:ident, $conversion_fn:ident) => {
#[test]
fn $func() {
let mut app = App::default();
app.data.radarr_data.$data_ref.set_items($items);
app.push_navigation_stack($block.into());
app.data.$servarr_data.$data_ref.set_items($items);
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.end.key, &mut app, $block, $context).handle();
assert_str_eq!(
app
.data
.radarr_data
.$servarr_data
.$data_ref
.current_selection()
.$field
@@ -291,12 +311,12 @@ mod test_utils {
"Test 3"
);
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &$context).handle();
$handler::with(DEFAULT_KEYBINDINGS.home.key, &mut app, $block, $context).handle();
assert_str_eq!(
app
.data
.radarr_data
.$servarr_data
.$data_ref
.current_selection()
.$field
@@ -311,18 +331,68 @@ mod test_utils {
macro_rules! test_handler_delegation {
($handler:ident, $base:expr, $active_block:expr) => {
let mut app = App::default();
app.push_navigation_stack($base.clone().into());
app.push_navigation_stack($active_block.clone().into());
app.data.sonarr_data.history.set_items(vec![$crate::models::sonarr_models::SonarrHistoryItem::default()]);
app.data.sonarr_data.root_folders.set_items(vec![$crate::models::servarr_models::RootFolder::default()]);
app.data.sonarr_data.indexers.set_items(vec![$crate::models::servarr_models::Indexer::default()]);
app.data.sonarr_data.blocklist.set_items(vec![$crate::models::sonarr_models::BlocklistItem::default()]);
app.data.radarr_data.movies.set_items(vec![$crate::models::radarr_models::Movie::default()]);
app.data.radarr_data.collections.set_items(vec![$crate::models::radarr_models::Collection::default()]);
app.data.radarr_data.collection_movies.set_items(vec![$crate::models::radarr_models::CollectionMovie::default()]);
app.data.radarr_data.indexers.set_items(vec![$crate::models::servarr_models::Indexer::default()]);
app.data.radarr_data.root_folders.set_items(vec![$crate::models::servarr_models::RootFolder::default()]);
app.data.radarr_data.blocklist.set_items(vec![$crate::models::radarr_models::BlocklistItem::default()]);
let mut movie_details_modal = $crate::models::servarr_data::radarr::modals::MovieDetailsModal::default();
movie_details_modal
.movie_history
.set_items(vec![$crate::models::radarr_models::MovieHistoryItem::default()]);
movie_details_modal.movie_cast.set_items(vec![$crate::models::radarr_models::Credit::default()]);
movie_details_modal.movie_crew.set_items(vec![$crate::models::radarr_models::Credit::default()]);
movie_details_modal.movie_releases.set_items(vec![$crate::models::radarr_models::RadarrRelease::default()]);
app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
let mut series_history = $crate::models::stateful_table::StatefulTable::default();
series_history.set_items(vec![
$crate::models::sonarr_models::SonarrHistoryItem::default(),
]);
app.data.sonarr_data.series_history = Some(series_history);
app.data.sonarr_data.series.set_items(vec![
$crate::models::sonarr_models::Series::default(),
]);
app.push_navigation_stack($base.into());
app.push_navigation_stack($active_block.into());
$handler::with(
&DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&$active_block,
&None,
)
.handle();
$handler::with(DEFAULT_KEYBINDINGS.esc.key, &mut app, $active_block, None).handle();
assert_eq!(app.get_current_route(), &$base.into());
assert_eq!(app.get_current_route(), $base.into());
};
}
#[macro_export]
macro_rules! assert_delete_prompt {
($handler:ident, $block:expr, $expected_block:expr) => {
let mut app = App::default();
$handler::with(DELETE_KEY, &mut app, $block, None).handle();
assert_eq!(app.get_current_route(), $expected_block.into());
};
($handler:ident, $app:expr, $block:expr, $expected_block:expr) => {
$handler::with(DELETE_KEY, &mut $app, $block, None).handle();
assert_eq!($app.get_current_route(), $expected_block.into());
};
}
#[macro_export]
macro_rules! assert_refresh_key {
($handler:ident, $block:expr) => {
let mut app = App::default();
app.push_navigation_stack($block.into());
$handler::with(DEFAULT_KEYBINDINGS.refresh.key, &mut app, $block, None).handle();
assert_eq!(app.get_current_route(), $block.into());
assert!(app.should_refresh);
};
}
}
+82 -3
View File
@@ -1,10 +1,19 @@
#[cfg(test)]
mod tests {
use crate::models::radarr_models::Movie;
use crate::models::sonarr_models::Series;
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::handle_events;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle};
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::HorizontallyScrollableText;
use crate::models::Route;
#[test]
fn test_handle_clear_errors() {
@@ -17,17 +26,87 @@ mod tests {
}
#[rstest]
fn test_handle_prompt_toggle_left_right(#[values(Key::Left, Key::Right)] key: Key) {
#[case(ActiveRadarrBlock::Movies.into(), ActiveRadarrBlock::SearchMovie.into())]
#[case(ActiveSonarrBlock::Series.into(), ActiveSonarrBlock::SearchSeries.into())]
fn test_handle_events(#[case] base_block: Route, #[case] top_block: Route) {
let mut app = App::default();
app.push_navigation_stack(base_block);
app.push_navigation_stack(top_block);
app
.data
.sonarr_data
.series
.set_items(vec![Series::default()]);
app
.data
.radarr_data
.movies
.set_items(vec![Movie::default()]);
handle_events(DEFAULT_KEYBINDINGS.esc.key, &mut app);
assert_eq!(app.get_current_route(), base_block);
}
#[rstest]
#[case(0, ActiveSonarrBlock::Series, ActiveSonarrBlock::Series)]
#[case(1, ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies)]
fn test_handle_change_tabs<T>(#[case] index: usize, #[case] left_block: T, #[case] right_block: T)
where
T: Into<Route> + Copy,
{
let mut app = App::default();
app.error = "Test".into();
app.server_tabs.set_index(index);
handle_events(DEFAULT_KEYBINDINGS.previous_servarr.key, &mut app);
assert_eq!(app.server_tabs.get_active_route(), left_block.into());
assert_eq!(app.get_current_route(), left_block.into());
assert!(app.is_first_render);
assert_eq!(app.error, HorizontallyScrollableText::default());
app.server_tabs.set_index(index);
app.is_first_render = false;
app.error = "Test".into();
handle_events(DEFAULT_KEYBINDINGS.next_servarr.key, &mut app);
assert_eq!(app.server_tabs.get_active_route(), right_block.into());
assert_eq!(app.get_current_route(), right_block.into());
assert!(app.is_first_render);
assert_eq!(app.error, HorizontallyScrollableText::default());
}
#[rstest]
fn test_handle_prompt_toggle_left_right_radarr(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
assert!(!app.data.radarr_data.prompt_confirm);
handle_prompt_toggle(&mut app, &key);
handle_prompt_toggle(&mut app, key);
assert!(app.data.radarr_data.prompt_confirm);
handle_prompt_toggle(&mut app, &key);
handle_prompt_toggle(&mut app, key);
assert!(!app.data.radarr_data.prompt_confirm);
}
#[rstest]
fn test_handle_prompt_toggle_left_right_sonarr(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
assert!(!app.data.sonarr_data.prompt_confirm);
handle_prompt_toggle(&mut app, key);
assert!(app.data.sonarr_data.prompt_confirm);
handle_prompt_toggle(&mut app, key);
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
+60 -22
View File
@@ -1,4 +1,5 @@
use radarr_handlers::RadarrHandler;
use sonarr_handlers::SonarrHandler;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
@@ -6,6 +7,7 @@ use crate::event::Key;
use crate::models::{HorizontallyScrollableText, Route};
mod radarr_handlers;
mod sonarr_handlers;
#[cfg(test)]
#[path = "handlers_tests.rs"]
@@ -14,45 +16,46 @@ mod handlers_tests;
#[cfg(test)]
#[path = "handler_test_utils.rs"]
pub mod handler_test_utils;
mod table_handler;
pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
pub trait KeyEventHandler<'a, 'b, T: Into<Route> + Copy> {
fn handle_key_event(&mut self) {
let key = self.get_key();
match key {
_ if *key == DEFAULT_KEYBINDINGS.up.key => {
_ if key == DEFAULT_KEYBINDINGS.up.key => {
if self.is_ready() {
self.handle_scroll_up();
}
}
_ if *key == DEFAULT_KEYBINDINGS.down.key => {
_ if key == DEFAULT_KEYBINDINGS.down.key => {
if self.is_ready() {
self.handle_scroll_down();
}
}
_ if *key == DEFAULT_KEYBINDINGS.home.key => {
_ if key == DEFAULT_KEYBINDINGS.home.key => {
if self.is_ready() {
self.handle_home();
}
}
_ if *key == DEFAULT_KEYBINDINGS.end.key => {
_ if key == DEFAULT_KEYBINDINGS.end.key => {
if self.is_ready() {
self.handle_end();
}
}
_ if *key == DEFAULT_KEYBINDINGS.delete.key => {
_ if key == DEFAULT_KEYBINDINGS.delete.key => {
if self.is_ready() {
self.handle_delete();
}
}
_ if *key == DEFAULT_KEYBINDINGS.left.key || *key == DEFAULT_KEYBINDINGS.right.key => {
_ if key == DEFAULT_KEYBINDINGS.left.key || key == DEFAULT_KEYBINDINGS.right.key => {
self.handle_left_right_action()
}
_ if *key == DEFAULT_KEYBINDINGS.submit.key => {
_ if key == DEFAULT_KEYBINDINGS.submit.key => {
if self.is_ready() {
self.handle_submit();
}
}
_ if *key == DEFAULT_KEYBINDINGS.esc.key => self.handle_esc(),
_ if key == DEFAULT_KEYBINDINGS.esc.key => self.handle_esc(),
_ => {
if self.is_ready() {
self.handle_char_key_event();
@@ -65,9 +68,9 @@ pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
self.handle_key_event();
}
fn accepts(active_block: &'a T) -> bool;
fn with(key: &'a Key, app: &'a mut App<'b>, active_block: &'a T, context: &'a Option<T>) -> Self;
fn get_key(&self) -> &Key;
fn accepts(active_block: T) -> bool;
fn with(key: Key, app: &'a mut App<'b>, active_block: T, context: Option<T>) -> Self;
fn get_key(&self) -> Key;
fn is_ready(&self) -> bool;
fn handle_scroll_up(&mut self);
fn handle_scroll_down(&mut self);
@@ -81,8 +84,24 @@ pub trait KeyEventHandler<'a, 'b, T: Into<Route>> {
}
pub fn handle_events(key: Key, app: &mut App<'_>) {
if let Route::Radarr(active_radarr_block, context) = *app.get_current_route() {
RadarrHandler::with(&key, app, &active_radarr_block, &context).handle()
if key == DEFAULT_KEYBINDINGS.next_servarr.key {
app.reset();
app.server_tabs.next();
app.pop_and_push_navigation_stack(app.server_tabs.get_active_route());
} else if key == DEFAULT_KEYBINDINGS.previous_servarr.key {
app.reset();
app.server_tabs.previous();
app.pop_and_push_navigation_stack(app.server_tabs.get_active_route());
} else {
match app.get_current_route() {
Route::Radarr(active_radarr_block, context) => {
RadarrHandler::with(key, app, active_radarr_block, context).handle()
}
Route::Sonarr(active_sonarr_block, context) => {
SonarrHandler::with(key, app, active_sonarr_block, context).handle()
}
_ => (),
}
}
}
@@ -92,11 +111,17 @@ fn handle_clear_errors(app: &mut App<'_>) {
}
}
fn handle_prompt_toggle(app: &mut App<'_>, key: &Key) {
fn handle_prompt_toggle(app: &mut App<'_>, key: Key) {
match key {
_ if *key == DEFAULT_KEYBINDINGS.left.key || *key == DEFAULT_KEYBINDINGS.right.key => {
if let Route::Radarr(_, _) = *app.get_current_route() {
app.data.radarr_data.prompt_confirm = !app.data.radarr_data.prompt_confirm;
_ if key == DEFAULT_KEYBINDINGS.left.key || key == DEFAULT_KEYBINDINGS.right.key => {
match app.get_current_route() {
Route::Radarr(_, _) => {
app.data.radarr_data.prompt_confirm = !app.data.radarr_data.prompt_confirm
}
Route::Sonarr(_, _) => {
app.data.sonarr_data.prompt_confirm = !app.data.sonarr_data.prompt_confirm
}
_ => (),
}
}
_ => (),
@@ -107,10 +132,10 @@ fn handle_prompt_toggle(app: &mut App<'_>, key: &Key) {
macro_rules! handle_text_box_left_right_keys {
($self:expr, $key:expr, $input:expr) => {
match $self.key {
_ if *$key == DEFAULT_KEYBINDINGS.left.key => {
_ if $key == DEFAULT_KEYBINDINGS.left.key => {
$input.scroll_left();
}
_ if *$key == DEFAULT_KEYBINDINGS.right.key => {
_ if $key == DEFAULT_KEYBINDINGS.right.key => {
$input.scroll_right();
}
_ => (),
@@ -122,13 +147,26 @@ macro_rules! handle_text_box_left_right_keys {
macro_rules! handle_text_box_keys {
($self:expr, $key:expr, $input:expr) => {
match $self.key {
_ if *$key == DEFAULT_KEYBINDINGS.backspace.key => {
_ if $key == DEFAULT_KEYBINDINGS.backspace.key => {
$input.pop();
}
Key::Char(character) => {
$input.push(*character);
$input.push(character);
}
_ => (),
}
};
}
#[macro_export]
macro_rules! handle_prompt_left_right_keys {
($self:expr, $confirm_prompt:expr, $data:ident) => {
if $self.app.data.$data.selected_block.get_active_block() == $confirm_prompt {
handle_prompt_toggle($self.app, $self.key);
} else if $self.key == DEFAULT_KEYBINDINGS.left.key {
$self.app.data.$data.selected_block.left();
} else {
$self.app.data.$data.selected_block.right();
}
};
}
@@ -28,6 +28,7 @@ mod tests {
test_iterable_scroll!(
test_blocklist_scroll,
BlocklistHandler,
radarr_data,
blocklist,
simple_stateful_iterable_vec!(BlocklistItem, String, source_title),
ActiveRadarrBlock::Blocklist,
@@ -52,7 +53,7 @@ mod tests {
source_title
));
BlocklistHandler::with(&key, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
BlocklistHandler::with(key, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
assert_str_eq!(
app
@@ -65,7 +66,7 @@ mod tests {
"Test 1"
);
BlocklistHandler::with(&key, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
BlocklistHandler::with(key, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
assert_str_eq!(
app
@@ -89,13 +90,8 @@ mod tests {
if key == Key::Up {
for i in (0..blocklist_field_vec.len()).rev() {
BlocklistHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::BlocklistSortPrompt,
&None,
)
.handle();
BlocklistHandler::with(key, &mut app, ActiveRadarrBlock::BlocklistSortPrompt, None)
.handle();
assert_eq!(
app
@@ -111,13 +107,8 @@ mod tests {
}
} else {
for i in 0..blocklist_field_vec.len() {
BlocklistHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::BlocklistSortPrompt,
&None,
)
.handle();
BlocklistHandler::with(key, &mut app, ActiveRadarrBlock::BlocklistSortPrompt, None)
.handle();
assert_eq!(
app
@@ -146,6 +137,7 @@ mod tests {
test_iterable_home_and_end!(
test_blocklist_home_and_end,
BlocklistHandler,
radarr_data,
blocklist,
extended_stateful_iterable_vec!(BlocklistItem, String, source_title),
ActiveRadarrBlock::Blocklist,
@@ -169,10 +161,10 @@ mod tests {
));
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
@@ -188,10 +180,10 @@ mod tests {
);
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
@@ -214,10 +206,10 @@ mod tests {
app.data.radarr_data.blocklist.sorting(sort_options());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::BlocklistSortPrompt,
&None,
ActiveRadarrBlock::BlocklistSortPrompt,
None,
)
.handle();
@@ -234,10 +226,10 @@ mod tests {
);
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::BlocklistSortPrompt,
&None,
ActiveRadarrBlock::BlocklistSortPrompt,
None,
)
.handle();
@@ -267,11 +259,11 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::DeleteBlocklistItemPrompt.into()
ActiveRadarrBlock::DeleteBlocklistItemPrompt.into()
);
}
@@ -282,12 +274,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
}
@@ -304,21 +293,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(3);
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
ActiveRadarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
}
#[rstest]
@@ -328,20 +314,20 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(3);
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
}
@@ -356,11 +342,11 @@ mod tests {
) {
let mut app = App::default();
BlocklistHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
BlocklistHandler::with(key, &mut app, active_radarr_block, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
BlocklistHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
BlocklistHandler::with(key, &mut app, active_radarr_block, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -382,11 +368,11 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::BlocklistItemDetails.into()
ActiveRadarrBlock::BlocklistItemDetails.into()
);
}
@@ -397,12 +383,9 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
#[rstest]
@@ -427,14 +410,14 @@ mod tests {
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), &base_route.into());
assert_eq!(app.get_current_route(), base_route.into());
}
#[rstest]
@@ -450,14 +433,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
#[test]
@@ -474,17 +454,14 @@ mod tests {
expected_vec.reverse();
BlocklistHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::BlocklistSortPrompt,
&None,
ActiveRadarrBlock::BlocklistSortPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
assert_eq!(app.data.radarr_data.blocklist.items, expected_vec);
}
}
@@ -517,9 +494,9 @@ mod tests {
app.push_navigation_stack(prompt_block.into());
app.data.radarr_data.prompt_confirm = true;
BlocklistHandler::with(&ESC_KEY, &mut app, &prompt_block, &None).handle();
BlocklistHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
assert_eq!(app.get_current_route(), &base_block.into());
assert_eq!(app.get_current_route(), base_block.into());
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -530,37 +507,32 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::BlocklistItemDetails.into());
BlocklistHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::BlocklistItemDetails,
&None,
ActiveRadarrBlock::BlocklistItemDetails,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
#[test]
fn test_blocklist_sort_prompt_block_esc() {
let mut app = App::default();
app.data.radarr_data.blocklist.set_items(vec![BlocklistItem::default()]);
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveRadarrBlock::BlocklistSortPrompt.into());
BlocklistHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::BlocklistSortPrompt,
&None,
ActiveRadarrBlock::BlocklistSortPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
#[rstest]
@@ -571,12 +543,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
DownloadsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle();
DownloadsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
assert!(app.error.text.is_empty());
}
}
@@ -596,17 +565,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
assert!(app.should_refresh);
}
@@ -618,17 +584,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
assert!(!app.should_refresh);
}
@@ -638,16 +601,16 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.clear.key,
DEFAULT_KEYBINDINGS.clear.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into()
ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into()
);
}
@@ -659,35 +622,33 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.clear.key,
DEFAULT_KEYBINDINGS.clear.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
#[test]
fn test_sort_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into());
app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.sort.key,
DEFAULT_KEYBINDINGS.sort.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::BlocklistSortPrompt.into()
ActiveRadarrBlock::BlocklistSortPrompt.into()
);
assert_eq!(
app.data.radarr_data.blocklist.sort.as_ref().unwrap().items,
@@ -704,17 +665,14 @@ mod tests {
app.data.radarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.sort.key,
DEFAULT_KEYBINDINGS.sort.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
assert!(app.data.radarr_data.blocklist.sort.is_none());
assert!(!app.data.radarr_data.blocklist.sort_asc);
}
@@ -741,10 +699,10 @@ mod tests {
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&prompt_block,
&None,
prompt_block,
None,
)
.handle();
@@ -753,7 +711,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), &base_route.into());
assert_eq!(app.get_current_route(), base_route.into());
}
}
@@ -896,9 +854,9 @@ mod tests {
fn test_blocklist_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if BLOCKLIST_BLOCKS.contains(&active_radarr_block) {
assert!(BlocklistHandler::accepts(&active_radarr_block));
assert!(BlocklistHandler::accepts(active_radarr_block));
} else {
assert!(!BlocklistHandler::accepts(&active_radarr_block));
assert!(!BlocklistHandler::accepts(active_radarr_block));
}
})
}
@@ -909,10 +867,10 @@ mod tests {
app.is_loading = true;
let handler = BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
);
assert!(!handler.is_ready());
@@ -924,10 +882,10 @@ mod tests {
app.is_loading = false;
let handler = BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
);
assert!(!handler.is_ready());
@@ -944,10 +902,10 @@ mod tests {
.set_items(vec![BlocklistItem::default()]);
let handler = BlocklistHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Blocklist,
&None,
ActiveRadarrBlock::Blocklist,
None,
);
assert!(handler.is_ready());
+42 -98
View File
@@ -1,7 +1,9 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::BlocklistItem;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS};
@@ -14,22 +16,43 @@ use crate::network::radarr_network::RadarrEvent;
mod blocklist_handler_tests;
pub(super) struct BlocklistHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> BlocklistHandler<'a, 'b> {
handle_table_events!(
self,
blocklist,
self.app.data.radarr_data.blocklist,
BlocklistItem
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
BLOCKLIST_BLOCKS.contains(active_block)
fn handle(&mut self) {
let blocklist_table_handling_props =
TableHandlingProps::new(ActiveRadarrBlock::Blocklist.into())
.sorting_block(ActiveRadarrBlock::BlocklistSortPrompt.into())
.sort_by_fn(|a: &BlocklistItem, b: &BlocklistItem| a.id.cmp(&b.id))
.sort_options(blocklist_sorting_options());
if !self.handle_blocklist_table_events(blocklist_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveRadarrBlock) -> bool {
BLOCKLIST_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> Self {
BlocklistHandler {
key,
@@ -39,7 +62,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -47,72 +70,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
!self.app.is_loading && !self.app.data.radarr_data.blocklist.is_empty()
}
fn handle_scroll_up(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_up(),
ActiveRadarrBlock::BlocklistSortPrompt => self
.app
.data
.radarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_down(),
ActiveRadarrBlock::BlocklistSortPrompt => self
.app
.data
.radarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_to_top(),
ActiveRadarrBlock::BlocklistSortPrompt => self
.app
.data
.radarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_to_bottom(),
ActiveRadarrBlock::BlocklistSortPrompt => self
.app
.data
.radarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
}
}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Blocklist {
if self.active_radarr_block == ActiveRadarrBlock::Blocklist {
self
.app
.push_navigation_stack(ActiveRadarrBlock::DeleteBlocklistItemPrompt.into());
@@ -145,18 +112,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::BlocklistSortPrompt => {
self
.app
.data
.radarr_data
.blocklist
.items
.sort_by(|a, b| a.id.cmp(&b.id));
self.app.data.radarr_data.blocklist.apply_sorting();
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::Blocklist => {
self
.app
@@ -173,7 +128,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false;
}
ActiveRadarrBlock::BlocklistItemDetails | ActiveRadarrBlock::BlocklistSortPrompt => {
ActiveRadarrBlock::BlocklistItemDetails => {
self.app.pop_navigation_stack();
}
_ => handle_clear_errors(self.app),
@@ -184,29 +139,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
let key = self.key;
match self.active_radarr_block {
ActiveRadarrBlock::Blocklist => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if *key == DEFAULT_KEYBINDINGS.clear.key => {
_ if key == DEFAULT_KEYBINDINGS.clear.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::BlocklistClearAllItemsPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.blocklist
.sorting(blocklist_sorting_options());
self
.app
.push_navigation_stack(ActiveRadarrBlock::BlocklistSortPrompt.into());
}
_ => (),
},
ActiveRadarrBlock::DeleteBlocklistItemPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteBlocklistItem(None));
@@ -215,7 +159,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
}
}
ActiveRadarrBlock::BlocklistClearAllItemsPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::ClearBlocklist);
@@ -1,7 +1,10 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::CollectionMovie;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, ADD_MOVIE_SELECTION_BLOCKS, COLLECTION_DETAILS_BLOCKS,
EDIT_COLLECTION_SELECTION_BLOCKS,
@@ -14,22 +17,40 @@ use crate::models::{BlockSelectionState, Scrollable};
mod collection_details_handler_tests;
pub(super) struct CollectionDetailsHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> CollectionDetailsHandler<'a, 'b> {
handle_table_events!(
self,
collection_movies,
self.app.data.radarr_data.collection_movies,
CollectionMovie
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
COLLECTION_DETAILS_BLOCKS.contains(active_block)
fn handle(&mut self) {
let collection_movies_table_handling_props =
TableHandlingProps::new(ActiveRadarrBlock::CollectionDetails.into());
if !self.handle_collection_movies_table_events(collection_movies_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveRadarrBlock) -> bool {
COLLECTION_DETAILS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> CollectionDetailsHandler<'a, 'b> {
CollectionDetailsHandler {
key,
@@ -39,7 +60,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -47,41 +68,20 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
!self.app.is_loading && !self.app.data.radarr_data.collection_movies.is_empty()
}
fn handle_scroll_up(&mut self) {
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
self.app.data.radarr_data.collection_movies.scroll_up()
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
self.app.data.radarr_data.collection_movies.scroll_down()
}
}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
self.app.data.radarr_data.collection_movies.scroll_to_top();
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
self
.app
.data
.radarr_data
.collection_movies
.scroll_to_bottom();
}
}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {}
fn handle_submit(&mut self) {
if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block {
if ActiveRadarrBlock::CollectionDetails == self.active_radarr_block {
let tmdb_id = self
.app
.data
@@ -111,7 +111,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
.into(),
);
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
self.app.data.radarr_data.add_movie_modal = Some((&self.app.data.radarr_data).into());
}
}
@@ -129,19 +129,19 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan
}
fn handle_char_key_event(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::CollectionDetails
&& *self.key == DEFAULT_KEYBINDINGS.edit.key
if self.active_radarr_block == ActiveRadarrBlock::CollectionDetails
&& self.key == DEFAULT_KEYBINDINGS.edit.key
{
self.app.push_navigation_stack(
(
ActiveRadarrBlock::EditCollectionPrompt,
Some(*self.active_radarr_block),
Some(self.active_radarr_block),
)
.into(),
);
self.app.data.radarr_data.edit_collection_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
}
}
}
@@ -24,6 +24,7 @@ mod tests {
test_iterable_scroll!(
test_collection_details_scroll,
CollectionDetailsHandler,
radarr_data,
collection_movies,
simple_stateful_iterable_vec!(CollectionMovie, HorizontallyScrollableText),
ActiveRadarrBlock::CollectionDetails,
@@ -50,7 +51,7 @@ mod tests {
HorizontallyScrollableText
));
CollectionDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::CollectionDetails, &None)
CollectionDetailsHandler::with(key, &mut app, ActiveRadarrBlock::CollectionDetails, None)
.handle();
assert_str_eq!(
@@ -64,7 +65,7 @@ mod tests {
"Test 1"
);
CollectionDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::CollectionDetails, &None)
CollectionDetailsHandler::with(key, &mut app, ActiveRadarrBlock::CollectionDetails, None)
.handle();
assert_str_eq!(
@@ -88,6 +89,7 @@ mod tests {
test_iterable_home_and_end!(
test_collection_details_home_end,
CollectionDetailsHandler,
radarr_data,
collection_movies,
extended_stateful_iterable_vec!(CollectionMovie, HorizontallyScrollableText),
ActiveRadarrBlock::CollectionDetails,
@@ -110,10 +112,10 @@ mod tests {
));
CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
)
.handle();
@@ -129,10 +131,10 @@ mod tests {
);
CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
)
.handle();
@@ -171,24 +173,24 @@ mod tests {
.set_items(vec![CollectionMovie::default()]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1, "B - Test 2".to_owned()), (0, "A - Test 1".to_owned())]);
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
.set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
CollectionDetailsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&(
(
ActiveRadarrBlock::AddMoviePrompt,
Some(ActiveRadarrBlock::CollectionDetails)
)
@@ -205,7 +207,7 @@ mod tests {
.is_empty());
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::AddMovieSelectRootFolder
ActiveRadarrBlock::AddMovieSelectRootFolder
);
assert!(!app
.data
@@ -250,16 +252,16 @@ mod tests {
.set_items(vec![CollectionMovie::default()]);
CollectionDetailsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::CollectionDetails.into()
ActiveRadarrBlock::CollectionDetails.into()
);
assert!(app.data.radarr_data.add_movie_modal.is_none());
}
@@ -279,16 +281,16 @@ mod tests {
.set_items(vec![Movie::default()]);
CollectionDetailsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::ViewMovieOverview.into()
ActiveRadarrBlock::ViewMovieOverview.into()
);
}
}
@@ -313,16 +315,16 @@ mod tests {
.set_items(vec![CollectionMovie::default()]);
CollectionDetailsHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
assert!(app.data.radarr_data.collection_movies.items.is_empty());
}
@@ -334,16 +336,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::ViewMovieOverview.into());
CollectionDetailsHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::ViewMovieOverview,
&None,
ActiveRadarrBlock::ViewMovieOverview,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::CollectionDetails.into()
ActiveRadarrBlock::CollectionDetails.into()
);
}
}
@@ -388,16 +390,16 @@ mod tests {
app.data.radarr_data = radarr_data;
CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.edit.key,
DEFAULT_KEYBINDINGS.edit.key,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::CollectionDetails.into()
ActiveRadarrBlock::CollectionDetails.into()
);
assert!(app.data.radarr_data.edit_collection_modal.is_none());
}
@@ -407,9 +409,9 @@ mod tests {
fn test_collection_details_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) {
assert!(CollectionDetailsHandler::accepts(&active_radarr_block));
assert!(CollectionDetailsHandler::accepts(active_radarr_block));
} else {
assert!(!CollectionDetailsHandler::accepts(&active_radarr_block));
assert!(!CollectionDetailsHandler::accepts(active_radarr_block));
}
});
}
@@ -420,10 +422,10 @@ mod tests {
app.is_loading = true;
let handler = CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
);
assert!(!handler.is_ready());
@@ -435,10 +437,10 @@ mod tests {
app.is_loading = false;
let handler = CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
);
assert!(!handler.is_ready());
@@ -455,10 +457,10 @@ mod tests {
.set_items(vec![CollectionMovie::default()]);
let handler = CollectionDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::CollectionDetails,
&None,
ActiveRadarrBlock::CollectionDetails,
None,
);
assert!(handler.is_ready());
File diff suppressed because it is too large Load Diff
@@ -12,22 +12,22 @@ use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod edit_collection_handler_tests;
pub(super) struct EditCollectionHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
EDIT_COLLECTION_BLOCKS.contains(active_block)
fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_COLLECTION_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> EditCollectionHandler<'a, 'b> {
EditCollectionHandler {
key,
@@ -37,7 +37,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -65,9 +65,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
.unwrap()
.quality_profile_list
.scroll_up(),
ActiveRadarrBlock::EditCollectionPrompt => {
self.app.data.radarr_data.selected_block.previous()
}
ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.up(),
_ => (),
}
}
@@ -92,7 +90,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
.unwrap()
.quality_profile_list
.scroll_down(),
ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.next(),
ActiveRadarrBlock::EditCollectionPrompt => self.app.data.radarr_data.selected_block.down(),
_ => (),
}
}
@@ -203,8 +201,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
| ActiveRadarrBlock::EditCollectionSelectQualityProfile => {
self.app.push_navigation_stack(
(
*self.app.data.radarr_data.selected_block.get_active_block(),
*self.context,
self.app.data.radarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
)
@@ -212,8 +210,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
ActiveRadarrBlock::EditCollectionRootFolderPathInput => {
self.app.push_navigation_stack(
(
*self.app.data.radarr_data.selected_block.get_active_block(),
*self.context,
self.app.data.radarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
);
@@ -308,8 +306,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
}
ActiveRadarrBlock::EditCollectionPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::EditCollectionConfirmPrompt
&& *key == DEFAULT_KEYBINDINGS.confirm.key
== ActiveRadarrBlock::EditCollectionConfirmPrompt
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(None));
@@ -44,10 +44,10 @@ mod tests {
if key == Key::Up {
for i in (0..minimum_availability_vec.len()).rev() {
EditCollectionHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
None,
)
.handle();
@@ -66,10 +66,10 @@ mod tests {
} else {
for i in 0..minimum_availability_vec.len() {
EditCollectionHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
None,
)
.handle();
@@ -104,10 +104,10 @@ mod tests {
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
EditCollectionHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
None,
)
.handle();
@@ -124,10 +124,10 @@ mod tests {
);
EditCollectionHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
None,
)
.handle();
@@ -149,26 +149,21 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
EditCollectionHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
.handle();
if key == Key::Up {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionToggleMonitored
ActiveRadarrBlock::EditCollectionToggleMonitored
);
} else {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionSelectQualityProfile
ActiveRadarrBlock::EditCollectionSelectQualityProfile
);
}
}
@@ -181,20 +176,15 @@ mod tests {
app.is_loading = true;
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
EditCollectionHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
.handle();
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability
);
}
}
@@ -224,10 +214,10 @@ mod tests {
.set_items(minimum_availability_vec.clone());
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
None,
)
.handle();
@@ -244,10 +234,10 @@ mod tests {
);
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
None,
)
.handle();
@@ -282,10 +272,10 @@ mod tests {
]);
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
None,
)
.handle();
@@ -302,10 +292,10 @@ mod tests {
);
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::EditCollectionSelectQualityProfile,
&None,
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
None,
)
.handle();
@@ -331,10 +321,10 @@ mod tests {
});
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
@@ -352,10 +342,10 @@ mod tests {
);
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
@@ -386,23 +376,13 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
EditCollectionHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
.handle();
assert!(app.data.radarr_data.prompt_confirm);
EditCollectionHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
)
.handle();
EditCollectionHandler::with(key, &mut app, ActiveRadarrBlock::EditCollectionPrompt, None)
.handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -416,10 +396,10 @@ mod tests {
});
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
@@ -437,10 +417,10 @@ mod tests {
);
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
@@ -484,10 +464,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
@@ -503,7 +483,7 @@ mod tests {
.is_empty());
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into()
ActiveRadarrBlock::EditCollectionPrompt.into()
);
}
@@ -514,24 +494,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
}
@@ -544,24 +524,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
@@ -579,24 +559,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into()
ActiveRadarrBlock::EditCollectionPrompt.into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
@@ -611,18 +591,18 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app.push_navigation_stack(current_route);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(
app
.data
@@ -635,14 +615,14 @@ mod tests {
);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(
app
.data
@@ -664,23 +644,23 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 2);
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 2);
app.push_navigation_stack(current_route);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(
app
.data
@@ -693,14 +673,14 @@ mod tests {
);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(
app
.data
@@ -731,20 +711,20 @@ mod tests {
.into(),
);
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(0, index);
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&Some(ActiveRadarrBlock::Collections),
ActiveRadarrBlock::EditCollectionPrompt,
Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(
app.get_current_route(),
&(selected_block, Some(ActiveRadarrBlock::Collections)).into()
(selected_block, Some(ActiveRadarrBlock::Collections)).into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
@@ -768,16 +748,16 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into());
EditCollectionHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&active_radarr_block,
&Some(ActiveRadarrBlock::Collections),
active_radarr_block,
Some(ActiveRadarrBlock::Collections),
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into()
ActiveRadarrBlock::EditCollectionPrompt.into()
);
if active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput {
@@ -806,17 +786,17 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into());
EditCollectionHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditCollectionPrompt.into()
ActiveRadarrBlock::EditCollectionPrompt.into()
);
}
@@ -828,16 +808,16 @@ mod tests {
app.data.radarr_data = create_test_radarr_data();
EditCollectionHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
let radarr_data = &app.data.radarr_data;
@@ -860,11 +840,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(active_radarr_block.into());
EditCollectionHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
EditCollectionHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
}
}
@@ -890,10 +870,10 @@ mod tests {
});
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
@@ -916,10 +896,10 @@ mod tests {
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
EditCollectionHandler::with(
&Key::Char('h'),
Key::Char('h'),
&mut app,
&ActiveRadarrBlock::EditCollectionRootFolderPathInput,
&None,
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
None,
)
.handle();
@@ -943,24 +923,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
.set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
@@ -974,9 +954,9 @@ mod tests {
fn test_edit_collection_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if EDIT_COLLECTION_BLOCKS.contains(&active_radarr_block) {
assert!(EditCollectionHandler::accepts(&active_radarr_block));
assert!(EditCollectionHandler::accepts(active_radarr_block));
} else {
assert!(!EditCollectionHandler::accepts(&active_radarr_block));
assert!(!EditCollectionHandler::accepts(active_radarr_block));
}
});
}
@@ -987,10 +967,10 @@ mod tests {
app.is_loading = true;
let handler = EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
);
assert!(!handler.is_ready());
@@ -1002,10 +982,10 @@ mod tests {
app.is_loading = false;
let handler = EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
);
assert!(!handler.is_ready());
@@ -1018,10 +998,10 @@ mod tests {
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
let handler = EditCollectionHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::EditCollectionPrompt,
&None,
ActiveRadarrBlock::EditCollectionPrompt,
None,
);
assert!(handler.is_ready());
+57 -269
View File
@@ -1,18 +1,19 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::radarr_handlers::collections::collection_details_handler::CollectionDetailsHandler;
use crate::handlers::radarr_handlers::collections::edit_collection_handler::EditCollectionHandler;
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::Collection;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, COLLECTIONS_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
};
use crate::models::stateful_table::SortOption;
use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable};
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod collection_details_handler;
mod edit_collection_handler;
@@ -22,38 +23,66 @@ mod edit_collection_handler;
mod collections_handler_tests;
pub(super) struct CollectionsHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> CollectionsHandler<'a, 'b> {
handle_table_events!(
self,
collections,
self.app.data.radarr_data.collections,
Collection
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'a, 'b> {
fn handle(&mut self) {
match self.active_radarr_block {
_ if CollectionDetailsHandler::accepts(self.active_radarr_block) => {
CollectionDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
let collections_table_handling_props =
TableHandlingProps::new(ActiveRadarrBlock::Collections.into())
.sorting_block(ActiveRadarrBlock::CollectionsSortPrompt.into())
.sort_by_fn(|a: &Collection, b: &Collection| a.id.cmp(&b.id))
.sort_options(collections_sorting_options())
.searching_block(ActiveRadarrBlock::SearchCollection.into())
.search_error_block(ActiveRadarrBlock::SearchCollectionError.into())
.search_field_fn(|collection| &collection.title.text)
.filtering_block(ActiveRadarrBlock::FilterCollections.into())
.filter_error_block(ActiveRadarrBlock::FilterCollectionsError.into())
.filter_field_fn(|collection| &collection.title.text);
if !self.handle_collections_table_events(collections_table_handling_props) {
match self.active_radarr_block {
_ if CollectionDetailsHandler::accepts(self.active_radarr_block) => {
CollectionDetailsHandler::with(
self.key,
self.app,
self.active_radarr_block,
self.context,
)
.handle();
}
_ if EditCollectionHandler::accepts(self.active_radarr_block) => {
EditCollectionHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
}
_ if EditCollectionHandler::accepts(self.active_radarr_block) => {
EditCollectionHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
}
}
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
CollectionDetailsHandler::accepts(active_block)
|| EditCollectionHandler::accepts(active_block)
|| COLLECTIONS_BLOCKS.contains(active_block)
|| COLLECTIONS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> CollectionsHandler<'a, 'b> {
CollectionsHandler {
key,
@@ -63,7 +92,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -71,105 +100,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
!self.app.is_loading && !self.app.data.radarr_data.collections.is_empty()
}
fn handle_scroll_up(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_up(),
ActiveRadarrBlock::CollectionsSortPrompt => self
.app
.data
.radarr_data
.collections
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_down(),
ActiveRadarrBlock::CollectionsSortPrompt => self
.app
.data
.radarr_data
.collections
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_to_top(),
ActiveRadarrBlock::SearchCollection => self
.app
.data
.radarr_data
.collections
.search
.as_mut()
.unwrap()
.scroll_home(),
ActiveRadarrBlock::FilterCollections => self
.app
.data
.radarr_data
.collections
.filter
.as_mut()
.unwrap()
.scroll_home(),
ActiveRadarrBlock::CollectionsSortPrompt => self
.app
.data
.radarr_data
.collections
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_to_bottom(),
ActiveRadarrBlock::SearchCollection => self
.app
.data
.radarr_data
.collections
.search
.as_mut()
.unwrap()
.reset_offset(),
ActiveRadarrBlock::FilterCollections => self
.app
.data
.radarr_data
.collections
.filter
.as_mut()
.unwrap()
.reset_offset(),
ActiveRadarrBlock::CollectionsSortPrompt => self
.app
.data
.radarr_data
.collections
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
}
}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
@@ -177,34 +114,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
match self.active_radarr_block {
ActiveRadarrBlock::Collections => handle_change_tab_left_right_keys(self.app, self.key),
ActiveRadarrBlock::UpdateAllCollectionsPrompt => handle_prompt_toggle(self.app, self.key),
ActiveRadarrBlock::SearchCollection => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.radarr_data
.collections
.search
.as_mut()
.unwrap()
)
}
ActiveRadarrBlock::FilterCollections => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.radarr_data
.collections
.filter
.as_mut()
.unwrap()
)
}
_ => (),
}
}
@@ -214,44 +123,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
ActiveRadarrBlock::Collections => self
.app
.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()),
ActiveRadarrBlock::SearchCollection => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
if self.app.data.radarr_data.collections.search.is_some() {
let has_match = self
.app
.data
.radarr_data
.collections
.apply_search(|collection| &collection.title.text);
if !has_match {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SearchCollectionError.into());
}
}
}
ActiveRadarrBlock::FilterCollections => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
if self.app.data.radarr_data.collections.filter.is_some() {
let has_matches = self
.app
.data
.radarr_data
.collections
.apply_filter(|collection| &collection.title.text);
if !has_matches {
self
.app
.push_navigation_stack(ActiveRadarrBlock::FilterCollectionsError.into());
}
}
}
ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections);
@@ -259,44 +130,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::CollectionsSortPrompt => {
self
.app
.data
.radarr_data
.collections
.items
.sort_by(|a, b| a.id.cmp(&b.id));
self.app.data.radarr_data.collections.apply_sorting();
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::FilterCollections | ActiveRadarrBlock::FilterCollectionsError => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.collections.reset_filter();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::SearchCollection | ActiveRadarrBlock::SearchCollectionError => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.collections.reset_search();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false;
}
ActiveRadarrBlock::CollectionsSortPrompt => {
self.app.pop_navigation_stack();
}
_ => {
self.app.data.radarr_data.collections.reset_search();
self.app.data.radarr_data.collections.reset_filter();
handle_clear_errors(self.app);
}
}
@@ -306,24 +150,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
let key = self.key;
match self.active_radarr_block {
ActiveRadarrBlock::Collections => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.search.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SearchCollection.into());
self.app.data.radarr_data.collections.search =
Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if *key == DEFAULT_KEYBINDINGS.filter.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::FilterCollections.into());
self.app.data.radarr_data.collections.reset_filter();
self.app.data.radarr_data.collections.filter =
Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if *key == DEFAULT_KEYBINDINGS.edit.key => {
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveRadarrBlock::EditCollectionPrompt,
@@ -334,59 +161,20 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
self.app.data.radarr_data.edit_collection_modal =
Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_COLLECTION_SELECTION_BLOCKS);
}
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.collections
.sorting(collections_sorting_options());
self
.app
.push_navigation_stack(ActiveRadarrBlock::CollectionsSortPrompt.into());
}
_ => (),
},
ActiveRadarrBlock::SearchCollection => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.radarr_data
.collections
.search
.as_mut()
.unwrap()
)
}
ActiveRadarrBlock::FilterCollections => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.radarr_data
.collections
.filter
.as_mut()
.unwrap()
)
}
ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections);
@@ -22,6 +22,7 @@ mod tests {
test_iterable_scroll!(
test_downloads_scroll,
DownloadsHandler,
radarr_data,
downloads,
DownloadRecord,
ActiveRadarrBlock::Downloads,
@@ -44,14 +45,14 @@ mod tests {
.downloads
.set_items(simple_stateful_iterable_vec!(DownloadRecord));
DownloadsHandler::with(&key, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
DownloadsHandler::with(key, &mut app, ActiveRadarrBlock::Downloads, None).handle();
assert_str_eq!(
app.data.radarr_data.downloads.current_selection().title,
"Test 1"
);
DownloadsHandler::with(&key, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
DownloadsHandler::with(key, &mut app, ActiveRadarrBlock::Downloads, None).handle();
assert_str_eq!(
app.data.radarr_data.downloads.current_selection().title,
@@ -69,6 +70,7 @@ mod tests {
test_iterable_home_and_end!(
test_downloads_home_end,
DownloadsHandler,
radarr_data,
downloads,
DownloadRecord,
ActiveRadarrBlock::Downloads,
@@ -87,10 +89,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(DownloadRecord));
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
@@ -100,10 +102,10 @@ mod tests {
);
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
@@ -130,11 +132,11 @@ mod tests {
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Downloads, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::DeleteDownloadPrompt.into()
ActiveRadarrBlock::DeleteDownloadPrompt.into()
);
}
@@ -149,12 +151,9 @@ mod tests {
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Downloads, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
}
}
@@ -171,20 +170,20 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(2);
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Collections.into()
ActiveRadarrBlock::Collections.into()
);
}
@@ -195,21 +194,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(2);
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
#[rstest]
@@ -223,11 +219,11 @@ mod tests {
) {
let mut app = App::default();
DownloadsHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
DownloadsHandler::with(key, &mut app, active_radarr_block, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
DownloadsHandler::with(&key, &mut app, &active_radarr_block, &None).handle();
DownloadsHandler::with(key, &mut app, active_radarr_block, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -269,14 +265,14 @@ mod tests {
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
DownloadsHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), &base_route.into());
assert_eq!(app.get_current_route(), base_route.into());
}
#[rstest]
@@ -295,11 +291,11 @@ mod tests {
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(&SUBMIT_KEY, &mut app, &prompt_block, &None).handle();
DownloadsHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), &base_route.into());
assert_eq!(app.get_current_route(), base_route.into());
}
}
@@ -323,9 +319,9 @@ mod tests {
app.push_navigation_stack(prompt_block.into());
app.data.radarr_data.prompt_confirm = true;
DownloadsHandler::with(&ESC_KEY, &mut app, &prompt_block, &None).handle();
DownloadsHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
assert_eq!(app.get_current_route(), &base_block.into());
assert_eq!(app.get_current_route(), base_block.into());
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -337,12 +333,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
DownloadsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle();
DownloadsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::Downloads, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
assert!(app.error.text.is_empty());
}
}
@@ -365,16 +358,16 @@ mod tests {
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::UpdateDownloadsPrompt.into()
ActiveRadarrBlock::UpdateDownloadsPrompt.into()
);
}
@@ -390,17 +383,14 @@ mod tests {
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
}
#[test]
@@ -414,17 +404,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Downloads.into());
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
assert!(app.should_refresh);
}
@@ -440,17 +427,14 @@ mod tests {
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Downloads.into());
assert!(!app.should_refresh);
}
@@ -480,10 +464,10 @@ mod tests {
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&prompt_block,
&None,
prompt_block,
None,
)
.handle();
@@ -492,7 +476,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), &base_route.into());
assert_eq!(app.get_current_route(), base_route.into());
}
}
@@ -500,9 +484,9 @@ mod tests {
fn test_downloads_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if DOWNLOADS_BLOCKS.contains(&active_radarr_block) {
assert!(DownloadsHandler::accepts(&active_radarr_block));
assert!(DownloadsHandler::accepts(active_radarr_block));
} else {
assert!(!DownloadsHandler::accepts(&active_radarr_block));
assert!(!DownloadsHandler::accepts(active_radarr_block));
}
})
}
@@ -513,10 +497,10 @@ mod tests {
app.is_loading = true;
let handler = DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
);
assert!(!handler.is_ready());
@@ -528,10 +512,10 @@ mod tests {
app.is_loading = false;
let handler = DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
);
assert!(!handler.is_ready());
@@ -548,10 +532,10 @@ mod tests {
.downloads
.set_items(vec![DownloadRecord::default()]);
let handler = DownloadsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Downloads,
&None,
ActiveRadarrBlock::Downloads,
None,
);
assert!(handler.is_ready());
+39 -34
View File
@@ -1,8 +1,11 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::DownloadRecord;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS};
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent;
@@ -12,22 +15,40 @@ use crate::network::radarr_network::RadarrEvent;
mod downloads_handler_tests;
pub(super) struct DownloadsHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> DownloadsHandler<'a, 'b> {
handle_table_events!(
self,
downloads,
self.app.data.radarr_data.downloads,
DownloadRecord
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
DOWNLOADS_BLOCKS.contains(active_block)
fn handle(&mut self) {
let downloads_table_handling_props =
TableHandlingProps::new(ActiveRadarrBlock::Downloads.into());
if !self.handle_downloads_table_events(downloads_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveRadarrBlock) -> bool {
DOWNLOADS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> DownloadsHandler<'a, 'b> {
DownloadsHandler {
key,
@@ -37,7 +58,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -45,32 +66,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
!self.app.is_loading && !self.app.data.radarr_data.downloads.is_empty()
}
fn handle_scroll_up(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
self.app.data.radarr_data.downloads.scroll_up()
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
self.app.data.radarr_data.downloads.scroll_down()
}
}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
self.app.data.radarr_data.downloads.scroll_to_top()
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
self.app.data.radarr_data.downloads.scroll_to_bottom()
}
}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Downloads {
if self.active_radarr_block == ActiveRadarrBlock::Downloads {
self
.app
.push_navigation_stack(ActiveRadarrBlock::DeleteDownloadPrompt.into())
@@ -121,18 +126,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
let key = self.key;
match self.active_radarr_block {
ActiveRadarrBlock::Downloads => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ => (),
},
ActiveRadarrBlock::DeleteDownloadPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None));
@@ -140,7 +145,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
}
}
ActiveRadarrBlock::UpdateDownloadsPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateDownloads);
@@ -4,29 +4,29 @@ 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};
use crate::{handle_prompt_left_right_keys, 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,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: 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 accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_INDEXER_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> EditIndexerHandler<'a, 'b> {
EditIndexerHandler {
key,
@@ -36,7 +36,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -45,14 +45,42 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
}
fn handle_scroll_up(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt {
self.app.data.radarr_data.selected_block.previous();
match self.active_radarr_block {
ActiveRadarrBlock::EditIndexerPrompt => {
self.app.data.radarr_data.selected_block.up();
}
ActiveRadarrBlock::EditIndexerPriorityInput => {
self
.app
.data
.radarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.priority += 1;
}
_ => (),
}
}
fn handle_scroll_down(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt {
self.app.data.radarr_data.selected_block.next();
match self.active_radarr_block {
ActiveRadarrBlock::EditIndexerPrompt => {
self.app.data.radarr_data.selected_block.down();
}
ActiveRadarrBlock::EditIndexerPriorityInput => {
let edit_indexer_modal = self
.app
.data
.radarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
if edit_indexer_modal.priority > 0 {
edit_indexer_modal.priority -= 1;
}
}
_ => (),
}
}
@@ -183,15 +211,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
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;
}
handle_prompt_left_right_keys!(
self,
ActiveRadarrBlock::EditIndexerConfirmPrompt,
radarr_data
);
}
ActiveRadarrBlock::EditIndexerNameInput => {
handle_text_box_left_right_keys!(
@@ -270,7 +294,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
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();
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;
@@ -291,6 +315,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
self.app.push_navigation_stack(selected_block.into());
self.app.should_ignore_quit_key = true;
}
ActiveRadarrBlock::EditIndexerPriorityInput => self
.app
.push_navigation_stack(ActiveRadarrBlock::EditIndexerPriorityInput.into()),
ActiveRadarrBlock::EditIndexerToggleEnableRss => {
let indexer = self
.app
@@ -334,6 +361,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::EditIndexerPriorityInput => self.app.pop_navigation_stack(),
_ => (),
}
}
@@ -349,6 +377,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
| ActiveRadarrBlock::EditIndexerUrlInput
| ActiveRadarrBlock::EditIndexerApiKeyInput
| ActiveRadarrBlock::EditIndexerSeedRatioInput
| ActiveRadarrBlock::EditIndexerPriorityInput
| ActiveRadarrBlock::EditIndexerTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
@@ -431,8 +460,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
}
ActiveRadarrBlock::EditIndexerPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::EditIndexerConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key
== ActiveRadarrBlock::EditIndexerConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer(None));
File diff suppressed because it is too large Load Diff
@@ -6,29 +6,29 @@ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS,
};
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
use crate::{handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys};
#[cfg(test)]
#[path = "edit_indexer_settings_handler_tests.rs"]
mod edit_indexer_settings_handler_tests;
pub(super) struct IndexerSettingsHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
INDEXER_SETTINGS_BLOCKS.contains(active_block)
fn accepts(active_block: ActiveRadarrBlock) -> bool {
INDEXER_SETTINGS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> IndexerSettingsHandler<'a, 'b> {
IndexerSettingsHandler {
key,
@@ -38,7 +38,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -50,7 +50,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
match self.active_radarr_block {
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
self.app.data.radarr_data.selected_block.previous();
self.app.data.radarr_data.selected_block.up();
}
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
indexer_settings.minimum_age += 1;
@@ -75,7 +75,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
match self.active_radarr_block {
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
self.app.data.radarr_data.selected_block.next()
self.app.data.radarr_data.selected_block.down()
}
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
if indexer_settings.minimum_age > 0 {
@@ -105,7 +105,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
}
fn handle_home(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
if self.active_radarr_block == ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
self
.app
.data
@@ -119,7 +119,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
}
fn handle_end(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
if self.active_radarr_block == ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput {
self
.app
.data
@@ -137,15 +137,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
fn handle_left_right_action(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt
{
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;
}
handle_prompt_left_right_keys!(
self,
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
radarr_data
);
}
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput => {
handle_text_box_left_right_keys!(
@@ -187,7 +183,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
| ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput => {
self.app.push_navigation_stack(
(
*self.app.data.radarr_data.selected_block.get_active_block(),
self.app.data.radarr_data.selected_block.get_active_block(),
None,
)
.into(),
@@ -258,8 +254,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
}
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key
== ActiveRadarrBlock::IndexerSettingsConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
@@ -27,7 +27,7 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(&$key, &mut app, &$block, &None).handle();
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
if $key == Key::Up {
assert_eq!(
@@ -64,7 +64,7 @@ mod tests {
0
);
IndexerSettingsHandler::with(&Key::Up, &mut app, &$block, &None).handle();
IndexerSettingsHandler::with(Key::Up, &mut app, $block, None).handle();
assert_eq!(
app
@@ -77,7 +77,7 @@ mod tests {
1
);
IndexerSettingsHandler::with(&$key, &mut app, &$block, &None).handle();
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
assert_eq!(
app
.data
@@ -98,26 +98,26 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
IndexerSettingsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
if key == Key::Up {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::IndexerSettingsMinimumAgeInput
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput
);
} else {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::IndexerSettingsMaximumSizeInput
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput
);
}
}
@@ -130,20 +130,20 @@ mod tests {
app.is_loading = true;
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
IndexerSettingsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::IndexerSettingsRetentionInput
ActiveRadarrBlock::IndexerSettingsRetentionInput
);
}
@@ -218,10 +218,10 @@ mod tests {
});
IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
@@ -239,10 +239,10 @@ mod tests {
);
IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
@@ -276,24 +276,24 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.index = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1;
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.y = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1;
IndexerSettingsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert!(app.data.radarr_data.prompt_confirm);
IndexerSettingsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
@@ -323,44 +323,44 @@ mod tests {
)]
fn test_left_right_block_toggle(
#[values(Key::Left, Key::Right)] key: Key,
#[case] starting_index: usize,
#[case] starting_y_index: usize,
#[case] left_block: ActiveRadarrBlock,
#[case] right_block: ActiveRadarrBlock,
) {
let mut app = App::default();
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.index = starting_index;
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.y = starting_y_index;
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&left_block
left_block
);
IndexerSettingsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&right_block
right_block
);
IndexerSettingsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&left_block
left_block
);
}
@@ -373,10 +373,10 @@ mod tests {
});
IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
@@ -394,10 +394,10 @@ mod tests {
);
IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
@@ -438,23 +438,23 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
assert_eq!(app.data.radarr_data.indexer_settings, None);
@@ -466,24 +466,24 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditAllIndexerSettings(None))
@@ -502,71 +502,80 @@ mod tests {
app.data.radarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
assert!(!app.should_refresh);
}
#[rstest]
#[case(ActiveRadarrBlock::IndexerSettingsMinimumAgeInput, 0)]
#[case(ActiveRadarrBlock::IndexerSettingsRetentionInput, 1)]
#[case(ActiveRadarrBlock::IndexerSettingsMaximumSizeInput, 2)]
#[case(ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput, 5)]
#[case(ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput, 6)]
#[case(ActiveRadarrBlock::IndexerSettingsMinimumAgeInput, 0, 0)]
#[case(ActiveRadarrBlock::IndexerSettingsRetentionInput, 1, 0)]
#[case(ActiveRadarrBlock::IndexerSettingsMaximumSizeInput, 2, 0)]
#[case(ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput, 0, 1)]
#[case(ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput, 1, 1)]
fn test_edit_indexer_settings_prompt_submit_selected_block(
#[case] selected_block: ActiveRadarrBlock,
#[case] index: usize,
#[case] y_index: usize,
#[case] x_index: usize,
) {
let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(x_index, y_index);
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &selected_block.into());
assert_eq!(app.get_current_route(), selected_block.into());
}
#[rstest]
fn test_edit_indexer_settings_prompt_submit_selected_block_no_op_when_not_ready(
#[values(0, 1, 2, 5, 6)] index: usize,
#[values((0, 0), (1, 0), (2, 0), (0, 1), (1, 1))] index: (usize, usize),
) {
let mut app = App::default();
app.is_loading = true;
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(index.1, index.0);
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
}
@@ -576,20 +585,20 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(7);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(1, 2);
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into()
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into()
);
assert!(app.should_ignore_quit_key);
}
@@ -599,21 +608,21 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(3);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(0, 3);
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
assert!(
app
@@ -626,16 +635,16 @@ mod tests {
);
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
assert!(
!app
@@ -653,21 +662,21 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(8);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(1, 3);
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
assert!(
app
@@ -680,16 +689,16 @@ mod tests {
);
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
assert!(
!app
@@ -716,10 +725,10 @@ mod tests {
);
IndexerSettingsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
@@ -735,7 +744,7 @@ mod tests {
.is_empty());
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
}
@@ -755,11 +764,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.push_navigation_stack(active_radarr_block.into());
IndexerSettingsHandler::with(&SUBMIT_KEY, &mut app, &active_radarr_block, &None).handle();
IndexerSettingsHandler::with(SUBMIT_KEY, &mut app, active_radarr_block, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
}
}
@@ -783,14 +792,14 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.data.radarr_data.indexer_settings, None);
}
@@ -806,14 +815,14 @@ mod tests {
app.should_ignore_quit_key = true;
IndexerSettingsHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.data.radarr_data.indexer_settings,
@@ -838,9 +847,9 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into());
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
IndexerSettingsHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(
app.data.radarr_data.indexer_settings,
Some(IndexerSettings::default())
@@ -870,10 +879,10 @@ mod tests {
});
IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
@@ -896,10 +905,10 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
&Key::Char('h'),
Key::Char('h'),
&mut app,
&ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
&None,
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
None,
)
.handle();
@@ -922,23 +931,23 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditAllIndexerSettings(None))
@@ -952,9 +961,9 @@ mod tests {
fn test_indexer_settings_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if INDEXER_SETTINGS_BLOCKS.contains(&active_radarr_block) {
assert!(IndexerSettingsHandler::accepts(&active_radarr_block));
assert!(IndexerSettingsHandler::accepts(active_radarr_block));
} else {
assert!(!IndexerSettingsHandler::accepts(&active_radarr_block));
assert!(!IndexerSettingsHandler::accepts(active_radarr_block));
}
})
}
@@ -965,10 +974,10 @@ mod tests {
app.is_loading = true;
let handler = IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(!handler.is_ready());
@@ -980,10 +989,10 @@ mod tests {
app.is_loading = false;
let handler = IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(!handler.is_ready());
@@ -996,10 +1005,10 @@ mod tests {
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
let handler = IndexerSettingsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
&None,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(handler.is_ready());
@@ -25,6 +25,7 @@ mod tests {
test_iterable_scroll!(
test_indexers_scroll,
IndexersHandler,
radarr_data,
indexers,
simple_stateful_iterable_vec!(Indexer, String, protocol),
ActiveRadarrBlock::Indexers,
@@ -47,14 +48,14 @@ mod tests {
.indexers
.set_items(simple_stateful_iterable_vec!(Indexer, String, protocol));
IndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
IndexersHandler::with(key, &mut app, ActiveRadarrBlock::Indexers, None).handle();
assert_str_eq!(
app.data.radarr_data.indexers.current_selection().protocol,
"Test 1"
);
IndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
IndexersHandler::with(key, &mut app, ActiveRadarrBlock::Indexers, None).handle();
assert_str_eq!(
app.data.radarr_data.indexers.current_selection().protocol,
@@ -71,6 +72,7 @@ mod tests {
test_iterable_home_and_end!(
test_indexers_home_end,
IndexersHandler,
radarr_data,
indexers,
extended_stateful_iterable_vec!(Indexer, String, protocol),
ActiveRadarrBlock::Indexers,
@@ -89,10 +91,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(Indexer, String, protocol));
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
@@ -102,10 +104,10 @@ mod tests {
);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
@@ -132,11 +134,11 @@ mod tests {
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
IndexersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::DeleteIndexerPrompt.into()
ActiveRadarrBlock::DeleteIndexerPrompt.into()
);
}
@@ -151,9 +153,9 @@ mod tests {
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
IndexersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
}
@@ -170,20 +172,20 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(5);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
}
@@ -194,18 +196,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(5);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::System.into()
ActiveRadarrBlock::System.into()
);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
}
#[rstest]
@@ -214,23 +216,11 @@ mod tests {
) {
let mut app = App::default();
IndexersHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
)
.handle();
IndexersHandler::with(key, &mut app, ActiveRadarrBlock::DeleteIndexerPrompt, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
IndexersHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
)
.handle();
IndexersHandler::with(key, &mut app, ActiveRadarrBlock::DeleteIndexerPrompt, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -306,11 +296,11 @@ mod tests {
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!(
app.get_current_route(),
&ActiveRadarrBlock::EditIndexerPrompt.into()
ActiveRadarrBlock::EditIndexerPrompt.into()
);
assert_eq!(
app.data.radarr_data.edit_indexer_modal,
@@ -323,12 +313,12 @@ mod tests {
if torrent_protocol {
assert_eq!(
app.data.radarr_data.selected_block.blocks,
&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
);
} else {
assert_eq!(
app.data.radarr_data.selected_block.blocks,
&EDIT_INDEXER_NZB_SELECTION_BLOCKS
EDIT_INDEXER_NZB_SELECTION_BLOCKS
);
}
}
@@ -344,9 +334,9 @@ mod tests {
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
IndexersHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(app.data.radarr_data.edit_indexer_modal, None);
}
@@ -363,10 +353,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
ActiveRadarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
@@ -375,7 +365,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteIndexer(None))
);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
#[test]
@@ -390,16 +380,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
ActiveRadarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
}
@@ -419,14 +409,14 @@ mod tests {
app.data.radarr_data.prompt_confirm = true;
IndexersHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
ActiveRadarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -438,9 +428,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::TestIndexer.into());
IndexersHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::TestIndexer, &None).handle();
IndexersHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::TestIndexer, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(app.data.radarr_data.indexer_test_error, None);
}
@@ -452,9 +442,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
IndexersHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle();
IndexersHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(app.error.text.is_empty());
}
}
@@ -469,51 +459,6 @@ mod tests {
use super::*;
#[test]
fn test_indexer_add() {
let mut app = App::default();
app
.data
.radarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.add.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddIndexer.into()
);
}
#[test]
fn test_indexer_add_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app
.data
.radarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.add.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
}
#[test]
fn test_refresh_indexers_key() {
let mut app = App::default();
@@ -525,14 +470,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(app.should_refresh);
}
@@ -548,14 +493,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(!app.should_refresh);
}
@@ -569,20 +514,20 @@ mod tests {
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.settings.key,
DEFAULT_KEYBINDINGS.settings.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
);
assert_eq!(
app.data.radarr_data.selected_block.blocks,
&INDEXER_SETTINGS_SELECTION_BLOCKS
INDEXER_SETTINGS_SELECTION_BLOCKS
);
}
@@ -598,14 +543,14 @@ mod tests {
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.settings.key,
DEFAULT_KEYBINDINGS.settings.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
#[test]
@@ -618,16 +563,16 @@ mod tests {
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test.key,
DEFAULT_KEYBINDINGS.test.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::TestIndexer.into()
ActiveRadarrBlock::TestIndexer.into()
);
}
@@ -643,14 +588,14 @@ mod tests {
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test.key,
DEFAULT_KEYBINDINGS.test.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
#[test]
@@ -663,16 +608,16 @@ mod tests {
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test_all.key,
DEFAULT_KEYBINDINGS.test_all.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::TestAllIndexers.into()
ActiveRadarrBlock::TestAllIndexers.into()
);
}
@@ -688,14 +633,14 @@ mod tests {
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.test_all.key,
DEFAULT_KEYBINDINGS.test_all.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
#[test]
@@ -710,10 +655,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::DeleteIndexerPrompt,
&None,
ActiveRadarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
@@ -722,7 +667,7 @@ mod tests {
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteIndexer(None))
);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
}
@@ -791,9 +736,9 @@ mod tests {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if indexers_blocks.contains(&active_radarr_block) {
assert!(IndexersHandler::accepts(&active_radarr_block));
assert!(IndexersHandler::accepts(active_radarr_block));
} else {
assert!(!IndexersHandler::accepts(&active_radarr_block));
assert!(!IndexersHandler::accepts(active_radarr_block));
}
})
}
@@ -804,10 +749,10 @@ mod tests {
app.is_loading = true;
let handler = IndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
);
assert!(!handler.is_ready());
@@ -819,10 +764,10 @@ mod tests {
app.is_loading = false;
let handler = IndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
);
assert!(!handler.is_ready());
@@ -839,10 +784,10 @@ mod tests {
.set_items(vec![Indexer::default()]);
let handler = IndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::Indexers,
&None,
ActiveRadarrBlock::Indexers,
None,
);
assert!(handler.is_ready());
+46 -56
View File
@@ -1,15 +1,18 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
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::test_all_indexers_handler::TestAllIndexersHandler;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS,
};
use crate::models::servarr_models::Indexer;
use crate::models::BlockSelectionState;
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent;
@@ -23,43 +26,51 @@ mod test_all_indexers_handler;
mod indexers_handler_tests;
pub(super) struct IndexersHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> IndexersHandler<'a, 'b> {
handle_table_events!(self, indexers, self.app.data.radarr_data.indexers, Indexer);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, 'b> {
fn handle(&mut self) {
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()
let indexer_table_handling_props = TableHandlingProps::new(ActiveRadarrBlock::Indexers.into());
if !self.handle_indexers_table_events(indexer_table_handling_props) {
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) => {
IndexerSettingsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ if TestAllIndexersHandler::accepts(self.active_radarr_block) => {
TestAllIndexersHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ => self.handle_key_event(),
}
_ if IndexerSettingsHandler::accepts(self.active_radarr_block) => {
IndexerSettingsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ if TestAllIndexersHandler::accepts(self.active_radarr_block) => {
TestAllIndexersHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle()
}
_ => self.handle_key_event(),
}
}
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
EditIndexerHandler::accepts(active_block)
|| IndexerSettingsHandler::accepts(active_block)
|| TestAllIndexersHandler::accepts(active_block)
|| INDEXERS_BLOCKS.contains(active_block)
|| INDEXERS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> IndexersHandler<'a, 'b> {
IndexersHandler {
key,
@@ -69,7 +80,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -77,32 +88,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
!self.app.is_loading && !self.app.data.radarr_data.indexers.is_empty()
}
fn handle_scroll_up(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
self.app.data.radarr_data.indexers.scroll_up();
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
self.app.data.radarr_data.indexers.scroll_down();
}
}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
self.app.data.radarr_data.indexers.scroll_to_top();
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
self.app.data.radarr_data.indexers.scroll_to_bottom();
}
}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Indexers {
if self.active_radarr_block == ActiveRadarrBlock::Indexers {
self
.app
.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
@@ -141,10 +136,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
.protocol;
if protocol == "torrent" {
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
} else {
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_INDEXER_NZB_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_INDEXER_NZB_SELECTION_BLOCKS);
}
}
_ => (),
@@ -169,35 +164,30 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
let key = self.key;
match self.active_radarr_block {
ActiveRadarrBlock::Indexers => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.add.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AddIndexer.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if *key == DEFAULT_KEYBINDINGS.test.key => {
_ if key == DEFAULT_KEYBINDINGS.test.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::TestIndexer.into());
}
_ if *key == DEFAULT_KEYBINDINGS.test_all.key => {
_ if key == DEFAULT_KEYBINDINGS.test_all.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::TestAllIndexers.into());
}
_ if *key == DEFAULT_KEYBINDINGS.settings.key => {
_ if key == DEFAULT_KEYBINDINGS.settings.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
}
_ => (),
},
ActiveRadarrBlock::DeleteIndexerPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None));
@@ -9,22 +9,22 @@ use crate::models::Scrollable;
mod test_all_indexers_handler_tests;
pub(super) struct TestAllIndexersHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
active_block == &ActiveRadarrBlock::TestAllIndexers
fn accepts(active_block: ActiveRadarrBlock) -> bool {
active_block == ActiveRadarrBlock::TestAllIndexers
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> TestAllIndexersHandler<'a, 'b> {
TestAllIndexersHandler {
key,
@@ -34,7 +34,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -49,7 +49,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
}
fn handle_scroll_up(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
if self.active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
self
.app
.data
@@ -62,7 +62,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
}
fn handle_scroll_down(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
if self.active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
self
.app
.data
@@ -75,7 +75,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
}
fn handle_home(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
if self.active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
self
.app
.data
@@ -88,7 +88,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
}
fn handle_end(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
if self.active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
self
.app
.data
@@ -107,7 +107,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl
fn handle_submit(&mut self) {}
fn handle_esc(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers {
if self.active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
self.app.pop_navigation_stack();
self.app.data.radarr_data.indexer_test_all_results = None;
}
@@ -33,7 +33,7 @@ mod tests {
));
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
TestAllIndexersHandler::with(key, &mut app, ActiveRadarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
@@ -48,7 +48,7 @@ mod tests {
"Test 2"
);
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
TestAllIndexersHandler::with(key, &mut app, ActiveRadarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
@@ -78,7 +78,7 @@ mod tests {
));
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
TestAllIndexersHandler::with(key, &mut app, ActiveRadarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
@@ -93,7 +93,7 @@ mod tests {
"Test 1"
);
TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None)
TestAllIndexersHandler::with(key, &mut app, ActiveRadarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
@@ -130,10 +130,10 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
)
.handle();
@@ -150,10 +150,10 @@ mod tests {
);
TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
)
.handle();
@@ -183,10 +183,10 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
)
.handle();
@@ -203,10 +203,10 @@ mod tests {
);
TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
)
.handle();
@@ -239,14 +239,14 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default());
TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(!app.data.radarr_data.prompt_confirm);
assert!(app.data.radarr_data.indexer_test_all_results.is_none());
}
@@ -256,9 +256,9 @@ mod tests {
fn test_test_all_indexers_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if active_radarr_block == ActiveRadarrBlock::TestAllIndexers {
assert!(TestAllIndexersHandler::accepts(&active_radarr_block));
assert!(TestAllIndexersHandler::accepts(active_radarr_block));
} else {
assert!(!TestAllIndexersHandler::accepts(&active_radarr_block));
assert!(!TestAllIndexersHandler::accepts(active_radarr_block));
}
});
}
@@ -269,10 +269,10 @@ mod tests {
app.is_loading = true;
let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
@@ -284,10 +284,10 @@ mod tests {
app.is_loading = false;
let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
@@ -300,10 +300,10 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default());
let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
@@ -318,10 +318,10 @@ mod tests {
app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results);
let handler = TestAllIndexersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::TestAllIndexers,
&None,
ActiveRadarrBlock::TestAllIndexers,
None,
);
assert!(handler.is_ready());
@@ -12,22 +12,22 @@ use crate::{handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
mod add_movie_handler_tests;
pub(super) struct AddMovieHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
ADD_MOVIE_BLOCKS.contains(active_block)
fn accepts(active_block: ActiveRadarrBlock) -> bool {
ADD_MOVIE_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> AddMovieHandler<'a, 'b> {
AddMovieHandler {
key,
@@ -37,7 +37,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -91,7 +91,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.unwrap()
.root_folder_list
.scroll_up(),
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.previous(),
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.up(),
_ => (),
}
}
@@ -142,7 +142,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.unwrap()
.root_folder_list
.scroll_down(),
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.next(),
ActiveRadarrBlock::AddMoviePrompt => self.app.data.radarr_data.selected_block.down(),
_ => (),
}
}
@@ -313,7 +313,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
fn handle_submit(&mut self) {
match self.active_radarr_block {
_ if *self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchInput
_ if self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchInput
&& !self
.app
.data
@@ -329,7 +329,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into());
self.app.should_ignore_quit_key = false;
}
_ if *self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchResults
_ if self.active_radarr_block == ActiveRadarrBlock::AddMovieSearchResults
&& self.app.data.radarr_data.add_searched_movies.is_some() =>
{
let tmdb_id = self
@@ -360,7 +360,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
self.app.data.radarr_data.add_movie_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
}
}
ActiveRadarrBlock::AddMoviePrompt => {
@@ -377,16 +377,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
| ActiveRadarrBlock::AddMovieSelectQualityProfile
| ActiveRadarrBlock::AddMovieSelectRootFolder => self.app.push_navigation_stack(
(
*self.app.data.radarr_data.selected_block.get_active_block(),
*self.context,
self.app.data.radarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
),
ActiveRadarrBlock::AddMovieTagsInput => {
self.app.push_navigation_stack(
(
*self.app.data.radarr_data.selected_block.get_active_block(),
*self.context,
self.app.data.radarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
);
@@ -463,8 +463,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
}
ActiveRadarrBlock::AddMoviePrompt => {
if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::AddMovieConfirmPrompt
&& *key == DEFAULT_KEYBINDINGS.confirm.key
== ActiveRadarrBlock::AddMovieConfirmPrompt
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
@@ -39,10 +39,10 @@ mod tests {
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -60,10 +60,10 @@ mod tests {
);
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -95,10 +95,10 @@ mod tests {
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -116,10 +116,10 @@ mod tests {
);
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -156,10 +156,10 @@ mod tests {
if key == Key::Up {
for i in (0..monitor_vec.len()).rev() {
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMonitor,
&None,
ActiveRadarrBlock::AddMovieSelectMonitor,
None,
)
.handle();
@@ -178,10 +178,10 @@ mod tests {
} else {
for i in 0..monitor_vec.len() {
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMonitor,
&None,
ActiveRadarrBlock::AddMovieSelectMonitor,
None,
)
.handle();
@@ -219,10 +219,10 @@ mod tests {
if key == Key::Up {
for i in (0..minimum_availability_vec.len()).rev() {
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -241,10 +241,10 @@ mod tests {
} else {
for i in 0..minimum_availability_vec.len() {
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -279,10 +279,10 @@ mod tests {
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::AddMovieSelectQualityProfile,
None,
)
.handle();
@@ -299,10 +299,10 @@ mod tests {
);
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::AddMovieSelectQualityProfile,
None,
)
.handle();
@@ -335,10 +335,10 @@ mod tests {
.set_items(simple_stateful_iterable_vec!(RootFolder, String, path));
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectRootFolder,
&None,
ActiveRadarrBlock::AddMovieSelectRootFolder,
None,
)
.handle();
@@ -356,10 +356,10 @@ mod tests {
);
AddMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectRootFolder,
&None,
ActiveRadarrBlock::AddMovieSelectRootFolder,
None,
)
.handle();
@@ -380,20 +380,20 @@ mod tests {
#[rstest]
fn test_add_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle();
AddMovieHandler::with(key, &mut app, ActiveRadarrBlock::AddMoviePrompt, None).handle();
if key == Key::Up {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::AddMovieSelectRootFolder
ActiveRadarrBlock::AddMovieSelectRootFolder
);
} else {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::AddMovieSelectMinimumAvailability
ActiveRadarrBlock::AddMovieSelectMinimumAvailability
);
}
}
@@ -402,14 +402,14 @@ mod tests {
fn test_add_movie_prompt_scroll_no_op_when_not_ready(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.is_loading = true;
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle();
AddMovieHandler::with(key, &mut app, ActiveRadarrBlock::AddMoviePrompt, None).handle();
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::AddMovieSelectMonitor
ActiveRadarrBlock::AddMovieSelectMonitor
);
}
}
@@ -436,10 +436,10 @@ mod tests {
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -457,10 +457,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -490,10 +490,10 @@ mod tests {
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -511,10 +511,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
@@ -547,10 +547,10 @@ mod tests {
.set_items(monitor_vec.clone());
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMonitor,
&None,
ActiveRadarrBlock::AddMovieSelectMonitor,
None,
)
.handle();
@@ -567,10 +567,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMonitor,
&None,
ActiveRadarrBlock::AddMovieSelectMonitor,
None,
)
.handle();
@@ -602,10 +602,10 @@ mod tests {
.set_items(minimum_availability_vec.clone());
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -622,10 +622,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -660,10 +660,10 @@ mod tests {
]);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::AddMovieSelectQualityProfile,
None,
)
.handle();
@@ -680,10 +680,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::AddMovieSelectQualityProfile,
None,
)
.handle();
@@ -714,10 +714,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(RootFolder, String, path));
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectRootFolder,
&None,
ActiveRadarrBlock::AddMovieSelectRootFolder,
None,
)
.handle();
@@ -735,10 +735,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieSelectRootFolder,
&None,
ActiveRadarrBlock::AddMovieSelectRootFolder,
None,
)
.handle();
@@ -762,10 +762,10 @@ mod tests {
app.data.radarr_data.add_movie_search = Some("Test".into());
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
@@ -782,10 +782,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
@@ -811,10 +811,10 @@ mod tests {
});
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
@@ -832,10 +832,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
@@ -866,11 +866,11 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle();
AddMovieHandler::with(key, &mut app, ActiveRadarrBlock::AddMoviePrompt, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle();
AddMovieHandler::with(key, &mut app, ActiveRadarrBlock::AddMoviePrompt, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -881,10 +881,10 @@ mod tests {
app.data.radarr_data.add_movie_search = Some("Test".into());
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
@@ -901,10 +901,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
@@ -930,10 +930,10 @@ mod tests {
});
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
@@ -951,10 +951,10 @@ mod tests {
);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
@@ -996,17 +996,17 @@ mod tests {
app.data.radarr_data.add_movie_search = Some("test".into());
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into()
ActiveRadarrBlock::AddMovieSearchResults.into()
);
}
@@ -1018,17 +1018,17 @@ mod tests {
app.should_ignore_quit_key = true;
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
assert!(app.should_ignore_quit_key);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchInput.into()
ActiveRadarrBlock::AddMovieSearchInput.into()
);
}
@@ -1042,20 +1042,20 @@ mod tests {
BiMap::from_iter([(1, "B - Test 2".to_owned()), (0, "A - Test 1".to_owned())]);
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMoviePrompt.into()
ActiveRadarrBlock::AddMoviePrompt.into()
);
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::AddMovieSelectRootFolder
ActiveRadarrBlock::AddMovieSelectRootFolder
);
assert!(app.data.radarr_data.add_movie_modal.is_some());
assert!(!app
@@ -1107,16 +1107,16 @@ mod tests {
add_searched_movies.set_items(vec![AddMovieSearchResult::default()]);
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into()
ActiveRadarrBlock::AddMovieSearchResults.into()
);
assert!(app.data.radarr_data.add_movie_modal.is_none());
}
@@ -1126,16 +1126,16 @@ mod tests {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into());
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into()
ActiveRadarrBlock::AddMovieSearchResults.into()
);
}
@@ -1152,16 +1152,16 @@ mod tests {
.set_items(vec![Movie::default()]);
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchResults,
&None,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieAlreadyInLibrary.into()
ActiveRadarrBlock::AddMovieAlreadyInLibrary.into()
);
}
@@ -1170,22 +1170,22 @@ mod tests {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
.set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMoviePrompt,
&None,
ActiveRadarrBlock::AddMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
}
@@ -1196,22 +1196,22 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
.set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMoviePrompt,
&None,
ActiveRadarrBlock::AddMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::AddMovie(None))
@@ -1227,7 +1227,7 @@ mod tests {
#[case(ActiveRadarrBlock::AddMovieTagsInput, 4)]
fn test_add_movie_prompt_selected_block_submit(
#[case] selected_block: ActiveRadarrBlock,
#[case] index: usize,
#[case] y_index: usize,
) {
let mut app = App::default();
app.push_navigation_stack(
@@ -1237,20 +1237,20 @@ mod tests {
)
.into(),
);
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index);
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(0, y_index);
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddMoviePrompt,
&Some(ActiveRadarrBlock::CollectionDetails),
ActiveRadarrBlock::AddMoviePrompt,
Some(ActiveRadarrBlock::CollectionDetails),
)
.handle();
assert_eq!(
app.get_current_route(),
&(selected_block, Some(ActiveRadarrBlock::CollectionDetails)).into()
(selected_block, Some(ActiveRadarrBlock::CollectionDetails)).into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
@@ -1275,16 +1275,16 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into());
AddMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&active_radarr_block,
&Some(ActiveRadarrBlock::CollectionDetails),
active_radarr_block,
Some(ActiveRadarrBlock::CollectionDetails),
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMoviePrompt.into()
ActiveRadarrBlock::AddMoviePrompt.into()
);
if active_radarr_block == ActiveRadarrBlock::AddMovieTagsInput {
@@ -1315,15 +1315,15 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into());
AddMovieHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
assert!(!app.should_ignore_quit_key);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(app.data.radarr_data.add_movie_search, None);
}
@@ -1336,17 +1336,17 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddMovieTagsInput.into());
AddMovieHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMoviePrompt.into()
ActiveRadarrBlock::AddMoviePrompt.into()
);
}
@@ -1368,11 +1368,11 @@ mod tests {
));
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
AddMovieHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
AddMovieHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchInput.into()
ActiveRadarrBlock::AddMovieSearchInput.into()
);
assert!(app.data.radarr_data.add_searched_movies.is_none());
assert!(app.should_ignore_quit_key);
@@ -1386,16 +1386,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddMovieAlreadyInLibrary.into());
AddMovieHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieAlreadyInLibrary,
&None,
ActiveRadarrBlock::AddMovieAlreadyInLibrary,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into()
ActiveRadarrBlock::AddMovieSearchResults.into()
);
}
@@ -1407,18 +1407,12 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into());
app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
AddMovieHandler::with(
&ESC_KEY,
&mut app,
&ActiveRadarrBlock::AddMoviePrompt,
&None,
)
.handle();
AddMovieHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::AddMoviePrompt, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMovieSearchResults.into()
ActiveRadarrBlock::AddMovieSearchResults.into()
);
assert!(app.data.radarr_data.add_movie_modal.is_none());
}
@@ -1432,17 +1426,17 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddMovieTagsInput.into());
AddMovieHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddMoviePrompt.into()
ActiveRadarrBlock::AddMoviePrompt.into()
);
}
@@ -1473,16 +1467,16 @@ mod tests {
);
AddMovieHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&active_radarr_block,
&Some(ActiveRadarrBlock::CollectionDetails),
active_radarr_block,
Some(ActiveRadarrBlock::CollectionDetails),
)
.handle();
assert_eq!(
app.get_current_route(),
&(
(
ActiveRadarrBlock::AddMoviePrompt,
Some(ActiveRadarrBlock::CollectionDetails),
)
@@ -1507,10 +1501,10 @@ mod tests {
app.data.radarr_data.add_movie_search = Some("Test".into());
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
@@ -1529,10 +1523,10 @@ mod tests {
});
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
@@ -1555,10 +1549,10 @@ mod tests {
app.data.radarr_data.add_movie_search = Some(HorizontallyScrollableText::default());
AddMovieHandler::with(
&Key::Char('h'),
Key::Char('h'),
&mut app,
&ActiveRadarrBlock::AddMovieSearchInput,
&None,
ActiveRadarrBlock::AddMovieSearchInput,
None,
)
.handle();
@@ -1574,10 +1568,10 @@ mod tests {
app.data.radarr_data.add_movie_modal = Some(AddMovieModal::default());
AddMovieHandler::with(
&Key::Char('h'),
Key::Char('h'),
&mut app,
&ActiveRadarrBlock::AddMovieTagsInput,
&None,
ActiveRadarrBlock::AddMovieTagsInput,
None,
)
.handle();
@@ -1600,22 +1594,22 @@ mod tests {
app.data.radarr_data.add_movie_modal = Some(AddMovieModal::default());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::AddMoviePrompt.into());
app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(ADD_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
.set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::AddMoviePrompt,
&None,
ActiveRadarrBlock::AddMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::AddMovie(None))
@@ -1628,9 +1622,9 @@ mod tests {
fn test_add_movie_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) {
assert!(AddMovieHandler::accepts(&active_radarr_block));
assert!(AddMovieHandler::accepts(active_radarr_block));
} else {
assert!(!AddMovieHandler::accepts(&active_radarr_block));
assert!(!AddMovieHandler::accepts(active_radarr_block));
}
});
}
@@ -1641,10 +1635,10 @@ mod tests {
app.is_loading = true;
let handler = AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::AddMoviePrompt,
&None,
ActiveRadarrBlock::AddMoviePrompt,
None,
);
assert!(!handler.is_ready());
@@ -1656,10 +1650,10 @@ mod tests {
app.is_loading = false;
let handler = AddMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::AddMoviePrompt,
&None,
ActiveRadarrBlock::AddMoviePrompt,
None,
);
assert!(handler.is_ready());
@@ -10,22 +10,22 @@ use crate::network::radarr_network::RadarrEvent;
mod delete_movie_handler_tests;
pub(super) struct DeleteMovieHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
DELETE_MOVIE_BLOCKS.contains(active_block)
fn accepts(active_block: ActiveRadarrBlock) -> bool {
DELETE_MOVIE_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> Self {
DeleteMovieHandler {
key,
@@ -35,7 +35,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -44,14 +44,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
}
fn handle_scroll_up(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
self.app.data.radarr_data.selected_block.previous();
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
self.app.data.radarr_data.selected_block.up();
}
}
fn handle_scroll_down(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
self.app.data.radarr_data.selected_block.next();
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
self.app.data.radarr_data.selected_block.down();
}
}
@@ -62,13 +62,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
handle_prompt_toggle(self.app, self.key);
}
}
fn handle_submit(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::DeleteMoviePrompt {
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
match self.app.data.radarr_data.selected_block.get_active_block() {
ActiveRadarrBlock::DeleteMovieConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
@@ -94,7 +94,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
}
fn handle_esc(&mut self) {
if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt {
self.app.pop_navigation_stack();
self.app.data.radarr_data.reset_delete_movie_preferences();
self.app.data.radarr_data.prompt_confirm = false;
@@ -102,10 +102,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
}
fn handle_char_key_event(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::DeleteMoviePrompt
if self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt
&& self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::DeleteMovieConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key
== ActiveRadarrBlock::DeleteMovieConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie(None));
@@ -21,22 +21,20 @@ mod tests {
#[rstest]
fn test_delete_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
.handle();
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
if key == Key::Up {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::DeleteMovieToggleDeleteFile
ActiveRadarrBlock::DeleteMovieToggleDeleteFile
);
} else {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::DeleteMovieConfirmPrompt
ActiveRadarrBlock::DeleteMovieConfirmPrompt
);
}
}
@@ -47,16 +45,14 @@ mod tests {
) {
let mut app = App::default();
app.is_loading = true;
app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
.handle();
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::DeleteMovieToggleAddListExclusion
ActiveRadarrBlock::DeleteMovieToggleAddListExclusion
);
}
}
@@ -70,13 +66,11 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
.handle();
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None)
.handle();
DeleteMovieHandler::with(key, &mut app, ActiveRadarrBlock::DeleteMoviePrompt, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -98,25 +92,24 @@ mod tests {
let mut app = App::default();
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
.set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true;
DeleteMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.data.radarr_data.prompt_confirm);
assert!(!app.data.radarr_data.delete_movie_files);
@@ -131,23 +124,22 @@ mod tests {
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true;
app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
.set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
DeleteMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteMovie(None))
@@ -169,16 +161,16 @@ mod tests {
app.data.radarr_data.add_list_exclusion = true;
DeleteMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::DeleteMoviePrompt.into()
ActiveRadarrBlock::DeleteMoviePrompt.into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
@@ -191,30 +183,29 @@ mod tests {
fn test_delete_movie_toggle_delete_files_submit() {
let current_route = ActiveRadarrBlock::DeleteMoviePrompt.into();
let mut app = App::default();
app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
DeleteMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.radarr_data.delete_movie_files, true);
DeleteMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.radarr_data.delete_movie_files, false);
}
}
@@ -236,14 +227,14 @@ mod tests {
app.data.radarr_data.add_list_exclusion = true;
DeleteMovieHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert!(!app.data.radarr_data.prompt_confirm);
assert!(!app.data.radarr_data.delete_movie_files);
assert!(!app.data.radarr_data.add_list_exclusion);
@@ -267,23 +258,22 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true;
app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
.set_index(0, DELETE_MOVIE_SELECTION_BLOCKS.len() - 1);
DeleteMovieHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteMovie(None))
@@ -299,9 +289,9 @@ mod tests {
fn test_delete_movie_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if DELETE_MOVIE_BLOCKS.contains(&active_radarr_block) {
assert!(DeleteMovieHandler::accepts(&active_radarr_block));
assert!(DeleteMovieHandler::accepts(active_radarr_block));
} else {
assert!(!DeleteMovieHandler::accepts(&active_radarr_block));
assert!(!DeleteMovieHandler::accepts(active_radarr_block));
}
});
}
@@ -312,10 +302,10 @@ mod tests {
app.is_loading = true;
let handler = DeleteMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
);
assert!(!handler.is_ready());
@@ -327,10 +317,10 @@ mod tests {
app.is_loading = false;
let handler = DeleteMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::DeleteMoviePrompt,
&None,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
);
assert!(handler.is_ready());
@@ -12,22 +12,22 @@ use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod edit_movie_handler_tests;
pub(super) struct EditMovieHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
EDIT_MOVIE_BLOCKS.contains(active_block)
fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_MOVIE_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> EditMovieHandler<'a, 'b> {
EditMovieHandler {
key,
@@ -37,7 +37,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -65,7 +65,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
.unwrap()
.quality_profile_list
.scroll_up(),
ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.previous(),
ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.up(),
_ => (),
}
}
@@ -90,7 +90,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
.unwrap()
.quality_profile_list
.scroll_down(),
ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.next(),
ActiveRadarrBlock::EditMoviePrompt => self.app.data.radarr_data.selected_block.down(),
_ => (),
}
}
@@ -231,16 +231,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability
| ActiveRadarrBlock::EditMovieSelectQualityProfile => self.app.push_navigation_stack(
(
*self.app.data.radarr_data.selected_block.get_active_block(),
*self.context,
self.app.data.radarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
),
ActiveRadarrBlock::EditMoviePathInput | ActiveRadarrBlock::EditMovieTagsInput => {
self.app.push_navigation_stack(
(
*self.app.data.radarr_data.selected_block.get_active_block(),
*self.context,
self.app.data.radarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
);
@@ -329,8 +329,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
}
ActiveRadarrBlock::EditMoviePrompt => {
if self.app.data.radarr_data.selected_block.get_active_block()
== &ActiveRadarrBlock::EditMovieConfirmPrompt
&& *key == DEFAULT_KEYBINDINGS.confirm.key
== ActiveRadarrBlock::EditMovieConfirmPrompt
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie(None));
@@ -42,10 +42,10 @@ mod tests {
if key == Key::Up {
for i in (0..minimum_availability_vec.len()).rev() {
EditMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -64,10 +64,10 @@ mod tests {
} else {
for i in 0..minimum_availability_vec.len() {
EditMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -102,10 +102,10 @@ mod tests {
.set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]);
EditMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::EditMovieSelectQualityProfile,
None,
)
.handle();
@@ -122,10 +122,10 @@ mod tests {
);
EditMovieHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::EditMovieSelectQualityProfile,
None,
)
.handle();
@@ -146,20 +146,20 @@ mod tests {
fn test_edit_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle();
EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
if key == Key::Up {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieToggleMonitored
ActiveRadarrBlock::EditMovieToggleMonitored
);
} else {
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieSelectQualityProfile
ActiveRadarrBlock::EditMovieSelectQualityProfile
);
}
}
@@ -169,14 +169,14 @@ mod tests {
let mut app = App::default();
app.is_loading = true;
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.next();
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.down();
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle();
EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability
ActiveRadarrBlock::EditMovieSelectMinimumAvailability
);
}
}
@@ -205,10 +205,10 @@ mod tests {
.set_items(minimum_availability_vec.clone());
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -225,10 +225,10 @@ mod tests {
);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
&None,
ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
None,
)
.handle();
@@ -263,10 +263,10 @@ mod tests {
]);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::EditMovieSelectQualityProfile,
None,
)
.handle();
@@ -283,10 +283,10 @@ mod tests {
);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::EditMovieSelectQualityProfile,
&None,
ActiveRadarrBlock::EditMovieSelectQualityProfile,
None,
)
.handle();
@@ -312,10 +312,10 @@ mod tests {
});
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::EditMoviePathInput,
&None,
ActiveRadarrBlock::EditMoviePathInput,
None,
)
.handle();
@@ -333,10 +333,10 @@ mod tests {
);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::EditMoviePathInput,
&None,
ActiveRadarrBlock::EditMoviePathInput,
None,
)
.handle();
@@ -363,10 +363,10 @@ mod tests {
});
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::EditMovieTagsInput,
&None,
ActiveRadarrBlock::EditMovieTagsInput,
None,
)
.handle();
@@ -384,10 +384,10 @@ mod tests {
);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::EditMovieTagsInput,
&None,
ActiveRadarrBlock::EditMovieTagsInput,
None,
)
.handle();
@@ -418,11 +418,11 @@ mod tests {
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle();
EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
assert!(app.data.radarr_data.prompt_confirm);
EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle();
EditMovieHandler::with(key, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -436,10 +436,10 @@ mod tests {
});
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::EditMoviePathInput,
&None,
ActiveRadarrBlock::EditMoviePathInput,
None,
)
.handle();
@@ -457,10 +457,10 @@ mod tests {
);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::EditMoviePathInput,
&None,
ActiveRadarrBlock::EditMoviePathInput,
None,
)
.handle();
@@ -487,10 +487,10 @@ mod tests {
});
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::EditMovieTagsInput,
&None,
ActiveRadarrBlock::EditMovieTagsInput,
None,
)
.handle();
@@ -508,10 +508,10 @@ mod tests {
);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::EditMovieTagsInput,
&None,
ActiveRadarrBlock::EditMovieTagsInput,
None,
)
.handle();
@@ -535,9 +535,7 @@ mod tests {
use rstest::rstest;
use crate::models::servarr_data::radarr::modals::EditMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{
EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS,
};
use crate::models::servarr_data::radarr::radarr_data::EDIT_MOVIE_SELECTION_BLOCKS;
use crate::models::{BlockSelectionState, Route};
use crate::network::radarr_network::RadarrEvent;
@@ -557,10 +555,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePathInput.into());
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePathInput,
&None,
ActiveRadarrBlock::EditMoviePathInput,
None,
)
.handle();
@@ -576,7 +574,7 @@ mod tests {
.is_empty());
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into()
ActiveRadarrBlock::EditMoviePrompt.into()
);
}
@@ -592,10 +590,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePathInput.into());
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMovieTagsInput,
&None,
ActiveRadarrBlock::EditMovieTagsInput,
None,
)
.handle();
@@ -611,7 +609,7 @@ mod tests {
.is_empty());
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into()
ActiveRadarrBlock::EditMoviePrompt.into()
);
}
@@ -621,22 +619,22 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
.set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1);
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
ActiveRadarrBlock::EditMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
}
@@ -647,22 +645,22 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
.set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1);
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
ActiveRadarrBlock::EditMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie(None))
@@ -681,16 +679,16 @@ mod tests {
app.data.radarr_data.prompt_confirm = true;
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
ActiveRadarrBlock::EditMoviePrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into()
ActiveRadarrBlock::EditMoviePrompt.into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
@@ -704,18 +702,18 @@ mod tests {
));
let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.push_navigation_stack(current_route);
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies),
ActiveRadarrBlock::EditMoviePrompt,
Some(ActiveRadarrBlock::Movies),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(
app
.data
@@ -728,14 +726,14 @@ mod tests {
);
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies),
ActiveRadarrBlock::EditMoviePrompt,
Some(ActiveRadarrBlock::Movies),
)
.handle();
assert_eq!(app.get_current_route(), &current_route);
assert_eq!(app.get_current_route(), current_route);
assert_eq!(
app
.data
@@ -755,7 +753,7 @@ mod tests {
#[case(ActiveRadarrBlock::EditMovieTagsInput, 4)]
fn test_edit_movie_prompt_selected_block_submit(
#[case] selected_block: ActiveRadarrBlock,
#[case] index: usize,
#[case] y_index: usize,
) {
let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
@@ -766,20 +764,20 @@ mod tests {
)
.into(),
);
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index);
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(0, y_index);
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies),
ActiveRadarrBlock::EditMoviePrompt,
Some(ActiveRadarrBlock::Movies),
)
.handle();
assert_eq!(
app.get_current_route(),
&(selected_block, Some(ActiveRadarrBlock::Movies)).into()
(selected_block, Some(ActiveRadarrBlock::Movies)).into()
);
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
@@ -792,7 +790,7 @@ mod tests {
#[rstest]
fn test_edit_movie_prompt_selected_block_submit_no_op_when_not_ready(
#[values(1, 2, 3, 4)] index: usize,
#[values(1, 2, 3, 4)] y_index: usize,
) {
let mut app = App::default();
app.is_loading = true;
@@ -804,20 +802,20 @@ mod tests {
)
.into(),
);
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(index);
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block.set_index(0, y_index);
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&Some(ActiveRadarrBlock::Movies),
ActiveRadarrBlock::EditMoviePrompt,
Some(ActiveRadarrBlock::Movies),
)
.handle();
assert_eq!(
app.get_current_route(),
&(
(
ActiveRadarrBlock::EditMoviePrompt,
Some(ActiveRadarrBlock::Movies),
)
@@ -843,16 +841,16 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into());
EditMovieHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&active_radarr_block,
&Some(ActiveRadarrBlock::Movies),
active_radarr_block,
Some(ActiveRadarrBlock::Movies),
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into()
ActiveRadarrBlock::EditMoviePrompt.into()
);
if active_radarr_block == ActiveRadarrBlock::EditMoviePathInput
@@ -888,12 +886,12 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.push_navigation_stack(active_radarr_block.into());
EditMovieHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
EditMovieHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::EditMoviePrompt.into()
ActiveRadarrBlock::EditMoviePrompt.into()
);
}
@@ -904,15 +902,9 @@ mod tests {
app.data.radarr_data = create_test_radarr_data();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
EditMovieHandler::with(
&ESC_KEY,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
)
.handle();
EditMovieHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::EditMoviePrompt, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert!(app.data.radarr_data.edit_movie_modal.is_none());
assert!(!app.data.radarr_data.prompt_confirm);
@@ -932,9 +924,9 @@ mod tests {
app.data.radarr_data = create_test_radarr_data();
app.push_navigation_stack(active_radarr_block.into());
EditMovieHandler::with(&ESC_KEY, &mut app, &active_radarr_block, &None).handle();
EditMovieHandler::with(ESC_KEY, &mut app, active_radarr_block, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
}
}
@@ -942,10 +934,7 @@ mod tests {
use super::*;
use crate::{
models::{
servarr_data::radarr::{
modals::EditMovieModal,
radarr_data::{EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS},
},
servarr_data::radarr::{modals::EditMovieModal, radarr_data::EDIT_MOVIE_SELECTION_BLOCKS},
BlockSelectionState,
},
network::radarr_network::RadarrEvent,
@@ -960,10 +949,10 @@ mod tests {
});
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::EditMoviePathInput,
&None,
ActiveRadarrBlock::EditMoviePathInput,
None,
)
.handle();
@@ -989,10 +978,10 @@ mod tests {
});
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::EditMovieTagsInput,
&None,
ActiveRadarrBlock::EditMovieTagsInput,
None,
)
.handle();
@@ -1015,10 +1004,10 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
EditMovieHandler::with(
&Key::Char('h'),
Key::Char('h'),
&mut app,
&ActiveRadarrBlock::EditMoviePathInput,
&None,
ActiveRadarrBlock::EditMoviePathInput,
None,
)
.handle();
@@ -1041,10 +1030,10 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
EditMovieHandler::with(
&Key::Char('h'),
Key::Char('h'),
&mut app,
&ActiveRadarrBlock::EditMovieTagsInput,
&None,
ActiveRadarrBlock::EditMovieTagsInput,
None,
)
.handle();
@@ -1067,22 +1056,22 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
app
.data
.radarr_data
.selected_block
.set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1);
.set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1);
EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
ActiveRadarrBlock::EditMoviePrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie(None))
@@ -1096,9 +1085,9 @@ mod tests {
fn test_edit_movie_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if EDIT_MOVIE_BLOCKS.contains(&active_radarr_block) {
assert!(EditMovieHandler::accepts(&active_radarr_block));
assert!(EditMovieHandler::accepts(active_radarr_block));
} else {
assert!(!EditMovieHandler::accepts(&active_radarr_block));
assert!(!EditMovieHandler::accepts(active_radarr_block));
}
});
}
@@ -1109,10 +1098,10 @@ mod tests {
app.is_loading = true;
let handler = EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
ActiveRadarrBlock::EditMoviePrompt,
None,
);
assert!(!handler.is_ready());
@@ -1124,10 +1113,10 @@ mod tests {
app.is_loading = false;
let handler = EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
ActiveRadarrBlock::EditMoviePrompt,
None,
);
assert!(!handler.is_ready());
@@ -1140,10 +1129,10 @@ mod tests {
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
let handler = EditMovieHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::EditMoviePrompt,
&None,
ActiveRadarrBlock::EditMoviePrompt,
None,
);
assert!(handler.is_ready());
File diff suppressed because it is too large Load Diff
+57 -252
View File
@@ -8,6 +8,8 @@ use crate::handlers::radarr_handlers::library::edit_movie_handler::EditMovieHand
use crate::handlers::radarr_handlers::library::movie_details_handler::MovieDetailsHandler;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingProps;
use crate::models::radarr_models::Movie;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, DELETE_MOVIE_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, LIBRARY_BLOCKS,
@@ -15,7 +17,6 @@ use crate::models::servarr_data::radarr::radarr_data::{
use crate::models::stateful_table::SortOption;
use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable};
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
mod add_movie_handler;
mod delete_movie_handler;
@@ -27,46 +28,65 @@ mod movie_details_handler;
mod library_handler_tests;
pub(super) struct LibraryHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> LibraryHandler<'a, 'b> {
handle_table_events!(self, movies, self.app.data.radarr_data.movies, Movie);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, 'b> {
fn handle(&mut self) {
match self.active_radarr_block {
_ if AddMovieHandler::accepts(self.active_radarr_block) => {
AddMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context).handle();
let movie_table_handling_props = TableHandlingProps::new(ActiveRadarrBlock::Movies.into())
.sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into())
.sort_by_fn(|a: &Movie, b: &Movie| a.id.cmp(&b.id))
.sort_options(movies_sorting_options())
.searching_block(ActiveRadarrBlock::SearchMovie.into())
.search_error_block(ActiveRadarrBlock::SearchMovieError.into())
.search_field_fn(|movie| &movie.title.text)
.filtering_block(ActiveRadarrBlock::FilterMovies.into())
.filter_error_block(ActiveRadarrBlock::FilterMoviesError.into())
.filter_field_fn(|movie| &movie.title.text);
if !self.handle_movies_table_events(movie_table_handling_props) {
match self.active_radarr_block {
_ if AddMovieHandler::accepts(self.active_radarr_block) => {
AddMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if DeleteMovieHandler::accepts(self.active_radarr_block) => {
DeleteMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if EditMovieHandler::accepts(self.active_radarr_block) => {
EditMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if MovieDetailsHandler::accepts(self.active_radarr_block) => {
MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
}
_ if DeleteMovieHandler::accepts(self.active_radarr_block) => {
DeleteMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ if EditMovieHandler::accepts(self.active_radarr_block) => {
EditMovieHandler::with(self.key, self.app, self.active_radarr_block, self.context).handle();
}
_ if MovieDetailsHandler::accepts(self.active_radarr_block) => {
MovieDetailsHandler::with(self.key, self.app, self.active_radarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
}
}
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
AddMovieHandler::accepts(active_block)
|| DeleteMovieHandler::accepts(active_block)
|| EditMovieHandler::accepts(active_block)
|| MovieDetailsHandler::accepts(active_block)
|| LIBRARY_BLOCKS.contains(active_block)
|| LIBRARY_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> LibraryHandler<'a, 'b> {
LibraryHandler {
key,
@@ -76,7 +96,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -84,117 +104,21 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
!self.app.is_loading && !self.app.data.radarr_data.movies.is_empty()
}
fn handle_scroll_up(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_up(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_down(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_to_top(),
ActiveRadarrBlock::SearchMovie => {
self
.app
.data
.radarr_data
.movies
.search
.as_mut()
.unwrap()
.scroll_home();
}
ActiveRadarrBlock::FilterMovies => {
self
.app
.data
.radarr_data
.movies
.filter
.as_mut()
.unwrap()
.scroll_home();
}
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_to_bottom(),
ActiveRadarrBlock::SearchMovie => self
.app
.data
.radarr_data
.movies
.search
.as_mut()
.unwrap()
.reset_offset(),
ActiveRadarrBlock::FilterMovies => self
.app
.data
.radarr_data
.movies
.filter
.as_mut()
.unwrap()
.reset_offset(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
}
}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Movies {
if self.active_radarr_block == ActiveRadarrBlock::Movies {
self
.app
.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
}
}
@@ -202,20 +126,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
match self.active_radarr_block {
ActiveRadarrBlock::Movies => handle_change_tab_left_right_keys(self.app, self.key),
ActiveRadarrBlock::UpdateAllMoviesPrompt => handle_prompt_toggle(self.app, self.key),
ActiveRadarrBlock::SearchMovie => {
handle_text_box_left_right_keys!(
self,
self.key,
self.app.data.radarr_data.movies.search.as_mut().unwrap()
)
}
ActiveRadarrBlock::FilterMovies => {
handle_text_box_left_right_keys!(
self,
self.key,
self.app.data.radarr_data.movies.filter.as_mut().unwrap()
)
}
_ => (),
}
}
@@ -225,44 +135,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
ActiveRadarrBlock::Movies => self
.app
.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()),
ActiveRadarrBlock::SearchMovie => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
if self.app.data.radarr_data.movies.search.is_some() {
let has_match = self
.app
.data
.radarr_data
.movies
.apply_search(|movie| &movie.title.text);
if !has_match {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SearchMovieError.into());
}
}
}
ActiveRadarrBlock::FilterMovies => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
if self.app.data.radarr_data.movies.filter.is_some() {
let has_matches = self
.app
.data
.radarr_data
.movies
.apply_filter(|movie| &movie.title.text);
if !has_matches {
self
.app
.push_navigation_stack(ActiveRadarrBlock::FilterMoviesError.into());
}
}
}
ActiveRadarrBlock::UpdateAllMoviesPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies);
@@ -270,44 +142,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::MoviesSortPrompt => {
self
.app
.data
.radarr_data
.movies
.items
.sort_by(|a, b| a.id.cmp(&b.id));
self.app.data.radarr_data.movies.apply_sorting();
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterMoviesError => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.movies.reset_filter();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchMovieError => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.movies.reset_search();
self.app.should_ignore_quit_key = false;
}
ActiveRadarrBlock::UpdateAllMoviesPrompt => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false;
}
ActiveRadarrBlock::MoviesSortPrompt => {
self.app.pop_navigation_stack();
}
_ => {
self.app.data.radarr_data.movies.reset_search();
self.app.data.radarr_data.movies.reset_filter();
handle_clear_errors(self.app);
}
}
@@ -317,22 +162,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
let key = self.key;
match self.active_radarr_block {
ActiveRadarrBlock::Movies => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.search.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SearchMovie.into());
self.app.data.radarr_data.movies.search = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if *key == DEFAULT_KEYBINDINGS.filter.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::FilterMovies.into());
self.app.data.radarr_data.movies.reset_filter();
self.app.data.radarr_data.movies.filter = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if *key == DEFAULT_KEYBINDINGS.edit.key => {
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveRadarrBlock::EditMoviePrompt,
@@ -342,52 +172,27 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
);
self.app.data.radarr_data.edit_movie_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
}
_ if *key == DEFAULT_KEYBINDINGS.add.key => {
_ if key == DEFAULT_KEYBINDINGS.add.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into());
self.app.data.radarr_data.add_movie_search = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.movies
.sorting(movies_sorting_options());
self
.app
.push_navigation_stack(ActiveRadarrBlock::MoviesSortPrompt.into());
}
_ => (),
},
ActiveRadarrBlock::SearchMovie => {
handle_text_box_keys!(
self,
key,
self.app.data.radarr_data.movies.search.as_mut().unwrap()
)
}
ActiveRadarrBlock::FilterMovies => {
handle_text_box_keys!(
self,
key,
self.app.data.radarr_data.movies.filter.as_mut().unwrap()
)
}
ActiveRadarrBlock::UpdateAllMoviesPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies);
@@ -3,8 +3,10 @@ use serde_json::Number;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::RadarrRelease;
use crate::models::radarr_models::{Credit, MovieHistoryItem, RadarrRelease};
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_MOVIE_SELECTION_BLOCKS, MOVIE_DETAILS_BLOCKS,
};
@@ -18,22 +20,96 @@ use crate::network::radarr_network::RadarrEvent;
mod movie_details_handler_tests;
pub(super) struct MovieDetailsHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> MovieDetailsHandler<'a, 'b> {
handle_table_events!(
self,
movie_releases,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases,
RadarrRelease
);
handle_table_events!(
self,
movie_history,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history,
MovieHistoryItem
);
handle_table_events!(
self,
movie_cast,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast,
Credit
);
handle_table_events!(
self,
movie_crew,
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew,
Credit
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
MOVIE_DETAILS_BLOCKS.contains(active_block)
fn handle(&mut self) {
let movie_history_table_handling_props =
TableHandlingProps::new(ActiveRadarrBlock::MovieHistory.into());
let movie_releases_table_handling_props =
TableHandlingProps::new(ActiveRadarrBlock::ManualSearch.into())
.sorting_block(ActiveRadarrBlock::ManualSearchSortPrompt.into())
.sort_options(releases_sorting_options());
let movie_cast_table_handling_props = TableHandlingProps::new(ActiveRadarrBlock::Cast.into());
let movie_crew_table_handling_props = TableHandlingProps::new(ActiveRadarrBlock::Crew.into());
if !self.handle_movie_history_table_events(movie_history_table_handling_props)
&& !self.handle_movie_releases_table_events(movie_releases_table_handling_props)
&& !self.handle_movie_cast_table_events(movie_cast_table_handling_props)
&& !self.handle_movie_crew_table_events(movie_crew_table_handling_props)
{
self.handle_key_event();
}
}
fn accepts(active_block: ActiveRadarrBlock) -> bool {
MOVIE_DETAILS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> MovieDetailsHandler<'a, 'b> {
MovieDetailsHandler {
key,
@@ -43,7 +119,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -73,8 +149,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
}
fn handle_scroll_up(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self
if self.active_radarr_block == ActiveRadarrBlock::MovieDetails {
self
.app
.data
.radarr_data
@@ -82,62 +158,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut()
.unwrap()
.movie_details
.scroll_up(),
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_up(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_up(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_up(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_up(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
.scroll_up()
}
}
fn handle_scroll_down(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self
if self.active_radarr_block == ActiveRadarrBlock::MovieDetails {
self
.app
.data
.radarr_data
@@ -145,62 +172,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut()
.unwrap()
.movie_details
.scroll_down(),
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_down(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_down(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_down(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_down(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
.scroll_down()
}
}
fn handle_home(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self
if self.active_radarr_block == ActiveRadarrBlock::MovieDetails {
self
.app
.data
.radarr_data
@@ -208,62 +186,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut()
.unwrap()
.movie_details
.scroll_to_top(),
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_to_top(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_to_top(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_to_top(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_to_top(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
.scroll_to_top()
}
}
fn handle_end(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self
if let ActiveRadarrBlock::MovieDetails = self.active_radarr_block {
self
.app
.data
.radarr_data
@@ -271,56 +200,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
.as_mut()
.unwrap()
.movie_details
.scroll_to_bottom(),
ActiveRadarrBlock::MovieHistory => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_history
.scroll_to_bottom(),
ActiveRadarrBlock::Cast => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_cast
.scroll_to_bottom(),
ActiveRadarrBlock::Crew => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_crew
.scroll_to_bottom(),
ActiveRadarrBlock::ManualSearch => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.scroll_to_bottom(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
.scroll_to_bottom()
}
}
@@ -334,16 +214,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::ManualSearch => match self.key {
_ if *self.key == DEFAULT_KEYBINDINGS.left.key => {
_ if self.key == DEFAULT_KEYBINDINGS.left.key => {
self.app.data.radarr_data.movie_info_tabs.previous();
self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.movie_info_tabs.get_active_route(),
self.app.data.radarr_data.movie_info_tabs.get_active_route(),
);
}
_ if *self.key == DEFAULT_KEYBINDINGS.right.key => {
_ if self.key == DEFAULT_KEYBINDINGS.right.key => {
self.app.data.radarr_data.movie_info_tabs.next();
self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.movie_info_tabs.get_active_route(),
self.app.data.radarr_data.movie_info_tabs.get_active_route(),
);
}
_ => (),
@@ -385,18 +265,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::ManualSearchSortPrompt => {
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.apply_sorting();
self.app.pop_navigation_stack();
}
_ => (),
}
}
@@ -414,8 +282,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
}
ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::UpdateAndScanPrompt
| ActiveRadarrBlock::ManualSearchConfirmPrompt
| ActiveRadarrBlock::ManualSearchSortPrompt => {
| ActiveRadarrBlock::ManualSearchConfirmPrompt => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false;
}
@@ -425,58 +292,44 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
fn handle_char_key_event(&mut self) {
let key = self.key;
match *self.active_radarr_block {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::ManualSearch => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.search.key => {
_ if key == DEFAULT_KEYBINDINGS.auto_search.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AutomaticallySearchMoviePrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.edit.key => {
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveRadarrBlock::EditMoviePrompt,
Some(*self.active_radarr_block),
Some(self.active_radarr_block),
)
.into(),
);
self.app.data.radarr_data.edit_movie_modal = Some((&self.app.data.radarr_data).into());
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
}
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAndScanPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
}
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.movie_details_modal
.as_mut()
.unwrap()
.movie_releases
.sorting(releases_sorting_options());
self
.app
.push_navigation_stack(ActiveRadarrBlock::ManualSearchSortPrompt.into());
.pop_and_push_navigation_stack(self.active_radarr_block.into());
}
_ => (),
},
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::TriggerAutomaticSearch(None));
@@ -485,7 +338,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
}
}
ActiveRadarrBlock::UpdateAndScanPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan(None));
@@ -493,7 +346,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
}
}
ActiveRadarrBlock::ManualSearchConfirmPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DownloadRelease(None));
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -27,10 +27,10 @@ mod radarr_handler_tests;
mod radarr_handler_test_utils;
pub(super) struct RadarrHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b> {
@@ -63,15 +63,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
}
}
fn accepts(_active_block: &'a ActiveRadarrBlock) -> bool {
fn accepts(_active_block: ActiveRadarrBlock) -> bool {
true
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> RadarrHandler<'a, 'b> {
RadarrHandler {
key,
@@ -81,7 +81,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -108,16 +108,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
fn handle_char_key_event(&mut self) {}
}
pub fn handle_change_tab_left_right_keys(app: &mut App<'_>, key: &Key) {
pub fn handle_change_tab_left_right_keys(app: &mut App<'_>, key: Key) {
let key_ref = key;
match key_ref {
_ if *key == DEFAULT_KEYBINDINGS.left.key => {
_ if key == DEFAULT_KEYBINDINGS.left.key => {
app.data.radarr_data.main_tabs.previous();
app.pop_and_push_navigation_stack(*app.data.radarr_data.main_tabs.get_active_route());
app.pop_and_push_navigation_stack(app.data.radarr_data.main_tabs.get_active_route());
}
_ if *key == DEFAULT_KEYBINDINGS.right.key => {
_ if key == DEFAULT_KEYBINDINGS.right.key => {
app.data.radarr_data.main_tabs.next();
app.pop_and_push_navigation_stack(*app.data.radarr_data.main_tabs.get_active_route());
app.pop_and_push_navigation_stack(app.data.radarr_data.main_tabs.get_active_route());
}
_ => (),
}
@@ -23,15 +23,15 @@ mod utils {
}]);
app.data.radarr_data = radarr_data;
$handler::with(&DEFAULT_KEYBINDINGS.edit.key, &mut app, &$block, &None).handle();
$handler::with(DEFAULT_KEYBINDINGS.edit.key, &mut app, $block, None).handle();
assert_eq!(
app.get_current_route(),
&(ActiveRadarrBlock::EditMoviePrompt, Some($context)).into()
(ActiveRadarrBlock::EditMoviePrompt, Some($context)).into()
);
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditMovieToggleMonitored
ActiveRadarrBlock::EditMovieToggleMonitored
);
assert_eq!(
app
@@ -111,7 +111,7 @@ mod utils {
);
assert_eq!(
app.data.radarr_data.selected_block.blocks,
&EDIT_MOVIE_SELECTION_BLOCKS
EDIT_MOVIE_SELECTION_BLOCKS
);
};
}
@@ -137,15 +137,15 @@ mod utils {
}]);
app.data.radarr_data = radarr_data;
$handler::with(&DEFAULT_KEYBINDINGS.edit.key, &mut app, &$block, &None).handle();
$handler::with(DEFAULT_KEYBINDINGS.edit.key, &mut app, $block, None).handle();
assert_eq!(
app.get_current_route(),
&(ActiveRadarrBlock::EditCollectionPrompt, Some($context)).into()
(ActiveRadarrBlock::EditCollectionPrompt, Some($context)).into()
);
assert_eq!(
app.data.radarr_data.selected_block.get_active_block(),
&ActiveRadarrBlock::EditCollectionToggleMonitored
ActiveRadarrBlock::EditCollectionToggleMonitored
);
assert_eq!(
app
@@ -224,52 +224,8 @@ mod utils {
);
assert_eq!(
app.data.radarr_data.selected_block.blocks,
&EDIT_COLLECTION_SELECTION_BLOCKS
EDIT_COLLECTION_SELECTION_BLOCKS
);
};
}
#[macro_export]
macro_rules! assert_delete_prompt {
($block:expr, $expected_block:expr) => {
let mut app = App::default();
RadarrHandler::with(&DELETE_KEY, &mut app, &$block, &None).handle();
assert_eq!(app.get_current_route(), &$expected_block.into());
};
($handler:ident, $block:expr, $expected_block:expr) => {
let mut app = App::default();
$handler::with(&DELETE_KEY, &mut app, &$block, &None).handle();
assert_eq!(app.get_current_route(), &$expected_block.into());
};
($app:expr, $block:expr, $expected_block:expr) => {
RadarrHandler::with(&DELETE_KEY, &mut $app, &$block, &None).handle();
assert_eq!($app.get_current_route(), &$expected_block.into());
};
($handler:ident, $app:expr, $block:expr, $expected_block:expr) => {
$handler::with(&DELETE_KEY, &mut $app, &$block, &None).handle();
assert_eq!($app.get_current_route(), &$expected_block.into());
};
}
#[macro_export]
macro_rules! assert_refresh_key {
($handler:ident, $block:expr) => {
let mut app = App::default();
app.push_navigation_stack($block.into());
$handler::with(&DEFAULT_KEYBINDINGS.refresh.key, &mut app, &$block, &None).handle();
assert_eq!(app.get_current_route(), &$block.into());
assert!(app.should_refresh);
};
}
}
@@ -27,23 +27,23 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.main_tabs.set_index(index);
handle_change_tab_left_right_keys(&mut app, &DEFAULT_KEYBINDINGS.left.key);
handle_change_tab_left_right_keys(&mut app, DEFAULT_KEYBINDINGS.left.key);
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&left_block.into()
left_block.into()
);
assert_eq!(app.get_current_route(), &left_block.into());
assert_eq!(app.get_current_route(), left_block.into());
app.data.radarr_data.main_tabs.set_index(index);
handle_change_tab_left_right_keys(&mut app, &DEFAULT_KEYBINDINGS.right.key);
handle_change_tab_left_right_keys(&mut app, DEFAULT_KEYBINDINGS.right.key);
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&right_block.into()
right_block.into()
);
assert_eq!(app.get_current_route(), &right_block.into());
assert_eq!(app.get_current_route(), right_block.into());
}
#[rstest]
@@ -213,7 +213,7 @@ mod tests {
#[test]
fn test_radarr_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
assert!(RadarrHandler::accepts(&active_radarr_block));
assert!(RadarrHandler::accepts(active_radarr_block));
})
}
@@ -223,10 +223,10 @@ mod tests {
app.is_loading = true;
let handler = RadarrHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
);
assert!(handler.is_ready());
@@ -2,33 +2,53 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::{HorizontallyScrollableText, Scrollable};
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys};
#[cfg(test)]
#[path = "root_folders_handler_tests.rs"]
mod root_folders_handler_tests;
pub(super) struct RootFoldersHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> RootFoldersHandler<'a, 'b> {
handle_table_events!(
self,
root_folders,
self.app.data.radarr_data.root_folders,
RootFolder
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
ROOT_FOLDERS_BLOCKS.contains(active_block)
fn handle(&mut self) {
let root_folder_table_handling_props =
TableHandlingProps::new(ActiveRadarrBlock::RootFolders.into());
if !self.handle_root_folders_table_events(root_folder_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveRadarrBlock) -> bool {
ROOT_FOLDERS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
) -> RootFoldersHandler<'a, 'b> {
RootFoldersHandler {
key,
@@ -38,7 +58,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -46,50 +66,38 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
!self.app.is_loading && !self.app.data.radarr_data.root_folders.is_empty()
}
fn handle_scroll_up(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::RootFolders {
self.app.data.radarr_data.root_folders.scroll_up()
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::RootFolders {
self.app.data.radarr_data.root_folders.scroll_down()
}
}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_top(),
ActiveRadarrBlock::AddRootFolderPrompt => self
if self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt {
self
.app
.data
.radarr_data
.edit_root_folder
.as_mut()
.unwrap()
.scroll_home(),
_ => (),
.scroll_home()
}
}
fn handle_end(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::RootFolders => self.app.data.radarr_data.root_folders.scroll_to_bottom(),
ActiveRadarrBlock::AddRootFolderPrompt => self
if self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt {
self
.app
.data
.radarr_data
.edit_root_folder
.as_mut()
.unwrap()
.reset_offset(),
_ => (),
.reset_offset()
}
}
fn handle_delete(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::RootFolders {
if self.active_radarr_block == ActiveRadarrBlock::RootFolders {
self
.app
.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into())
@@ -121,7 +129,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
self.app.pop_navigation_stack();
}
_ if *self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt
_ if self.active_radarr_block == ActiveRadarrBlock::AddRootFolderPrompt
&& !self
.app
.data
@@ -161,10 +169,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
let key = self.key;
match self.active_radarr_block {
ActiveRadarrBlock::RootFolders => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if *key == DEFAULT_KEYBINDINGS.add.key => {
_ if key == DEFAULT_KEYBINDINGS.add.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
@@ -181,7 +189,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
)
}
ActiveRadarrBlock::DeleteRootFolderPrompt => {
if *key == DEFAULT_KEYBINDINGS.confirm.key {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteRootFolder(None));
@@ -23,6 +23,7 @@ mod tests {
test_iterable_scroll!(
test_root_folders_scroll,
RootFoldersHandler,
radarr_data,
root_folders,
simple_stateful_iterable_vec!(RootFolder, String, path),
ActiveRadarrBlock::RootFolders,
@@ -42,14 +43,14 @@ mod tests {
.root_folders
.set_items(simple_stateful_iterable_vec!(RootFolder, String, path));
RootFoldersHandler::with(&key, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle();
RootFoldersHandler::with(key, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
assert_str_eq!(
app.data.radarr_data.root_folders.current_selection().path,
"Test 1"
);
RootFoldersHandler::with(&key, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle();
RootFoldersHandler::with(key, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
assert_str_eq!(
app.data.radarr_data.root_folders.current_selection().path,
@@ -71,6 +72,7 @@ mod tests {
test_iterable_home_and_end!(
test_root_folders_home_end,
RootFoldersHandler,
radarr_data,
root_folders,
extended_stateful_iterable_vec!(RootFolder, String, path),
ActiveRadarrBlock::RootFolders,
@@ -89,10 +91,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(RootFolder, String, path));
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
@@ -102,10 +104,10 @@ mod tests {
);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
@@ -126,10 +128,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some("Test".into());
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -146,10 +148,10 @@ mod tests {
);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -183,17 +185,11 @@ mod tests {
.root_folders
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(
&DELETE_KEY,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
)
.handle();
RootFoldersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::DeleteRootFolderPrompt.into()
ActiveRadarrBlock::DeleteRootFolderPrompt.into()
);
}
@@ -208,17 +204,11 @@ mod tests {
.root_folders
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(
&DELETE_KEY,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
)
.handle();
RootFoldersHandler::with(DELETE_KEY, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
}
}
@@ -238,21 +228,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(4);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::Blocklist.into()
ActiveRadarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Blocklist.into());
}
#[rstest]
@@ -262,18 +249,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(4);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Indexers.into()
ActiveRadarrBlock::Indexers.into()
);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
#[rstest]
@@ -283,20 +270,20 @@ mod tests {
let mut app = App::default();
RootFoldersHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt,
&None,
ActiveRadarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert!(app.data.radarr_data.prompt_confirm);
RootFoldersHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt,
&None,
ActiveRadarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
@@ -309,10 +296,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some("Test".into());
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -329,10 +316,10 @@ mod tests {
);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -374,10 +361,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
RootFoldersHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -389,7 +376,7 @@ mod tests {
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
}
@@ -403,10 +390,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::AddRootFolderPrompt.into());
RootFoldersHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -415,7 +402,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm_action.is_none());
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddRootFolderPrompt.into()
ActiveRadarrBlock::AddRootFolderPrompt.into()
);
}
@@ -432,10 +419,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt,
&None,
ActiveRadarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
@@ -446,7 +433,7 @@ mod tests {
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
}
@@ -462,10 +449,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt,
&None,
ActiveRadarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
@@ -473,7 +460,7 @@ mod tests {
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
}
}
@@ -493,16 +480,16 @@ mod tests {
app.data.radarr_data.prompt_confirm = true;
RootFoldersHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt,
&None,
ActiveRadarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -516,16 +503,16 @@ mod tests {
app.should_ignore_quit_key = true;
RootFoldersHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert!(app.data.radarr_data.edit_root_folder.is_none());
@@ -541,11 +528,11 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
RootFoldersHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle();
RootFoldersHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::RootFolders, None).handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert!(app.error.text.is_empty());
}
@@ -568,16 +555,16 @@ mod tests {
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.add.key,
DEFAULT_KEYBINDINGS.add.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::AddRootFolderPrompt.into()
ActiveRadarrBlock::AddRootFolderPrompt.into()
);
assert!(app.should_ignore_quit_key);
assert!(app.data.radarr_data.edit_root_folder.is_some());
@@ -595,16 +582,16 @@ mod tests {
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.add.key,
DEFAULT_KEYBINDINGS.add.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert!(!app.should_ignore_quit_key);
assert!(app.data.radarr_data.edit_root_folder.is_none());
@@ -621,16 +608,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert!(app.should_refresh);
}
@@ -647,16 +634,16 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
assert!(!app.should_refresh);
}
@@ -672,10 +659,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some("/nfs/test".into());
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.backspace.key,
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -696,10 +683,10 @@ mod tests {
app.data.radarr_data.edit_root_folder = Some(HorizontallyScrollableText::default());
RootFoldersHandler::with(
&Key::Char('h'),
Key::Char('h'),
&mut app,
&ActiveRadarrBlock::AddRootFolderPrompt,
&None,
ActiveRadarrBlock::AddRootFolderPrompt,
None,
)
.handle();
@@ -721,10 +708,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::DeleteRootFolderPrompt,
&None,
ActiveRadarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
@@ -735,7 +722,7 @@ mod tests {
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::RootFolders.into()
ActiveRadarrBlock::RootFolders.into()
);
}
}
@@ -744,9 +731,9 @@ mod tests {
fn test_root_folders_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if ROOT_FOLDERS_BLOCKS.contains(&active_radarr_block) {
assert!(RootFoldersHandler::accepts(&active_radarr_block));
assert!(RootFoldersHandler::accepts(active_radarr_block));
} else {
assert!(!RootFoldersHandler::accepts(&active_radarr_block));
assert!(!RootFoldersHandler::accepts(active_radarr_block));
}
})
}
@@ -757,10 +744,10 @@ mod tests {
app.is_loading = true;
let handler = RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
);
assert!(!handler.is_ready());
@@ -772,10 +759,10 @@ mod tests {
app.is_loading = false;
let handler = RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
);
assert!(!handler.is_ready());
@@ -792,10 +779,10 @@ mod tests {
.root_folders
.set_items(vec![RootFolder::default()]);
let handler = RootFoldersHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::RootFolders,
&None,
ActiveRadarrBlock::RootFolders,
None,
);
assert!(handler.is_ready());
+16 -16
View File
@@ -14,10 +14,10 @@ mod system_details_handler;
mod system_handler_tests;
pub(super) struct SystemHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b> {
@@ -31,15 +31,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
}
}
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
SystemDetailsHandler::accepts(active_block) || active_block == &ActiveRadarrBlock::System
fn accepts(active_block: ActiveRadarrBlock) -> bool {
SystemDetailsHandler::accepts(active_block) || active_block == ActiveRadarrBlock::System
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> SystemHandler<'a, 'b> {
SystemHandler {
key,
@@ -49,7 +49,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
@@ -71,7 +71,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::System {
if self.active_radarr_block == ActiveRadarrBlock::System {
handle_change_tab_left_right_keys(self.app, self.key);
}
}
@@ -83,18 +83,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
}
fn handle_char_key_event(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::System {
if self.active_radarr_block == ActiveRadarrBlock::System {
let key = self.key;
match self.key {
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if *key == DEFAULT_KEYBINDINGS.events.key => {
_ if key == DEFAULT_KEYBINDINGS.events.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SystemQueuedEvents.into());
}
_ if *key == DEFAULT_KEYBINDINGS.logs.key => {
_ if key == DEFAULT_KEYBINDINGS.logs.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SystemLogs.into());
@@ -106,12 +106,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b
.set_items(self.app.data.radarr_data.logs.items.to_vec());
self.app.data.radarr_data.log_details.scroll_to_bottom();
}
_ if *key == DEFAULT_KEYBINDINGS.tasks.key => {
_ if key == DEFAULT_KEYBINDINGS.tasks.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
}
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into());
@@ -12,22 +12,22 @@ use crate::network::radarr_network::RadarrEvent;
mod system_details_handler_tests;
pub(super) struct SystemDetailsHandler<'a, 'b> {
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
active_radarr_block: ActiveRadarrBlock,
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
SYSTEM_DETAILS_BLOCKS.contains(active_block)
fn accepts(active_block: ActiveRadarrBlock) -> bool {
SYSTEM_DETAILS_BLOCKS.contains(&active_block)
}
fn with(
key: &'a Key,
key: Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
context: &'a Option<ActiveRadarrBlock>,
active_block: ActiveRadarrBlock,
context: Option<ActiveRadarrBlock>,
) -> SystemDetailsHandler<'a, 'b> {
SystemDetailsHandler {
key,
@@ -37,13 +37,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
}
}
fn get_key(&self) -> &Key {
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading
&& (!self.app.data.radarr_data.log_details.is_empty()
|| !self.app.data.radarr_data.tasks.is_empty()
|| !self.app.data.radarr_data.updates.is_empty())
}
@@ -100,7 +101,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
match self.active_radarr_block {
ActiveRadarrBlock::SystemLogs => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.left.key => {
_ if key == DEFAULT_KEYBINDINGS.left.key => {
self
.app
.data
@@ -110,7 +111,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
.iter()
.for_each(|log| log.scroll_right());
}
_ if *key == DEFAULT_KEYBINDINGS.right.key => {
_ if key == DEFAULT_KEYBINDINGS.right.key => {
self
.app
.data
@@ -163,14 +164,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
}
fn handle_char_key_event(&mut self) {
if SYSTEM_DETAILS_BLOCKS.contains(self.active_radarr_block)
&& self.key == &DEFAULT_KEYBINDINGS.refresh.key
if SYSTEM_DETAILS_BLOCKS.contains(&self.active_radarr_block)
&& self.key == DEFAULT_KEYBINDINGS.refresh.key
{
self.app.should_refresh = true;
}
if self.active_radarr_block == &ActiveRadarrBlock::SystemTaskStartConfirmPrompt
&& *self.key == DEFAULT_KEYBINDINGS.confirm.key
if self.active_radarr_block == ActiveRadarrBlock::SystemTaskStartConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask(None));
@@ -26,6 +26,7 @@ mod tests {
test_iterable_scroll!(
test_log_details_scroll,
SystemDetailsHandler,
radarr_data,
log_details,
simple_stateful_iterable_vec!(HorizontallyScrollableText, String, text),
ActiveRadarrBlock::SystemLogs,
@@ -49,14 +50,14 @@ mod tests {
text
));
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemLogs, &None).handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemLogs, None).handle();
assert_str_eq!(
app.data.radarr_data.log_details.current_selection().text,
"Test 1"
);
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemLogs, &None).handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemLogs, None).handle();
assert_str_eq!(
app.data.radarr_data.log_details.current_selection().text,
@@ -76,14 +77,14 @@ mod tests {
.tasks
.set_items(simple_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name,
"Test 2"
);
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name,
@@ -104,14 +105,14 @@ mod tests {
.tasks
.set_items(simple_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name,
"Test 1"
);
SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_str_eq!(
app.data.radarr_data.tasks.current_selection().name,
@@ -131,26 +132,16 @@ mod tests {
.queued_events
.set_items(simple_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
.handle();
assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name,
"Test 2"
);
SystemDetailsHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
.handle();
assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name,
@@ -171,26 +162,16 @@ mod tests {
.queued_events
.set_items(simple_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
.handle();
assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name,
"Test 1"
);
SystemDetailsHandler::with(
&key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
)
.handle();
SystemDetailsHandler::with(key, &mut app, ActiveRadarrBlock::SystemQueuedEvents, None)
.handle();
assert_str_eq!(
app.data.radarr_data.queued_events.current_selection().name,
@@ -204,20 +185,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.up.key,
DEFAULT_KEYBINDINGS.up.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
assert_eq!(app.data.radarr_data.updates.offset, 0);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.down.key,
DEFAULT_KEYBINDINGS.down.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
@@ -231,20 +212,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.up.key,
DEFAULT_KEYBINDINGS.up.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
assert_eq!(app.data.radarr_data.updates.offset, 0);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.down.key,
DEFAULT_KEYBINDINGS.down.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
@@ -261,6 +242,7 @@ mod tests {
test_iterable_home_and_end!(
test_log_details_home_end,
SystemDetailsHandler,
radarr_data,
log_details,
extended_stateful_iterable_vec!(HorizontallyScrollableText, String, text),
ActiveRadarrBlock::SystemLogs,
@@ -283,10 +265,10 @@ mod tests {
));
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::SystemLogs,
&None,
ActiveRadarrBlock::SystemLogs,
None,
)
.handle();
@@ -296,10 +278,10 @@ mod tests {
);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::SystemLogs,
&None,
ActiveRadarrBlock::SystemLogs,
None,
)
.handle();
@@ -321,10 +303,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
ActiveRadarrBlock::SystemTasks,
None,
)
.handle();
@@ -334,10 +316,10 @@ mod tests {
);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
ActiveRadarrBlock::SystemTasks,
None,
)
.handle();
@@ -360,10 +342,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(RadarrTask, String, name));
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
ActiveRadarrBlock::SystemTasks,
None,
)
.handle();
@@ -373,10 +355,10 @@ mod tests {
);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
ActiveRadarrBlock::SystemTasks,
None,
)
.handle();
@@ -398,10 +380,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
ActiveRadarrBlock::SystemQueuedEvents,
None,
)
.handle();
@@ -411,10 +393,10 @@ mod tests {
);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
ActiveRadarrBlock::SystemQueuedEvents,
None,
)
.handle();
@@ -437,10 +419,10 @@ mod tests {
.set_items(extended_stateful_iterable_vec!(QueueEvent, String, name));
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
ActiveRadarrBlock::SystemQueuedEvents,
None,
)
.handle();
@@ -450,10 +432,10 @@ mod tests {
);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
ActiveRadarrBlock::SystemQueuedEvents,
None,
)
.handle();
@@ -469,20 +451,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
assert_eq!(app.data.radarr_data.updates.offset, 1);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
@@ -496,20 +478,20 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned());
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
assert_eq!(app.data.radarr_data.updates.offset, 0);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
)
.handle();
@@ -534,10 +516,10 @@ mod tests {
.set_items(vec!["t1".into(), "t22".into()]);
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -545,10 +527,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "t22");
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -556,10 +538,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "22");
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -567,10 +549,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "2");
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -578,10 +560,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "");
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -589,10 +571,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "");
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -600,10 +582,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "2");
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -611,10 +593,10 @@ mod tests {
assert_eq!(app.data.radarr_data.log_details.items[1].to_string(), "22");
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
@@ -629,20 +611,20 @@ mod tests {
let mut app = App::default();
SystemDetailsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None,
ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
None,
)
.handle();
assert!(app.data.radarr_data.prompt_confirm);
SystemDetailsHandler::with(
&key,
key,
&mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None,
ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
None,
)
.handle();
@@ -664,17 +646,12 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
SystemDetailsHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
)
.handle();
SystemDetailsHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::SystemTasks, None)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()
ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()
);
}
@@ -685,17 +662,12 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
SystemDetailsHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::SystemTasks,
&None,
)
.handle();
SystemDetailsHandler::with(SUBMIT_KEY, &mut app, ActiveRadarrBlock::SystemTasks, None)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into()
ActiveRadarrBlock::SystemTasks.into()
);
}
@@ -708,10 +680,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
SystemDetailsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None,
ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
None,
)
.handle();
@@ -722,7 +694,7 @@ mod tests {
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into()
ActiveRadarrBlock::SystemTasks.into()
);
}
@@ -734,10 +706,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
SystemDetailsHandler::with(
&SUBMIT_KEY,
SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None,
ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
None,
)
.handle();
@@ -745,7 +717,7 @@ mod tests {
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into()
ActiveRadarrBlock::SystemTasks.into()
);
}
}
@@ -776,10 +748,9 @@ mod tests {
.log_details
.set_items(vec![HorizontallyScrollableText::default()]);
SystemDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::SystemLogs, &None)
.handle();
SystemDetailsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::SystemLogs, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.data.radarr_data.log_details.items.is_empty());
}
@@ -795,10 +766,9 @@ mod tests {
.tasks
.set_items(vec![RadarrTask::default()]);
SystemDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::SystemTasks, &None)
.handle();
SystemDetailsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::SystemTasks, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
}
#[rstest]
@@ -814,14 +784,14 @@ mod tests {
.set_items(vec![QueueEvent::default()]);
SystemDetailsHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::SystemQueuedEvents,
&None,
ActiveRadarrBlock::SystemQueuedEvents,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
}
#[rstest]
@@ -831,10 +801,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into());
app.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into());
SystemDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::SystemUpdates, &None)
SystemDetailsHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::SystemUpdates, None)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
}
#[test]
@@ -845,16 +815,16 @@ mod tests {
app.data.radarr_data.prompt_confirm = true;
SystemDetailsHandler::with(
&ESC_KEY,
ESC_KEY,
&mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None,
ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into()
ActiveRadarrBlock::SystemTasks.into()
);
assert!(!app.data.radarr_data.prompt_confirm);
}
@@ -882,14 +852,14 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into());
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
assert_eq!(app.get_current_route(), &active_radarr_block.into());
assert_eq!(app.get_current_route(), active_radarr_block.into());
assert!(app.should_refresh);
}
@@ -909,14 +879,14 @@ mod tests {
app.push_navigation_stack(active_radarr_block.into());
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&active_radarr_block,
&None,
active_radarr_block,
None,
)
.handle();
assert_eq!(app.get_current_route(), &active_radarr_block.into());
assert_eq!(app.get_current_route(), active_radarr_block.into());
assert!(!app.should_refresh);
}
@@ -928,10 +898,10 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.confirm.key,
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
&ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
&None,
ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
None,
)
.handle();
@@ -942,7 +912,7 @@ mod tests {
);
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into()
ActiveRadarrBlock::SystemTasks.into()
);
}
}
@@ -951,9 +921,9 @@ mod tests {
fn test_system_details_handler_accepts() {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if SYSTEM_DETAILS_BLOCKS.contains(&active_radarr_block) {
assert!(SystemDetailsHandler::accepts(&active_radarr_block));
assert!(SystemDetailsHandler::accepts(active_radarr_block));
} else {
assert!(!SystemDetailsHandler::accepts(&active_radarr_block));
assert!(!SystemDetailsHandler::accepts(active_radarr_block));
}
})
}
@@ -964,25 +934,25 @@ mod tests {
app.is_loading = true;
let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_system_details_handler_not_ready_when_both_log_details_and_updates_are_empty() {
fn test_system_details_handler_not_ready_when_log_details_and_updates_and_tasks_are_empty() {
let mut app = App::default();
app.is_loading = false;
let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
);
assert!(!handler.is_ready());
@@ -999,10 +969,30 @@ mod tests {
.set_items(vec![HorizontallyScrollableText::default()]);
let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
);
assert!(handler.is_ready());
}
#[test]
fn test_system_details_handler_ready_when_not_loading_and_tasks_is_not_empty() {
let mut app = App::default();
app.is_loading = false;
app
.data
.radarr_data
.tasks
.set_items(vec![RadarrTask::default()]);
let handler = SystemDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::SystemTasks,
None,
);
assert!(handler.is_ready());
@@ -1015,10 +1005,10 @@ mod tests {
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
let handler = SystemDetailsHandler::with(
&DEFAULT_KEYBINDINGS.esc.key,
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
&ActiveRadarrBlock::SystemUpdates,
&None,
ActiveRadarrBlock::SystemUpdates,
None,
);
assert!(handler.is_ready());
@@ -28,18 +28,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(6);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.left.key,
DEFAULT_KEYBINDINGS.left.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Indexers.into()
ActiveRadarrBlock::Indexers.into()
);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
#[rstest]
@@ -49,18 +49,18 @@ mod tests {
app.data.radarr_data.main_tabs.set_index(6);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.right.key,
DEFAULT_KEYBINDINGS.right.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(
app.data.radarr_data.main_tabs.get_active_route(),
&ActiveRadarrBlock::Movies.into()
ActiveRadarrBlock::Movies.into()
);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
}
}
@@ -79,9 +79,9 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into());
app.push_navigation_stack(ActiveRadarrBlock::System.into());
SystemHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::System, &None).handle();
SystemHandler::with(ESC_KEY, &mut app, ActiveRadarrBlock::System, None).handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.error.text.is_empty());
}
}
@@ -112,16 +112,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemUpdates.into()
ActiveRadarrBlock::SystemUpdates.into()
);
}
@@ -146,14 +146,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
}
#[test]
@@ -175,16 +175,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.events.key,
DEFAULT_KEYBINDINGS.events.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemQueuedEvents.into()
ActiveRadarrBlock::SystemQueuedEvents.into()
);
}
@@ -209,14 +209,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.events.key,
DEFAULT_KEYBINDINGS.events.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
}
#[test]
@@ -239,14 +239,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into());
SystemHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.should_refresh);
}
@@ -272,14 +272,14 @@ mod tests {
app.push_navigation_stack(ActiveRadarrBlock::System.into());
SystemHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key,
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(!app.should_refresh);
}
@@ -302,16 +302,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.logs.key,
DEFAULT_KEYBINDINGS.logs.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemLogs.into()
ActiveRadarrBlock::SystemLogs.into()
);
assert_eq!(
app.data.radarr_data.log_details.items,
@@ -344,14 +344,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.logs.key,
DEFAULT_KEYBINDINGS.logs.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
assert!(app.data.radarr_data.log_details.is_empty());
}
@@ -374,16 +374,16 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.tasks.key,
DEFAULT_KEYBINDINGS.tasks.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::SystemTasks.into()
ActiveRadarrBlock::SystemTasks.into()
);
}
@@ -408,14 +408,14 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
SystemHandler::with(
&DEFAULT_KEYBINDINGS.tasks.key,
DEFAULT_KEYBINDINGS.tasks.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
assert_eq!(app.get_current_route(), ActiveRadarrBlock::System.into());
}
}
@@ -444,9 +444,9 @@ mod tests {
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
if system_blocks.contains(&active_radarr_block) {
assert!(SystemHandler::accepts(&active_radarr_block));
assert!(SystemHandler::accepts(active_radarr_block));
} else {
assert!(!SystemHandler::accepts(&active_radarr_block));
assert!(!SystemHandler::accepts(active_radarr_block));
}
})
}
@@ -457,10 +457,10 @@ mod tests {
app.is_loading = true;
let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
@@ -482,10 +482,10 @@ mod tests {
.set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
@@ -503,10 +503,10 @@ mod tests {
.set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
@@ -524,10 +524,10 @@ mod tests {
.set_items(vec![RadarrTask::default()]);
let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
@@ -550,10 +550,10 @@ mod tests {
.set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
DEFAULT_KEYBINDINGS.update.key,
&mut app,
&ActiveRadarrBlock::System,
&None,
ActiveRadarrBlock::System,
None,
);
assert!(system_handler.is_ready());
@@ -0,0 +1,945 @@
#[cfg(test)]
mod tests {
use std::cmp::Ordering;
use chrono::DateTime;
use pretty_assertions::{assert_eq, assert_str_eq};
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::blocklist::{blocklist_sorting_options, BlocklistHandler};
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, BLOCKLIST_BLOCKS};
use crate::models::servarr_models::{Language, Quality, QualityWrapper};
use crate::models::sonarr_models::BlocklistItem;
use crate::models::stateful_table::SortOption;
mod test_handle_scroll_up_and_down {
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest;
use crate::models::sonarr_models::BlocklistItem;
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
use super::*;
test_iterable_scroll!(
test_blocklist_scroll,
BlocklistHandler,
sonarr_data,
blocklist,
simple_stateful_iterable_vec!(BlocklistItem, String, source_title),
ActiveSonarrBlock::Blocklist,
None,
source_title,
to_string
);
#[rstest]
fn test_blocklist_scroll_no_op_when_not_ready(
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = true;
app
.data
.sonarr_data
.blocklist
.set_items(simple_stateful_iterable_vec!(
BlocklistItem,
String,
source_title
));
BlocklistHandler::with(key, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_str_eq!(
app
.data
.sonarr_data
.blocklist
.current_selection()
.source_title
.to_string(),
"Test 1"
);
BlocklistHandler::with(key, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_str_eq!(
app
.data
.sonarr_data
.blocklist
.current_selection()
.source_title
.to_string(),
"Test 1"
);
}
#[rstest]
fn test_blocklist_sort_scroll(
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
) {
let blocklist_field_vec = sort_options();
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.sorting(sort_options());
if key == Key::Up {
for i in (0..blocklist_field_vec.len()).rev() {
BlocklistHandler::with(key, &mut app, ActiveSonarrBlock::BlocklistSortPrompt, None)
.handle();
assert_eq!(
app
.data
.sonarr_data
.blocklist
.sort
.as_ref()
.unwrap()
.current_selection(),
&blocklist_field_vec[i]
);
}
} else {
for i in 0..blocklist_field_vec.len() {
BlocklistHandler::with(key, &mut app, ActiveSonarrBlock::BlocklistSortPrompt, None)
.handle();
assert_eq!(
app
.data
.sonarr_data
.blocklist
.sort
.as_ref()
.unwrap()
.current_selection(),
&blocklist_field_vec[(i + 1) % blocklist_field_vec.len()]
);
}
}
}
}
mod test_handle_home_end {
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::models::sonarr_models::BlocklistItem;
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
use super::*;
test_iterable_home_and_end!(
test_blocklist_home_and_end,
BlocklistHandler,
sonarr_data,
blocklist,
extended_stateful_iterable_vec!(BlocklistItem, String, source_title),
ActiveSonarrBlock::Blocklist,
None,
source_title,
to_string
);
#[test]
fn test_blocklist_home_and_end_no_op_when_not_ready() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = true;
app
.data
.sonarr_data
.blocklist
.set_items(extended_stateful_iterable_vec!(
BlocklistItem,
String,
source_title
));
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.blocklist
.current_selection()
.source_title
.to_string(),
"Test 1"
);
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.blocklist
.current_selection()
.source_title
.to_string(),
"Test 1"
);
}
#[test]
fn test_blocklist_sort_home_end() {
let blocklist_field_vec = sort_options();
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.sorting(sort_options());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::BlocklistSortPrompt,
None,
)
.handle();
assert_eq!(
app
.data
.sonarr_data
.blocklist
.sort
.as_ref()
.unwrap()
.current_selection(),
&blocklist_field_vec[blocklist_field_vec.len() - 1]
);
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::BlocklistSortPrompt,
None,
)
.handle();
assert_eq!(
app
.data
.sonarr_data
.blocklist
.sort
.as_ref()
.unwrap()
.current_selection(),
&blocklist_field_vec[0]
);
}
}
mod test_handle_delete {
use pretty_assertions::assert_eq;
use super::*;
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
#[test]
fn test_delete_blocklist_item_prompt() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteBlocklistItemPrompt.into()
);
}
#[test]
fn test_delete_blocklist_item_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_blocklist_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(2);
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Downloads.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
}
#[rstest]
fn test_blocklist_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(2);
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::History.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
}
#[rstest]
fn test_blocklist_left_right_prompt_toggle(
#[values(
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt
)]
active_sonarr_block: ActiveSonarrBlock,
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
BlocklistHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_blocklist_submit() {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::BlocklistItemDetails.into()
);
}
#[test]
fn test_blocklist_submit_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
SonarrEvent::DeleteBlocklistItem(None)
)]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt,
SonarrEvent::ClearBlocklist
)]
fn test_blocklist_prompt_confirm_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
#[rstest]
fn test_blocklist_prompt_decline_submit(
#[values(
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt
)]
prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[test]
fn test_blocklist_sort_prompt_submit() {
let mut app = App::default();
app.data.sonarr_data.blocklist.sort_asc = true;
app.data.sonarr_data.blocklist.sorting(sort_options());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveSonarrBlock::BlocklistSortPrompt.into());
let mut expected_vec = blocklist_vec();
expected_vec.sort_by(|a, b| a.id.cmp(&b.id));
expected_vec.reverse();
BlocklistHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::BlocklistSortPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert_eq!(app.data.sonarr_data.blocklist.items, expected_vec);
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt
)]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt
)]
fn test_blocklist_prompt_blocks_esc(
#[case] base_block: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(base_block.into());
app.push_navigation_stack(prompt_block.into());
app.data.sonarr_data.prompt_confirm = true;
BlocklistHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
assert_eq!(app.get_current_route(), base_block.into());
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[test]
fn test_esc_blocklist_item_details() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveSonarrBlock::BlocklistItemDetails.into());
BlocklistHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::BlocklistItemDetails,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[test]
fn test_blocklist_sort_prompt_block_esc() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveSonarrBlock::BlocklistSortPrompt.into());
BlocklistHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::BlocklistSortPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::Blocklist, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
#[test]
fn test_refresh_blocklist_key() {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_blocklist_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert!(!app.should_refresh);
}
#[test]
fn test_clear_blocklist_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.clear.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::BlocklistClearAllItemsPrompt.into()
);
}
#[test]
fn test_clear_blocklist_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.clear.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[test]
fn test_sort_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.sort.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::BlocklistSortPrompt.into()
);
assert_eq!(
app.data.sonarr_data.blocklist.sort.as_ref().unwrap().items,
blocklist_sorting_options()
);
assert!(!app.data.sonarr_data.blocklist.sort_asc);
}
#[test]
fn test_sort_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.sort.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
assert!(app.data.sonarr_data.blocklist.sort.is_none());
assert!(!app.data.sonarr_data.blocklist.sort_asc);
}
#[rstest]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
SonarrEvent::DeleteBlocklistItem(None)
)]
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt,
SonarrEvent::ClearBlocklist
)]
fn test_blocklist_prompt_confirm(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
BlocklistHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
prompt_block,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
}
#[test]
fn test_blocklist_sorting_options_series_title() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
a.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(
&b.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase(),
)
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[0].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Series Title");
}
#[test]
fn test_blocklist_sorting_options_source_title() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
a.source_title
.to_lowercase()
.cmp(&b.source_title.to_lowercase())
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[1].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Source Title");
}
#[test]
fn test_blocklist_sorting_options_language() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
a.language
.name
.to_lowercase()
.cmp(&b.language.name.to_lowercase())
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[2].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Language");
}
#[test]
fn test_blocklist_sorting_options_quality() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering = |a, b| {
a.quality
.quality
.name
.to_lowercase()
.cmp(&b.quality.quality.name.to_lowercase())
};
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[3].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Quality");
}
#[test]
fn test_blocklist_sorting_options_date() {
let expected_cmp_fn: fn(&BlocklistItem, &BlocklistItem) -> Ordering =
|a, b| a.date.cmp(&b.date);
let mut expected_blocklist_vec = blocklist_vec();
expected_blocklist_vec.sort_by(expected_cmp_fn);
let sort_option = blocklist_sorting_options()[4].clone();
let mut sorted_blocklist_vec = blocklist_vec();
sorted_blocklist_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_blocklist_vec, expected_blocklist_vec);
assert_str_eq!(sort_option.name, "Date");
}
#[test]
fn test_blocklist_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if BLOCKLIST_BLOCKS.contains(&active_sonarr_block) {
assert!(BlocklistHandler::accepts(active_sonarr_block));
} else {
assert!(!BlocklistHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_blocklist_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = true;
let handler = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_blocklist_handler_not_ready_when_blocklist_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = false;
let handler = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_blocklist_handler_ready_when_not_loading_and_blocklist_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Blocklist.into());
app.is_loading = false;
app
.data
.sonarr_data
.blocklist
.set_items(vec![BlocklistItem::default()]);
let handler = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
);
assert!(handler.is_ready());
}
fn blocklist_vec() -> Vec<BlocklistItem> {
vec![
BlocklistItem {
id: 3,
source_title: "test 1".to_owned(),
language: Language {
id: 1,
name: "telgu".to_owned(),
},
quality: QualityWrapper {
quality: Quality {
name: "HD - 1080p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-01-10T07:28:45Z").unwrap()),
series_title: Some("test 3".into()),
..BlocklistItem::default()
},
BlocklistItem {
id: 2,
source_title: "test 2".to_owned(),
language: Language {
id: 3,
name: "chinese".to_owned(),
},
quality: QualityWrapper {
quality: Quality {
name: "SD - 720p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
series_title: Some("test 2".into()),
..BlocklistItem::default()
},
BlocklistItem {
id: 1,
source_title: "test 3".to_owned(),
language: Language {
id: 1,
name: "english".to_owned(),
},
quality: QualityWrapper {
quality: Quality {
name: "HD - 1080p".to_owned(),
},
},
date: DateTime::from(DateTime::parse_from_rfc3339("2024-03-10T07:28:45Z").unwrap()),
series_title: None,
..BlocklistItem::default()
},
]
}
fn sort_options() -> Vec<SortOption<BlocklistItem>> {
vec![SortOption {
name: "Test 1",
cmp_fn: Some(|a, b| {
b.source_title
.to_lowercase()
.cmp(&a.source_title.to_lowercase())
}),
}]
}
}
@@ -0,0 +1,302 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, BLOCKLIST_BLOCKS};
use crate::models::sonarr_models::BlocklistItem;
use crate::models::stateful_table::SortOption;
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "blocklist_handler_tests.rs"]
mod blocklist_handler_tests;
pub(super) struct BlocklistHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> BlocklistHandler<'a, 'b> {
handle_table_events!(
self,
blocklist,
self.app.data.sonarr_data.blocklist,
BlocklistItem
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for BlocklistHandler<'a, 'b> {
fn handle(&mut self) {
let blocklist_table_handling_props =
TableHandlingProps::new(ActiveSonarrBlock::Blocklist.into())
.sorting_block(ActiveSonarrBlock::BlocklistSortPrompt.into())
.sort_by_fn(|a: &BlocklistItem, b: &BlocklistItem| a.id.cmp(&b.id))
.sort_options(blocklist_sorting_options());
if !self.handle_blocklist_table_events(blocklist_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
BLOCKLIST_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> Self {
BlocklistHandler {
key,
app,
active_sonarr_block: active_block,
_context: context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.blocklist.is_empty()
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => self.app.data.sonarr_data.blocklist.scroll_up(),
ActiveSonarrBlock::BlocklistSortPrompt => self
.app
.data
.sonarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => self.app.data.sonarr_data.blocklist.scroll_down(),
ActiveSonarrBlock::BlocklistSortPrompt => self
.app
.data
.sonarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => self.app.data.sonarr_data.blocklist.scroll_to_top(),
ActiveSonarrBlock::BlocklistSortPrompt => self
.app
.data
.sonarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => self.app.data.sonarr_data.blocklist.scroll_to_bottom(),
ActiveSonarrBlock::BlocklistSortPrompt => self
.app
.data
.sonarr_data
.blocklist
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
}
}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::Blocklist {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteBlocklistItemPrompt.into());
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::DeleteBlocklistItemPrompt
| ActiveSonarrBlock::BlocklistClearAllItemsPrompt => handle_prompt_toggle(self.app, self.key),
_ => {}
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteBlocklistItemPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteBlocklistItem(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::ClearBlocklist);
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::BlocklistSortPrompt => {
self
.app
.data
.sonarr_data
.blocklist
.items
.sort_by(|a, b| a.id.cmp(&b.id));
self.app.data.sonarr_data.blocklist.apply_sorting();
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::Blocklist => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::BlocklistItemDetails.into());
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteBlocklistItemPrompt
| ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::BlocklistItemDetails | ActiveSonarrBlock::BlocklistSortPrompt => {
self.app.pop_navigation_stack();
}
_ => handle_clear_errors(self.app),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::Blocklist => match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if key == DEFAULT_KEYBINDINGS.clear.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::BlocklistClearAllItemsPrompt.into());
}
_ if key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.sonarr_data
.blocklist
.sorting(blocklist_sorting_options());
self
.app
.push_navigation_stack(ActiveSonarrBlock::BlocklistSortPrompt.into());
}
_ => (),
},
ActiveSonarrBlock::DeleteBlocklistItemPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteBlocklistItem(None));
self.app.pop_navigation_stack();
}
}
ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::ClearBlocklist);
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
fn blocklist_sorting_options() -> Vec<SortOption<BlocklistItem>> {
vec![
SortOption {
name: "Series Title",
cmp_fn: Some(|a, b| {
a.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(
&b.series_title
.as_ref()
.unwrap_or(&String::new())
.to_lowercase(),
)
}),
},
SortOption {
name: "Source Title",
cmp_fn: Some(|a, b| {
a.source_title
.to_lowercase()
.cmp(&b.source_title.to_lowercase())
}),
},
SortOption {
name: "Language",
cmp_fn: Some(|a, b| {
a.language
.name
.to_lowercase()
.cmp(&b.language.name.to_lowercase())
}),
},
SortOption {
name: "Quality",
cmp_fn: Some(|a, b| {
a.quality
.quality
.name
.to_lowercase()
.cmp(&b.quality.quality.name.to_lowercase())
}),
},
SortOption {
name: "Date",
cmp_fn: Some(|a, b| a.date.cmp(&b.date)),
},
]
}
@@ -0,0 +1,550 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::downloads::DownloadsHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DOWNLOADS_BLOCKS};
use crate::models::sonarr_models::DownloadRecord;
mod test_handle_scroll_up_and_down {
use rstest::rstest;
use crate::models::sonarr_models::DownloadRecord;
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
use super::*;
test_iterable_scroll!(
test_downloads_scroll,
DownloadsHandler,
sonarr_data,
downloads,
DownloadRecord,
ActiveSonarrBlock::Downloads,
None,
title
);
#[rstest]
fn test_downloads_scroll_no_op_when_not_ready(
#[values(
DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key
)]
key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = true;
app
.data
.sonarr_data
.downloads
.set_items(simple_stateful_iterable_vec!(DownloadRecord));
DownloadsHandler::with(key, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_str_eq!(
app.data.sonarr_data.downloads.current_selection().title,
"Test 1"
);
DownloadsHandler::with(key, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_str_eq!(
app.data.sonarr_data.downloads.current_selection().title,
"Test 1"
);
}
}
mod test_handle_home_end {
use crate::models::sonarr_models::DownloadRecord;
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
use super::*;
test_iterable_home_and_end!(
test_downloads_home_end,
DownloadsHandler,
sonarr_data,
downloads,
DownloadRecord,
ActiveSonarrBlock::Downloads,
None,
title
);
#[test]
fn test_downloads_home_end_no_op_when_not_ready() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = true;
app
.data
.sonarr_data
.downloads
.set_items(extended_stateful_iterable_vec!(DownloadRecord));
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.downloads.current_selection().title,
"Test 1"
);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.downloads.current_selection().title,
"Test 1"
);
}
}
mod test_handle_delete {
use pretty_assertions::assert_eq;
use super::*;
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
#[test]
fn test_delete_download_prompt() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteDownloadPrompt.into()
);
}
#[test]
fn test_delete_download_prompt_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
}
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_downloads_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(1);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Series.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
}
#[rstest]
fn test_downloads_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(1);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Blocklist.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Blocklist.into());
}
#[rstest]
fn test_downloads_left_right_prompt_toggle(
#[values(
ActiveSonarrBlock::DeleteDownloadPrompt,
ActiveSonarrBlock::UpdateDownloadsPrompt
)]
active_sonarr_block: ActiveSonarrBlock,
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
DownloadsHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
DownloadsHandler::with(key, &mut app, active_sonarr_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[rstest]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(None)
)]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::UpdateDownloadsPrompt,
SonarrEvent::UpdateDownloads
)]
fn test_downloads_prompt_confirm_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
#[rstest]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::DeleteDownloadPrompt)]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::UpdateDownloadsPrompt)]
fn test_downloads_prompt_decline_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(SUBMIT_KEY, &mut app, prompt_block, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), base_route.into());
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::DeleteDownloadPrompt)]
#[case(ActiveSonarrBlock::Downloads, ActiveSonarrBlock::UpdateDownloadsPrompt)]
fn test_downloads_prompt_blocks_esc(
#[case] base_block: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(base_block.into());
app.push_navigation_stack(prompt_block.into());
app.data.sonarr_data.prompt_confirm = true;
DownloadsHandler::with(ESC_KEY, &mut app, prompt_block, None).handle();
assert_eq!(app.get_current_route(), base_block.into());
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
DownloadsHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::Downloads, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
#[test]
fn test_update_downloads_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::UpdateDownloadsPrompt.into()
);
}
#[test]
fn test_update_downloads_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
}
#[test]
fn test_refresh_downloads_key() {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_downloads_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Downloads.into());
assert!(!app.should_refresh);
}
#[rstest]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(None)
)]
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::UpdateDownloadsPrompt,
SonarrEvent::UpdateDownloads
)]
fn test_downloads_prompt_confirm_submit(
#[case] base_route: ActiveSonarrBlock,
#[case] prompt_block: ActiveSonarrBlock,
#[case] expected_action: SonarrEvent,
) {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
DownloadsHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
prompt_block,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(expected_action)
);
assert_eq!(app.get_current_route(), base_route.into());
}
}
#[test]
fn test_downloads_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if DOWNLOADS_BLOCKS.contains(&active_sonarr_block) {
assert!(DownloadsHandler::accepts(active_sonarr_block));
} else {
assert!(!DownloadsHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_downloads_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = true;
let handler = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_downloads_handler_not_ready_when_downloads_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = false;
let handler = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_downloads_handler_ready_when_not_loading_and_downloads_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Downloads.into());
app.is_loading = false;
app
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
let handler = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,158 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DOWNLOADS_BLOCKS};
use crate::models::sonarr_models::DownloadRecord;
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "downloads_handler_tests.rs"]
mod downloads_handler_tests;
pub(super) struct DownloadsHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> DownloadsHandler<'a, 'b> {
handle_table_events!(
self,
downloads,
self.app.data.sonarr_data.downloads,
DownloadRecord
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DownloadsHandler<'a, 'b> {
fn handle(&mut self) {
let download_table_handling_props =
TableHandlingProps::new(ActiveSonarrBlock::Downloads.into());
if !self.handle_downloads_table_events(download_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
DOWNLOADS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> DownloadsHandler<'a, 'b> {
DownloadsHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.downloads.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::Downloads {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteDownloadPrompt.into())
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Downloads => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::DeleteDownloadPrompt | ActiveSonarrBlock::UpdateDownloadsPrompt => {
handle_prompt_toggle(self.app, self.key)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteDownloadPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteDownload(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::UpdateDownloadsPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::UpdateDownloads);
}
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteDownloadPrompt | ActiveSonarrBlock::UpdateDownloadsPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
_ => handle_clear_errors(self.app),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::Downloads => match self.key {
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::UpdateDownloadsPrompt.into());
}
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ => (),
},
ActiveSonarrBlock::DeleteDownloadPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteDownload(None));
self.app.pop_navigation_stack();
}
}
ActiveSonarrBlock::UpdateDownloadsPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::UpdateDownloads);
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
File diff suppressed because it is too large Load Diff
+167
View File
@@ -0,0 +1,167 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, HISTORY_BLOCKS};
use crate::models::sonarr_models::SonarrHistoryItem;
use crate::models::stateful_table::SortOption;
use crate::models::Scrollable;
#[cfg(test)]
#[path = "history_handler_tests.rs"]
mod history_handler_tests;
pub(super) struct HistoryHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> HistoryHandler<'a, 'b> {
handle_table_events!(
self,
history,
self.app.data.sonarr_data.history,
SonarrHistoryItem
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for HistoryHandler<'a, 'b> {
fn handle(&mut self) {
let history_table_handling_props = TableHandlingProps::new(ActiveSonarrBlock::History.into())
.sorting_block(ActiveSonarrBlock::HistorySortPrompt.into())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.sort_options(history_sorting_options())
.searching_block(ActiveSonarrBlock::SearchHistory.into())
.search_error_block(ActiveSonarrBlock::SearchHistoryError.into())
.search_field_fn(|history| &history.source_title.text)
.filtering_block(ActiveSonarrBlock::FilterHistory.into())
.filter_error_block(ActiveSonarrBlock::FilterHistoryError.into())
.filter_field_fn(|history| &history.source_title.text);
if !self.handle_history_table_events(history_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
HISTORY_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> Self {
HistoryHandler {
key,
app,
active_sonarr_block: active_block,
_context: context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.history.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::History {
handle_change_tab_left_right_keys(self.app, self.key)
}
}
fn handle_submit(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::History {
self
.app
.push_navigation_stack(ActiveSonarrBlock::HistoryItemDetails.into());
}
}
fn handle_esc(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::HistoryItemDetails {
self.app.pop_navigation_stack();
} else {
handle_clear_errors(self.app);
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
if self.active_sonarr_block == ActiveSonarrBlock::History {
match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ => (),
}
}
}
}
pub(in crate::handlers::sonarr_handlers) fn history_sorting_options(
) -> Vec<SortOption<SonarrHistoryItem>> {
vec![
SortOption {
name: "Source Title",
cmp_fn: Some(|a, b| {
a.source_title
.text
.to_lowercase()
.cmp(&b.source_title.text.to_lowercase())
}),
},
SortOption {
name: "Event Type",
cmp_fn: Some(|a, b| {
a.event_type
.to_string()
.to_lowercase()
.cmp(&b.event_type.to_string().to_lowercase())
}),
},
SortOption {
name: "Language",
cmp_fn: Some(|a, b| {
a.language
.name
.to_lowercase()
.cmp(&b.language.name.to_lowercase())
}),
},
SortOption {
name: "Quality",
cmp_fn: Some(|a, b| {
a.quality
.quality
.name
.to_lowercase()
.cmp(&b.quality.quality.name.to_lowercase())
}),
},
SortOption {
name: "Date",
cmp_fn: Some(|a, b| a.date.cmp(&b.date)),
},
]
}
@@ -0,0 +1,476 @@
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::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_prompt_left_right_keys, 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: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditIndexerHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EDIT_INDEXER_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> EditIndexerHandler<'a, 'b> {
EditIndexerHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && self.app.data.sonarr_data.edit_indexer_modal.is_some()
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
self.app.data.sonarr_data.selected_block.up();
}
ActiveSonarrBlock::EditIndexerPriorityInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.priority += 1;
}
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
self.app.data.sonarr_data.selected_block.down();
}
ActiveSonarrBlock::EditIndexerPriorityInput => {
let edit_indexer_modal = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
if edit_indexer_modal.priority > 0 {
edit_indexer_modal.priority -= 1;
}
}
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerNameInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
.scroll_home();
}
ActiveSonarrBlock::EditIndexerUrlInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
.scroll_home();
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
.scroll_home();
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
.scroll_home();
}
ActiveSonarrBlock::EditIndexerTagsInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.tags
.scroll_home();
}
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerNameInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
.reset_offset();
}
ActiveSonarrBlock::EditIndexerUrlInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
.reset_offset();
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
.reset_offset();
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
.reset_offset();
}
ActiveSonarrBlock::EditIndexerTagsInput => {
self
.app
.data
.sonarr_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_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
handle_prompt_left_right_keys!(
self,
ActiveSonarrBlock::EditIndexerConfirmPrompt,
sonarr_data
);
}
ActiveSonarrBlock::EditIndexerNameInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
);
}
ActiveSonarrBlock::EditIndexerUrlInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
);
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
);
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
);
}
ActiveSonarrBlock::EditIndexerTagsInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.tags
);
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
let selected_block = self.app.data.sonarr_data.selected_block.get_active_block();
match selected_block {
ActiveSonarrBlock::EditIndexerConfirmPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditIndexer(None));
self.app.should_refresh = true;
} else {
sonarr_data.edit_indexer_modal = None;
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::EditIndexerNameInput
| ActiveSonarrBlock::EditIndexerUrlInput
| ActiveSonarrBlock::EditIndexerApiKeyInput
| ActiveSonarrBlock::EditIndexerSeedRatioInput
| ActiveSonarrBlock::EditIndexerTagsInput => {
self.app.push_navigation_stack(selected_block.into());
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::EditIndexerPriorityInput => self
.app
.push_navigation_stack(ActiveSonarrBlock::EditIndexerPriorityInput.into()),
ActiveSonarrBlock::EditIndexerToggleEnableRss => {
let indexer = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
indexer.enable_rss = Some(!indexer.enable_rss.unwrap_or_default());
}
ActiveSonarrBlock::EditIndexerToggleEnableAutomaticSearch => {
let indexer = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
indexer.enable_automatic_search =
Some(!indexer.enable_automatic_search.unwrap_or_default());
}
ActiveSonarrBlock::EditIndexerToggleEnableInteractiveSearch => {
let indexer = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap();
indexer.enable_interactive_search =
Some(!indexer.enable_interactive_search.unwrap_or_default());
}
_ => (),
}
}
ActiveSonarrBlock::EditIndexerNameInput
| ActiveSonarrBlock::EditIndexerUrlInput
| ActiveSonarrBlock::EditIndexerApiKeyInput
| ActiveSonarrBlock::EditIndexerSeedRatioInput
| ActiveSonarrBlock::EditIndexerTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
ActiveSonarrBlock::EditIndexerPriorityInput => self.app.pop_navigation_stack(),
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditIndexerPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
self.app.data.sonarr_data.edit_indexer_modal = None;
}
ActiveSonarrBlock::EditIndexerNameInput
| ActiveSonarrBlock::EditIndexerUrlInput
| ActiveSonarrBlock::EditIndexerApiKeyInput
| ActiveSonarrBlock::EditIndexerSeedRatioInput
| ActiveSonarrBlock::EditIndexerPriorityInput
| ActiveSonarrBlock::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_sonarr_block {
ActiveSonarrBlock::EditIndexerNameInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.name
);
}
ActiveSonarrBlock::EditIndexerUrlInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.url
);
}
ActiveSonarrBlock::EditIndexerApiKeyInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.api_key
);
}
ActiveSonarrBlock::EditIndexerSeedRatioInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.seed_ratio
);
}
ActiveSonarrBlock::EditIndexerTagsInput => {
handle_text_box_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_mut()
.unwrap()
.tags
);
}
ActiveSonarrBlock::EditIndexerPrompt => {
if self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::EditIndexerConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditIndexer(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,182 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_prompt_left_right_keys;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
};
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "edit_indexer_settings_handler_tests.rs"]
mod edit_indexer_settings_handler_tests;
pub(super) struct IndexerSettingsHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexerSettingsHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
INDEXER_SETTINGS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> IndexerSettingsHandler<'a, 'b> {
IndexerSettingsHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && self.app.data.sonarr_data.indexer_settings.is_some()
}
fn handle_scroll_up(&mut self) {
let indexer_settings = self.app.data.sonarr_data.indexer_settings.as_mut().unwrap();
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
self.app.data.sonarr_data.selected_block.up();
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput => {
indexer_settings.minimum_age += 1;
}
ActiveSonarrBlock::IndexerSettingsRetentionInput => {
indexer_settings.retention += 1;
}
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput => {
indexer_settings.maximum_size += 1;
}
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => {
indexer_settings.rss_sync_interval += 1;
}
_ => (),
}
}
fn handle_scroll_down(&mut self) {
let indexer_settings = self.app.data.sonarr_data.indexer_settings.as_mut().unwrap();
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
self.app.data.sonarr_data.selected_block.down()
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput => {
if indexer_settings.minimum_age > 0 {
indexer_settings.minimum_age -= 1;
}
}
ActiveSonarrBlock::IndexerSettingsRetentionInput => {
if indexer_settings.retention > 0 {
indexer_settings.retention -= 1;
}
}
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput => {
if indexer_settings.maximum_size > 0 {
indexer_settings.maximum_size -= 1;
}
}
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => {
if indexer_settings.rss_sync_interval > 0 {
indexer_settings.rss_sync_interval -= 1;
}
}
_ => (),
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::AllIndexerSettingsPrompt {
handle_prompt_left_right_keys!(
self,
ActiveSonarrBlock::IndexerSettingsConfirmPrompt,
sonarr_data
);
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::IndexerSettingsConfirmPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditAllIndexerSettings(None));
self.app.should_refresh = true;
} else {
sonarr_data.indexer_settings = None;
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput
| ActiveSonarrBlock::IndexerSettingsRetentionInput
| ActiveSonarrBlock::IndexerSettingsMaximumSizeInput
| ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => {
self.app.push_navigation_stack(
(
self.app.data.sonarr_data.selected_block.get_active_block(),
None,
)
.into(),
)
}
_ => (),
}
}
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput
| ActiveSonarrBlock::IndexerSettingsRetentionInput
| ActiveSonarrBlock::IndexerSettingsMaximumSizeInput
| ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput => self.app.pop_navigation_stack(),
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
self.app.data.sonarr_data.indexer_settings = None;
}
_ => self.app.pop_navigation_stack(),
}
}
fn handle_char_key_event(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::AllIndexerSettingsPrompt
&& self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::IndexerSettingsConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::EditAllIndexerSettings(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
}
@@ -0,0 +1,570 @@
#[cfg(test)]
mod tests {
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
};
use crate::models::sonarr_models::IndexerSettings;
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS;
use crate::models::sonarr_models::IndexerSettings;
use crate::models::BlockSelectionState;
use super::*;
macro_rules! test_i64_counter_scroll_value {
($block:expr, $key:expr, $data_ref:ident, $negatives:literal) => {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
if $key == Key::Up {
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
1
);
} else {
if $negatives {
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
-1
);
} else {
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
0
);
IndexerSettingsHandler::with(Key::Up, &mut app, $block, None).handle();
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
1
);
IndexerSettingsHandler::with($key, &mut app, $block, None).handle();
assert_eq!(
app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.$data_ref,
0
);
}
}
};
}
#[rstest]
fn test_edit_indexer_settings_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
if key == Key::Up {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput
);
} else {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput
);
}
}
#[rstest]
fn test_edit_indexer_settings_prompt_scroll_no_op_when_not_ready(
#[values(Key::Up, Key::Down)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::IndexerSettingsRetentionInput
);
}
#[rstest]
fn test_edit_indexer_settings_minimum_age_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
key,
minimum_age,
false
);
}
#[rstest]
fn test_edit_indexer_settings_retention_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsRetentionInput,
key,
retention,
false
);
}
#[rstest]
fn test_edit_indexer_settings_maximum_size_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
key,
maximum_size,
false
);
}
#[rstest]
fn test_edit_indexer_settings_rss_sync_interval_scroll(#[values(Key::Up, Key::Down)] key: Key) {
test_i64_counter_scroll_value!(
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput,
key,
rss_sync_interval,
false
);
}
}
mod test_handle_left_right_action {
use crate::models::servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use rstest::rstest;
use super::*;
#[rstest]
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.y = INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1;
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
IndexerSettingsHandler::with(
key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::{
models::{
servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
sonarr_models::IndexerSettings, BlockSelectionState,
},
network::sonarr_network::SonarrEvent,
};
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_edit_indexer_settings_prompt_prompt_decline_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
assert_eq!(app.data.sonarr_data.indexer_settings, None);
}
#[test]
fn test_edit_indexer_settings_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditAllIndexerSettings(None))
);
assert!(app.data.sonarr_data.indexer_settings.is_some());
assert!(app.should_refresh);
}
#[test]
fn test_edit_indexer_settings_prompt_prompt_confirmation_submit_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
assert!(!app.should_refresh);
}
#[rstest]
#[case(ActiveSonarrBlock::IndexerSettingsMinimumAgeInput, 0)]
#[case(ActiveSonarrBlock::IndexerSettingsRetentionInput, 1)]
#[case(ActiveSonarrBlock::IndexerSettingsMaximumSizeInput, 2)]
#[case(ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput, 3)]
fn test_edit_indexer_settings_prompt_submit_selected_block(
#[case] selected_block: ActiveSonarrBlock,
#[case] y_index: usize,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.set_index(0, y_index);
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), selected_block.into());
}
#[rstest]
fn test_edit_indexer_settings_prompt_submit_selected_block_no_op_when_not_ready(
#[values(0, 1, 2, 3, 4)] y_index: usize,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.set_index(0, y_index);
IndexerSettingsHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
}
#[rstest]
fn test_edit_indexer_settings_selected_block_submit(
#[values(
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
ActiveSonarrBlock::IndexerSettingsRetentionInput,
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.push_navigation_stack(active_sonarr_block.into());
IndexerSettingsHandler::with(SUBMIT_KEY, &mut app, active_sonarr_block, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::sonarr_models::IndexerSettings;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
fn test_edit_indexer_settings_prompt_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.indexer_settings, None);
}
#[rstest]
fn test_edit_indexer_settings_selected_blocks_esc(
#[values(
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
ActiveSonarrBlock::IndexerSettingsRetentionInput,
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(active_sonarr_block.into());
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(ESC_KEY, &mut app, active_sonarr_block, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.indexer_settings,
Some(IndexerSettings::default())
);
}
}
mod test_handle_key_char {
use crate::{
models::{
servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
sonarr_models::IndexerSettings, BlockSelectionState,
},
network::sonarr_network::SonarrEvent,
};
use super::*;
#[test]
fn test_edit_indexer_settings_prompt_prompt_confirmation_confirm() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditAllIndexerSettings(None))
);
assert!(app.data.sonarr_data.indexer_settings.is_some());
assert!(app.should_refresh);
}
}
#[test]
fn test_indexer_settings_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if INDEXER_SETTINGS_BLOCKS.contains(&active_sonarr_block) {
assert!(IndexerSettingsHandler::accepts(active_sonarr_block));
} else {
assert!(!IndexerSettingsHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_edit_indexer_settings_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let handler = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_edit_indexer_settings_handler_not_ready_when_indexer_settings_is_none() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let handler = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_edit_indexer_settings_handler_ready_when_not_loading_and_indexer_settings_is_some() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
let handler = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,806 @@
#[cfg(test)]
mod tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::IndexersHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
};
use crate::models::servarr_models::Indexer;
use crate::test_handler_delegation;
mod test_handle_scroll_up_and_down {
use rstest::rstest;
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
use super::*;
test_iterable_scroll!(
test_indexers_scroll,
IndexersHandler,
sonarr_data,
indexers,
simple_stateful_iterable_vec!(Indexer, String, protocol),
ActiveSonarrBlock::Indexers,
None,
protocol
);
#[rstest]
fn test_indexers_scroll_no_op_when_not_ready(
#[values(
DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key
)]
key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app
.data
.sonarr_data
.indexers
.set_items(simple_stateful_iterable_vec!(Indexer, String, protocol));
IndexersHandler::with(key, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_str_eq!(
app.data.sonarr_data.indexers.current_selection().protocol,
"Test 1"
);
IndexersHandler::with(key, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_str_eq!(
app.data.sonarr_data.indexers.current_selection().protocol,
"Test 1"
);
}
}
mod test_handle_home_end {
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
use super::*;
test_iterable_home_and_end!(
test_indexers_home_end,
IndexersHandler,
sonarr_data,
indexers,
extended_stateful_iterable_vec!(Indexer, String, protocol),
ActiveSonarrBlock::Indexers,
None,
protocol
);
#[test]
fn test_indexers_home_end_no_op_when_not_ready() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app
.data
.sonarr_data
.indexers
.set_items(extended_stateful_iterable_vec!(Indexer, String, protocol));
IndexersHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.indexers.current_selection().protocol,
"Test 1"
);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.indexers.current_selection().protocol,
"Test 1"
);
}
}
mod test_handle_delete {
use pretty_assertions::assert_eq;
use super::*;
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
#[test]
fn test_delete_indexer_prompt() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteIndexerPrompt.into()
);
}
#[test]
fn test_delete_indexer_prompt_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_indexers_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(5);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
#[rstest]
fn test_indexers_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(5);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::System.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
}
#[rstest]
fn test_left_right_delete_indexer_prompt_toggle(
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(key, &mut app, ActiveSonarrBlock::DeleteIndexerPrompt, None).handle();
assert!(app.data.sonarr_data.prompt_confirm);
IndexersHandler::with(key, &mut app, ActiveSonarrBlock::DeleteIndexerPrompt, None).handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::sonarr::sonarr_data::{
SonarrData, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
};
use crate::models::servarr_models::{Indexer, IndexerField};
use bimap::BiMap;
use pretty_assertions::assert_eq;
use serde_json::{Number, Value};
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[rstest]
fn test_edit_indexer_submit(#[values(true, false)] torrent_protocol: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
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 sonarr_data = SonarrData {
tags_map: BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]),
..SonarrData::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()
};
sonarr_data.indexers.set_items(vec![indexer]);
app.data.sonarr_data = sonarr_data;
IndexersHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::EditIndexerPrompt.into()
);
assert_eq!(
app.data.sonarr_data.edit_indexer_modal,
Some((&app.data.sonarr_data).into())
);
assert_eq!(
app.data.sonarr_data.edit_indexer_modal,
Some(expected_edit_indexer_modal)
);
if torrent_protocol {
assert_eq!(
app.data.sonarr_data.selected_block.blocks,
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS
);
} else {
assert_eq!(
app.data.sonarr_data.selected_block.blocks,
EDIT_INDEXER_NZB_SELECTION_BLOCKS
);
}
}
#[test]
fn test_edit_indexer_submit_no_op_when_not_ready() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(app.data.sonarr_data.edit_indexer_modal, None);
}
#[test]
fn test_delete_indexer_prompt_confirm_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteIndexer(None))
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_prompt_decline_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
fn test_delete_indexer_prompt_block_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
IndexersHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[rstest]
fn test_test_indexer_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.data.sonarr_data.indexer_test_error = Some("test result".to_owned());
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::TestIndexer.into());
IndexersHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::TestIndexer, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(app.data.sonarr_data.indexer_test_error, None);
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::Indexers, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use crate::{
models::servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
network::sonarr_network::SonarrEvent,
};
use super::*;
#[test]
fn test_refresh_indexers_key() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_indexers_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
IndexersHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.should_refresh);
}
#[test]
fn test_indexer_settings_key() {
let mut app = App::default();
app.data.sonarr_data.indexers.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.settings.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AllIndexerSettingsPrompt.into()
);
assert_eq!(
app.data.sonarr_data.selected_block.blocks,
INDEXER_SETTINGS_SELECTION_BLOCKS
);
}
#[test]
fn test_indexer_settings_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.settings.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_test_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::TestIndexer.into()
);
}
#[test]
fn test_test_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_test_all_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test_all.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::TestAllIndexers.into()
);
}
#[test]
fn test_test_all_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
IndexersHandler::with(
DEFAULT_KEYBINDINGS.test_all.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[test]
fn test_delete_indexer_prompt_confirm() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
IndexersHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::DeleteIndexerPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteIndexer(None))
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
}
#[rstest]
fn test_delegates_edit_indexer_blocks_to_edit_indexer_handler(
#[values(
ActiveSonarrBlock::EditIndexerPrompt,
ActiveSonarrBlock::EditIndexerConfirmPrompt,
ActiveSonarrBlock::EditIndexerApiKeyInput,
ActiveSonarrBlock::EditIndexerNameInput,
ActiveSonarrBlock::EditIndexerSeedRatioInput,
ActiveSonarrBlock::EditIndexerToggleEnableRss,
ActiveSonarrBlock::EditIndexerToggleEnableAutomaticSearch,
ActiveSonarrBlock::EditIndexerToggleEnableInteractiveSearch,
ActiveSonarrBlock::EditIndexerUrlInput,
ActiveSonarrBlock::EditIndexerTagsInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
IndexersHandler,
ActiveSonarrBlock::Indexers,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_indexer_settings_blocks_to_indexer_settings_handler(
#[values(
ActiveSonarrBlock::AllIndexerSettingsPrompt,
ActiveSonarrBlock::IndexerSettingsConfirmPrompt,
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
ActiveSonarrBlock::IndexerSettingsRetentionInput,
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
IndexersHandler,
ActiveSonarrBlock::Indexers,
active_sonarr_block
);
}
#[test]
fn test_delegates_test_all_indexers_block_to_test_all_indexers_handler() {
test_handler_delegation!(
IndexersHandler,
ActiveSonarrBlock::Indexers,
ActiveSonarrBlock::TestAllIndexers
);
}
#[test]
fn test_indexers_handler_accepts() {
let mut indexers_blocks = Vec::new();
indexers_blocks.extend(INDEXERS_BLOCKS);
indexers_blocks.extend(INDEXER_SETTINGS_BLOCKS);
indexers_blocks.extend(EDIT_INDEXER_BLOCKS);
indexers_blocks.push(ActiveSonarrBlock::TestAllIndexers);
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if indexers_blocks.contains(&active_sonarr_block) {
assert!(IndexersHandler::accepts(active_sonarr_block));
} else {
assert!(!IndexersHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_indexers_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let handler = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_indexers_handler_not_ready_when_indexers_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let handler = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_indexers_handler_ready_when_not_loading_and_indexers_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
let handler = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,200 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
use crate::handlers::sonarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS,
};
use crate::models::servarr_models::Indexer;
use crate::models::BlockSelectionState;
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
mod edit_indexer_handler;
mod edit_indexer_settings_handler;
mod test_all_indexers_handler;
#[cfg(test)]
#[path = "indexers_handler_tests.rs"]
mod indexers_handler_tests;
pub(super) struct IndexersHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> IndexersHandler<'a, 'b> {
handle_table_events!(self, indexers, self.app.data.sonarr_data.indexers, Indexer);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexersHandler<'a, 'b> {
fn handle(&mut self) {
let indexers_table_handling_props = TableHandlingProps::new(ActiveSonarrBlock::Indexers.into());
if !self.handle_indexers_table_events(indexers_table_handling_props) {
match self.active_sonarr_block {
_ if EditIndexerHandler::accepts(self.active_sonarr_block) => {
EditIndexerHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ if IndexerSettingsHandler::accepts(self.active_sonarr_block) => {
IndexerSettingsHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ if TestAllIndexersHandler::accepts(self.active_sonarr_block) => {
TestAllIndexersHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ => self.handle_key_event(),
}
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EditIndexerHandler::accepts(active_block)
|| IndexerSettingsHandler::accepts(active_block)
|| TestAllIndexersHandler::accepts(active_block)
|| INDEXERS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> IndexersHandler<'a, 'b> {
IndexersHandler {
key,
app,
active_sonarr_block: active_block,
context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.indexers.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::Indexers {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Indexers => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::DeleteIndexerPrompt => handle_prompt_toggle(self.app, self.key),
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteIndexerPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteIndexer(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::Indexers => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::EditIndexerPrompt.into());
self.app.data.sonarr_data.edit_indexer_modal = Some((&self.app.data.sonarr_data).into());
let protocol = &self
.app
.data
.sonarr_data
.indexers
.current_selection()
.protocol;
if protocol == "torrent" {
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(EDIT_INDEXER_TORRENT_SELECTION_BLOCKS);
} else {
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(EDIT_INDEXER_NZB_SELECTION_BLOCKS);
}
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteIndexerPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::TestIndexer => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.indexer_test_error = None;
}
_ => handle_clear_errors(self.app),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::Indexers => match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if key == DEFAULT_KEYBINDINGS.test.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::TestIndexer.into());
}
_ if key == DEFAULT_KEYBINDINGS.test_all.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::TestAllIndexers.into());
}
_ if key == DEFAULT_KEYBINDINGS.settings.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AllIndexerSettingsPrompt.into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(INDEXER_SETTINGS_SELECTION_BLOCKS);
}
_ => (),
},
ActiveSonarrBlock::DeleteIndexerPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteIndexer(None));
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
@@ -0,0 +1,117 @@
use crate::app::App;
use crate::event::Key;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::Scrollable;
#[cfg(test)]
#[path = "test_all_indexers_handler_tests.rs"]
mod test_all_indexers_handler_tests;
pub(super) struct TestAllIndexersHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for TestAllIndexersHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
active_block == ActiveSonarrBlock::TestAllIndexers
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> TestAllIndexersHandler<'a, 'b> {
TestAllIndexersHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
let table_is_ready = if let Some(table) = &self.app.data.sonarr_data.indexer_test_all_results {
!table.is_empty()
} else {
false
};
!self.app.is_loading && table_is_ready
}
fn handle_scroll_up(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
self
.app
.data
.sonarr_data
.indexer_test_all_results
.as_mut()
.unwrap()
.scroll_up()
}
}
fn handle_scroll_down(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
self
.app
.data
.sonarr_data
.indexer_test_all_results
.as_mut()
.unwrap()
.scroll_down()
}
}
fn handle_home(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
self
.app
.data
.sonarr_data
.indexer_test_all_results
.as_mut()
.unwrap()
.scroll_to_top()
}
}
fn handle_end(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
self
.app
.data
.sonarr_data
.indexer_test_all_results
.as_mut()
.unwrap()
.scroll_to_bottom()
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {}
fn handle_submit(&mut self) {}
fn handle_esc(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.indexer_test_all_results = None;
}
}
fn handle_char_key_event(&mut self) {}
}
@@ -0,0 +1,337 @@
#[cfg(test)]
mod tests {
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::stateful_table::StatefulTable;
use strum::IntoEnumIterator;
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_str_eq;
use rstest::rstest;
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::stateful_table::StatefulTable;
use crate::simple_stateful_iterable_vec;
use super::*;
#[rstest]
fn test_test_all_indexers_results_scroll(
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
let mut indexer_test_results = StatefulTable::default();
indexer_test_results.set_items(simple_stateful_iterable_vec!(
IndexerTestResultModalItem,
String,
name
));
app.data.sonarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(key, &mut app, ActiveSonarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 2"
);
TestAllIndexersHandler::with(key, &mut app, ActiveSonarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 1"
);
}
#[rstest]
fn test_test_all_indexers_results_scroll_no_op_when_not_ready(
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let mut indexer_test_results = StatefulTable::default();
indexer_test_results.set_items(simple_stateful_iterable_vec!(
IndexerTestResultModalItem,
String,
name
));
app.data.sonarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(key, &mut app, ActiveSonarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 1"
);
TestAllIndexersHandler::with(key, &mut app, ActiveSonarrBlock::TestAllIndexers, None)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 1"
);
}
}
mod test_handle_home_end {
use crate::extended_stateful_iterable_vec;
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::stateful_table::StatefulTable;
use pretty_assertions::assert_str_eq;
use super::*;
#[test]
fn test_test_all_indexers_results_home_end() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
let mut indexer_test_results = StatefulTable::default();
indexer_test_results.set_items(extended_stateful_iterable_vec!(
IndexerTestResultModalItem,
String,
name
));
app.data.sonarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 3"
);
TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 1"
);
}
#[test]
fn test_test_all_indexers_results_home_end_no_op_when_not_ready() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let mut indexer_test_results = StatefulTable::default();
indexer_test_results.set_items(extended_stateful_iterable_vec!(
IndexerTestResultModalItem,
String,
name
));
app.data.sonarr_data.indexer_test_all_results = Some(indexer_test_results);
TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 1"
);
TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
)
.handle();
assert_str_eq!(
app
.data
.sonarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.current_selection()
.name,
"Test 1"
);
}
}
mod test_handle_esc {
use super::*;
use crate::models::stateful_table::StatefulTable;
use pretty_assertions::assert_eq;
use rstest::rstest;
#[rstest]
fn test_test_all_indexers_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::TestAllIndexers.into());
app.data.sonarr_data.indexer_test_all_results = Some(StatefulTable::default());
TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.indexer_test_all_results.is_none());
}
}
#[test]
fn test_test_all_indexers_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
assert!(TestAllIndexersHandler::accepts(active_sonarr_block));
} else {
assert!(!TestAllIndexersHandler::accepts(active_sonarr_block));
}
});
}
#[test]
fn test_test_all_indexers_handler_is_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = true;
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_test_all_indexers_handler_is_not_ready_when_results_is_none() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_test_all_indexers_handler_is_not_ready_when_results_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
app.data.sonarr_data.indexer_test_all_results = Some(StatefulTable::default());
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_test_all_indexers_handler_is_ready_when_results_is_not_empty_and_is_loaded() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.is_loading = false;
let mut indexer_test_results = StatefulTable::default();
indexer_test_results.set_items(vec![IndexerTestResultModalItem::default()]);
app.data.sonarr_data.indexer_test_all_results = Some(indexer_test_results);
let handler = TestAllIndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::TestAllIndexers,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,550 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, ADD_SERIES_BLOCKS, ADD_SERIES_SELECTION_BLOCKS,
};
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
#[cfg(test)]
#[path = "add_series_handler_tests.rs"]
mod add_series_handler_tests;
pub(super) struct AddSeriesHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for AddSeriesHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
ADD_SERIES_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> AddSeriesHandler<'a, 'b> {
AddSeriesHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchResults => self
.app
.data
.sonarr_data
.add_searched_series
.as_mut()
.unwrap()
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_up(),
ActiveSonarrBlock::AddSeriesPrompt => self.app.data.sonarr_data.selected_block.up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchResults => self
.app
.data
.sonarr_data
.add_searched_series
.as_mut()
.unwrap()
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_down(),
ActiveSonarrBlock::AddSeriesPrompt => self.app.data.sonarr_data.selected_block.down(),
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchResults => self
.app
.data
.sonarr_data
.add_searched_series
.as_mut()
.unwrap()
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_to_top(),
ActiveSonarrBlock::AddSeriesSearchInput => self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
.scroll_home(),
ActiveSonarrBlock::AddSeriesTagsInput => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
.scroll_home(),
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchResults => self
.app
.data
.sonarr_data
.add_searched_series
.as_mut()
.unwrap()
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectMonitor => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.monitor_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSelectRootFolder => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.root_folder_list
.scroll_to_bottom(),
ActiveSonarrBlock::AddSeriesSearchInput => self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
.reset_offset(),
ActiveSonarrBlock::AddSeriesTagsInput => self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
.reset_offset(),
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesPrompt => handle_prompt_toggle(self.app, self.key),
ActiveSonarrBlock::AddSeriesSearchInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
)
}
ActiveSonarrBlock::AddSeriesTagsInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
_ if self.active_sonarr_block == ActiveSonarrBlock::AddSeriesSearchInput
&& !self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
.text
.is_empty() =>
{
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddSeriesSearchResults.into());
self.app.should_ignore_quit_key = false;
}
_ if self.active_sonarr_block == ActiveSonarrBlock::AddSeriesSearchResults
&& self.app.data.sonarr_data.add_searched_series.is_some() =>
{
let tvdb_id = self
.app
.data
.sonarr_data
.add_searched_series
.as_ref()
.unwrap()
.current_selection()
.tvdb_id;
if self
.app
.data
.sonarr_data
.series
.items
.iter()
.any(|series| series.tvdb_id == tvdb_id)
{
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddSeriesAlreadyInLibrary.into());
} else {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddSeriesPrompt.into());
self.app.data.sonarr_data.add_series_modal = Some((&self.app.data.sonarr_data).into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(ADD_SERIES_SELECTION_BLOCKS);
}
}
ActiveSonarrBlock::AddSeriesPrompt => {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::AddSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddSeries(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::AddSeriesSelectMonitor
| ActiveSonarrBlock::AddSeriesSelectSeriesType
| ActiveSonarrBlock::AddSeriesSelectQualityProfile
| ActiveSonarrBlock::AddSeriesSelectLanguageProfile
| ActiveSonarrBlock::AddSeriesSelectRootFolder => self.app.push_navigation_stack(
self
.app
.data
.sonarr_data
.selected_block
.get_active_block()
.into(),
),
ActiveSonarrBlock::AddSeriesTagsInput => {
self.app.push_navigation_stack(
self
.app
.data
.sonarr_data
.selected_block
.get_active_block()
.into(),
);
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::AddSeriesToggleUseSeasonFolder => {
self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.use_season_folder = !self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.use_season_folder;
}
_ => (),
}
}
ActiveSonarrBlock::AddSeriesSelectMonitor
| ActiveSonarrBlock::AddSeriesSelectSeriesType
| ActiveSonarrBlock::AddSeriesSelectQualityProfile
| ActiveSonarrBlock::AddSeriesSelectLanguageProfile
| ActiveSonarrBlock::AddSeriesSelectRootFolder => self.app.pop_navigation_stack(),
ActiveSonarrBlock::AddSeriesTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchInput => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.add_series_search = None;
self.app.should_ignore_quit_key = false;
}
ActiveSonarrBlock::AddSeriesSearchResults
| ActiveSonarrBlock::AddSeriesEmptySearchResults => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.add_searched_series = None;
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::AddSeriesPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.add_series_modal = None;
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::AddSeriesSelectMonitor
| ActiveSonarrBlock::AddSeriesSelectSeriesType
| ActiveSonarrBlock::AddSeriesSelectQualityProfile
| ActiveSonarrBlock::AddSeriesSelectLanguageProfile
| ActiveSonarrBlock::AddSeriesAlreadyInLibrary
| ActiveSonarrBlock::AddSeriesSelectRootFolder => self.app.pop_navigation_stack(),
ActiveSonarrBlock::AddSeriesTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => (),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::AddSeriesSearchInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.add_series_search
.as_mut()
.unwrap()
)
}
ActiveSonarrBlock::AddSeriesTagsInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.add_series_modal
.as_mut()
.unwrap()
.tags
)
}
ActiveSonarrBlock::AddSeriesPrompt => {
if self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::AddSeriesConfirmPrompt
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddSeries(None));
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
use crate::{
app::{key_binding::DEFAULT_KEYBINDINGS, App},
event::Key,
handlers::{handle_prompt_toggle, KeyEventHandler},
models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS},
network::sonarr_network::SonarrEvent,
};
#[cfg(test)]
#[path = "delete_series_handler_tests.rs"]
mod delete_series_handler_tests;
pub(super) struct DeleteSeriesHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DeleteSeriesHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
DELETE_SERIES_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> Self {
DeleteSeriesHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading
}
fn handle_scroll_up(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
self.app.data.sonarr_data.selected_block.up();
}
}
fn handle_scroll_down(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
self.app.data.sonarr_data.selected_block.down();
}
}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
handle_prompt_toggle(self.app, self.key);
}
}
fn handle_submit(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::DeleteSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteSeries(None));
self.app.should_refresh = true;
} else {
self.app.data.sonarr_data.reset_delete_series_preferences();
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::DeleteSeriesToggleDeleteFile => {
self.app.data.sonarr_data.delete_series_files =
!self.app.data.sonarr_data.delete_series_files;
}
ActiveSonarrBlock::DeleteSeriesToggleAddListExclusion => {
self.app.data.sonarr_data.add_list_exclusion =
!self.app.data.sonarr_data.add_list_exclusion;
}
_ => (),
}
}
}
fn handle_esc(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.reset_delete_series_preferences();
self.app.data.sonarr_data.prompt_confirm = false;
}
}
fn handle_char_key_event(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::DeleteSeriesPrompt
&& self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::DeleteSeriesConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteSeries(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
}
@@ -0,0 +1,339 @@
#[cfg(test)]
mod tests {
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::library::delete_series_handler::DeleteSeriesHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS};
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::servarr_data::sonarr::sonarr_data::DELETE_SERIES_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use super::*;
#[rstest]
fn test_delete_series_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) {
let mut app = App::default();
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
if key == Key::Up {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::DeleteSeriesToggleDeleteFile
);
} else {
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::DeleteSeriesConfirmPrompt
);
}
}
#[rstest]
fn test_delete_series_prompt_scroll_no_op_when_not_ready(
#[values(Key::Up, Key::Down)] key: Key,
) {
let mut app = App::default();
app.is_loading = true;
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app.data.sonarr_data.selected_block.down();
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::DeleteSeriesToggleAddListExclusion
);
}
}
mod test_handle_left_right_action {
use rstest::rstest;
use super::*;
#[rstest]
fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
DeleteSeriesHandler::with(key, &mut app, ActiveSonarrBlock::DeleteSeriesPrompt, None)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use crate::models::servarr_data::sonarr::sonarr_data::DELETE_SERIES_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_delete_series_prompt_prompt_decline_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, DELETE_SERIES_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.data.sonarr_data.delete_series_files);
assert!(!app.data.sonarr_data.add_list_exclusion);
}
#[test]
fn test_delete_series_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, DELETE_SERIES_SELECTION_BLOCKS.len() - 1);
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteSeries(None))
);
assert!(app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
}
#[test]
fn test_delete_series_confirm_prompt_prompt_confirmation_submit_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteSeriesPrompt.into()
);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert!(!app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
}
#[test]
fn test_delete_series_toggle_delete_files_submit() {
let current_route = ActiveSonarrBlock::DeleteSeriesPrompt.into();
let mut app = App::default();
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.sonarr_data.delete_series_files, true);
DeleteSeriesHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), current_route);
assert_eq!(app.data.sonarr_data.delete_series_files, false);
}
}
mod test_handle_esc {
use super::*;
use rstest::rstest;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
fn test_delete_series_prompt_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
DeleteSeriesHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.data.sonarr_data.delete_series_files);
assert!(!app.data.sonarr_data.add_list_exclusion);
}
}
mod test_handle_key_char {
use crate::{
models::{
servarr_data::sonarr::sonarr_data::DELETE_SERIES_SELECTION_BLOCKS, BlockSelectionState,
},
network::sonarr_network::SonarrEvent,
};
use super::*;
#[test]
fn test_delete_series_confirm_prompt_prompt_confirm() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
.data
.sonarr_data
.selected_block
.set_index(0, DELETE_SERIES_SELECTION_BLOCKS.len() - 1);
DeleteSeriesHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteSeries(None))
);
assert!(app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
}
}
#[test]
fn test_delete_series_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if DELETE_SERIES_BLOCKS.contains(&active_sonarr_block) {
assert!(DeleteSeriesHandler::accepts(active_sonarr_block));
} else {
assert!(!DeleteSeriesHandler::accepts(active_sonarr_block));
}
});
}
#[test]
fn test_delete_series_handler_not_ready_when_loading() {
let mut app = App::default();
app.is_loading = true;
let handler = DeleteSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_delete_series_handler_ready_when_not_loading() {
let mut app = App::default();
app.is_loading = false;
let handler = DeleteSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,404 @@
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::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS};
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
#[cfg(test)]
#[path = "edit_series_handler_tests.rs"]
mod edit_series_handler_tests;
pub(super) struct EditSeriesHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditSeriesHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EDIT_SERIES_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> EditSeriesHandler<'a, 'b> {
EditSeriesHandler {
key,
app,
active_sonarr_block: active_block,
context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && self.app.data.sonarr_data.edit_series_modal.is_some()
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_up(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_up(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_up(),
ActiveSonarrBlock::EditSeriesPrompt => self.app.data.sonarr_data.selected_block.up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_down(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_down(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_down(),
ActiveSonarrBlock::EditSeriesPrompt => self.app.data.sonarr_data.selected_block.down(),
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_top(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_top(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_top(),
ActiveSonarrBlock::EditSeriesPathInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
.scroll_home(),
ActiveSonarrBlock::EditSeriesTagsInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
.scroll_home(),
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesSelectSeriesType => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.series_type_list
.scroll_to_bottom(),
ActiveSonarrBlock::EditSeriesSelectQualityProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.quality_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.language_profile_list
.scroll_to_bottom(),
ActiveSonarrBlock::EditSeriesPathInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
.reset_offset(),
ActiveSonarrBlock::EditSeriesTagsInput => self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
.reset_offset(),
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesPrompt => handle_prompt_toggle(self.app, self.key),
ActiveSonarrBlock::EditSeriesPathInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
)
}
ActiveSonarrBlock::EditSeriesTagsInput => {
handle_text_box_left_right_keys!(
self,
self.key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesPrompt => {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::EditSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None));
self.app.should_refresh = true;
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::EditSeriesSelectSeriesType
| ActiveSonarrBlock::EditSeriesSelectQualityProfile
| ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.push_navigation_stack(
(
self.app.data.sonarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
),
ActiveSonarrBlock::EditSeriesPathInput | ActiveSonarrBlock::EditSeriesTagsInput => {
self.app.push_navigation_stack(
(
self.app.data.sonarr_data.selected_block.get_active_block(),
self.context,
)
.into(),
);
self.app.should_ignore_quit_key = true;
}
ActiveSonarrBlock::EditSeriesToggleMonitored => {
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.monitored = Some(
!self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.monitored
.unwrap_or_default(),
)
}
ActiveSonarrBlock::EditSeriesToggleSeasonFolder => {
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.use_season_folders = Some(
!self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.use_season_folders
.unwrap_or_default(),
)
}
_ => (),
}
}
ActiveSonarrBlock::EditSeriesSelectSeriesType
| ActiveSonarrBlock::EditSeriesSelectQualityProfile
| ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.pop_navigation_stack(),
ActiveSonarrBlock::EditSeriesPathInput | ActiveSonarrBlock::EditSeriesTagsInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesTagsInput | ActiveSonarrBlock::EditSeriesPathInput => {
self.app.pop_navigation_stack();
self.app.should_ignore_quit_key = false;
}
ActiveSonarrBlock::EditSeriesPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.edit_series_modal = None;
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::EditSeriesSelectSeriesType
| ActiveSonarrBlock::EditSeriesSelectQualityProfile
| ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.pop_navigation_stack(),
_ => (),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::EditSeriesPathInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.path
)
}
ActiveSonarrBlock::EditSeriesTagsInput => {
handle_text_box_keys!(
self,
key,
self
.app
.data
.sonarr_data
.edit_series_modal
.as_mut()
.unwrap()
.tags
)
}
ActiveSonarrBlock::EditSeriesPrompt => {
if self.app.data.sonarr_data.selected_block.get_active_block()
== ActiveSonarrBlock::EditSeriesConfirmPrompt
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+298
View File
@@ -0,0 +1,298 @@
use add_series_handler::AddSeriesHandler;
mod edit_series_handler;
use delete_series_handler::DeleteSeriesHandler;
use edit_series_handler::EditSeriesHandler;
use crate::{
app::App,
event::Key,
handle_table_events,
handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler},
models::{
servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, DELETE_SERIES_SELECTION_BLOCKS, EDIT_SERIES_SELECTION_BLOCKS,
LIBRARY_BLOCKS,
},
sonarr_models::Series,
stateful_table::SortOption,
BlockSelectionState, HorizontallyScrollableText, Scrollable,
},
network::sonarr_network::SonarrEvent,
};
use super::handle_change_tab_left_right_keys;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::handlers::sonarr_handlers::library::series_details_handler::SeriesDetailsHandler;
use crate::handlers::table_handler::TableHandlingProps;
mod add_series_handler;
mod delete_series_handler;
#[cfg(test)]
#[path = "library_handler_tests.rs"]
mod library_handler_tests;
mod series_details_handler;
pub(super) struct LibraryHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> LibraryHandler<'a, 'b> {
handle_table_events!(self, series, self.app.data.sonarr_data.series, Series);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, 'b> {
fn handle(&mut self) {
let series_table_handling_props = TableHandlingProps::new(ActiveSonarrBlock::Series.into())
.sorting_block(ActiveSonarrBlock::SeriesSortPrompt.into())
.sort_by_fn(|a: &Series, b: &Series| a.id.cmp(&b.id))
.sort_options(series_sorting_options())
.searching_block(ActiveSonarrBlock::SearchSeries.into())
.search_error_block(ActiveSonarrBlock::SearchSeriesError.into())
.search_field_fn(|series| &series.title.text)
.filtering_block(ActiveSonarrBlock::FilterSeries.into())
.filter_error_block(ActiveSonarrBlock::FilterSeriesError.into())
.filter_field_fn(|series| &series.title.text);
if !self.handle_series_table_events(series_table_handling_props) {
match self.active_sonarr_block {
_ if AddSeriesHandler::accepts(self.active_sonarr_block) => {
AddSeriesHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle();
}
_ if DeleteSeriesHandler::accepts(self.active_sonarr_block) => {
DeleteSeriesHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle();
}
_ if EditSeriesHandler::accepts(self.active_sonarr_block) => {
EditSeriesHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle();
}
_ if SeriesDetailsHandler::accepts(self.active_sonarr_block) => {
SeriesDetailsHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle();
}
_ => self.handle_key_event(),
}
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
AddSeriesHandler::accepts(active_block)
|| DeleteSeriesHandler::accepts(active_block)
|| EditSeriesHandler::accepts(active_block)
|| SeriesDetailsHandler::accepts(active_block)
|| LIBRARY_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> LibraryHandler<'a, 'b> {
LibraryHandler {
key,
app,
active_sonarr_block: active_block,
context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.series.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::Series {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Series => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::UpdateAllSeriesPrompt => handle_prompt_toggle(self.app, self.key),
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::Series => self
.app
.push_navigation_stack(ActiveSonarrBlock::SeriesDetails.into()),
ActiveSonarrBlock::UpdateAllSeriesPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::UpdateAllSeries);
}
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::UpdateAllSeriesPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
_ => {
handle_clear_errors(self.app);
}
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::Series => match self.key {
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveSonarrBlock::EditSeriesPrompt,
Some(ActiveSonarrBlock::Series),
)
.into(),
);
self.app.data.sonarr_data.edit_series_modal = Some((&self.app.data.sonarr_data).into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
}
_ if key == DEFAULT_KEYBINDINGS.add.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddSeriesSearchInput.into());
self.app.data.sonarr_data.add_series_search = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::UpdateAllSeriesPrompt.into());
}
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ => (),
},
ActiveSonarrBlock::UpdateAllSeriesPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::UpdateAllSeries);
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
fn series_sorting_options() -> Vec<SortOption<Series>> {
vec![
SortOption {
name: "Title",
cmp_fn: Some(|a, b| {
a.title
.text
.to_lowercase()
.cmp(&b.title.text.to_lowercase())
}),
},
SortOption {
name: "Year",
cmp_fn: Some(|a, b| a.year.cmp(&b.year)),
},
SortOption {
name: "Network",
cmp_fn: Some(|a, b| {
a.network
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(&b.network.as_ref().unwrap_or(&String::new()).to_lowercase())
}),
},
SortOption {
name: "Status",
cmp_fn: Some(|a, b| {
a.status
.to_string()
.to_lowercase()
.cmp(&b.status.to_string().to_lowercase())
}),
},
SortOption {
name: "Rating",
cmp_fn: Some(|a, b| {
a.certification
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(
&b.certification
.as_ref()
.unwrap_or(&String::new())
.to_lowercase(),
)
}),
},
SortOption {
name: "Type",
cmp_fn: Some(|a, b| a.series_type.to_string().cmp(&b.series_type.to_string())),
},
SortOption {
name: "Quality",
cmp_fn: Some(|a, b| a.quality_profile_id.cmp(&b.quality_profile_id)),
},
SortOption {
name: "Language",
cmp_fn: Some(|a, b| a.language_profile_id.cmp(&b.language_profile_id)),
},
SortOption {
name: "Monitored",
cmp_fn: Some(|a, b| a.monitored.cmp(&b.monitored)),
},
SortOption {
name: "Tags",
cmp_fn: Some(|a, b| {
let a_str = a
.tags
.iter()
.map(|tag| tag.as_i64().unwrap().to_string())
.collect::<Vec<String>>()
.join(",");
let b_str = b
.tags
.iter()
.map(|tag| tag.as_i64().unwrap().to_string())
.collect::<Vec<String>>()
.join(",");
a_str.cmp(&b_str)
}),
},
]
}
@@ -0,0 +1,310 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::sonarr_handlers::history::history_sorting_options;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EDIT_SERIES_SELECTION_BLOCKS, SERIES_DETAILS_BLOCKS,
};
use crate::models::sonarr_models::{Season, SonarrHistoryItem};
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "series_details_handler_tests.rs"]
mod series_details_handler_tests;
pub(super) struct SeriesDetailsHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> SeriesDetailsHandler<'a, 'b> {
handle_table_events!(self, season, self.app.data.sonarr_data.seasons, Season);
handle_table_events!(
self,
series_history,
self
.app
.data
.sonarr_data
.series_history
.as_mut()
.expect("Series history is undefined"),
SonarrHistoryItem
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler<'a, 'b> {
fn handle(&mut self) {
let season_table_handling_props =
TableHandlingProps::new(ActiveSonarrBlock::SeriesDetails.into())
.searching_block(ActiveSonarrBlock::SearchSeason.into())
.search_error_block(ActiveSonarrBlock::SearchSeasonError.into())
.search_field_fn(|season: &Season| {
season
.title
.as_ref()
.expect("Season was not populated with title in handlers")
});
let series_history_table_handling_props =
TableHandlingProps::new(ActiveSonarrBlock::SeriesHistory.into())
.sorting_block(ActiveSonarrBlock::SeriesHistorySortPrompt.into())
.sort_options(history_sorting_options())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.searching_block(ActiveSonarrBlock::SearchSeriesHistory.into())
.search_error_block(ActiveSonarrBlock::SearchSeriesHistoryError.into())
.search_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text)
.filtering_block(ActiveSonarrBlock::FilterSeriesHistory.into())
.filter_error_block(ActiveSonarrBlock::FilterSeriesHistoryError.into())
.filter_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text);
if !self.handle_season_table_events(season_table_handling_props)
&& !self.handle_series_history_table_events(series_history_table_handling_props)
{
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
SERIES_DETAILS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> SeriesDetailsHandler<'a, 'b> {
SeriesDetailsHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
if self.active_sonarr_block == ActiveSonarrBlock::SeriesHistory {
!self.app.is_loading && self.app.data.sonarr_data.series_history.is_some()
} else {
!self.app.is_loading
}
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SeriesDetails | ActiveSonarrBlock::SeriesHistory => match self.key {
_ if self.key == DEFAULT_KEYBINDINGS.left.key => {
self.app.data.sonarr_data.series_info_tabs.previous();
self.app.pop_and_push_navigation_stack(
self
.app
.data
.sonarr_data
.series_info_tabs
.get_active_route(),
);
}
_ if self.key == DEFAULT_KEYBINDINGS.right.key => {
self.app.data.sonarr_data.series_info_tabs.next();
self.app.pop_and_push_navigation_stack(
self
.app
.data
.sonarr_data
.series_info_tabs
.get_active_route(),
);
}
_ => (),
},
ActiveSonarrBlock::UpdateAndScanSeriesPrompt
| ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
handle_prompt_toggle(self.app, self.key)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SeriesDetails if !self.app.data.sonarr_data.seasons.is_empty() => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::SeasonDetails.into());
}
ActiveSonarrBlock::SeriesHistory
if !self
.app
.data
.sonarr_data
.series_history
.as_ref()
.expect("Series history should be Some")
.is_empty() =>
{
self
.app
.push_navigation_stack(ActiveSonarrBlock::SeriesHistoryDetails.into());
}
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::TriggerAutomaticSeriesSearch(None));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::UpdateAndScanSeries(None));
}
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::UpdateAndScanSeriesPrompt
| ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
ActiveSonarrBlock::SeriesHistoryDetails => {
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::SeriesHistory => {
if self
.app
.data
.sonarr_data
.series_history
.as_ref()
.expect("Series history is not populated")
.filtered_items
.is_some()
{
self
.app
.data
.sonarr_data
.series_history
.as_mut()
.expect("Series history is not populated")
.reset_filter();
} else {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.reset_series_info_tabs();
}
}
ActiveSonarrBlock::SeriesDetails => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.reset_series_info_tabs();
}
_ => (),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::SeriesDetails => match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => self
.app
.pop_and_push_navigation_stack(self.active_sonarr_block.into()),
_ if key == DEFAULT_KEYBINDINGS.auto_search.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AutomaticallySearchSeriesPrompt.into());
}
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::UpdateAndScanSeriesPrompt.into());
}
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveSonarrBlock::EditSeriesPrompt,
Some(self.active_sonarr_block),
)
.into(),
);
self.app.data.sonarr_data.edit_series_modal = Some((&self.app.data.sonarr_data).into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
}
_ => (),
},
ActiveSonarrBlock::SeriesHistory => match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => self
.app
.pop_and_push_navigation_stack(self.active_sonarr_block.into()),
_ if key == DEFAULT_KEYBINDINGS.auto_search.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AutomaticallySearchSeriesPrompt.into());
}
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveSonarrBlock::EditSeriesPrompt,
Some(self.active_sonarr_block),
)
.into(),
);
self.app.data.sonarr_data.edit_series_modal = Some((&self.app.data.sonarr_data).into());
self.app.data.sonarr_data.selected_block =
BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
}
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::UpdateAndScanSeriesPrompt.into());
}
_ => (),
},
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::TriggerAutomaticSeriesSearch(None));
self.app.pop_navigation_stack();
}
}
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::UpdateAndScanSeries(None));
}
self.app.pop_navigation_stack();
}
_ => (),
}
}
}
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
use blocklist::BlocklistHandler;
use downloads::DownloadsHandler;
use history::HistoryHandler;
use indexers::IndexersHandler;
use library::LibraryHandler;
use root_folders::RootFoldersHandler;
use system::SystemHandler;
use crate::{
app::{key_binding::DEFAULT_KEYBINDINGS, App},
event::Key,
models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock,
};
use super::KeyEventHandler;
mod blocklist;
mod downloads;
mod history;
mod indexers;
mod library;
mod root_folders;
mod system;
#[cfg(test)]
#[path = "sonarr_handler_tests.rs"]
mod sonarr_handler_tests;
#[cfg(test)]
#[path = "sonarr_handler_test_utils.rs"]
mod sonarr_handler_test_utils;
pub(super) struct SonarrHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SonarrHandler<'a, 'b> {
fn handle(&mut self) {
match self.active_sonarr_block {
_ if LibraryHandler::accepts(self.active_sonarr_block) => {
LibraryHandler::with(self.key, self.app, self.active_sonarr_block, self.context).handle();
}
_ if DownloadsHandler::accepts(self.active_sonarr_block) => {
DownloadsHandler::with(self.key, self.app, self.active_sonarr_block, self.context).handle()
}
_ if BlocklistHandler::accepts(self.active_sonarr_block) => {
BlocklistHandler::with(self.key, self.app, self.active_sonarr_block, self.context).handle()
}
_ if HistoryHandler::accepts(self.active_sonarr_block) => {
HistoryHandler::with(self.key, self.app, self.active_sonarr_block, self.context).handle()
}
_ if RootFoldersHandler::accepts(self.active_sonarr_block) => {
RootFoldersHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ if IndexersHandler::accepts(self.active_sonarr_block) => {
IndexersHandler::with(self.key, self.app, self.active_sonarr_block, self.context).handle()
}
_ if SystemHandler::accepts(self.active_sonarr_block) => {
SystemHandler::with(self.key, self.app, self.active_sonarr_block, self.context).handle()
}
_ => self.handle_key_event(),
}
}
fn accepts(_active_block: ActiveSonarrBlock) -> bool {
true
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> SonarrHandler<'a, 'b> {
SonarrHandler {
key,
app,
active_sonarr_block: active_block,
context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
true
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {}
fn handle_submit(&mut self) {}
fn handle_esc(&mut self) {}
fn handle_char_key_event(&mut self) {}
}
pub fn handle_change_tab_left_right_keys(app: &mut App<'_>, key: Key) {
let key_ref = key;
match key_ref {
_ if key == DEFAULT_KEYBINDINGS.left.key => {
app.data.sonarr_data.main_tabs.previous();
app.pop_and_push_navigation_stack(app.data.sonarr_data.main_tabs.get_active_route());
}
_ if key == DEFAULT_KEYBINDINGS.right.key => {
app.data.sonarr_data.main_tabs.next();
app.pop_and_push_navigation_stack(app.data.sonarr_data.main_tabs.get_active_route());
}
_ => (),
}
}
@@ -0,0 +1,203 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingProps;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::{HorizontallyScrollableText, Scrollable};
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys};
#[cfg(test)]
#[path = "root_folders_handler_tests.rs"]
mod root_folders_handler_tests;
pub(super) struct RootFoldersHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> RootFoldersHandler<'a, 'b> {
handle_table_events!(
self,
root_folders,
self.app.data.sonarr_data.root_folders,
RootFolder
);
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for RootFoldersHandler<'a, 'b> {
fn handle(&mut self) {
let root_folders_table_handling_props =
TableHandlingProps::new(ActiveSonarrBlock::RootFolders.into());
if !self.handle_root_folders_table_events(root_folders_table_handling_props) {
self.handle_key_event();
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
ROOT_FOLDERS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
) -> RootFoldersHandler<'a, 'b> {
RootFoldersHandler {
key,
app,
active_sonarr_block: active_block,
_context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading && !self.app.data.sonarr_data.root_folders.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::AddRootFolderPrompt {
self
.app
.data
.sonarr_data
.edit_root_folder
.as_mut()
.unwrap()
.scroll_home()
}
}
fn handle_end(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::AddRootFolderPrompt {
self
.app
.data
.sonarr_data
.edit_root_folder
.as_mut()
.unwrap()
.reset_offset()
}
}
fn handle_delete(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::RootFolders {
self
.app
.push_navigation_stack(ActiveSonarrBlock::DeleteRootFolderPrompt.into())
}
}
fn handle_left_right_action(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::RootFolders => handle_change_tab_left_right_keys(self.app, self.key),
ActiveSonarrBlock::DeleteRootFolderPrompt => handle_prompt_toggle(self.app, self.key),
ActiveSonarrBlock::AddRootFolderPrompt => {
handle_text_box_left_right_keys!(
self,
self.key,
self.app.data.sonarr_data.edit_root_folder.as_mut().unwrap()
)
}
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteRootFolderPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteRootFolder(None));
}
self.app.pop_navigation_stack();
}
_ if self.active_sonarr_block == ActiveSonarrBlock::AddRootFolderPrompt
&& !self
.app
.data
.sonarr_data
.edit_root_folder
.as_ref()
.unwrap()
.text
.is_empty() =>
{
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddRootFolder(None));
self.app.data.sonarr_data.prompt_confirm = true;
self.app.should_ignore_quit_key = false;
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::AddRootFolderPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.edit_root_folder = None;
self.app.data.sonarr_data.prompt_confirm = false;
self.app.should_ignore_quit_key = false;
}
ActiveSonarrBlock::DeleteRootFolderPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
_ => handle_clear_errors(self.app),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::RootFolders => match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if key == DEFAULT_KEYBINDINGS.add.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::AddRootFolderPrompt.into());
self.app.data.sonarr_data.edit_root_folder = Some(HorizontallyScrollableText::default());
self.app.should_ignore_quit_key = true;
}
_ => (),
},
ActiveSonarrBlock::AddRootFolderPrompt => {
handle_text_box_keys!(
self,
key,
self.app.data.sonarr_data.edit_root_folder.as_mut().unwrap()
)
}
ActiveSonarrBlock::DeleteRootFolderPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteRootFolder(None));
self.app.pop_navigation_stack();
}
}
_ => (),
}
}
}
@@ -0,0 +1,805 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::root_folders::RootFoldersHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::HorizontallyScrollableText;
mod test_handle_scroll_up_and_down {
use rstest::rstest;
use crate::models::servarr_models::RootFolder;
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
use super::*;
test_iterable_scroll!(
test_root_folders_scroll,
RootFoldersHandler,
sonarr_data,
root_folders,
simple_stateful_iterable_vec!(RootFolder, String, path),
ActiveSonarrBlock::RootFolders,
None,
path
);
#[rstest]
fn test_root_folders_scroll_no_op_when_not_ready(
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.is_loading = true;
app
.data
.sonarr_data
.root_folders
.set_items(simple_stateful_iterable_vec!(RootFolder, String, path));
RootFoldersHandler::with(key, &mut app, ActiveSonarrBlock::RootFolders, None).handle();
assert_str_eq!(
app.data.sonarr_data.root_folders.current_selection().path,
"Test 1"
);
RootFoldersHandler::with(key, &mut app, ActiveSonarrBlock::RootFolders, None).handle();
assert_str_eq!(
app.data.sonarr_data.root_folders.current_selection().path,
"Test 1"
);
}
}
mod test_handle_home_end {
use std::sync::atomic::Ordering;
use pretty_assertions::assert_eq;
use crate::models::servarr_models::RootFolder;
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
use super::*;
test_iterable_home_and_end!(
test_root_folders_home_end,
RootFoldersHandler,
sonarr_data,
root_folders,
extended_stateful_iterable_vec!(RootFolder, String, path),
ActiveSonarrBlock::RootFolders,
None,
path
);
#[test]
fn test_root_folders_home_end_no_op_when_not_ready() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.is_loading = true;
app
.data
.sonarr_data
.root_folders
.set_items(extended_stateful_iterable_vec!(RootFolder, String, path));
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.root_folders.current_selection().path,
"Test 1"
);
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.root_folders.current_selection().path,
"Test 1"
);
}
#[test]
fn test_add_root_folder_prompt_home_end_keys() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::AddRootFolderPrompt.into());
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.data.sonarr_data.edit_root_folder = Some("Test".into());
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.home.key,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app
.data
.sonarr_data
.edit_root_folder
.as_ref()
.unwrap()
.offset
.load(Ordering::SeqCst),
4
);
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.end.key,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app
.data
.sonarr_data
.edit_root_folder
.as_ref()
.unwrap()
.offset
.load(Ordering::SeqCst),
0
);
}
}
mod test_handle_delete {
use pretty_assertions::assert_eq;
use super::*;
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
#[test]
fn test_delete_root_folder_prompt() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::RootFolders, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::DeleteRootFolderPrompt.into()
);
}
#[test]
fn test_delete_root_folder_prompt_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(DELETE_KEY, &mut app, ActiveSonarrBlock::RootFolders, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
}
mod test_handle_left_right_action {
use std::sync::atomic::Ordering;
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
#[rstest]
fn test_root_folders_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(4);
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::History.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::History.into());
}
#[rstest]
fn test_root_folders_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(4);
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Indexers.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[rstest]
fn test_left_right_delete_root_folder_prompt_toggle(
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
RootFoldersHandler::with(
key,
&mut app,
ActiveSonarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
RootFoldersHandler::with(
key,
&mut app,
ActiveSonarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[test]
fn test_add_root_folder_prompt_left_right_keys() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.data.sonarr_data.edit_root_folder = Some("Test".into());
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app
.data
.sonarr_data
.edit_root_folder
.as_ref()
.unwrap()
.offset
.load(Ordering::SeqCst),
1
);
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app
.data
.sonarr_data
.edit_root_folder
.as_ref()
.unwrap()
.offset
.load(Ordering::SeqCst),
0
);
}
}
mod test_handle_submit {
use pretty_assertions::assert_eq;
use crate::network::sonarr_network::SonarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
#[test]
fn test_add_root_folder_prompt_confirm_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.data.sonarr_data.edit_root_folder = Some("Test".into());
app.data.sonarr_data.prompt_confirm = true;
app.should_ignore_quit_key = true;
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::AddRootFolderPrompt.into());
RootFoldersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::AddRootFolder(None))
);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
#[test]
fn test_add_root_folder_prompt_confirm_submit_noop_on_empty_folder() {
let mut app = App::default();
app.data.sonarr_data.edit_root_folder = Some(HorizontallyScrollableText::default());
app.data.sonarr_data.prompt_confirm = false;
app.should_ignore_quit_key = true;
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::AddRootFolderPrompt.into());
RootFoldersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(app.should_ignore_quit_key);
assert!(app.data.sonarr_data.prompt_confirm_action.is_none());
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AddRootFolderPrompt.into()
);
}
#[test]
fn test_delete_root_folder_prompt_confirm_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteRootFolder(None))
);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
#[test]
fn test_delete_root_folder_prompt_decline_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with(
SUBMIT_KEY,
&mut app,
ActiveSonarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.data.sonarr_data.prompt_confirm_action, None);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
}
mod test_handle_esc {
use super::*;
use pretty_assertions::assert_eq;
use rstest::rstest;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[test]
fn test_delete_root_folder_prompt_block_esc() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteRootFolderPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
RootFoldersHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
}
#[test]
fn test_add_root_folder_prompt_esc() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::AddRootFolderPrompt.into());
app.data.sonarr_data.edit_root_folder = Some("/nfs/test".into());
app.should_ignore_quit_key = true;
RootFoldersHandler::with(
ESC_KEY,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert!(app.data.sonarr_data.edit_root_folder.is_none());
assert!(!app.data.sonarr_data.prompt_confirm);
assert!(!app.should_ignore_quit_key);
}
#[rstest]
fn test_default_esc(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.is_loading = is_ready;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
RootFoldersHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::RootFolders, None).handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::network::sonarr_network::SonarrEvent;
use super::*;
#[test]
fn test_root_folder_add() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.add.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::AddRootFolderPrompt.into()
);
assert!(app.should_ignore_quit_key);
assert!(app.data.sonarr_data.edit_root_folder.is_some());
}
#[test]
fn test_root_folder_add_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.add.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert!(!app.should_ignore_quit_key);
assert!(app.data.sonarr_data.edit_root_folder.is_none());
}
#[test]
fn test_refresh_root_folders_key() {
let mut app = App::default();
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert!(app.should_refresh);
}
#[test]
fn test_refresh_root_folders_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert!(!app.should_refresh);
}
#[test]
fn test_add_root_folder_prompt_backspace_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.data.sonarr_data.edit_root_folder = Some("/nfs/test".into());
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.backspace.key,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.edit_root_folder.as_ref().unwrap().text,
"/nfs/tes"
);
}
#[test]
fn test_add_root_folder_prompt_char_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.data.sonarr_data.edit_root_folder = Some(HorizontallyScrollableText::default());
RootFoldersHandler::with(
Key::Char('h'),
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.handle();
assert_str_eq!(
app.data.sonarr_data.edit_root_folder.as_ref().unwrap().text,
"h"
);
}
#[test]
fn test_delete_root_folder_prompt_confirm() {
let mut app = App::default();
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteRootFolderPrompt.into());
RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::DeleteRootFolderPrompt,
None,
)
.handle();
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteRootFolder(None))
);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
}
}
#[test]
fn test_root_folders_handler_accepts() {
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if ROOT_FOLDERS_BLOCKS.contains(&active_sonarr_block) {
assert!(RootFoldersHandler::accepts(active_sonarr_block));
} else {
assert!(!RootFoldersHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_root_folders_handler_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.is_loading = true;
let handler = RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_root_folders_handler_not_ready_when_root_folders_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.is_loading = false;
let handler = RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
);
assert!(!handler.is_ready());
}
#[test]
fn test_root_folders_handler_ready_when_not_loading_and_root_folders_is_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.is_loading = false;
app
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
let handler = RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::RootFolders,
None,
);
assert!(handler.is_ready());
}
}
@@ -0,0 +1,157 @@
#[cfg(test)]
#[macro_use]
mod utils {
#[macro_export]
macro_rules! test_edit_series_key {
($handler:ident, $block:expr, $context:expr) => {
let mut app = App::default();
let mut sonarr_data = SonarrData {
quality_profile_map: bimap::BiMap::from_iter([
(2222, "HD - 1080p".to_owned()),
(1111, "Any".to_owned()),
]),
language_profiles_map: bimap::BiMap::from_iter([
(2222, "English".to_owned()),
(1111, "Any".to_owned()),
]),
tags_map: bimap::BiMap::from_iter([(1, "test".to_owned())]),
..create_test_sonarr_data()
};
sonarr_data.series.set_items(vec![Series {
path: "/nfs/series/Test".to_owned().into(),
monitored: true,
season_folder: true,
quality_profile_id: 2222,
language_profile_id: 2222,
series_type: SeriesType::Anime,
tags: vec![Number::from(1)],
..Series::default()
}]);
app.data.sonarr_data = sonarr_data;
$handler::with(DEFAULT_KEYBINDINGS.edit.key, &mut app, $block, None).handle();
assert_eq!(
app.get_current_route(),
(ActiveSonarrBlock::EditSeriesPrompt, Some($context)).into()
);
assert_eq!(
app.data.sonarr_data.selected_block.get_active_block(),
ActiveSonarrBlock::EditSeriesToggleMonitored
);
assert_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.series_type_list
.items,
Vec::from_iter(SeriesType::iter())
);
assert_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.series_type_list
.current_selection(),
&SeriesType::Anime
);
assert_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.quality_profile_list
.items,
vec!["Any".to_owned(), "HD - 1080p".to_owned()]
);
assert_str_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.quality_profile_list
.current_selection(),
"HD - 1080p"
);
assert_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.language_profile_list
.items,
vec!["Any".to_owned(), "English".to_owned()]
);
assert_str_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.language_profile_list
.current_selection(),
"English"
);
assert_str_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.path
.text,
"/nfs/series/Test"
);
assert_str_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.tags
.text,
"test"
);
assert_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.monitored,
Some(true)
);
assert_eq!(
app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.use_season_folders,
Some(true)
);
assert_eq!(
app.data.sonarr_data.selected_block.blocks,
$crate::models::servarr_data::sonarr::sonarr_data::EDIT_SERIES_SELECTION_BLOCKS
);
};
}
}
@@ -0,0 +1,225 @@
#[cfg(test)]
mod tests {
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::sonarr_handlers::SonarrHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::test_handler_delegation;
use pretty_assertions::assert_eq;
use rstest::rstest;
#[rstest]
#[case(0, ActiveSonarrBlock::System, ActiveSonarrBlock::Downloads)]
#[case(1, ActiveSonarrBlock::Series, ActiveSonarrBlock::Blocklist)]
#[case(2, ActiveSonarrBlock::Downloads, ActiveSonarrBlock::History)]
#[case(3, ActiveSonarrBlock::Blocklist, ActiveSonarrBlock::RootFolders)]
#[case(4, ActiveSonarrBlock::History, ActiveSonarrBlock::Indexers)]
#[case(5, ActiveSonarrBlock::RootFolders, ActiveSonarrBlock::System)]
#[case(6, ActiveSonarrBlock::Indexers, ActiveSonarrBlock::Series)]
fn test_sonarr_handler_change_tab_left_right_keys(
#[case] index: usize,
#[case] left_block: ActiveSonarrBlock,
#[case] right_block: ActiveSonarrBlock,
) {
let mut app = App::default();
app.data.sonarr_data.main_tabs.set_index(index);
handle_change_tab_left_right_keys(&mut app, DEFAULT_KEYBINDINGS.left.key);
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
left_block.into()
);
assert_eq!(app.get_current_route(), left_block.into());
app.data.sonarr_data.main_tabs.set_index(index);
handle_change_tab_left_right_keys(&mut app, DEFAULT_KEYBINDINGS.right.key);
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
right_block.into()
);
assert_eq!(app.get_current_route(), right_block.into());
}
#[rstest]
fn test_delegates_library_blocks_to_library_handler(
#[values(
ActiveSonarrBlock::AddSeriesAlreadyInLibrary,
ActiveSonarrBlock::AddSeriesEmptySearchResults,
ActiveSonarrBlock::AddSeriesPrompt,
ActiveSonarrBlock::AddSeriesSearchInput,
ActiveSonarrBlock::AddSeriesSearchResults,
ActiveSonarrBlock::AddSeriesSelectLanguageProfile,
ActiveSonarrBlock::AddSeriesSelectMonitor,
ActiveSonarrBlock::AddSeriesSelectQualityProfile,
ActiveSonarrBlock::AddSeriesSelectRootFolder,
ActiveSonarrBlock::AddSeriesSelectSeriesType,
ActiveSonarrBlock::AddSeriesTagsInput,
// ActiveSonarrBlock::AutomaticallySearchEpisodePrompt,
// ActiveSonarrBlock::AutomaticallySearchSeasonPrompt,
// ActiveSonarrBlock::AutomaticallySearchSeriesPrompt,
// ActiveSonarrBlock::DeleteEpisodeFilePrompt,
ActiveSonarrBlock::DeleteSeriesPrompt,
ActiveSonarrBlock::EditSeriesPrompt,
ActiveSonarrBlock::EditSeriesPathInput,
ActiveSonarrBlock::EditSeriesSelectSeriesType,
ActiveSonarrBlock::EditSeriesSelectQualityProfile,
ActiveSonarrBlock::EditSeriesSelectLanguageProfile,
ActiveSonarrBlock::EditSeriesTagsInput,
// ActiveSonarrBlock::EpisodeDetails,
// ActiveSonarrBlock::EpisodeFile,
// ActiveSonarrBlock::EpisodeHistory,
// ActiveSonarrBlock::EpisodesSortPrompt,
// ActiveSonarrBlock::FilterEpisodes,
// ActiveSonarrBlock::FilterEpisodesError,
ActiveSonarrBlock::FilterSeries,
ActiveSonarrBlock::FilterSeriesError,
// ActiveSonarrBlock::FilterSeriesHistory,
// ActiveSonarrBlock::FilterSeriesHistoryError,
// ActiveSonarrBlock::ManualEpisodeSearch,
// ActiveSonarrBlock::ManualEpisodeSearchConfirmPrompt,
// ActiveSonarrBlock::ManualEpisodeSearchSortPrompt,
// ActiveSonarrBlock::ManualSeasonSearch,
// ActiveSonarrBlock::ManualSeasonSearchConfirmPrompt,
// ActiveSonarrBlock::ManualSeasonSearchSortPrompt,
// ActiveSonarrBlock::SearchEpisodes,
// ActiveSonarrBlock::SearchEpisodesError,
// ActiveSonarrBlock::SearchSeason,
// ActiveSonarrBlock::SearchSeasonError,
ActiveSonarrBlock::SearchSeries,
ActiveSonarrBlock::SearchSeriesError,
// ActiveSonarrBlock::SearchSeriesHistory,
// ActiveSonarrBlock::SearchSeriesHistoryError,
// ActiveSonarrBlock::SeasonDetails,
ActiveSonarrBlock::Series,
// ActiveSonarrBlock::SeriesDetails,
// ActiveSonarrBlock::SeriesHistory,
// ActiveSonarrBlock::SeriesHistorySortPrompt,
ActiveSonarrBlock::SeriesSortPrompt,
ActiveSonarrBlock::UpdateAllSeriesPrompt,
// ActiveSonarrBlock::UpdateAndScanSeriesPrompt
// ActiveSonarrBlock::SeriesHistoryDetails,
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SonarrHandler,
ActiveSonarrBlock::Series,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_downloads_blocks_to_downloads_handler(
#[values(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
ActiveSonarrBlock::UpdateDownloadsPrompt
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SonarrHandler,
ActiveSonarrBlock::Downloads,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_blocklist_blocks_to_blocklist_handler(
#[values(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::BlocklistItemDetails,
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
ActiveSonarrBlock::BlocklistClearAllItemsPrompt,
ActiveSonarrBlock::BlocklistSortPrompt
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SonarrHandler,
ActiveSonarrBlock::Blocklist,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_history_blocks_to_history_handler(
#[values(
ActiveSonarrBlock::History,
ActiveSonarrBlock::HistoryItemDetails,
ActiveSonarrBlock::HistorySortPrompt,
ActiveSonarrBlock::FilterHistory,
ActiveSonarrBlock::FilterHistoryError,
ActiveSonarrBlock::SearchHistory,
ActiveSonarrBlock::SearchHistoryError
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SonarrHandler,
ActiveSonarrBlock::History,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_root_folders_blocks_to_root_folders_handler(
#[values(
ActiveSonarrBlock::RootFolders,
ActiveSonarrBlock::AddRootFolderPrompt,
ActiveSonarrBlock::DeleteRootFolderPrompt
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SonarrHandler,
ActiveSonarrBlock::RootFolders,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_indexers_blocks_to_indexers_handler(
#[values(
ActiveSonarrBlock::DeleteIndexerPrompt,
ActiveSonarrBlock::Indexers,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
ActiveSonarrBlock::IndexerSettingsConfirmPrompt,
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
ActiveSonarrBlock::IndexerSettingsRetentionInput,
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SonarrHandler,
ActiveSonarrBlock::Indexers,
active_sonarr_block
);
}
#[rstest]
fn test_delegates_system_blocks_to_system_handler(
#[values(
ActiveSonarrBlock::System,
ActiveSonarrBlock::SystemLogs,
ActiveSonarrBlock::SystemQueuedEvents,
ActiveSonarrBlock::SystemTasks,
ActiveSonarrBlock::SystemTaskStartConfirmPrompt,
ActiveSonarrBlock::SystemUpdates
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SonarrHandler,
ActiveSonarrBlock::System,
active_sonarr_block
);
}
}
+123
View File
@@ -0,0 +1,123 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::sonarr_handlers::system::system_details_handler::SystemDetailsHandler;
use crate::handlers::{handle_clear_errors, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::Scrollable;
mod system_details_handler;
#[cfg(test)]
#[path = "system_handler_tests.rs"]
mod system_handler_tests;
pub(super) struct SystemHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SystemHandler<'a, 'b> {
fn handle(&mut self) {
match self.active_sonarr_block {
_ if SystemDetailsHandler::accepts(self.active_sonarr_block) => {
SystemDetailsHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
.handle()
}
_ => self.handle_key_event(),
}
}
fn accepts(active_block: ActiveSonarrBlock) -> bool {
SystemDetailsHandler::accepts(active_block) || active_block == ActiveSonarrBlock::System
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> SystemHandler<'a, 'b> {
SystemHandler {
key,
app,
active_sonarr_block: active_block,
context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading
&& !self.app.data.sonarr_data.logs.is_empty()
&& !self.app.data.sonarr_data.queued_events.is_empty()
&& !self.app.data.sonarr_data.tasks.is_empty()
}
fn handle_scroll_up(&mut self) {}
fn handle_scroll_down(&mut self) {}
fn handle_home(&mut self) {}
fn handle_end(&mut self) {}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::System {
handle_change_tab_left_right_keys(self.app, self.key);
}
}
fn handle_submit(&mut self) {}
fn handle_esc(&mut self) {
handle_clear_errors(self.app)
}
fn handle_char_key_event(&mut self) {
if self.active_sonarr_block == ActiveSonarrBlock::System {
let key = self.key;
match self.key {
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if key == DEFAULT_KEYBINDINGS.events.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::SystemQueuedEvents.into());
}
_ if key == DEFAULT_KEYBINDINGS.logs.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::SystemLogs.into());
self
.app
.data
.sonarr_data
.log_details
.set_items(self.app.data.sonarr_data.logs.items.to_vec());
self.app.data.sonarr_data.log_details.scroll_to_bottom();
}
_ if key == DEFAULT_KEYBINDINGS.tasks.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::SystemTasks.into());
}
_ if key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::SystemUpdates.into());
}
_ => (),
}
}
}
}
@@ -0,0 +1,181 @@
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::sonarr::sonarr_data::{ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS};
use crate::models::stateful_list::StatefulList;
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
#[path = "system_details_handler_tests.rs"]
mod system_details_handler_tests;
pub(super) struct SystemDetailsHandler<'a, 'b> {
key: Key,
app: &'a mut App<'b>,
active_sonarr_block: ActiveSonarrBlock,
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SystemDetailsHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
SYSTEM_DETAILS_BLOCKS.contains(&active_block)
}
fn with(
key: Key,
app: &'a mut App<'b>,
active_block: ActiveSonarrBlock,
context: Option<ActiveSonarrBlock>,
) -> SystemDetailsHandler<'a, 'b> {
SystemDetailsHandler {
key,
app,
active_sonarr_block: active_block,
_context: context,
}
}
fn get_key(&self) -> Key {
self.key
}
fn is_ready(&self) -> bool {
!self.app.is_loading
&& (!self.app.data.sonarr_data.log_details.is_empty()
|| !self.app.data.sonarr_data.tasks.is_empty()
|| !self.app.data.sonarr_data.updates.is_empty())
}
fn handle_scroll_up(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SystemLogs => self.app.data.sonarr_data.log_details.scroll_up(),
ActiveSonarrBlock::SystemTasks => self.app.data.sonarr_data.tasks.scroll_up(),
ActiveSonarrBlock::SystemUpdates => self.app.data.sonarr_data.updates.scroll_up(),
ActiveSonarrBlock::SystemQueuedEvents => self.app.data.sonarr_data.queued_events.scroll_up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SystemLogs => self.app.data.sonarr_data.log_details.scroll_down(),
ActiveSonarrBlock::SystemTasks => self.app.data.sonarr_data.tasks.scroll_down(),
ActiveSonarrBlock::SystemUpdates => self.app.data.sonarr_data.updates.scroll_down(),
ActiveSonarrBlock::SystemQueuedEvents => {
self.app.data.sonarr_data.queued_events.scroll_down()
}
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SystemLogs => self.app.data.sonarr_data.log_details.scroll_to_top(),
ActiveSonarrBlock::SystemTasks => self.app.data.sonarr_data.tasks.scroll_to_top(),
ActiveSonarrBlock::SystemUpdates => self.app.data.sonarr_data.updates.scroll_to_top(),
ActiveSonarrBlock::SystemQueuedEvents => {
self.app.data.sonarr_data.queued_events.scroll_to_top()
}
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SystemLogs => self.app.data.sonarr_data.log_details.scroll_to_bottom(),
ActiveSonarrBlock::SystemTasks => self.app.data.sonarr_data.tasks.scroll_to_bottom(),
ActiveSonarrBlock::SystemUpdates => self.app.data.sonarr_data.updates.scroll_to_bottom(),
ActiveSonarrBlock::SystemQueuedEvents => {
self.app.data.sonarr_data.queued_events.scroll_to_bottom()
}
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
let key = self.key;
match self.active_sonarr_block {
ActiveSonarrBlock::SystemLogs => match self.key {
_ if key == DEFAULT_KEYBINDINGS.left.key => {
self
.app
.data
.sonarr_data
.log_details
.items
.iter()
.for_each(|log| log.scroll_right());
}
_ if key == DEFAULT_KEYBINDINGS.right.key => {
self
.app
.data
.sonarr_data
.log_details
.items
.iter()
.for_each(|log| log.scroll_left());
}
_ => (),
},
ActiveSonarrBlock::SystemTaskStartConfirmPrompt => handle_prompt_toggle(self.app, self.key),
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SystemTasks => {
self
.app
.push_navigation_stack(ActiveSonarrBlock::SystemTaskStartConfirmPrompt.into());
}
ActiveSonarrBlock::SystemTaskStartConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::StartTask(None));
}
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::SystemLogs => {
self.app.data.sonarr_data.log_details = StatefulList::default();
self.app.pop_navigation_stack()
}
ActiveSonarrBlock::SystemQueuedEvents
| ActiveSonarrBlock::SystemTasks
| ActiveSonarrBlock::SystemUpdates => self.app.pop_navigation_stack(),
ActiveSonarrBlock::SystemTaskStartConfirmPrompt => {
self.app.pop_navigation_stack();
self.app.data.sonarr_data.prompt_confirm = false;
}
_ => (),
}
}
fn handle_char_key_event(&mut self) {
if SYSTEM_DETAILS_BLOCKS.contains(&self.active_sonarr_block)
&& self.key == DEFAULT_KEYBINDINGS.refresh.key
{
self.app.should_refresh = true;
}
if self.active_sonarr_block == ActiveSonarrBlock::SystemTaskStartConfirmPrompt
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::StartTask(None));
self.app.pop_navigation_stack();
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,572 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use rstest::rstest;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::system::SystemHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS,
};
use crate::models::servarr_models::QueueEvent;
use crate::models::sonarr_models::SonarrTask;
use crate::test_handler_delegation;
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use super::*;
#[rstest]
fn test_system_tab_left(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(6);
SystemHandler::with(
DEFAULT_KEYBINDINGS.left.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Indexers.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
#[rstest]
fn test_system_tab_right(#[values(true, false)] is_ready: bool) {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.is_loading = is_ready;
app.data.sonarr_data.main_tabs.set_index(6);
SystemHandler::with(
DEFAULT_KEYBINDINGS.right.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(
app.data.sonarr_data.main_tabs.get_active_route(),
ActiveSonarrBlock::Series.into()
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
}
}
mod test_handle_esc {
use pretty_assertions::assert_eq;
use super::*;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
#[rstest]
fn test_default_esc(#[values(true, false)] is_loading: bool) {
let mut app = App::default();
app.is_loading = is_loading;
app.error = "test error".to_owned().into();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.push_navigation_stack(ActiveSonarrBlock::System.into());
SystemHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::System, None).handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
assert!(app.error.text.is_empty());
}
}
mod test_handle_key_char {
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::models::HorizontallyScrollableText;
use super::*;
#[test]
fn test_update_system_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::SystemUpdates.into()
);
}
#[test]
fn test_update_system_key_no_op_if_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
}
#[test]
fn test_queued_events_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.events.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::SystemQueuedEvents.into()
);
}
#[test]
fn test_queued_events_key_no_op_if_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.events.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
}
#[test]
fn test_refresh_system_key() {
let mut app = App::default();
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
app.push_navigation_stack(ActiveSonarrBlock::System.into());
SystemHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
assert!(app.should_refresh);
}
#[test]
fn test_refresh_system_key_no_op_if_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
app.push_navigation_stack(ActiveSonarrBlock::System.into());
SystemHandler::with(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
assert!(!app.should_refresh);
}
#[test]
fn test_logs_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.logs.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::SystemLogs.into()
);
assert_eq!(
app.data.sonarr_data.log_details.items,
app.data.sonarr_data.logs.items
);
assert_str_eq!(
app.data.sonarr_data.log_details.current_selection().text,
"test 2"
);
}
#[test]
fn test_logs_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.logs.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
assert!(app.data.sonarr_data.log_details.is_empty());
}
#[test]
fn test_tasks_key() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.tasks.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::SystemTasks.into()
);
}
#[test]
fn test_tasks_key_no_op_when_not_ready() {
let mut app = App::default();
app.is_loading = true;
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.logs.set_items(vec![
HorizontallyScrollableText::from("test 1"),
HorizontallyScrollableText::from("test 2"),
]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
SystemHandler::with(
DEFAULT_KEYBINDINGS.tasks.key,
&mut app,
ActiveSonarrBlock::System,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::System.into());
}
}
#[rstest]
fn test_delegates_system_details_blocks_to_system_details_handler(
#[values(
ActiveSonarrBlock::SystemLogs,
ActiveSonarrBlock::SystemQueuedEvents,
ActiveSonarrBlock::SystemTasks,
ActiveSonarrBlock::SystemTaskStartConfirmPrompt,
ActiveSonarrBlock::SystemUpdates
)]
active_sonarr_block: ActiveSonarrBlock,
) {
test_handler_delegation!(
SystemHandler,
ActiveSonarrBlock::System,
active_sonarr_block
);
}
#[test]
fn test_system_handler_accepts() {
let mut system_blocks = vec![ActiveSonarrBlock::System];
system_blocks.extend(SYSTEM_DETAILS_BLOCKS);
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
if system_blocks.contains(&active_sonarr_block) {
assert!(SystemHandler::accepts(active_sonarr_block));
} else {
assert!(!SystemHandler::accepts(active_sonarr_block));
}
})
}
#[test]
fn test_system_handler_is_not_ready_when_loading() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.is_loading = true;
let system_handler = SystemHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
}
#[test]
fn test_system_handler_is_not_ready_when_logs_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.is_loading = false;
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
}
#[test]
fn test_system_handler_is_not_ready_when_tasks_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.is_loading = false;
app.data.sonarr_data.logs.set_items(vec!["test".into()]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
}
#[test]
fn test_system_handler_is_not_ready_when_queued_events_is_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.is_loading = false;
app.data.sonarr_data.logs.set_items(vec!["test".into()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
let system_handler = SystemHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::System,
None,
);
assert!(!system_handler.is_ready());
}
#[test]
fn test_system_handler_is_ready_when_all_required_tables_are_not_empty() {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.is_loading = false;
app.data.sonarr_data.logs.set_items(vec!["test".into()]);
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
app
.data
.sonarr_data
.queued_events
.set_items(vec![QueueEvent::default()]);
let system_handler = SystemHandler::with(
DEFAULT_KEYBINDINGS.update.key,
&mut app,
ActiveSonarrBlock::System,
None,
);
assert!(system_handler.is_ready());
}
}
+402
View File
@@ -0,0 +1,402 @@
use crate::models::stateful_table::SortOption;
use crate::models::Route;
use derive_setters::Setters;
use std::cmp::Ordering;
use std::fmt::Debug;
#[derive(Setters)]
pub struct TableHandlingProps<T>
where
T: Clone + PartialEq + Eq + Debug + Default,
{
#[setters(strip_option)]
pub sorting_block: Option<Route>,
#[setters(strip_option)]
pub sort_options: Option<Vec<SortOption<T>>>,
#[setters(strip_option)]
pub sort_by_fn: Option<fn(&T, &T) -> Ordering>,
#[setters(strip_option)]
pub searching_block: Option<Route>,
#[setters(strip_option)]
pub search_error_block: Option<Route>,
#[setters(strip_option)]
pub search_field_fn: Option<fn(&T) -> &str>,
#[setters(strip_option)]
pub filtering_block: Option<Route>,
#[setters(strip_option)]
pub filter_error_block: Option<Route>,
#[setters(strip_option)]
pub filter_field_fn: Option<fn(&T) -> &str>,
#[setters(skip)]
pub table_block: Route,
}
#[macro_export]
macro_rules! handle_table_events {
($self:expr, $name:ty, $table:expr, $row:ident) => {
paste::paste! {
fn [<handle_ $name _table_events>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
if $self.is_ready() {
match $self.key {
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.up.key => $self.[<handle_ $name _table_scroll_up>](props),
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.down.key => $self.[<handle_ $name _table_scroll_down>](props),
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.home.key => $self.[<handle_ $name _table_home>](props),
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.end.key => $self.[<handle_ $name _table_end>](props),
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.left.key
|| $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.right.key =>
{
$self.[<handle_ $name _table_left_right>](props)
}
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.submit.key => $self.[<handle_ $name _table_submit>](props),
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.esc.key => $self.[<handle_ $name _table_esc>](props),
_ if props.searching_block.is_some()
&& $self.app.get_current_route() == *props.searching_block.as_ref().unwrap() =>
{
$self.[<handle_ $name _table_search_box_input>]()
}
_ if props.filtering_block.is_some()
&& $self.app.get_current_route() == *props.filtering_block.as_ref().unwrap() =>
{
$self.[<handle_ $name _table_filter_box_input>]()
}
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.filter.key => $self.[<handle_ $name _table_filter_key>](props),
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.search.key => $self.[<handle_ $name _table_search_key>](props),
_ if $self.key == $crate::app::key_binding::DEFAULT_KEYBINDINGS.sort.key => $self.[<handle_ $name _table_sort_key>](props),
_ => false,
}
} else {
false
}
}
fn [<handle_ $name _table_scroll_up>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
match $self.app.get_current_route() {
_ if props.table_block == $self.app.get_current_route() => {
$table.scroll_up();
true
}
_ if props.sorting_block.is_some()
&& $self.app.get_current_route() == *props.sorting_block.as_ref().unwrap() =>
{
$table.sort.as_mut().unwrap().scroll_up();
true
}
_ => false,
}
}
fn [<handle_ $name _table_scroll_down>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
match $self.app.get_current_route() {
_ if props.table_block == $self.app.get_current_route() => {
$table.scroll_down();
true
}
_ if props.sorting_block.is_some()
&& $self.app.get_current_route() == *props.sorting_block.as_ref().unwrap() =>
{
$table
.sort
.as_mut()
.unwrap()
.scroll_down();
true
}
_ => false,
}
}
fn [<handle_ $name _table_home>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
match $self.app.get_current_route() {
_ if props.table_block == $self.app.get_current_route() => {
$table.scroll_to_top();
true
}
_ if props.sorting_block.is_some()
&& $self.app.get_current_route() == *props.sorting_block.as_ref().unwrap() =>
{
$table
.sort
.as_mut()
.unwrap()
.scroll_to_top();
true
}
_ if props.searching_block.is_some()
&& $self.app.get_current_route() == *props.searching_block.as_ref().unwrap() =>
{
$table
.search
.as_mut()
.unwrap()
.scroll_home();
true
}
_ if props.filtering_block.is_some()
&& $self.app.get_current_route() == *props.filtering_block.as_ref().unwrap() =>
{
$table
.filter
.as_mut()
.unwrap()
.scroll_home();
true
}
_ => false,
}
}
fn [<handle_ $name _table_end>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
match $self.app.get_current_route() {
_ if props.table_block == $self.app.get_current_route() => {
$table.scroll_to_bottom();
true
}
_ if props.sorting_block.is_some()
&& $self.app.get_current_route() == *props.sorting_block.as_ref().unwrap() =>
{
$table
.sort
.as_mut()
.unwrap()
.scroll_to_bottom();
true
}
_ if props.searching_block.is_some()
&& $self.app.get_current_route() == *props.searching_block.as_ref().unwrap() =>
{
$table
.search
.as_mut()
.unwrap()
.reset_offset();
true
}
_ if props.filtering_block.is_some()
&& $self.app.get_current_route() == *props.filtering_block.as_ref().unwrap() =>
{
$table
.filter
.as_mut()
.unwrap()
.reset_offset();
true
}
_ => false,
}
}
fn [<handle_ $name _table_left_right>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
match $self.app.get_current_route() {
_ if props.searching_block.is_some()
&& $self.app.get_current_route() == *props.searching_block.as_ref().unwrap() =>
{
$crate::handle_text_box_left_right_keys!(
$self,
$self.key,
$table.search.as_mut().unwrap()
);
true
}
_ if props.filtering_block.is_some()
&& $self.app.get_current_route() == *props.filtering_block.as_ref().unwrap() =>
{
$crate::handle_text_box_left_right_keys!(
$self,
$self.key,
$table.filter.as_mut().unwrap()
);
true
}
_ => false,
}
}
fn [<handle _$name _table_submit>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
match $self.app.get_current_route() {
_ if props.sorting_block.is_some()
&& $self.app.get_current_route() == *props.sorting_block.as_ref().unwrap() =>
{
if let Some(sort_by_fn) = props.sort_by_fn {
$table.items.sort_by(sort_by_fn);
}
$table.apply_sorting();
$self.app.pop_navigation_stack();
true
}
_ if props.searching_block.is_some()
&& $self.app.get_current_route() == *props.searching_block.as_ref().unwrap() =>
{
$self.app.pop_navigation_stack();
$self.app.should_ignore_quit_key = false;
if $table.search.is_some() {
let search_field_fn = props
.search_field_fn
.expect("Search field function is required");
let has_match = $table.apply_search(search_field_fn);
if !has_match {
$self.app.push_navigation_stack(
props
.search_error_block
.expect("Search error block is undefined"),
);
}
}
true
}
_ if props.filtering_block.is_some()
&& $self.app.get_current_route() == *props.filtering_block.as_ref().unwrap() =>
{
$self.app.pop_navigation_stack();
$self.app.should_ignore_quit_key = false;
if $table.filter.is_some() {
let filter_field_fn = props
.filter_field_fn
.expect("Search field function is required");
let has_match = $table.apply_filter(filter_field_fn);
if !has_match {
$self.app.push_navigation_stack(
props
.filter_error_block
.expect("Search error block is undefined"),
);
}
}
true
}
_ => false,
}
}
fn [<handle_ $name _table_esc>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
match $self.app.get_current_route() {
_ if props.sorting_block.is_some()
&& $self.app.get_current_route() == *props.sorting_block.as_ref().unwrap() =>
{
$self.app.pop_navigation_stack();
true
}
_ if (props.searching_block.is_some()
&& $self.app.get_current_route() == *props.searching_block.as_ref().unwrap())
|| (props.search_error_block.is_some()
&& $self.app.get_current_route() == *props.search_error_block.as_ref().unwrap()) =>
{
$self.app.pop_navigation_stack();
$table.reset_search();
$self.app.should_ignore_quit_key = false;
true
}
_ if (props.filtering_block.is_some()
&& $self.app.get_current_route() == *props.filtering_block.as_ref().unwrap())
|| (props.filter_error_block.is_some()
&& $self.app.get_current_route() == *props.filter_error_block.as_ref().unwrap()) =>
{
$self.app.pop_navigation_stack();
$table.reset_filter();
$self.app.should_ignore_quit_key = false;
true
}
_ if props.table_block == $self.app.get_current_route()
&& $table.filtered_items.is_some() =>
{
$table.reset_filter();
true
}
_ => false,
}
}
fn [<handle_ $name _table_filter_key>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if props.table_block == $self.app.get_current_route()) {
$self
.app
.push_navigation_stack(props.filtering_block.expect("Filtering block is undefined").into());
$table.reset_filter();
$table.filter = Some($crate::models::HorizontallyScrollableText::default());
$self.app.should_ignore_quit_key = true;
true
} else {
false
}
}
fn [<handle_ $name _table_search_key>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if props.table_block == $self.app.get_current_route()) {
$self
.app
.push_navigation_stack(props.searching_block.expect("Searching block is undefined"));
$table.search = Some($crate::models::HorizontallyScrollableText::default());
$self.app.should_ignore_quit_key = true;
true
} else {
false
}
}
fn [<handle_ $name _table_sort_key>](&mut $self, props: $crate::handlers::table_handler::TableHandlingProps<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if props.table_block == $self.app.get_current_route()) {
$table.sorting(
props
.sort_options
.as_ref()
.expect("Sort options are undefined")
.clone(),
);
$self
.app
.push_navigation_stack(props.sorting_block.expect("Sorting block is undefined"));
true
} else {
false
}
}
fn [<handle_ $name _table_search_box_input>](&mut $self) -> bool {
$crate::handle_text_box_keys!(
$self,
$self.key,
$table.search.as_mut().unwrap()
);
true
}
fn [<handle_ $name _table_filter_box_input>](&mut $self) -> bool {
$crate::handle_text_box_keys!(
$self,
$self.key,
$table.filter.as_mut().unwrap()
);
true
}
}
};
}
impl<T> TableHandlingProps<T>
where
T: Clone + PartialEq + Eq + Debug + Default,
{
pub fn new(table_block: Route) -> Self {
TableHandlingProps {
sorting_block: None,
sort_options: None,
sort_by_fn: None,
searching_block: None,
search_error_block: None,
search_field_fn: None,
filtering_block: None,
filter_error_block: None,
filter_field_fn: None,
table_block,
}
}
}
+8 -9
View File
@@ -6,15 +6,14 @@ pub const RADARR_LOGO: &str = "⠀⣠⣶⢶⣶⣤⣀⠀⠀⠀⠀
";
// Allowing this code for now since we'll eventually be implementing additional Servarr support and we'll need it then
#[allow(dead_code)]
pub const SONARR_LOGO: &str = "
pub const SONARR_LOGO: &str = "⠀⣠⣴⣶⣿⣿⣿⣿⣶⣦⣄⠀
";
// Allowing this code for now since we'll eventually be implementing additional Servarr support and we'll need it then
#[allow(dead_code)]
+1 -4
View File
@@ -177,7 +177,6 @@ async fn start_ui(app: &Arc<Mutex<App<'_>>>) -> Result<()> {
terminal.hide_cursor()?;
let input_events = Events::new();
let mut is_first_render = true;
loop {
let mut app = app.lock().await;
@@ -193,10 +192,8 @@ async fn start_ui(app: &Arc<Mutex<App<'_>>>) -> Result<()> {
handlers::handle_events(key, &mut app);
}
InputEvent::Tick => app.on_tick(is_first_render).await,
InputEvent::Tick => app.on_tick().await,
}
is_first_render = false;
}
terminal.show_cursor()?;
+32 -18
View File
@@ -290,8 +290,8 @@ impl TabState {
&self.tabs[self.index]
}
pub fn get_active_route(&self) -> &Route {
&self.tabs[self.index].route
pub fn get_active_route(&self) -> Route {
self.tabs[self.index].route
}
pub fn get_active_tab_help(&self) -> &str {
@@ -320,33 +320,46 @@ pub struct BlockSelectionState<'a, T>
where
T: Sized + Clone + Copy + Default,
{
pub blocks: &'a [T],
pub index: usize,
pub blocks: &'a [&'a [T]],
pub x: usize,
pub y: usize,
}
impl<'a, T> BlockSelectionState<'a, T>
where
T: Sized + Clone + Copy + Default,
{
pub fn new(blocks: &'a [T]) -> BlockSelectionState<'a, T> {
BlockSelectionState { blocks, index: 0 }
pub fn new(blocks: &'a [&'a [T]]) -> BlockSelectionState<'a, T> {
BlockSelectionState { blocks, x: 0, y: 0 }
}
pub fn get_active_block(&self) -> &T {
&self.blocks[self.index]
pub fn get_active_block(&self) -> T {
self.blocks[self.y][self.x]
}
pub fn next(&mut self) {
self.index = (self.index + 1) % self.blocks.len();
}
pub fn previous(&mut self) {
if self.index > 0 {
self.index -= 1;
pub fn left(&mut self) {
if self.x > 0 {
self.x -= 1;
} else {
self.index = self.blocks.len() - 1;
self.x = self.blocks[0].len() - 1;
}
}
pub fn right(&mut self) {
self.x = (self.x + 1) % self.blocks[0].len();
}
pub fn up(&mut self) {
if self.y > 0 {
self.y -= 1;
} else {
self.y = self.blocks.len() - 1;
}
}
pub fn down(&mut self) {
self.y = (self.y + 1) % self.blocks.len();
}
}
#[cfg(test)]
@@ -354,8 +367,9 @@ impl<'a, T> BlockSelectionState<'a, T>
where
T: Sized + Clone + Copy + Default,
{
pub fn set_index(&mut self, index: usize) {
self.index = index;
pub fn set_index(&mut self, x: usize, y: usize) {
self.x = x;
self.y = y;
}
}

Some files were not shown because too many files have changed in this diff Show More