feat: Refactor all keybinding tips into a dynamically changing menu that can be invoked via '?' [#32]
This commit is contained in:
@@ -5,7 +5,6 @@ mod tests {
|
|||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, SERVARR_CONTEXT_CLUES};
|
|
||||||
use crate::app::{interpolate_env_vars, App, AppConfig, Data, ServarrConfig};
|
use crate::app::{interpolate_env_vars, App, AppConfig, Data, ServarrConfig};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
||||||
@@ -39,40 +38,24 @@ mod tests {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Sonarr Test".to_owned(),
|
title: "Sonarr Test".to_owned(),
|
||||||
route: ActiveSonarrBlock::default().into(),
|
route: ActiveSonarrBlock::default().into(),
|
||||||
help: format!(
|
|
||||||
"<↑↓> scroll | <C-u/d> page up/down | ←→ change tab | {} ",
|
|
||||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
|
||||||
),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(sonarr_config_1),
|
config: Some(sonarr_config_1),
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Radarr 1".to_owned(),
|
title: "Radarr 1".to_owned(),
|
||||||
route: ActiveRadarrBlock::default().into(),
|
route: ActiveRadarrBlock::default().into(),
|
||||||
help: format!(
|
|
||||||
"<↑↓> scroll | <C-u/d> page up/down | ←→ change tab | {} ",
|
|
||||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
|
||||||
),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(radarr_config_2),
|
config: Some(radarr_config_2),
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Radarr Test".to_owned(),
|
title: "Radarr Test".to_owned(),
|
||||||
route: ActiveRadarrBlock::default().into(),
|
route: ActiveRadarrBlock::default().into(),
|
||||||
help: format!(
|
|
||||||
"<↑↓> scroll | <C-u/d> page up/down | ←→ change tab | {} ",
|
|
||||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
|
||||||
),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(radarr_config_1),
|
config: Some(radarr_config_1),
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Sonarr 1".to_owned(),
|
title: "Sonarr 1".to_owned(),
|
||||||
route: ActiveSonarrBlock::default().into(),
|
route: ActiveSonarrBlock::default().into(),
|
||||||
help: format!(
|
|
||||||
"<↑↓> scroll | <C-u/d> page up/down | ←→ change tab | {} ",
|
|
||||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
|
||||||
),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(sonarr_config_2),
|
config: Some(sonarr_config_2),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,25 +1,51 @@
|
|||||||
use crate::app::key_binding::{KeyBinding, DEFAULT_KEYBINDINGS};
|
use crate::app::key_binding::{KeyBinding, DEFAULT_KEYBINDINGS};
|
||||||
|
use crate::app::radarr::radarr_context_clues::RadarrContextClueProvider;
|
||||||
|
use crate::app::sonarr::sonarr_context_clues::SonarrContextClueProvider;
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::Route;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "context_clues_tests.rs"]
|
#[path = "context_clues_tests.rs"]
|
||||||
mod context_clues_tests;
|
mod context_clues_tests;
|
||||||
|
|
||||||
pub(in crate::app) type ContextClue = (KeyBinding, &'static str);
|
pub type ContextClue = (KeyBinding, &'static str);
|
||||||
|
|
||||||
pub fn build_context_clue_string(context_clues: &[(KeyBinding, &str)]) -> String {
|
pub trait ContextClueProvider {
|
||||||
context_clues
|
fn get_context_clues(_app: &mut App<'_>) -> Option<&'static [ContextClue]>;
|
||||||
.iter()
|
|
||||||
.map(|(key_binding, desc)| format!("{} {desc}", key_binding.key))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(" | ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static SERVARR_CONTEXT_CLUES: [ContextClue; 2] = [
|
pub struct ServarrContextClueProvider;
|
||||||
|
|
||||||
|
impl ContextClueProvider for ServarrContextClueProvider {
|
||||||
|
fn get_context_clues(app: &mut App<'_>) -> Option<&'static [ContextClue]> {
|
||||||
|
match app.get_current_route() {
|
||||||
|
Route::Radarr(_, _) => RadarrContextClueProvider::get_context_clues(app),
|
||||||
|
Route::Sonarr(_, _) => SonarrContextClueProvider::get_context_clues(app),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static SERVARR_CONTEXT_CLUES: [ContextClue; 10] = [
|
||||||
|
(DEFAULT_KEYBINDINGS.up, "scroll up"),
|
||||||
|
(DEFAULT_KEYBINDINGS.down, "scroll down"),
|
||||||
|
(DEFAULT_KEYBINDINGS.left, "previous tab"),
|
||||||
|
(DEFAULT_KEYBINDINGS.right, "next tab"),
|
||||||
|
(DEFAULT_KEYBINDINGS.pg_up, DEFAULT_KEYBINDINGS.pg_up.desc),
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.pg_down,
|
||||||
|
DEFAULT_KEYBINDINGS.pg_down.desc,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.next_servarr,
|
DEFAULT_KEYBINDINGS.next_servarr,
|
||||||
DEFAULT_KEYBINDINGS.next_servarr.desc,
|
DEFAULT_KEYBINDINGS.next_servarr.desc,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.previous_servarr,
|
||||||
|
DEFAULT_KEYBINDINGS.previous_servarr.desc,
|
||||||
|
),
|
||||||
(DEFAULT_KEYBINDINGS.quit, DEFAULT_KEYBINDINGS.quit.desc),
|
(DEFAULT_KEYBINDINGS.quit, DEFAULT_KEYBINDINGS.quit.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.help, DEFAULT_KEYBINDINGS.help.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static BARE_POPUP_CONTEXT_CLUES: [ContextClue; 1] =
|
pub static BARE_POPUP_CONTEXT_CLUES: [ContextClue; 1] =
|
||||||
|
|||||||
@@ -3,24 +3,15 @@ mod test {
|
|||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
|
||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
ContextClueProvider, ServarrContextClueProvider, BARE_POPUP_CONTEXT_CLUES,
|
||||||
DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES,
|
BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES,
|
||||||
SERVARR_CONTEXT_CLUES, SYSTEM_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};
|
use crate::app::{key_binding::DEFAULT_KEYBINDINGS, App};
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
#[test]
|
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
fn test_build_context_clue_string() {
|
use crate::models::servarr_data::ActiveKeybindingBlock;
|
||||||
let test_context_clues_array = [
|
|
||||||
(DEFAULT_KEYBINDINGS.add, "add"),
|
|
||||||
(DEFAULT_KEYBINDINGS.delete, "delete"),
|
|
||||||
];
|
|
||||||
|
|
||||||
assert_str_eq!(
|
|
||||||
build_context_clue_string(&test_context_clues_array),
|
|
||||||
"<a> add | <del> delete"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_servarr_context_clues() {
|
fn test_servarr_context_clues() {
|
||||||
@@ -28,13 +19,53 @@ mod test {
|
|||||||
|
|
||||||
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.up);
|
||||||
|
assert_str_eq!(*description, "scroll up");
|
||||||
|
|
||||||
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.down);
|
||||||
|
assert_str_eq!(*description, "scroll down");
|
||||||
|
|
||||||
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.left);
|
||||||
|
assert_str_eq!(*description, "previous tab");
|
||||||
|
|
||||||
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.right);
|
||||||
|
assert_str_eq!(*description, "next tab");
|
||||||
|
|
||||||
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.pg_up);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.pg_up.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.pg_down);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.pg_down.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.next_servarr);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.next_servarr);
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.next_servarr.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.next_servarr.desc);
|
||||||
|
|
||||||
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
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();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.quit);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.quit);
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.quit.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.quit.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = servarr_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.help);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.help.desc);
|
||||||
assert_eq!(servarr_context_clues_iter.next(), None);
|
assert_eq!(servarr_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,4 +235,42 @@ mod test {
|
|||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||||
assert_eq!(system_context_clues_iter.next(), None);
|
assert_eq!(system_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_servarr_context_clue_provider_delegates_to_radarr_provider() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
|
||||||
|
|
||||||
|
let context_clues = ServarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
&crate::app::radarr::radarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
|
context_clues.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_servarr_context_clue_provider_delegates_to_sonarr_provider() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveSonarrBlock::SystemTasks.into());
|
||||||
|
|
||||||
|
let context_clues = ServarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
&crate::app::sonarr::sonarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
|
context_clues.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_servarr_context_clue_provider_unsupported_route_returns_none() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveKeybindingBlock::Help.into());
|
||||||
|
|
||||||
|
let context_clues = ServarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_none());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ generate_keybindings! {
|
|||||||
search,
|
search,
|
||||||
auto_search,
|
auto_search,
|
||||||
settings,
|
settings,
|
||||||
|
help,
|
||||||
filter,
|
filter,
|
||||||
sort,
|
sort,
|
||||||
edit,
|
edit,
|
||||||
@@ -121,6 +122,11 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
|
|||||||
alt: None,
|
alt: None,
|
||||||
desc: "settings",
|
desc: "settings",
|
||||||
},
|
},
|
||||||
|
help: KeyBinding {
|
||||||
|
key: Key::Char('?'),
|
||||||
|
alt: None,
|
||||||
|
desc: "show/hide keybindings",
|
||||||
|
},
|
||||||
filter: KeyBinding {
|
filter: KeyBinding {
|
||||||
key: Key::Char('f'),
|
key: Key::Char('f'),
|
||||||
alt: None,
|
alt: None,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ mod test {
|
|||||||
#[case(DEFAULT_KEYBINDINGS.auto_search, Key::Char('S'), None, "auto search")]
|
#[case(DEFAULT_KEYBINDINGS.auto_search, Key::Char('S'), None, "auto search")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.search, Key::Char('s'), None, "search")]
|
#[case(DEFAULT_KEYBINDINGS.search, Key::Char('s'), None, "search")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.settings, Key::Char('S'), None, "settings")]
|
#[case(DEFAULT_KEYBINDINGS.settings, Key::Char('S'), None, "settings")]
|
||||||
|
#[case(DEFAULT_KEYBINDINGS.help, Key::Char('?'), None, "show/hide keybindings")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.filter, Key::Char('f'), None, "filter")]
|
#[case(DEFAULT_KEYBINDINGS.filter, Key::Char('f'), None, "filter")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.sort, Key::Char('o'), None, "sort")]
|
#[case(DEFAULT_KEYBINDINGS.sort, Key::Char('o'), None, "sort")]
|
||||||
#[case(DEFAULT_KEYBINDINGS.edit, Key::Char('e'), None, "edit")]
|
#[case(DEFAULT_KEYBINDINGS.edit, Key::Char('e'), None, "edit")]
|
||||||
|
|||||||
+4
-15
@@ -10,10 +10,11 @@ use tokio::sync::mpsc::Sender;
|
|||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use veil::Redact;
|
use veil::Redact;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, SERVARR_CONTEXT_CLUES};
|
|
||||||
use crate::cli::Command;
|
use crate::cli::Command;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
||||||
|
use crate::models::servarr_models::KeybindingItem;
|
||||||
|
use crate::models::stateful_table::StatefulTable;
|
||||||
use crate::models::{HorizontallyScrollableText, Route, TabRoute, TabState};
|
use crate::models::{HorizontallyScrollableText, Route, TabRoute, TabState};
|
||||||
use crate::network::NetworkEvent;
|
use crate::network::NetworkEvent;
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ pub struct App<'a> {
|
|||||||
pub cancellation_token: CancellationToken,
|
pub cancellation_token: CancellationToken,
|
||||||
pub is_first_render: bool,
|
pub is_first_render: bool,
|
||||||
pub server_tabs: TabState,
|
pub server_tabs: TabState,
|
||||||
|
pub keymapping_table: Option<StatefulTable<KeybindingItem>>,
|
||||||
pub error: HorizontallyScrollableText,
|
pub error: HorizontallyScrollableText,
|
||||||
pub tick_until_poll: u64,
|
pub tick_until_poll: u64,
|
||||||
pub ticks_until_scroll: u64,
|
pub ticks_until_scroll: u64,
|
||||||
@@ -51,10 +53,6 @@ impl App<'_> {
|
|||||||
cancellation_token: CancellationToken,
|
cancellation_token: CancellationToken,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut server_tabs = Vec::new();
|
let mut server_tabs = Vec::new();
|
||||||
let help = format!(
|
|
||||||
"<↑↓> scroll | <C-u/d> page up/down | ←→ change tab | {} ",
|
|
||||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(radarr_configs) = config.radarr {
|
if let Some(radarr_configs) = config.radarr {
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
@@ -69,7 +67,6 @@ impl App<'_> {
|
|||||||
server_tabs.push(TabRoute {
|
server_tabs.push(TabRoute {
|
||||||
title: name,
|
title: name,
|
||||||
route: ActiveRadarrBlock::Movies.into(),
|
route: ActiveRadarrBlock::Movies.into(),
|
||||||
help: help.clone(),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(radarr_config),
|
config: Some(radarr_config),
|
||||||
});
|
});
|
||||||
@@ -90,7 +87,6 @@ impl App<'_> {
|
|||||||
server_tabs.push(TabRoute {
|
server_tabs.push(TabRoute {
|
||||||
title: name,
|
title: name,
|
||||||
route: ActiveSonarrBlock::Series.into(),
|
route: ActiveSonarrBlock::Series.into(),
|
||||||
help: help.clone(),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(sonarr_config),
|
config: Some(sonarr_config),
|
||||||
});
|
});
|
||||||
@@ -215,6 +211,7 @@ impl Default for App<'_> {
|
|||||||
navigation_stack: Vec::new(),
|
navigation_stack: Vec::new(),
|
||||||
network_tx: None,
|
network_tx: None,
|
||||||
cancellation_token: CancellationToken::new(),
|
cancellation_token: CancellationToken::new(),
|
||||||
|
keymapping_table: None,
|
||||||
error: HorizontallyScrollableText::default(),
|
error: HorizontallyScrollableText::default(),
|
||||||
is_first_render: true,
|
is_first_render: true,
|
||||||
server_tabs: TabState::new(Vec::new()),
|
server_tabs: TabState::new(Vec::new()),
|
||||||
@@ -239,20 +236,12 @@ impl App<'_> {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Radarr".to_owned(),
|
title: "Radarr".to_owned(),
|
||||||
route: ActiveRadarrBlock::Movies.into(),
|
route: ActiveRadarrBlock::Movies.into(),
|
||||||
help: format!(
|
|
||||||
"<↑↓> scroll | ←→ change tab | {} ",
|
|
||||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
|
||||||
),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(ServarrConfig::default()),
|
config: Some(ServarrConfig::default()),
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Sonarr".to_owned(),
|
title: "Sonarr".to_owned(),
|
||||||
route: ActiveSonarrBlock::Series.into(),
|
route: ActiveSonarrBlock::Series.into(),
|
||||||
help: format!(
|
|
||||||
"<↑↓> scroll | ←→ change tab | {} ",
|
|
||||||
build_context_clue_string(&SERVARR_CONTEXT_CLUES)
|
|
||||||
),
|
|
||||||
contextual_help: None,
|
contextual_help: None,
|
||||||
config: Some(ServarrConfig::default()),
|
config: Some(ServarrConfig::default()),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
use crate::app::context_clues::ContextClue;
|
use crate::app::context_clues::{
|
||||||
|
ContextClue, ContextClueProvider, BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
||||||
|
};
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
|
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, EDIT_COLLECTION_BLOCKS, EDIT_INDEXER_BLOCKS,
|
||||||
|
EDIT_MOVIE_BLOCKS, INDEXER_SETTINGS_BLOCKS, MOVIE_DETAILS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::models::Route;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "radarr_context_clues_tests.rs"]
|
#[path = "radarr_context_clues_tests.rs"]
|
||||||
@@ -53,7 +61,7 @@ pub static MOVIE_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
|||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static MANUAL_MOVIE_SEARCH_CONTEXT_CLUES: [ContextClue; 6] = [
|
pub static MANUAL_MOVIE_SEARCH_CONTEXT_CLUES: [ContextClue; 7] = [
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
@@ -65,12 +73,10 @@ pub static MANUAL_MOVIE_SEARCH_CONTEXT_CLUES: [ContextClue; 6] = [
|
|||||||
DEFAULT_KEYBINDINGS.auto_search,
|
DEFAULT_KEYBINDINGS.auto_search,
|
||||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
),
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES: [ContextClue; 1] =
|
|
||||||
[(DEFAULT_KEYBINDINGS.submit, "details")];
|
|
||||||
|
|
||||||
pub static ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [
|
pub static ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||||
(DEFAULT_KEYBINDINGS.submit, "details"),
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, "edit search"),
|
(DEFAULT_KEYBINDINGS.esc, "edit search"),
|
||||||
@@ -86,3 +92,54 @@ pub static COLLECTION_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
|
|||||||
(DEFAULT_KEYBINDINGS.edit, "edit collection"),
|
(DEFAULT_KEYBINDINGS.edit, "edit collection"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub(in crate::app) struct RadarrContextClueProvider;
|
||||||
|
|
||||||
|
impl ContextClueProvider for RadarrContextClueProvider {
|
||||||
|
fn get_context_clues(app: &mut App<'_>) -> Option<&'static [ContextClue]> {
|
||||||
|
let Route::Radarr(active_radarr_block, context_option) = app.get_current_route() else {
|
||||||
|
panic!("RadarrContextClueProvider::get_context_clues called with non-Radarr route");
|
||||||
|
};
|
||||||
|
match active_radarr_block {
|
||||||
|
_ if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) => app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.movie_info_tabs
|
||||||
|
.get_active_route_contextual_help(),
|
||||||
|
ActiveRadarrBlock::TestAllIndexers
|
||||||
|
| ActiveRadarrBlock::AddMovieSearchInput
|
||||||
|
| ActiveRadarrBlock::AddMovieEmptySearchResults
|
||||||
|
| ActiveRadarrBlock::SystemLogs
|
||||||
|
| ActiveRadarrBlock::SystemUpdates => Some(&BARE_POPUP_CONTEXT_CLUES),
|
||||||
|
_ if context_option.unwrap_or(active_radarr_block)
|
||||||
|
== ActiveRadarrBlock::ViewMovieOverview =>
|
||||||
|
{
|
||||||
|
Some(&BARE_POPUP_CONTEXT_CLUES)
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::SystemTasks => Some(&SYSTEM_TASKS_CONTEXT_CLUES),
|
||||||
|
_ if EDIT_COLLECTION_BLOCKS.contains(&active_radarr_block)
|
||||||
|
|| EDIT_INDEXER_BLOCKS.contains(&active_radarr_block)
|
||||||
|
|| INDEXER_SETTINGS_BLOCKS.contains(&active_radarr_block)
|
||||||
|
|| EDIT_MOVIE_BLOCKS.contains(&active_radarr_block) =>
|
||||||
|
{
|
||||||
|
Some(&CONFIRMATION_PROMPT_CONTEXT_CLUES)
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::AddMoviePrompt
|
||||||
|
| ActiveRadarrBlock::AddMovieSelectMonitor
|
||||||
|
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability
|
||||||
|
| ActiveRadarrBlock::AddMovieSelectQualityProfile
|
||||||
|
| ActiveRadarrBlock::AddMovieSelectRootFolder
|
||||||
|
| ActiveRadarrBlock::AddMovieTagsInput
|
||||||
|
| ActiveRadarrBlock::SystemTaskStartConfirmPrompt => Some(&CONFIRMATION_PROMPT_CONTEXT_CLUES),
|
||||||
|
_ if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) => {
|
||||||
|
Some(&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES)
|
||||||
|
}
|
||||||
|
ActiveRadarrBlock::CollectionDetails => Some(&COLLECTION_DETAILS_CONTEXT_CLUES),
|
||||||
|
_ => app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.main_tabs
|
||||||
|
.get_active_route_contextual_help(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use crate::app::context_clues::{
|
||||||
|
ContextClue, ContextClueProvider, BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES,
|
||||||
|
CONFIRMATION_PROMPT_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
|
};
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::app::radarr::radarr_context_clues::{
|
use crate::app::radarr::radarr_context_clues::{
|
||||||
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES,
|
RadarrContextClueProvider, ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES,
|
||||||
COLLECTION_DETAILS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES,
|
COLLECTION_DETAILS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
|
||||||
MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
|
|
||||||
MOVIE_DETAILS_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
MOVIE_DETAILS_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_library_context_clues() {
|
fn test_library_context_clues() {
|
||||||
@@ -179,26 +186,15 @@ mod tests {
|
|||||||
|
|
||||||
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
|
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
|
||||||
assert_eq!(manual_movie_search_context_clues_iter.next(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_manual_movie_search_contextual_context_clues() {
|
|
||||||
let mut manual_movie_search_contextual_context_clues_iter =
|
|
||||||
MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES.iter();
|
|
||||||
|
|
||||||
let (key_binding, description) = manual_movie_search_contextual_context_clues_iter
|
|
||||||
.next()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||||
assert_str_eq!(*description, "details");
|
assert_str_eq!(*description, "details");
|
||||||
assert_eq!(
|
|
||||||
manual_movie_search_contextual_context_clues_iter.next(),
|
let (key_binding, description) = manual_movie_search_context_clues_iter.next().unwrap();
|
||||||
None
|
|
||||||
);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||||
|
|
||||||
|
assert_eq!(manual_movie_search_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -254,4 +250,255 @@ mod tests {
|
|||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||||
assert_eq!(collection_details_context_clues_iter.next(), None);
|
assert_eq!(collection_details_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "RadarrContextClueProvider::get_context_clues called with non-Radarr route"
|
||||||
|
)]
|
||||||
|
fn test_radarr_context_clue_provider_get_context_clues_non_radarr_route() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveSonarrBlock::default().into());
|
||||||
|
|
||||||
|
// This should panic because the route is not a Radarr route
|
||||||
|
RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(ActiveRadarrBlock::TestAllIndexers, None)]
|
||||||
|
#[case(ActiveRadarrBlock::AddMovieSearchInput, None)]
|
||||||
|
#[case(ActiveRadarrBlock::AddMovieEmptySearchResults, None)]
|
||||||
|
#[case(ActiveRadarrBlock::SystemLogs, None)]
|
||||||
|
#[case(ActiveRadarrBlock::SystemUpdates, None)]
|
||||||
|
#[case(ActiveRadarrBlock::ViewMovieOverview, None)]
|
||||||
|
#[case(
|
||||||
|
ActiveRadarrBlock::CollectionDetails,
|
||||||
|
Some(ActiveRadarrBlock::ViewMovieOverview)
|
||||||
|
)]
|
||||||
|
fn test_radarr_context_clue_provider_bare_popup_context_clues(
|
||||||
|
#[case] active_radarr_block: ActiveRadarrBlock,
|
||||||
|
#[case] context_option: Option<ActiveRadarrBlock>,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack((active_radarr_block, context_option).into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&BARE_POPUP_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(0, ActiveRadarrBlock::MovieDetails, &MOVIE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(1, ActiveRadarrBlock::MovieHistory, &MOVIE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(2, ActiveRadarrBlock::FileInfo, &MOVIE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(3, ActiveRadarrBlock::Cast, &MOVIE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(4, ActiveRadarrBlock::Crew, &MOVIE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(5, ActiveRadarrBlock::ManualSearch, &MANUAL_MOVIE_SEARCH_CONTEXT_CLUES)]
|
||||||
|
fn test_radarr_context_clue_provider_movie_details_block_context_clues(
|
||||||
|
#[case] index: usize,
|
||||||
|
#[case] active_radarr_block: ActiveRadarrBlock,
|
||||||
|
#[case] expected_context_clues: &[ContextClue],
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.data.radarr_data.movie_info_tabs.set_index(index);
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(expected_context_clues, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_radarr_context_clue_provider_confirmation_prompt_context_clues(
|
||||||
|
#[values(
|
||||||
|
ActiveRadarrBlock::AddMoviePrompt,
|
||||||
|
ActiveRadarrBlock::AddMovieSelectMonitor,
|
||||||
|
ActiveRadarrBlock::AddMovieSelectMinimumAvailability,
|
||||||
|
ActiveRadarrBlock::AddMovieSelectQualityProfile,
|
||||||
|
ActiveRadarrBlock::AddMovieSelectRootFolder,
|
||||||
|
ActiveRadarrBlock::AddMovieTagsInput,
|
||||||
|
ActiveRadarrBlock::SystemTaskStartConfirmPrompt
|
||||||
|
)]
|
||||||
|
active_radarr_block: ActiveRadarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&CONFIRMATION_PROMPT_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_radarr_context_clue_provider_confirmation_prompt_context_clues_edit_collection_blocks(
|
||||||
|
#[values(
|
||||||
|
ActiveRadarrBlock::EditCollectionPrompt,
|
||||||
|
ActiveRadarrBlock::EditCollectionConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditCollectionRootFolderPathInput,
|
||||||
|
ActiveRadarrBlock::EditCollectionSelectMinimumAvailability,
|
||||||
|
ActiveRadarrBlock::EditCollectionSelectQualityProfile,
|
||||||
|
ActiveRadarrBlock::EditCollectionToggleSearchOnAdd,
|
||||||
|
ActiveRadarrBlock::EditCollectionToggleMonitored
|
||||||
|
)]
|
||||||
|
active_radarr_block: ActiveRadarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&CONFIRMATION_PROMPT_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_radarr_context_clue_provider_confirmation_prompt_context_clues_edit_indexer_blocks(
|
||||||
|
#[values(
|
||||||
|
ActiveRadarrBlock::EditIndexerPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditIndexerApiKeyInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerNameInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerSeedRatioInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
ActiveRadarrBlock::EditIndexerPriorityInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerUrlInput,
|
||||||
|
ActiveRadarrBlock::EditIndexerTagsInput
|
||||||
|
)]
|
||||||
|
active_radarr_block: ActiveRadarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&CONFIRMATION_PROMPT_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_radarr_context_clue_provider_confirmation_prompt_context_clues_indexer_settings_blocks(
|
||||||
|
#[values(
|
||||||
|
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsRetentionInput,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsToggleAllowHardcodedSubs,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsTogglePreferIndexerFlags,
|
||||||
|
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput
|
||||||
|
)]
|
||||||
|
active_radarr_block: ActiveRadarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&CONFIRMATION_PROMPT_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_radarr_context_clue_provider_confirmation_prompt_context_clues_edit_movie_blocks(
|
||||||
|
#[values(
|
||||||
|
ActiveRadarrBlock::EditMoviePrompt,
|
||||||
|
ActiveRadarrBlock::EditMovieConfirmPrompt,
|
||||||
|
ActiveRadarrBlock::EditMoviePathInput,
|
||||||
|
ActiveRadarrBlock::EditMovieSelectMinimumAvailability,
|
||||||
|
ActiveRadarrBlock::EditMovieSelectQualityProfile,
|
||||||
|
ActiveRadarrBlock::EditMovieTagsInput,
|
||||||
|
ActiveRadarrBlock::EditMovieToggleMonitored
|
||||||
|
)]
|
||||||
|
active_radarr_block: ActiveRadarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&CONFIRMATION_PROMPT_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_radarr_context_clue_provider_add_movie_search_results_context_clues(
|
||||||
|
#[values(
|
||||||
|
ActiveRadarrBlock::AddMovieSearchResults,
|
||||||
|
ActiveRadarrBlock::AddMovieAlreadyInLibrary
|
||||||
|
)]
|
||||||
|
active_radarr_block: ActiveRadarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES,
|
||||||
|
context_clues.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_context_clue_provider_collection_details_context_clues() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&COLLECTION_DETAILS_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_context_clue_provider_system_tasks_context_clues() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(&SYSTEM_TASKS_CONTEXT_CLUES, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(0, ActiveRadarrBlock::Movies, &LIBRARY_CONTEXT_CLUES)]
|
||||||
|
#[case(1, ActiveRadarrBlock::Collections, &COLLECTIONS_CONTEXT_CLUES)]
|
||||||
|
#[case(2, ActiveRadarrBlock::Downloads, &DOWNLOADS_CONTEXT_CLUES)]
|
||||||
|
#[case(3, ActiveRadarrBlock::Blocklist, &BLOCKLIST_CONTEXT_CLUES)]
|
||||||
|
#[case(4, ActiveRadarrBlock::RootFolders, &ROOT_FOLDERS_CONTEXT_CLUES)]
|
||||||
|
#[case(5, ActiveRadarrBlock::Indexers, &INDEXERS_CONTEXT_CLUES)]
|
||||||
|
#[case(6, ActiveRadarrBlock::System, &SYSTEM_CONTEXT_CLUES)]
|
||||||
|
fn test_radarr_context_clue_provider_radarr_blocks_context_clues(
|
||||||
|
#[case] index: usize,
|
||||||
|
#[case] active_radarr_block: ActiveRadarrBlock,
|
||||||
|
#[case] expected_context_clues: &[ContextClue],
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.data.radarr_data.main_tabs.set_index(index);
|
||||||
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = RadarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(expected_context_clues, context_clues.unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
use crate::app::{context_clues::ContextClue, key_binding::DEFAULT_KEYBINDINGS};
|
use crate::app::context_clues::{
|
||||||
|
ContextClueProvider, BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
||||||
|
};
|
||||||
|
use crate::app::{context_clues::ContextClue, key_binding::DEFAULT_KEYBINDINGS, App};
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{
|
||||||
|
ActiveSonarrBlock, ADD_SERIES_BLOCKS, EDIT_INDEXER_BLOCKS, EDIT_SERIES_BLOCKS,
|
||||||
|
EPISODE_DETAILS_BLOCKS, INDEXER_SETTINGS_BLOCKS, SEASON_DETAILS_BLOCKS, SERIES_DETAILS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::models::Route;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "sonarr_context_clues_tests.rs"]
|
#[path = "sonarr_context_clues_tests.rs"]
|
||||||
@@ -79,12 +87,7 @@ pub static SERIES_HISTORY_CONTEXT_CLUES: [ContextClue; 9] = [
|
|||||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES: [ContextClue; 2] = [
|
pub static SEASON_DETAILS_CONTEXT_CLUES: [ContextClue; 7] = [
|
||||||
(DEFAULT_KEYBINDINGS.submit, "episode details"),
|
|
||||||
(DEFAULT_KEYBINDINGS.delete, "delete episode"),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub static SEASON_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
@@ -99,9 +102,11 @@ pub static SEASON_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
|||||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
),
|
),
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "episode details"),
|
||||||
|
(DEFAULT_KEYBINDINGS.delete, "delete episode"),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static SEASON_HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
|
pub static SEASON_HISTORY_CONTEXT_CLUES: [ContextClue; 7] = [
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
@@ -113,10 +118,11 @@ pub static SEASON_HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
|
|||||||
DEFAULT_KEYBINDINGS.auto_search,
|
DEFAULT_KEYBINDINGS.auto_search,
|
||||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
),
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static MANUAL_SEASON_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
|
pub static MANUAL_SEASON_SEARCH_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
@@ -126,10 +132,11 @@ pub static MANUAL_SEASON_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
|
|||||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
),
|
),
|
||||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static MANUAL_EPISODE_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
|
pub static MANUAL_EPISODE_SEARCH_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
@@ -139,12 +146,10 @@ pub static MANUAL_EPISODE_SEARCH_CONTEXT_CLUES: [ContextClue; 4] = [
|
|||||||
DEFAULT_KEYBINDINGS.auto_search.desc,
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
),
|
),
|
||||||
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.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] = [
|
pub static EPISODE_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
@@ -157,7 +162,84 @@ pub static EPISODE_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
|
|||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub static SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES: [ContextClue; 4] = [
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.auto_search,
|
||||||
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
|
];
|
||||||
|
|
||||||
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
|
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||||
(DEFAULT_KEYBINDINGS.submit, "start task"),
|
(DEFAULT_KEYBINDINGS.submit, "start task"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub(in crate::app) struct SonarrContextClueProvider;
|
||||||
|
|
||||||
|
impl ContextClueProvider for SonarrContextClueProvider {
|
||||||
|
fn get_context_clues(app: &mut App<'_>) -> Option<&'static [ContextClue]> {
|
||||||
|
let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() else {
|
||||||
|
panic!("SonarrContextClueProvider::get_context_clues called with non-Sonarr route");
|
||||||
|
};
|
||||||
|
match active_sonarr_block {
|
||||||
|
_ if SERIES_DETAILS_BLOCKS.contains(&active_sonarr_block) => app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_info_tabs
|
||||||
|
.get_active_route_contextual_help(),
|
||||||
|
_ if SEASON_DETAILS_BLOCKS.contains(&active_sonarr_block) => app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.season_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.season_details_tabs
|
||||||
|
.get_active_route_contextual_help(),
|
||||||
|
_ if EPISODE_DETAILS_BLOCKS.contains(&active_sonarr_block) => app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.season_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.episode_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.episode_details_tabs
|
||||||
|
.get_active_route_contextual_help(),
|
||||||
|
ActiveSonarrBlock::TestAllIndexers
|
||||||
|
| ActiveSonarrBlock::AddSeriesSearchInput
|
||||||
|
| ActiveSonarrBlock::AddSeriesEmptySearchResults
|
||||||
|
| ActiveSonarrBlock::SystemLogs
|
||||||
|
| ActiveSonarrBlock::SystemUpdates => Some(&BARE_POPUP_CONTEXT_CLUES),
|
||||||
|
_ if EDIT_INDEXER_BLOCKS.contains(&active_sonarr_block)
|
||||||
|
|| INDEXER_SETTINGS_BLOCKS.contains(&active_sonarr_block)
|
||||||
|
|| EDIT_SERIES_BLOCKS.contains(&active_sonarr_block) =>
|
||||||
|
{
|
||||||
|
Some(&CONFIRMATION_PROMPT_CONTEXT_CLUES)
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::AddSeriesPrompt
|
||||||
|
| ActiveSonarrBlock::AddSeriesSelectMonitor
|
||||||
|
| ActiveSonarrBlock::AddSeriesSelectSeriesType
|
||||||
|
| ActiveSonarrBlock::AddSeriesSelectQualityProfile
|
||||||
|
| ActiveSonarrBlock::AddSeriesSelectLanguageProfile
|
||||||
|
| ActiveSonarrBlock::AddSeriesSelectRootFolder
|
||||||
|
| ActiveSonarrBlock::AddSeriesTagsInput
|
||||||
|
| ActiveSonarrBlock::SystemTaskStartConfirmPrompt => Some(&CONFIRMATION_PROMPT_CONTEXT_CLUES),
|
||||||
|
_ if ADD_SERIES_BLOCKS.contains(&active_sonarr_block) => {
|
||||||
|
Some(&ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES)
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SystemTasks => Some(&SYSTEM_TASKS_CONTEXT_CLUES),
|
||||||
|
_ => app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.main_tabs
|
||||||
|
.get_active_route_contextual_help(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use crate::app::context_clues::{
|
||||||
|
ContextClue, ContextClueProvider, BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES,
|
||||||
|
CONFIRMATION_PROMPT_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
|
};
|
||||||
|
use crate::app::sonarr::sonarr_context_clues::{
|
||||||
|
SonarrContextClueProvider, SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES,
|
||||||
|
};
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
key_binding::DEFAULT_KEYBINDINGS,
|
key_binding::DEFAULT_KEYBINDINGS,
|
||||||
sonarr::sonarr_context_clues::{
|
sonarr::sonarr_context_clues::{
|
||||||
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES, DETAILS_CONTEXTUAL_CONTEXT_CLUES,
|
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES, EPISODE_DETAILS_CONTEXT_CLUES,
|
||||||
EPISODE_DETAILS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXT_CLUES,
|
HISTORY_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXT_CLUES,
|
||||||
MANUAL_SEASON_SEARCH_CONTEXT_CLUES, SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES,
|
MANUAL_SEASON_SEARCH_CONTEXT_CLUES, SEASON_DETAILS_CONTEXT_CLUES,
|
||||||
SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES,
|
SEASON_HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES, SERIES_DETAILS_CONTEXT_CLUES,
|
||||||
SERIES_DETAILS_CONTEXT_CLUES, SERIES_HISTORY_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
SERIES_HISTORY_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
},
|
},
|
||||||
|
App,
|
||||||
};
|
};
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
|
use crate::models::servarr_data::sonarr::modals::{EpisodeDetailsModal, SeasonDetailsModal};
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
||||||
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_series_search_results_context_clues() {
|
fn test_add_series_search_results_context_clues() {
|
||||||
@@ -252,23 +264,18 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||||
assert_eq!(season_details_context_clues_iter.next(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
|
||||||
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_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||||
assert_str_eq!(*description, "episode details");
|
assert_str_eq!(*description, "episode details");
|
||||||
|
|
||||||
let (key_binding, description) = season_details_contextual_context_clues_iter.next().unwrap();
|
let (key_binding, description) = season_details_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.delete);
|
||||||
assert_str_eq!(*description, "delete episode");
|
assert_str_eq!(*description, "delete episode");
|
||||||
assert_eq!(season_details_contextual_context_clues_iter.next(), None);
|
|
||||||
|
assert_eq!(season_details_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -301,6 +308,11 @@ mod tests {
|
|||||||
|
|
||||||
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||||
|
assert_str_eq!(*description, "details");
|
||||||
|
|
||||||
|
let (key_binding, description) = season_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||||
assert_str_eq!(*description, "cancel filter/close");
|
assert_str_eq!(*description, "cancel filter/close");
|
||||||
assert_eq!(season_history_context_clues_iter.next(), None);
|
assert_eq!(season_history_context_clues_iter.next(), None);
|
||||||
@@ -327,6 +339,11 @@ mod tests {
|
|||||||
|
|
||||||
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
|
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||||
|
assert_str_eq!(*description, "details");
|
||||||
|
|
||||||
|
let (key_binding, description) = manual_season_search_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||||
assert_eq!(manual_season_search_context_clues_iter.next(), None);
|
assert_eq!(manual_season_search_context_clues_iter.next(), None);
|
||||||
@@ -353,21 +370,16 @@ mod tests {
|
|||||||
|
|
||||||
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
|
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||||
|
assert_str_eq!(*description, "details");
|
||||||
|
|
||||||
|
let (key_binding, description) = manual_episode_search_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||||
assert_eq!(manual_episode_search_context_clues_iter.next(), None);
|
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]
|
#[test]
|
||||||
fn test_episode_details_context_clues() {
|
fn test_episode_details_context_clues() {
|
||||||
let mut episode_details_context_clues_iter = EPISODE_DETAILS_CONTEXT_CLUES.iter();
|
let mut episode_details_context_clues_iter = EPISODE_DETAILS_CONTEXT_CLUES.iter();
|
||||||
@@ -389,6 +401,32 @@ mod tests {
|
|||||||
assert_eq!(episode_details_context_clues_iter.next(), None);
|
assert_eq!(episode_details_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_selectable_episode_details_context_clues() {
|
||||||
|
let mut episode_details_context_clues_iter = SELECTABLE_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.submit);
|
||||||
|
assert_str_eq!(*description, "details");
|
||||||
|
|
||||||
|
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]
|
#[test]
|
||||||
fn test_system_tasks_context_clues() {
|
fn test_system_tasks_context_clues() {
|
||||||
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter();
|
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter();
|
||||||
@@ -404,4 +442,258 @@ mod tests {
|
|||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.esc.desc);
|
||||||
assert_eq!(system_tasks_context_clues_iter.next(), None);
|
assert_eq!(system_tasks_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "SonarrContextClueProvider::get_context_clues called with non-Sonarr route"
|
||||||
|
)]
|
||||||
|
fn test_sonarr_context_clue_provider_get_context_clues_non_sonarr_route() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::default().into());
|
||||||
|
|
||||||
|
// This should panic because the route is not a Sonarr route
|
||||||
|
SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(0, ActiveSonarrBlock::SeriesDetails, &SERIES_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(1, ActiveSonarrBlock::SeriesHistory, &SERIES_HISTORY_CONTEXT_CLUES)]
|
||||||
|
fn test_sonarr_context_clue_provider_series_info_tabs(
|
||||||
|
#[case] index: usize,
|
||||||
|
#[case] active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
#[case] expected_context_clues: &[ContextClue],
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.sonarr_data = SonarrData::default();
|
||||||
|
app.data.sonarr_data.series_info_tabs.set_index(index);
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(expected_context_clues, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(0, ActiveSonarrBlock::SeasonDetails, &SEASON_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(1, ActiveSonarrBlock::SeasonHistory, &SEASON_HISTORY_CONTEXT_CLUES)]
|
||||||
|
#[case(2, ActiveSonarrBlock::ManualSeasonSearch, &MANUAL_SEASON_SEARCH_CONTEXT_CLUES)]
|
||||||
|
fn test_sonarr_context_clue_provider_season_details_tabs(
|
||||||
|
#[case] index: usize,
|
||||||
|
#[case] active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
#[case] expected_context_clues: &[ContextClue],
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
let mut season_details_modal = SeasonDetailsModal::default();
|
||||||
|
season_details_modal.season_details_tabs.set_index(index);
|
||||||
|
let sonarr_data = SonarrData {
|
||||||
|
season_details_modal: Some(season_details_modal),
|
||||||
|
..SonarrData::default()
|
||||||
|
};
|
||||||
|
app.data.sonarr_data = sonarr_data;
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(expected_context_clues, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(0, ActiveSonarrBlock::EpisodeDetails, &EPISODE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(1, ActiveSonarrBlock::EpisodeHistory, &SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(2, ActiveSonarrBlock::EpisodeFile, &EPISODE_DETAILS_CONTEXT_CLUES)]
|
||||||
|
#[case(3, ActiveSonarrBlock::ManualEpisodeSearch, &MANUAL_EPISODE_SEARCH_CONTEXT_CLUES)]
|
||||||
|
fn test_sonarr_context_clue_provider_episode_details_tabs(
|
||||||
|
#[case] index: usize,
|
||||||
|
#[case] active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
#[case] expected_context_clues: &[ContextClue],
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
let mut episode_details_modal = EpisodeDetailsModal::default();
|
||||||
|
episode_details_modal.episode_details_tabs.set_index(index);
|
||||||
|
let sonarr_data = SonarrData {
|
||||||
|
season_details_modal: Some(SeasonDetailsModal {
|
||||||
|
episode_details_modal: Some(episode_details_modal),
|
||||||
|
..SeasonDetailsModal::default()
|
||||||
|
}),
|
||||||
|
..SonarrData::default()
|
||||||
|
};
|
||||||
|
app.data.sonarr_data = sonarr_data;
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(expected_context_clues, context_clues.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_sonarr_context_clue_provider_bare_popup_context_clues(
|
||||||
|
#[values(
|
||||||
|
ActiveSonarrBlock::TestAllIndexers,
|
||||||
|
ActiveSonarrBlock::AddSeriesSearchInput,
|
||||||
|
ActiveSonarrBlock::AddSeriesEmptySearchResults,
|
||||||
|
ActiveSonarrBlock::SystemLogs,
|
||||||
|
ActiveSonarrBlock::SystemUpdates
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(context_clues.unwrap(), &BARE_POPUP_CONTEXT_CLUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_sonarr_context_clue_provider_confirmation_prompt_context_clues(
|
||||||
|
#[values(
|
||||||
|
ActiveSonarrBlock::AddSeriesPrompt,
|
||||||
|
ActiveSonarrBlock::AddSeriesSelectMonitor,
|
||||||
|
ActiveSonarrBlock::AddSeriesSelectSeriesType,
|
||||||
|
ActiveSonarrBlock::AddSeriesSelectQualityProfile,
|
||||||
|
ActiveSonarrBlock::AddSeriesSelectLanguageProfile,
|
||||||
|
ActiveSonarrBlock::AddSeriesSelectRootFolder,
|
||||||
|
ActiveSonarrBlock::AddSeriesTagsInput,
|
||||||
|
ActiveSonarrBlock::SystemTaskStartConfirmPrompt
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(context_clues.unwrap(), &CONFIRMATION_PROMPT_CONTEXT_CLUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_sonarr_context_clue_provider_confirmation_prompt_popup_clues_edit_indexer_blocks(
|
||||||
|
#[values(
|
||||||
|
ActiveSonarrBlock::EditIndexerPrompt,
|
||||||
|
ActiveSonarrBlock::EditIndexerConfirmPrompt,
|
||||||
|
ActiveSonarrBlock::EditIndexerApiKeyInput,
|
||||||
|
ActiveSonarrBlock::EditIndexerNameInput,
|
||||||
|
ActiveSonarrBlock::EditIndexerSeedRatioInput,
|
||||||
|
ActiveSonarrBlock::EditIndexerToggleEnableRss,
|
||||||
|
ActiveSonarrBlock::EditIndexerToggleEnableAutomaticSearch,
|
||||||
|
ActiveSonarrBlock::EditIndexerToggleEnableInteractiveSearch,
|
||||||
|
ActiveSonarrBlock::EditIndexerPriorityInput,
|
||||||
|
ActiveSonarrBlock::EditIndexerUrlInput,
|
||||||
|
ActiveSonarrBlock::EditIndexerTagsInput
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(context_clues.unwrap(), &CONFIRMATION_PROMPT_CONTEXT_CLUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_sonarr_context_clue_provider_confirmation_prompt_popup_clues_indexer_settings_blocks(
|
||||||
|
#[values(
|
||||||
|
ActiveSonarrBlock::AllIndexerSettingsPrompt,
|
||||||
|
ActiveSonarrBlock::IndexerSettingsConfirmPrompt,
|
||||||
|
ActiveSonarrBlock::IndexerSettingsMaximumSizeInput,
|
||||||
|
ActiveSonarrBlock::IndexerSettingsMinimumAgeInput,
|
||||||
|
ActiveSonarrBlock::IndexerSettingsRetentionInput,
|
||||||
|
ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(context_clues.unwrap(), &CONFIRMATION_PROMPT_CONTEXT_CLUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_sonarr_context_clue_provider_confirmation_prompt_popup_clues_edit_series_blocks(
|
||||||
|
#[values(
|
||||||
|
ActiveSonarrBlock::EditSeriesPrompt,
|
||||||
|
ActiveSonarrBlock::EditSeriesConfirmPrompt,
|
||||||
|
ActiveSonarrBlock::EditSeriesPathInput,
|
||||||
|
ActiveSonarrBlock::EditSeriesSelectSeriesType,
|
||||||
|
ActiveSonarrBlock::EditSeriesSelectQualityProfile,
|
||||||
|
ActiveSonarrBlock::EditSeriesSelectLanguageProfile,
|
||||||
|
ActiveSonarrBlock::EditSeriesTagsInput,
|
||||||
|
ActiveSonarrBlock::EditSeriesToggleMonitored,
|
||||||
|
ActiveSonarrBlock::EditSeriesToggleSeasonFolder
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(context_clues.unwrap(), &CONFIRMATION_PROMPT_CONTEXT_CLUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_sonarr_context_clue_provider_add_series_search_results_clues(
|
||||||
|
#[values(
|
||||||
|
ActiveSonarrBlock::AddSeriesAlreadyInLibrary,
|
||||||
|
ActiveSonarrBlock::AddSeriesSearchResults
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
context_clues.unwrap(),
|
||||||
|
&ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sonarr_context_clue_provider_system_tasks_clues() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
|
||||||
|
app.push_navigation_stack(ActiveSonarrBlock::SystemTasks.into());
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(context_clues.unwrap(), &SYSTEM_TASKS_CONTEXT_CLUES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(0, ActiveSonarrBlock::Series, &SERIES_CONTEXT_CLUES)]
|
||||||
|
#[case(1, ActiveSonarrBlock::Downloads, &DOWNLOADS_CONTEXT_CLUES)]
|
||||||
|
#[case(2, ActiveSonarrBlock::Blocklist, &BLOCKLIST_CONTEXT_CLUES)]
|
||||||
|
#[case(3, ActiveSonarrBlock::History, &HISTORY_CONTEXT_CLUES)]
|
||||||
|
#[case(4, ActiveSonarrBlock::RootFolders, &ROOT_FOLDERS_CONTEXT_CLUES)]
|
||||||
|
#[case(5, ActiveSonarrBlock::Indexers, &INDEXERS_CONTEXT_CLUES)]
|
||||||
|
#[case(6, ActiveSonarrBlock::System, &SYSTEM_CONTEXT_CLUES)]
|
||||||
|
fn test_sonarr_context_clue_provider_sonarr_tabs(
|
||||||
|
#[case] index: usize,
|
||||||
|
#[case] active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
#[case] expected_context_clues: &[ContextClue],
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.sonarr_data = SonarrData::default();
|
||||||
|
app.data.sonarr_data.main_tabs.set_index(index);
|
||||||
|
app.push_navigation_stack(active_sonarr_block.into());
|
||||||
|
|
||||||
|
let context_clues = SonarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert!(context_clues.is_some());
|
||||||
|
assert_eq!(expected_context_clues, context_clues.unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-17
@@ -31,23 +31,23 @@ pub enum Key {
|
|||||||
impl Display for Key {
|
impl Display for Key {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Key::Char(c) => write!(f, "<{c}>"),
|
Key::Char(c) => write!(f, "{c}"),
|
||||||
Key::Ctrl(c) => write!(f, "<C-{c}>"),
|
Key::Ctrl(c) => write!(f, "ctrl-{c}"),
|
||||||
Key::Up => write!(f, "<↑>"),
|
Key::Up => write!(f, "↑"),
|
||||||
Key::Down => write!(f, "<↓>"),
|
Key::Down => write!(f, "↓"),
|
||||||
Key::Left => write!(f, "<←>"),
|
Key::Left => write!(f, "←"),
|
||||||
Key::Right => write!(f, "<→>"),
|
Key::Right => write!(f, "→"),
|
||||||
Key::PgDown => write!(f, "<C-d>"),
|
Key::PgDown => write!(f, "pgDown"),
|
||||||
Key::PgUp => write!(f, "<C-u>"),
|
Key::PgUp => write!(f, "pgUp"),
|
||||||
Key::Enter => write!(f, "<enter>"),
|
Key::Enter => write!(f, "enter"),
|
||||||
Key::Esc => write!(f, "<esc>"),
|
Key::Esc => write!(f, "esc"),
|
||||||
Key::Backspace => write!(f, "<backspace>"),
|
Key::Backspace => write!(f, "backspace"),
|
||||||
Key::Home => write!(f, "<home>"),
|
Key::Home => write!(f, "home"),
|
||||||
Key::End => write!(f, "<end>"),
|
Key::End => write!(f, "end"),
|
||||||
Key::Tab => write!(f, "<tab>"),
|
Key::Tab => write!(f, "tab"),
|
||||||
Key::BackTab => write!(f, "<shift-tab>"),
|
Key::BackTab => write!(f, "shift-tab"),
|
||||||
Key::Delete => write!(f, "<del>"),
|
Key::Delete => write!(f, "del"),
|
||||||
_ => write!(f, "<{self:?}>"),
|
_ => write!(f, "{self:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ mod tests {
|
|||||||
#[case(Key::Down, "↓")]
|
#[case(Key::Down, "↓")]
|
||||||
#[case(Key::Left, "←")]
|
#[case(Key::Left, "←")]
|
||||||
#[case(Key::Right, "→")]
|
#[case(Key::Right, "→")]
|
||||||
#[case(Key::PgDown, "C-d")]
|
#[case(Key::PgDown, "pgDown")]
|
||||||
#[case(Key::PgUp, "C-u")]
|
#[case(Key::PgUp, "pgUp")]
|
||||||
#[case(Key::Enter, "enter")]
|
#[case(Key::Enter, "enter")]
|
||||||
#[case(Key::Esc, "esc")]
|
#[case(Key::Esc, "esc")]
|
||||||
#[case(Key::Backspace, "backspace")]
|
#[case(Key::Backspace, "backspace")]
|
||||||
@@ -22,9 +22,9 @@ mod tests {
|
|||||||
#[case(Key::BackTab, "shift-tab")]
|
#[case(Key::BackTab, "shift-tab")]
|
||||||
#[case(Key::Delete, "del")]
|
#[case(Key::Delete, "del")]
|
||||||
#[case(Key::Char('q'), "q")]
|
#[case(Key::Char('q'), "q")]
|
||||||
#[case(Key::Ctrl('q'), "C-q")]
|
#[case(Key::Ctrl('q'), "ctrl-q")]
|
||||||
fn test_key_formatter(#[case] key: Key, #[case] expected_str: &str) {
|
fn test_key_formatter(#[case] key: Key, #[case] expected_str: &str) {
|
||||||
assert_str_eq!(format!("{key}"), format!("<{expected_str}>"));
|
assert_str_eq!(format!("{key}"), format!("{expected_str}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -6,13 +6,20 @@ mod tests {
|
|||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::context_clues::SERVARR_CONTEXT_CLUES;
|
||||||
|
use crate::app::key_binding::{KeyBinding, DEFAULT_KEYBINDINGS};
|
||||||
|
use crate::app::radarr::radarr_context_clues::{
|
||||||
|
LIBRARY_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES,
|
||||||
|
};
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
use crate::handlers::handle_events;
|
|
||||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle};
|
use crate::handlers::{handle_clear_errors, handle_prompt_toggle};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::handlers::{handle_events, populate_keymapping_table};
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
|
use crate::models::servarr_data::ActiveKeybindingBlock;
|
||||||
|
use crate::models::servarr_models::KeybindingItem;
|
||||||
|
use crate::models::stateful_table::StatefulTable;
|
||||||
use crate::models::HorizontallyScrollableText;
|
use crate::models::HorizontallyScrollableText;
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
|
|
||||||
@@ -82,6 +89,82 @@ mod tests {
|
|||||||
assert!(app.cancellation_token.is_cancelled());
|
assert!(app.cancellation_token.is_cancelled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_populate_keybindings_table_on_help_button_press() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
let expected_keybinding_items = Vec::from(SERVARR_CONTEXT_CLUES)
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
app.push_navigation_stack(ActiveKeybindingBlock::Help.into());
|
||||||
|
|
||||||
|
handle_events(DEFAULT_KEYBINDINGS.help.key, &mut app);
|
||||||
|
|
||||||
|
assert!(app.keymapping_table.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
expected_keybinding_items,
|
||||||
|
app.keymapping_table.unwrap().items
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_ignore_help_button_when_ignore_special_keys_for_textbox_input_is_true() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.ignore_special_keys_for_textbox_input = true;
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::default().into());
|
||||||
|
|
||||||
|
handle_events(DEFAULT_KEYBINDINGS.help.key, &mut app);
|
||||||
|
|
||||||
|
assert!(app.keymapping_table.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_empties_keybindings_table_on_help_button_press_when_keybindings_table_is_already_populated(
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
let keybinding_items = Vec::from(SERVARR_CONTEXT_CLUES)
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut stateful_table = StatefulTable::default();
|
||||||
|
stateful_table.set_items(keybinding_items);
|
||||||
|
app.keymapping_table = Some(stateful_table);
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::default().into());
|
||||||
|
|
||||||
|
handle_events(DEFAULT_KEYBINDINGS.help.key, &mut app);
|
||||||
|
|
||||||
|
assert!(app.keymapping_table.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_shows_keymapping_popup_when_keymapping_table_is_populated() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
let keybinding_items = Vec::from(SERVARR_CONTEXT_CLUES)
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut stateful_table = StatefulTable::default();
|
||||||
|
stateful_table.set_items(keybinding_items);
|
||||||
|
app.keymapping_table = Some(stateful_table);
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::default().into());
|
||||||
|
let expected_selection = KeybindingItem {
|
||||||
|
key: SERVARR_CONTEXT_CLUES[1].0.key.to_string(),
|
||||||
|
alt_key: SERVARR_CONTEXT_CLUES[1]
|
||||||
|
.0
|
||||||
|
.alt
|
||||||
|
.map_or(String::new(), |k| k.to_string()),
|
||||||
|
desc: SERVARR_CONTEXT_CLUES[1].1.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_events(DEFAULT_KEYBINDINGS.down.key, &mut app);
|
||||||
|
|
||||||
|
assert!(app.keymapping_table.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
&expected_selection,
|
||||||
|
app.keymapping_table.unwrap().current_selection()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_handle_prompt_toggle_left_right_radarr(#[values(Key::Left, Key::Right)] key: Key) {
|
fn test_handle_prompt_toggle_left_right_radarr(#[values(Key::Left, Key::Right)] key: Key) {
|
||||||
let mut app = App::test_default();
|
let mut app = App::test_default();
|
||||||
@@ -113,4 +196,95 @@ mod tests {
|
|||||||
|
|
||||||
assert!(!app.data.sonarr_data.prompt_confirm);
|
assert!(!app.data.sonarr_data.prompt_confirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_populate_keymapping_table_global_options() {
|
||||||
|
let expected_keybinding_items = Vec::from(SERVARR_CONTEXT_CLUES)
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| {
|
||||||
|
let (key, alt_key) = if key.alt.is_some() {
|
||||||
|
(key.key.to_string(), key.alt.as_ref().unwrap().to_string())
|
||||||
|
} else {
|
||||||
|
(key.key.to_string(), String::new())
|
||||||
|
};
|
||||||
|
KeybindingItem {
|
||||||
|
key,
|
||||||
|
alt_key,
|
||||||
|
desc: desc.to_string(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveKeybindingBlock::Help.into());
|
||||||
|
|
||||||
|
populate_keymapping_table(&mut app);
|
||||||
|
|
||||||
|
assert!(app.keymapping_table.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
expected_keybinding_items,
|
||||||
|
app.keymapping_table.unwrap().items
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_populate_keymapping_table_populates_servarr_specific_tab_info_before_global_options() {
|
||||||
|
let mut expected_keybinding_items = LIBRARY_CONTEXT_CLUES
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
expected_keybinding_items.extend(
|
||||||
|
SERVARR_CONTEXT_CLUES
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc)),
|
||||||
|
);
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::default().into());
|
||||||
|
|
||||||
|
populate_keymapping_table(&mut app);
|
||||||
|
|
||||||
|
assert!(app.keymapping_table.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
expected_keybinding_items,
|
||||||
|
app.keymapping_table.unwrap().items
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_populate_keymapping_table_populates_delegated_servarr_context_provider_options_before_global_options(
|
||||||
|
) {
|
||||||
|
let mut expected_keybinding_items = MOVIE_DETAILS_CONTEXT_CLUES
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
expected_keybinding_items.extend(
|
||||||
|
SERVARR_CONTEXT_CLUES
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc)),
|
||||||
|
);
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.radarr_data = RadarrData::default();
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into());
|
||||||
|
|
||||||
|
populate_keymapping_table(&mut app);
|
||||||
|
|
||||||
|
assert!(app.keymapping_table.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
expected_keybinding_items,
|
||||||
|
app.keymapping_table.unwrap().items
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn context_clue_to_keybinding_item(key: &KeyBinding, desc: &&str) -> KeybindingItem {
|
||||||
|
let (key, alt_key) = if key.alt.is_some() {
|
||||||
|
(key.key.to_string(), key.alt.as_ref().unwrap().to_string())
|
||||||
|
} else {
|
||||||
|
(key.key.to_string(), String::new())
|
||||||
|
};
|
||||||
|
KeybindingItem {
|
||||||
|
key,
|
||||||
|
alt_key,
|
||||||
|
desc: desc.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
use crate::app::App;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handle_table_events;
|
||||||
|
use crate::handlers::table_handler::TableHandlingConfig;
|
||||||
|
use crate::handlers::KeyEventHandler;
|
||||||
|
use crate::models::servarr_data::ActiveKeybindingBlock;
|
||||||
|
use crate::models::servarr_models::KeybindingItem;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "keybinding_handler_tests.rs"]
|
||||||
|
mod keybinding_handler_tests;
|
||||||
|
|
||||||
|
pub(super) struct KeybindingHandler<'a, 'b> {
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeybindingHandler<'_, '_> {
|
||||||
|
handle_table_events!(
|
||||||
|
self,
|
||||||
|
keybindings,
|
||||||
|
self.app.keymapping_table.as_mut().unwrap(),
|
||||||
|
KeybindingItem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveKeybindingBlock> for KeybindingHandler<'a, 'b> {
|
||||||
|
fn handle(&mut self) {
|
||||||
|
let keybinding_table_handling_config = TableHandlingConfig::new(self.app.get_current_route());
|
||||||
|
|
||||||
|
if !self.handle_keybindings_table_events(keybinding_table_handling_config) {
|
||||||
|
self.handle_key_event();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accepts(_active_block: ActiveKeybindingBlock) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
_active_block: ActiveKeybindingBlock,
|
||||||
|
_context: Option<ActiveKeybindingBlock>,
|
||||||
|
) -> KeybindingHandler<'a, 'b> {
|
||||||
|
KeybindingHandler { key, app }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> Key {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ignore_special_keys(&self) -> bool {
|
||||||
|
self.app.ignore_special_keys_for_textbox_input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.app.keymapping_table.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
self.app.keymapping_table = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_char_key_event(&mut self) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handlers::KeyEventHandler;
|
||||||
|
use crate::handlers::KeybindingHandler;
|
||||||
|
use crate::models::servarr_data::ActiveKeybindingBlock;
|
||||||
|
use crate::models::stateful_table::StatefulTable;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
mod test_handle_esc {
|
||||||
|
use super::*;
|
||||||
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_esc_empties_keymapping_table() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
|
||||||
|
|
||||||
|
KeybindingHandler::new(ESC_KEY, &mut app, ActiveKeybindingBlock::Help, None).handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
|
||||||
|
assert!(app.keymapping_table.is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keybinding_handler_accepts() {
|
||||||
|
assert!(KeybindingHandler::accepts(ActiveKeybindingBlock::Help));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keybinding_handler_not_ready_when_keybinding_is_empty() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = false;
|
||||||
|
|
||||||
|
let handler = KeybindingHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveKeybindingBlock::Help,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_keybinding_handler_ready_when_keymapping_table_is_not_empty(
|
||||||
|
#[values(true, false)] is_loading: bool,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.keymapping_table = Some(StatefulTable::default());
|
||||||
|
app.is_loading = is_loading;
|
||||||
|
|
||||||
|
let handler = KeybindingHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveKeybindingBlock::Help,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(handler.is_ready());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
use radarr_handlers::RadarrHandler;
|
use radarr_handlers::RadarrHandler;
|
||||||
use sonarr_handlers::SonarrHandler;
|
use sonarr_handlers::SonarrHandler;
|
||||||
|
|
||||||
|
use crate::app::context_clues::{
|
||||||
|
ContextClueProvider, ServarrContextClueProvider, SERVARR_CONTEXT_CLUES,
|
||||||
|
};
|
||||||
|
use crate::app::key_binding::KeyBinding;
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
|
use crate::handlers::keybinding_handler::KeybindingHandler;
|
||||||
use crate::matches_key;
|
use crate::matches_key;
|
||||||
|
use crate::models::servarr_data::ActiveKeybindingBlock;
|
||||||
|
use crate::models::servarr_models::KeybindingItem;
|
||||||
|
use crate::models::stateful_table::StatefulTable;
|
||||||
use crate::models::{HorizontallyScrollableText, Route};
|
use crate::models::{HorizontallyScrollableText, Route};
|
||||||
|
|
||||||
|
mod keybinding_handler;
|
||||||
mod radarr_handlers;
|
mod radarr_handlers;
|
||||||
mod sonarr_handlers;
|
mod sonarr_handlers;
|
||||||
|
|
||||||
@@ -97,8 +106,17 @@ pub fn handle_events(key: Key, app: &mut App<'_>) {
|
|||||||
app.server_tabs.previous();
|
app.server_tabs.previous();
|
||||||
app.pop_and_push_navigation_stack(app.server_tabs.get_active_route());
|
app.pop_and_push_navigation_stack(app.server_tabs.get_active_route());
|
||||||
app.cancellation_token.cancel();
|
app.cancellation_token.cancel();
|
||||||
|
} else if matches_key!(help, key) && !app.ignore_special_keys_for_textbox_input {
|
||||||
|
if app.keymapping_table.is_none() {
|
||||||
|
populate_keymapping_table(app);
|
||||||
|
} else {
|
||||||
|
app.keymapping_table = None;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
match app.get_current_route() {
|
match app.get_current_route() {
|
||||||
|
_ if app.keymapping_table.is_some() => {
|
||||||
|
KeybindingHandler::new(key, app, ActiveKeybindingBlock::Help, None).handle();
|
||||||
|
}
|
||||||
Route::Radarr(active_radarr_block, context) => {
|
Route::Radarr(active_radarr_block, context) => {
|
||||||
RadarrHandler::new(key, app, active_radarr_block, context).handle()
|
RadarrHandler::new(key, app, active_radarr_block, context).handle()
|
||||||
}
|
}
|
||||||
@@ -110,6 +128,48 @@ pub fn handle_events(key: Key, app: &mut App<'_>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn populate_keymapping_table(app: &mut App<'_>) {
|
||||||
|
let context_clue_to_keybinding_item = |key: &KeyBinding, desc: &&str| {
|
||||||
|
let (key, alt_key) = if key.alt.is_some() {
|
||||||
|
(key.key.to_string(), key.alt.as_ref().unwrap().to_string())
|
||||||
|
} else {
|
||||||
|
(key.key.to_string(), String::new())
|
||||||
|
};
|
||||||
|
KeybindingItem {
|
||||||
|
key,
|
||||||
|
alt_key,
|
||||||
|
desc: desc.to_string(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut keybindings = Vec::new();
|
||||||
|
let global_keybindings = Vec::from(SERVARR_CONTEXT_CLUES)
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if let Some(contextual_help) = app.server_tabs.get_active_route_contextual_help() {
|
||||||
|
keybindings.extend(
|
||||||
|
contextual_help
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(contextual_help) = ServarrContextClueProvider::get_context_clues(app) {
|
||||||
|
keybindings.extend(
|
||||||
|
contextual_help
|
||||||
|
.iter()
|
||||||
|
.map(|(key, desc)| context_clue_to_keybinding_item(key, desc)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
keybindings.extend(global_keybindings);
|
||||||
|
|
||||||
|
let mut table = StatefulTable::default();
|
||||||
|
table.set_items(keybindings);
|
||||||
|
app.keymapping_table = Some(table);
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_clear_errors(app: &mut App<'_>) {
|
fn handle_clear_errors(app: &mut App<'_>) {
|
||||||
if !app.error.text.is_empty() {
|
if !app.error.text.is_empty() {
|
||||||
app.error = HorizontallyScrollableText::default();
|
app.error = HorizontallyScrollableText::default();
|
||||||
|
|||||||
+7
-9
@@ -1,6 +1,7 @@
|
|||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use crate::app::context_clues::ContextClue;
|
||||||
use crate::app::ServarrConfig;
|
use crate::app::ServarrConfig;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
use radarr_models::RadarrSerdeable;
|
use radarr_models::RadarrSerdeable;
|
||||||
@@ -9,6 +10,7 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
|||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
use servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
use servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
use sonarr_models::SonarrSerdeable;
|
use sonarr_models::SonarrSerdeable;
|
||||||
|
|
||||||
pub mod radarr_models;
|
pub mod radarr_models;
|
||||||
pub mod servarr_data;
|
pub mod servarr_data;
|
||||||
pub mod servarr_models;
|
pub mod servarr_models;
|
||||||
@@ -33,6 +35,7 @@ pub enum Route {
|
|||||||
Bazarr,
|
Bazarr,
|
||||||
Prowlarr,
|
Prowlarr,
|
||||||
Tautulli,
|
Tautulli,
|
||||||
|
Keybindings,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
@@ -271,8 +274,7 @@ impl HorizontallyScrollableText {
|
|||||||
pub struct TabRoute {
|
pub struct TabRoute {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub route: Route,
|
pub route: Route,
|
||||||
pub help: String,
|
pub contextual_help: Option<&'static [ContextClue]>,
|
||||||
pub contextual_help: Option<String>,
|
|
||||||
pub config: Option<ServarrConfig>,
|
pub config: Option<ServarrConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +288,7 @@ impl TabState {
|
|||||||
TabState { tabs, index: 0 }
|
TabState { tabs, index: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allowing this code for now since we'll eventually be implementing additional Servarr support and we'll need it then
|
// Allowing this code for now since we'll eventually be implementing additional Servarr support, and we'll need it then
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn set_index(&mut self, index: usize) -> &TabRoute {
|
pub fn set_index(&mut self, index: usize) -> &TabRoute {
|
||||||
self.index = index;
|
self.index = index;
|
||||||
@@ -337,12 +339,8 @@ impl TabState {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_active_tab_help(&self) -> &str {
|
pub fn get_active_route_contextual_help(&self) -> Option<&'static [ContextClue]> {
|
||||||
&self.tabs[self.index].help
|
self.tabs[self.index].contextual_help
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_active_tab_contextual_help(&self) -> Option<String> {
|
|
||||||
self.tabs[self.index].contextual_help.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(&mut self) {
|
pub fn next(&mut self) {
|
||||||
|
|||||||
+13
-20
@@ -3,6 +3,8 @@ mod tests {
|
|||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use crate::app::context_clues::ContextClue;
|
||||||
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::app::ServarrConfig;
|
use crate::app::ServarrConfig;
|
||||||
use crate::models::from_f64;
|
use crate::models::from_f64;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
@@ -18,6 +20,10 @@ mod tests {
|
|||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
const BLOCKS: &[&[i32]] = &[&[11, 12], &[21, 22], &[31, 32]];
|
const BLOCKS: &[&[i32]] = &[&[11, 12], &[21, 22], &[31, 32]];
|
||||||
|
static HELP_KEYBINDINGS: [ContextClue; 1] =
|
||||||
|
[(DEFAULT_KEYBINDINGS.help, DEFAULT_KEYBINDINGS.help.desc)];
|
||||||
|
static ESC_KEYBINDINGS: [ContextClue; 1] =
|
||||||
|
[(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc)];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scrollable_text_with_string() {
|
fn test_scrollable_text_with_string() {
|
||||||
@@ -617,26 +623,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tab_state_get_active_tab_help() {
|
fn test_tab_state_get_active_route_contextual_help() {
|
||||||
let tabs = create_test_tab_routes();
|
let tabs = create_test_tab_routes();
|
||||||
let second_tab_help = tabs[1].help.clone();
|
let second_tab_help = tabs[1].contextual_help;
|
||||||
let tab_state = TabState { tabs, index: 1 };
|
let tab_state = TabState { tabs, index: 1 };
|
||||||
|
|
||||||
let tab_help = tab_state.get_active_tab_help();
|
let tab_help = tab_state.get_active_route_contextual_help();
|
||||||
|
|
||||||
assert_str_eq!(tab_help, second_tab_help);
|
assert!(tab_help.is_some());
|
||||||
}
|
assert_eq!(tab_help.unwrap(), second_tab_help.unwrap());
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_tab_state_get_active_tab_contextual_help() {
|
|
||||||
let tabs = create_test_tab_routes();
|
|
||||||
let second_tab_contextual_help = tabs[1].contextual_help.clone().unwrap();
|
|
||||||
let tab_state = TabState { tabs, index: 1 };
|
|
||||||
|
|
||||||
let tab_contextual_help = tab_state.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
assert!(tab_contextual_help.is_some());
|
|
||||||
assert_str_eq!(tab_contextual_help.unwrap(), second_tab_contextual_help);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -823,15 +818,13 @@ mod tests {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Test 1".to_owned(),
|
title: "Test 1".to_owned(),
|
||||||
route: ActiveRadarrBlock::Movies.into(),
|
route: ActiveRadarrBlock::Movies.into(),
|
||||||
help: "Help for Test 1".to_owned(),
|
contextual_help: Some(&HELP_KEYBINDINGS),
|
||||||
contextual_help: Some("Contextual Help for Test 1".to_owned()),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Test 2".to_owned(),
|
title: "Test 2".to_owned(),
|
||||||
route: ActiveRadarrBlock::Collections.into(),
|
route: ActiveRadarrBlock::Collections.into(),
|
||||||
help: "Help for Test 2".to_owned(),
|
contextual_help: Some(&ESC_KEYBINDINGS),
|
||||||
contextual_help: Some("Contextual Help for Test 2".to_owned()),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
use crate::models::Route;
|
||||||
|
|
||||||
pub mod modals;
|
pub mod modals;
|
||||||
pub mod radarr;
|
pub mod radarr;
|
||||||
pub mod sonarr;
|
pub mod sonarr;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "servarr_data_tests.rs"]
|
||||||
|
mod servarr_data_tests;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||||
|
pub enum ActiveKeybindingBlock {
|
||||||
|
#[default]
|
||||||
|
Help,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ActiveKeybindingBlock> for Route {
|
||||||
|
fn from(_active_keybinding_block: ActiveKeybindingBlock) -> Route {
|
||||||
|
Route::Keybindings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
build_context_clue_string, BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES,
|
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::radarr::radarr_context_clues::{
|
use crate::app::radarr::radarr_context_clues::{
|
||||||
COLLECTIONS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES,
|
COLLECTIONS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
|
||||||
MANUAL_MOVIE_SEARCH_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES,
|
MOVIE_DETAILS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::models::radarr_models::{
|
use crate::models::radarr_models::{
|
||||||
AddMovieSearchResult, BlocklistItem, Collection, CollectionMovie, DownloadRecord,
|
AddMovieSearchResult, BlocklistItem, Collection, CollectionMovie, DownloadRecord,
|
||||||
@@ -123,50 +123,43 @@ impl<'a> Default for RadarrData<'a> {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Library".to_string(),
|
title: "Library".to_string(),
|
||||||
route: ActiveRadarrBlock::Movies.into(),
|
route: ActiveRadarrBlock::Movies.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&LIBRARY_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&LIBRARY_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Collections".to_string(),
|
title: "Collections".to_string(),
|
||||||
route: ActiveRadarrBlock::Collections.into(),
|
route: ActiveRadarrBlock::Collections.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&COLLECTIONS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&COLLECTIONS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Downloads".to_string(),
|
title: "Downloads".to_string(),
|
||||||
route: ActiveRadarrBlock::Downloads.into(),
|
route: ActiveRadarrBlock::Downloads.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&DOWNLOADS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&DOWNLOADS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Blocklist".to_string(),
|
title: "Blocklist".to_string(),
|
||||||
route: ActiveRadarrBlock::Blocklist.into(),
|
route: ActiveRadarrBlock::Blocklist.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&BLOCKLIST_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&BLOCKLIST_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Root Folders".to_string(),
|
title: "Root Folders".to_string(),
|
||||||
route: ActiveRadarrBlock::RootFolders.into(),
|
route: ActiveRadarrBlock::RootFolders.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&ROOT_FOLDERS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&ROOT_FOLDERS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Indexers".to_string(),
|
title: "Indexers".to_string(),
|
||||||
route: ActiveRadarrBlock::Indexers.into(),
|
route: ActiveRadarrBlock::Indexers.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&INDEXERS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&INDEXERS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "System".to_string(),
|
title: "System".to_string(),
|
||||||
route: ActiveRadarrBlock::System.into(),
|
route: ActiveRadarrBlock::System.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&SYSTEM_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&SYSTEM_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
@@ -174,45 +167,37 @@ impl<'a> Default for RadarrData<'a> {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Details".to_string(),
|
title: "Details".to_string(),
|
||||||
route: ActiveRadarrBlock::MovieDetails.into(),
|
route: ActiveRadarrBlock::MovieDetails.into(),
|
||||||
help: build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&MOVIE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: None,
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "History".to_string(),
|
title: "History".to_string(),
|
||||||
route: ActiveRadarrBlock::MovieHistory.into(),
|
route: ActiveRadarrBlock::MovieHistory.into(),
|
||||||
help: build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&MOVIE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: None,
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "File".to_string(),
|
title: "File".to_string(),
|
||||||
route: ActiveRadarrBlock::FileInfo.into(),
|
route: ActiveRadarrBlock::FileInfo.into(),
|
||||||
help: build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&MOVIE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: None,
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Cast".to_string(),
|
title: "Cast".to_string(),
|
||||||
route: ActiveRadarrBlock::Cast.into(),
|
route: ActiveRadarrBlock::Cast.into(),
|
||||||
help: build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&MOVIE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: None,
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Crew".to_string(),
|
title: "Crew".to_string(),
|
||||||
route: ActiveRadarrBlock::Crew.into(),
|
route: ActiveRadarrBlock::Crew.into(),
|
||||||
help: build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&MOVIE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: None,
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Manual Search".to_string(),
|
title: "Manual Search".to_string(),
|
||||||
route: ActiveRadarrBlock::ManualSearch.into(),
|
route: ActiveRadarrBlock::ManualSearch.into(),
|
||||||
help: build_context_clue_string(&MANUAL_MOVIE_SEARCH_CONTEXT_CLUES),
|
contextual_help: Some(&MANUAL_MOVIE_SEARCH_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(
|
|
||||||
&MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES,
|
|
||||||
)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ mod tests {
|
|||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
|
||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
build_context_clue_string, BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES,
|
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::radarr::radarr_context_clues::{
|
use crate::app::radarr::radarr_context_clues::{
|
||||||
COLLECTIONS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES,
|
COLLECTIONS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
|
||||||
MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
|
|
||||||
MOVIE_DETAILS_CONTEXT_CLUES,
|
MOVIE_DETAILS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,10 +106,10 @@ mod tests {
|
|||||||
radarr_data.main_tabs.tabs[0].route,
|
radarr_data.main_tabs.tabs[0].route,
|
||||||
ActiveRadarrBlock::Movies.into()
|
ActiveRadarrBlock::Movies.into()
|
||||||
);
|
);
|
||||||
assert!(radarr_data.main_tabs.tabs[0].help.is_empty());
|
assert!(radarr_data.main_tabs.tabs[0].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.main_tabs.tabs[0].contextual_help,
|
radarr_data.main_tabs.tabs[0].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&LIBRARY_CONTEXT_CLUES))
|
&LIBRARY_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.main_tabs.tabs[0].config, None);
|
assert_eq!(radarr_data.main_tabs.tabs[0].config, None);
|
||||||
|
|
||||||
@@ -119,10 +118,10 @@ mod tests {
|
|||||||
radarr_data.main_tabs.tabs[1].route,
|
radarr_data.main_tabs.tabs[1].route,
|
||||||
ActiveRadarrBlock::Collections.into()
|
ActiveRadarrBlock::Collections.into()
|
||||||
);
|
);
|
||||||
assert!(radarr_data.main_tabs.tabs[1].help.is_empty());
|
assert!(radarr_data.main_tabs.tabs[1].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.main_tabs.tabs[1].contextual_help,
|
radarr_data.main_tabs.tabs[1].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&COLLECTIONS_CONTEXT_CLUES))
|
&COLLECTIONS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.main_tabs.tabs[1].config, None);
|
assert_eq!(radarr_data.main_tabs.tabs[1].config, None);
|
||||||
|
|
||||||
@@ -131,10 +130,10 @@ mod tests {
|
|||||||
radarr_data.main_tabs.tabs[2].route,
|
radarr_data.main_tabs.tabs[2].route,
|
||||||
ActiveRadarrBlock::Downloads.into()
|
ActiveRadarrBlock::Downloads.into()
|
||||||
);
|
);
|
||||||
assert!(radarr_data.main_tabs.tabs[2].help.is_empty());
|
assert!(radarr_data.main_tabs.tabs[2].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.main_tabs.tabs[2].contextual_help,
|
radarr_data.main_tabs.tabs[2].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&DOWNLOADS_CONTEXT_CLUES))
|
&DOWNLOADS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.main_tabs.tabs[2].config, None);
|
assert_eq!(radarr_data.main_tabs.tabs[2].config, None);
|
||||||
|
|
||||||
@@ -143,10 +142,10 @@ mod tests {
|
|||||||
radarr_data.main_tabs.tabs[3].route,
|
radarr_data.main_tabs.tabs[3].route,
|
||||||
ActiveRadarrBlock::Blocklist.into()
|
ActiveRadarrBlock::Blocklist.into()
|
||||||
);
|
);
|
||||||
assert!(radarr_data.main_tabs.tabs[3].help.is_empty());
|
assert!(radarr_data.main_tabs.tabs[3].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.main_tabs.tabs[3].contextual_help,
|
radarr_data.main_tabs.tabs[3].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&BLOCKLIST_CONTEXT_CLUES))
|
&BLOCKLIST_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.main_tabs.tabs[3].config, None);
|
assert_eq!(radarr_data.main_tabs.tabs[3].config, None);
|
||||||
|
|
||||||
@@ -155,10 +154,10 @@ mod tests {
|
|||||||
radarr_data.main_tabs.tabs[4].route,
|
radarr_data.main_tabs.tabs[4].route,
|
||||||
ActiveRadarrBlock::RootFolders.into()
|
ActiveRadarrBlock::RootFolders.into()
|
||||||
);
|
);
|
||||||
assert!(radarr_data.main_tabs.tabs[4].help.is_empty());
|
assert!(radarr_data.main_tabs.tabs[4].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.main_tabs.tabs[4].contextual_help,
|
radarr_data.main_tabs.tabs[4].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&ROOT_FOLDERS_CONTEXT_CLUES))
|
&ROOT_FOLDERS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.main_tabs.tabs[4].config, None);
|
assert_eq!(radarr_data.main_tabs.tabs[4].config, None);
|
||||||
|
|
||||||
@@ -167,10 +166,10 @@ mod tests {
|
|||||||
radarr_data.main_tabs.tabs[5].route,
|
radarr_data.main_tabs.tabs[5].route,
|
||||||
ActiveRadarrBlock::Indexers.into()
|
ActiveRadarrBlock::Indexers.into()
|
||||||
);
|
);
|
||||||
assert!(radarr_data.main_tabs.tabs[5].help.is_empty());
|
assert!(radarr_data.main_tabs.tabs[5].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.main_tabs.tabs[5].contextual_help,
|
radarr_data.main_tabs.tabs[5].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&INDEXERS_CONTEXT_CLUES))
|
&INDEXERS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.main_tabs.tabs[5].config, None);
|
assert_eq!(radarr_data.main_tabs.tabs[5].config, None);
|
||||||
|
|
||||||
@@ -179,10 +178,10 @@ mod tests {
|
|||||||
radarr_data.main_tabs.tabs[6].route,
|
radarr_data.main_tabs.tabs[6].route,
|
||||||
ActiveRadarrBlock::System.into()
|
ActiveRadarrBlock::System.into()
|
||||||
);
|
);
|
||||||
assert!(radarr_data.main_tabs.tabs[6].help.is_empty());
|
assert!(radarr_data.main_tabs.tabs[6].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.main_tabs.tabs[6].contextual_help,
|
radarr_data.main_tabs.tabs[6].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&SYSTEM_CONTEXT_CLUES))
|
&SYSTEM_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.main_tabs.tabs[6].config, None);
|
assert_eq!(radarr_data.main_tabs.tabs[6].config, None);
|
||||||
|
|
||||||
@@ -193,13 +192,13 @@ mod tests {
|
|||||||
radarr_data.movie_info_tabs.tabs[0].route,
|
radarr_data.movie_info_tabs.tabs[0].route,
|
||||||
ActiveRadarrBlock::MovieDetails.into()
|
ActiveRadarrBlock::MovieDetails.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
|
||||||
radarr_data.movie_info_tabs.tabs[0].help,
|
|
||||||
build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
assert!(radarr_data.movie_info_tabs.tabs[0]
|
assert!(radarr_data.movie_info_tabs.tabs[0]
|
||||||
.contextual_help
|
.contextual_help
|
||||||
.is_none());
|
.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
radarr_data.movie_info_tabs.tabs[0].contextual_help.unwrap(),
|
||||||
|
&MOVIE_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
assert_eq!(radarr_data.movie_info_tabs.tabs[0].config, None);
|
assert_eq!(radarr_data.movie_info_tabs.tabs[0].config, None);
|
||||||
|
|
||||||
assert_str_eq!(radarr_data.movie_info_tabs.tabs[1].title, "History");
|
assert_str_eq!(radarr_data.movie_info_tabs.tabs[1].title, "History");
|
||||||
@@ -207,13 +206,13 @@ mod tests {
|
|||||||
radarr_data.movie_info_tabs.tabs[1].route,
|
radarr_data.movie_info_tabs.tabs[1].route,
|
||||||
ActiveRadarrBlock::MovieHistory.into()
|
ActiveRadarrBlock::MovieHistory.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
|
||||||
radarr_data.movie_info_tabs.tabs[1].help,
|
|
||||||
build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
assert!(radarr_data.movie_info_tabs.tabs[1]
|
assert!(radarr_data.movie_info_tabs.tabs[1]
|
||||||
.contextual_help
|
.contextual_help
|
||||||
.is_none());
|
.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
radarr_data.movie_info_tabs.tabs[1].contextual_help.unwrap(),
|
||||||
|
&MOVIE_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
assert_eq!(radarr_data.movie_info_tabs.tabs[1].config, None);
|
assert_eq!(radarr_data.movie_info_tabs.tabs[1].config, None);
|
||||||
|
|
||||||
assert_str_eq!(radarr_data.movie_info_tabs.tabs[2].title, "File");
|
assert_str_eq!(radarr_data.movie_info_tabs.tabs[2].title, "File");
|
||||||
@@ -221,13 +220,13 @@ mod tests {
|
|||||||
radarr_data.movie_info_tabs.tabs[2].route,
|
radarr_data.movie_info_tabs.tabs[2].route,
|
||||||
ActiveRadarrBlock::FileInfo.into()
|
ActiveRadarrBlock::FileInfo.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
|
||||||
radarr_data.movie_info_tabs.tabs[2].help,
|
|
||||||
build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
assert!(radarr_data.movie_info_tabs.tabs[2]
|
assert!(radarr_data.movie_info_tabs.tabs[2]
|
||||||
.contextual_help
|
.contextual_help
|
||||||
.is_none());
|
.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
radarr_data.movie_info_tabs.tabs[2].contextual_help.unwrap(),
|
||||||
|
&MOVIE_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
assert_eq!(radarr_data.movie_info_tabs.tabs[2].config, None);
|
assert_eq!(radarr_data.movie_info_tabs.tabs[2].config, None);
|
||||||
|
|
||||||
assert_str_eq!(radarr_data.movie_info_tabs.tabs[3].title, "Cast");
|
assert_str_eq!(radarr_data.movie_info_tabs.tabs[3].title, "Cast");
|
||||||
@@ -235,13 +234,13 @@ mod tests {
|
|||||||
radarr_data.movie_info_tabs.tabs[3].route,
|
radarr_data.movie_info_tabs.tabs[3].route,
|
||||||
ActiveRadarrBlock::Cast.into()
|
ActiveRadarrBlock::Cast.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
|
||||||
radarr_data.movie_info_tabs.tabs[3].help,
|
|
||||||
build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
assert!(radarr_data.movie_info_tabs.tabs[3]
|
assert!(radarr_data.movie_info_tabs.tabs[3]
|
||||||
.contextual_help
|
.contextual_help
|
||||||
.is_none());
|
.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
radarr_data.movie_info_tabs.tabs[3].contextual_help.unwrap(),
|
||||||
|
&MOVIE_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
assert_eq!(radarr_data.movie_info_tabs.tabs[3].config, None);
|
assert_eq!(radarr_data.movie_info_tabs.tabs[3].config, None);
|
||||||
|
|
||||||
assert_str_eq!(radarr_data.movie_info_tabs.tabs[4].title, "Crew");
|
assert_str_eq!(radarr_data.movie_info_tabs.tabs[4].title, "Crew");
|
||||||
@@ -249,13 +248,13 @@ mod tests {
|
|||||||
radarr_data.movie_info_tabs.tabs[4].route,
|
radarr_data.movie_info_tabs.tabs[4].route,
|
||||||
ActiveRadarrBlock::Crew.into()
|
ActiveRadarrBlock::Crew.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
|
||||||
radarr_data.movie_info_tabs.tabs[4].help,
|
|
||||||
build_context_clue_string(&MOVIE_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
assert!(radarr_data.movie_info_tabs.tabs[4]
|
assert!(radarr_data.movie_info_tabs.tabs[4]
|
||||||
.contextual_help
|
.contextual_help
|
||||||
.is_none());
|
.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
radarr_data.movie_info_tabs.tabs[4].contextual_help.unwrap(),
|
||||||
|
&MOVIE_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
assert_eq!(radarr_data.movie_info_tabs.tabs[4].config, None);
|
assert_eq!(radarr_data.movie_info_tabs.tabs[4].config, None);
|
||||||
|
|
||||||
assert_str_eq!(radarr_data.movie_info_tabs.tabs[5].title, "Manual Search");
|
assert_str_eq!(radarr_data.movie_info_tabs.tabs[5].title, "Manual Search");
|
||||||
@@ -263,15 +262,12 @@ mod tests {
|
|||||||
radarr_data.movie_info_tabs.tabs[5].route,
|
radarr_data.movie_info_tabs.tabs[5].route,
|
||||||
ActiveRadarrBlock::ManualSearch.into()
|
ActiveRadarrBlock::ManualSearch.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
assert!(radarr_data.movie_info_tabs.tabs[5]
|
||||||
radarr_data.movie_info_tabs.tabs[5].help,
|
.contextual_help
|
||||||
build_context_clue_string(&MANUAL_MOVIE_SEARCH_CONTEXT_CLUES)
|
.is_some());
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
radarr_data.movie_info_tabs.tabs[5].contextual_help,
|
radarr_data.movie_info_tabs.tabs[5].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(
|
&MANUAL_MOVIE_SEARCH_CONTEXT_CLUES
|
||||||
&MANUAL_MOVIE_SEARCH_CONTEXTUAL_CONTEXT_CLUES
|
|
||||||
))
|
|
||||||
);
|
);
|
||||||
assert_eq!(radarr_data.movie_info_tabs.tabs[5].config, None);
|
assert_eq!(radarr_data.movie_info_tabs.tabs[5].config, None);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::models::servarr_data::ActiveKeybindingBlock;
|
||||||
|
use crate::models::Route;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_active_keybinding_block_to_route() {
|
||||||
|
assert_eq!(Route::from(ActiveKeybindingBlock::Help), Route::Keybindings);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,12 @@
|
|||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use super::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
use super::sonarr_data::{ActiveSonarrBlock, SonarrData};
|
||||||
|
use crate::app::sonarr::sonarr_context_clues::SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES;
|
||||||
use crate::models::sonarr_models::EpisodeFile;
|
use crate::models::sonarr_models::EpisodeFile;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::sonarr::sonarr_context_clues::{
|
||||||
context_clues::build_context_clue_string,
|
EPISODE_DETAILS_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXT_CLUES,
|
||||||
sonarr::sonarr_context_clues::{
|
MANUAL_SEASON_SEARCH_CONTEXT_CLUES, SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES,
|
||||||
DETAILS_CONTEXTUAL_CONTEXT_CLUES, EPISODE_DETAILS_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,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
models::{
|
models::{
|
||||||
servarr_data::modals::EditIndexerModal,
|
servarr_data::modals::EditIndexerModal,
|
||||||
@@ -282,29 +278,25 @@ impl Default for EpisodeDetailsModal {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Details".to_string(),
|
title: "Details".to_string(),
|
||||||
route: ActiveSonarrBlock::EpisodeDetails.into(),
|
route: ActiveSonarrBlock::EpisodeDetails.into(),
|
||||||
help: build_context_clue_string(&EPISODE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&EPISODE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: None,
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "History".to_string(),
|
title: "History".to_string(),
|
||||||
route: ActiveSonarrBlock::EpisodeHistory.into(),
|
route: ActiveSonarrBlock::EpisodeHistory.into(),
|
||||||
help: build_context_clue_string(&EPISODE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "File".to_string(),
|
title: "File".to_string(),
|
||||||
route: ActiveSonarrBlock::EpisodeFile.into(),
|
route: ActiveSonarrBlock::EpisodeFile.into(),
|
||||||
help: build_context_clue_string(&EPISODE_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&EPISODE_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: None,
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Manual Search".to_string(),
|
title: "Manual Search".to_string(),
|
||||||
route: ActiveSonarrBlock::ManualEpisodeSearch.into(),
|
route: ActiveSonarrBlock::ManualEpisodeSearch.into(),
|
||||||
help: build_context_clue_string(&MANUAL_EPISODE_SEARCH_CONTEXT_CLUES),
|
contextual_help: Some(&MANUAL_EPISODE_SEARCH_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
@@ -333,24 +325,19 @@ impl Default for SeasonDetailsModal {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Episodes".to_string(),
|
title: "Episodes".to_string(),
|
||||||
route: ActiveSonarrBlock::SeasonDetails.into(),
|
route: ActiveSonarrBlock::SeasonDetails.into(),
|
||||||
help: build_context_clue_string(&SEASON_DETAILS_CONTEXT_CLUES),
|
contextual_help: Some(&SEASON_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(
|
|
||||||
&SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES,
|
|
||||||
)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "History".to_string(),
|
title: "History".to_string(),
|
||||||
route: ActiveSonarrBlock::SeasonHistory.into(),
|
route: ActiveSonarrBlock::SeasonHistory.into(),
|
||||||
help: build_context_clue_string(&SEASON_HISTORY_CONTEXT_CLUES),
|
contextual_help: Some(&SEASON_HISTORY_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Manual Search".to_string(),
|
title: "Manual Search".to_string(),
|
||||||
route: ActiveSonarrBlock::ManualSeasonSearch.into(),
|
route: ActiveSonarrBlock::ManualSeasonSearch.into(),
|
||||||
help: build_context_clue_string(&MANUAL_SEASON_SEARCH_CONTEXT_CLUES),
|
contextual_help: Some(&MANUAL_SEASON_SEARCH_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ mod tests {
|
|||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::app::context_clues::build_context_clue_string;
|
|
||||||
use crate::app::sonarr::sonarr_context_clues::{
|
use crate::app::sonarr::sonarr_context_clues::{
|
||||||
DETAILS_CONTEXTUAL_CONTEXT_CLUES, EPISODE_DETAILS_CONTEXT_CLUES,
|
EPISODE_DETAILS_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXT_CLUES,
|
||||||
MANUAL_EPISODE_SEARCH_CONTEXT_CLUES, MANUAL_SEASON_SEARCH_CONTEXT_CLUES,
|
MANUAL_SEASON_SEARCH_CONTEXT_CLUES, SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES,
|
||||||
SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES, SEASON_DETAILS_CONTEXT_CLUES,
|
SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES,
|
||||||
SEASON_HISTORY_CONTEXT_CLUES,
|
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::sonarr::modals::{
|
use crate::models::servarr_data::sonarr::modals::{
|
||||||
EditSeriesModal, EpisodeDetailsModal, SeasonDetailsModal,
|
EditSeriesModal, EpisodeDetailsModal, SeasonDetailsModal,
|
||||||
@@ -257,13 +255,15 @@ mod tests {
|
|||||||
episode_details_modal.episode_details_tabs.tabs[0].route,
|
episode_details_modal.episode_details_tabs.tabs[0].route,
|
||||||
ActiveSonarrBlock::EpisodeDetails.into()
|
ActiveSonarrBlock::EpisodeDetails.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
|
||||||
episode_details_modal.episode_details_tabs.tabs[0].help,
|
|
||||||
build_context_clue_string(&EPISODE_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
assert!(episode_details_modal.episode_details_tabs.tabs[0]
|
assert!(episode_details_modal.episode_details_tabs.tabs[0]
|
||||||
.contextual_help
|
.contextual_help
|
||||||
.is_none());
|
.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
episode_details_modal.episode_details_tabs.tabs[0]
|
||||||
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&EPISODE_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
episode_details_modal.episode_details_tabs.tabs[0].config,
|
episode_details_modal.episode_details_tabs.tabs[0].config,
|
||||||
None
|
None
|
||||||
@@ -277,13 +277,14 @@ mod tests {
|
|||||||
episode_details_modal.episode_details_tabs.tabs[1].route,
|
episode_details_modal.episode_details_tabs.tabs[1].route,
|
||||||
ActiveSonarrBlock::EpisodeHistory.into()
|
ActiveSonarrBlock::EpisodeHistory.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
assert!(episode_details_modal.episode_details_tabs.tabs[1]
|
||||||
episode_details_modal.episode_details_tabs.tabs[1].help,
|
.contextual_help
|
||||||
build_context_clue_string(&EPISODE_DETAILS_CONTEXT_CLUES)
|
.is_some());
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
episode_details_modal.episode_details_tabs.tabs[1].contextual_help,
|
episode_details_modal.episode_details_tabs.tabs[1]
|
||||||
Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES))
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
episode_details_modal.episode_details_tabs.tabs[1].config,
|
episode_details_modal.episode_details_tabs.tabs[1].config,
|
||||||
@@ -298,13 +299,15 @@ mod tests {
|
|||||||
episode_details_modal.episode_details_tabs.tabs[2].route,
|
episode_details_modal.episode_details_tabs.tabs[2].route,
|
||||||
ActiveSonarrBlock::EpisodeFile.into()
|
ActiveSonarrBlock::EpisodeFile.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
|
||||||
episode_details_modal.episode_details_tabs.tabs[2].help,
|
|
||||||
build_context_clue_string(&EPISODE_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
assert!(episode_details_modal.episode_details_tabs.tabs[2]
|
assert!(episode_details_modal.episode_details_tabs.tabs[2]
|
||||||
.contextual_help
|
.contextual_help
|
||||||
.is_none());
|
.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
episode_details_modal.episode_details_tabs.tabs[2]
|
||||||
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&EPISODE_DETAILS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
episode_details_modal.episode_details_tabs.tabs[2].config,
|
episode_details_modal.episode_details_tabs.tabs[2].config,
|
||||||
None
|
None
|
||||||
@@ -318,13 +321,14 @@ mod tests {
|
|||||||
episode_details_modal.episode_details_tabs.tabs[3].route,
|
episode_details_modal.episode_details_tabs.tabs[3].route,
|
||||||
ActiveSonarrBlock::ManualEpisodeSearch.into()
|
ActiveSonarrBlock::ManualEpisodeSearch.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
assert!(episode_details_modal.episode_details_tabs.tabs[3]
|
||||||
episode_details_modal.episode_details_tabs.tabs[3].help,
|
.contextual_help
|
||||||
build_context_clue_string(&MANUAL_EPISODE_SEARCH_CONTEXT_CLUES)
|
.is_some());
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
episode_details_modal.episode_details_tabs.tabs[3].contextual_help,
|
episode_details_modal.episode_details_tabs.tabs[3]
|
||||||
Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES))
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&MANUAL_EPISODE_SEARCH_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
episode_details_modal.episode_details_tabs.tabs[3].config,
|
episode_details_modal.episode_details_tabs.tabs[3].config,
|
||||||
@@ -352,15 +356,14 @@ mod tests {
|
|||||||
season_details_modal.season_details_tabs.tabs[0].route,
|
season_details_modal.season_details_tabs.tabs[0].route,
|
||||||
ActiveSonarrBlock::SeasonDetails.into()
|
ActiveSonarrBlock::SeasonDetails.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
assert!(season_details_modal.season_details_tabs.tabs[0]
|
||||||
season_details_modal.season_details_tabs.tabs[0].help,
|
.contextual_help
|
||||||
build_context_clue_string(&SEASON_DETAILS_CONTEXT_CLUES)
|
.is_some());
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
season_details_modal.season_details_tabs.tabs[0].contextual_help,
|
season_details_modal.season_details_tabs.tabs[0]
|
||||||
Some(build_context_clue_string(
|
.contextual_help
|
||||||
&SEASON_DETAILS_CONTEXTUAL_CONTEXT_CLUES
|
.unwrap(),
|
||||||
))
|
&SEASON_DETAILS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
season_details_modal.season_details_tabs.tabs[0].config,
|
season_details_modal.season_details_tabs.tabs[0].config,
|
||||||
@@ -375,13 +378,14 @@ mod tests {
|
|||||||
season_details_modal.season_details_tabs.tabs[1].route,
|
season_details_modal.season_details_tabs.tabs[1].route,
|
||||||
ActiveSonarrBlock::SeasonHistory.into()
|
ActiveSonarrBlock::SeasonHistory.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
assert!(season_details_modal.season_details_tabs.tabs[1]
|
||||||
season_details_modal.season_details_tabs.tabs[1].help,
|
.contextual_help
|
||||||
build_context_clue_string(&SEASON_HISTORY_CONTEXT_CLUES)
|
.is_some());
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
season_details_modal.season_details_tabs.tabs[1].contextual_help,
|
season_details_modal.season_details_tabs.tabs[1]
|
||||||
Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES))
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&SEASON_HISTORY_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
season_details_modal.season_details_tabs.tabs[1].config,
|
season_details_modal.season_details_tabs.tabs[1].config,
|
||||||
@@ -396,13 +400,14 @@ mod tests {
|
|||||||
season_details_modal.season_details_tabs.tabs[2].route,
|
season_details_modal.season_details_tabs.tabs[2].route,
|
||||||
ActiveSonarrBlock::ManualSeasonSearch.into()
|
ActiveSonarrBlock::ManualSeasonSearch.into()
|
||||||
);
|
);
|
||||||
assert_str_eq!(
|
assert!(season_details_modal.season_details_tabs.tabs[2]
|
||||||
season_details_modal.season_details_tabs.tabs[2].help,
|
.contextual_help
|
||||||
build_context_clue_string(&MANUAL_SEASON_SEARCH_CONTEXT_CLUES)
|
.is_some());
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
season_details_modal.season_details_tabs.tabs[2].contextual_help,
|
season_details_modal.season_details_tabs.tabs[2]
|
||||||
Some(build_context_clue_string(&DETAILS_CONTEXTUAL_CONTEXT_CLUES))
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&MANUAL_SEASON_SEARCH_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
season_details_modal.season_details_tabs.tabs[2].config,
|
season_details_modal.season_details_tabs.tabs[2].config,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use strum::EnumIter;
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
context_clues::{
|
context_clues::{
|
||||||
build_context_clue_string, BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES,
|
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
},
|
},
|
||||||
sonarr::sonarr_context_clues::{
|
sonarr::sonarr_context_clues::{
|
||||||
HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES, SERIES_DETAILS_CONTEXT_CLUES,
|
HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES, SERIES_DETAILS_CONTEXT_CLUES,
|
||||||
@@ -130,50 +130,43 @@ impl<'a> Default for SonarrData<'a> {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Library".to_string(),
|
title: "Library".to_string(),
|
||||||
route: ActiveSonarrBlock::Series.into(),
|
route: ActiveSonarrBlock::Series.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&SERIES_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&SERIES_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Downloads".to_string(),
|
title: "Downloads".to_string(),
|
||||||
route: ActiveSonarrBlock::Downloads.into(),
|
route: ActiveSonarrBlock::Downloads.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&DOWNLOADS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&DOWNLOADS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Blocklist".to_string(),
|
title: "Blocklist".to_string(),
|
||||||
route: ActiveSonarrBlock::Blocklist.into(),
|
route: ActiveSonarrBlock::Blocklist.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&BLOCKLIST_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&BLOCKLIST_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "History".to_string(),
|
title: "History".to_string(),
|
||||||
route: ActiveSonarrBlock::History.into(),
|
route: ActiveSonarrBlock::History.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&HISTORY_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&HISTORY_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Root Folders".to_string(),
|
title: "Root Folders".to_string(),
|
||||||
route: ActiveSonarrBlock::RootFolders.into(),
|
route: ActiveSonarrBlock::RootFolders.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&ROOT_FOLDERS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&ROOT_FOLDERS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Indexers".to_string(),
|
title: "Indexers".to_string(),
|
||||||
route: ActiveSonarrBlock::Indexers.into(),
|
route: ActiveSonarrBlock::Indexers.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&INDEXERS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&INDEXERS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "System".to_string(),
|
title: "System".to_string(),
|
||||||
route: ActiveSonarrBlock::System.into(),
|
route: ActiveSonarrBlock::System.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&SYSTEM_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&SYSTEM_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
@@ -181,15 +174,13 @@ impl<'a> Default for SonarrData<'a> {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Seasons".to_string(),
|
title: "Seasons".to_string(),
|
||||||
route: ActiveSonarrBlock::SeriesDetails.into(),
|
route: ActiveSonarrBlock::SeriesDetails.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&SERIES_DETAILS_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&SERIES_DETAILS_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
title: "History".to_string(),
|
title: "History".to_string(),
|
||||||
route: ActiveSonarrBlock::SeriesHistory.into(),
|
route: ActiveSonarrBlock::SeriesHistory.into(),
|
||||||
help: String::new(),
|
contextual_help: Some(&SERIES_HISTORY_CONTEXT_CLUES),
|
||||||
contextual_help: Some(build_context_clue_string(&SERIES_HISTORY_CONTEXT_CLUES)),
|
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
context_clues::{
|
context_clues::{
|
||||||
build_context_clue_string, BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES,
|
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
},
|
},
|
||||||
sonarr::sonarr_context_clues::{
|
sonarr::sonarr_context_clues::{
|
||||||
HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES, SERIES_DETAILS_CONTEXT_CLUES,
|
HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES, SERIES_DETAILS_CONTEXT_CLUES,
|
||||||
@@ -123,10 +123,10 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[0].route,
|
sonarr_data.main_tabs.tabs[0].route,
|
||||||
ActiveSonarrBlock::Series.into()
|
ActiveSonarrBlock::Series.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[0].help.is_empty());
|
assert!(sonarr_data.main_tabs.tabs[0].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[0].contextual_help,
|
sonarr_data.main_tabs.tabs[0].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&SERIES_CONTEXT_CLUES))
|
&SERIES_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[0].config, None);
|
assert_eq!(sonarr_data.main_tabs.tabs[0].config, None);
|
||||||
|
|
||||||
@@ -135,10 +135,10 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[1].route,
|
sonarr_data.main_tabs.tabs[1].route,
|
||||||
ActiveSonarrBlock::Downloads.into()
|
ActiveSonarrBlock::Downloads.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[1].help.is_empty());
|
assert!(sonarr_data.main_tabs.tabs[1].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[1].contextual_help,
|
sonarr_data.main_tabs.tabs[1].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&DOWNLOADS_CONTEXT_CLUES))
|
&DOWNLOADS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[1].config, None);
|
assert_eq!(sonarr_data.main_tabs.tabs[1].config, None);
|
||||||
|
|
||||||
@@ -147,10 +147,10 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[2].route,
|
sonarr_data.main_tabs.tabs[2].route,
|
||||||
ActiveSonarrBlock::Blocklist.into()
|
ActiveSonarrBlock::Blocklist.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[2].help.is_empty());
|
assert!(sonarr_data.main_tabs.tabs[2].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[2].contextual_help,
|
sonarr_data.main_tabs.tabs[2].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&BLOCKLIST_CONTEXT_CLUES))
|
&BLOCKLIST_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[2].config, None);
|
assert_eq!(sonarr_data.main_tabs.tabs[2].config, None);
|
||||||
|
|
||||||
@@ -159,10 +159,10 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[3].route,
|
sonarr_data.main_tabs.tabs[3].route,
|
||||||
ActiveSonarrBlock::History.into()
|
ActiveSonarrBlock::History.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[3].help.is_empty());
|
assert!(sonarr_data.main_tabs.tabs[3].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[3].contextual_help,
|
sonarr_data.main_tabs.tabs[3].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&HISTORY_CONTEXT_CLUES))
|
&HISTORY_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[3].config, None);
|
assert_eq!(sonarr_data.main_tabs.tabs[3].config, None);
|
||||||
|
|
||||||
@@ -171,10 +171,10 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[4].route,
|
sonarr_data.main_tabs.tabs[4].route,
|
||||||
ActiveSonarrBlock::RootFolders.into()
|
ActiveSonarrBlock::RootFolders.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[4].help.is_empty());
|
assert!(sonarr_data.main_tabs.tabs[4].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[4].contextual_help,
|
sonarr_data.main_tabs.tabs[4].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&ROOT_FOLDERS_CONTEXT_CLUES))
|
&ROOT_FOLDERS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[4].config, None);
|
assert_eq!(sonarr_data.main_tabs.tabs[4].config, None);
|
||||||
|
|
||||||
@@ -183,10 +183,10 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[5].route,
|
sonarr_data.main_tabs.tabs[5].route,
|
||||||
ActiveSonarrBlock::Indexers.into()
|
ActiveSonarrBlock::Indexers.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[5].help.is_empty());
|
assert!(sonarr_data.main_tabs.tabs[5].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[5].contextual_help,
|
sonarr_data.main_tabs.tabs[5].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&INDEXERS_CONTEXT_CLUES))
|
&INDEXERS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[5].config, None);
|
assert_eq!(sonarr_data.main_tabs.tabs[5].config, None);
|
||||||
|
|
||||||
@@ -195,10 +195,10 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[6].route,
|
sonarr_data.main_tabs.tabs[6].route,
|
||||||
ActiveSonarrBlock::System.into()
|
ActiveSonarrBlock::System.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[6].help.is_empty());
|
assert!(sonarr_data.main_tabs.tabs[6].contextual_help.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[6].contextual_help,
|
sonarr_data.main_tabs.tabs[6].contextual_help.unwrap(),
|
||||||
Some(build_context_clue_string(&SYSTEM_CONTEXT_CLUES))
|
&SYSTEM_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[6].config, None);
|
assert_eq!(sonarr_data.main_tabs.tabs[6].config, None);
|
||||||
|
|
||||||
@@ -209,10 +209,14 @@ mod tests {
|
|||||||
sonarr_data.series_info_tabs.tabs[0].route,
|
sonarr_data.series_info_tabs.tabs[0].route,
|
||||||
ActiveSonarrBlock::SeriesDetails.into()
|
ActiveSonarrBlock::SeriesDetails.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.series_info_tabs.tabs[0].help.is_empty());
|
assert!(sonarr_data.series_info_tabs.tabs[0]
|
||||||
|
.contextual_help
|
||||||
|
.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.series_info_tabs.tabs[0].contextual_help,
|
sonarr_data.series_info_tabs.tabs[0]
|
||||||
Some(build_context_clue_string(&SERIES_DETAILS_CONTEXT_CLUES))
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&SERIES_DETAILS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.series_info_tabs.tabs[0].config, None);
|
assert_eq!(sonarr_data.series_info_tabs.tabs[0].config, None);
|
||||||
|
|
||||||
@@ -221,10 +225,14 @@ mod tests {
|
|||||||
sonarr_data.series_info_tabs.tabs[1].route,
|
sonarr_data.series_info_tabs.tabs[1].route,
|
||||||
ActiveSonarrBlock::SeriesHistory.into()
|
ActiveSonarrBlock::SeriesHistory.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.series_info_tabs.tabs[1].help.is_empty());
|
assert!(sonarr_data.series_info_tabs.tabs[1]
|
||||||
|
.contextual_help
|
||||||
|
.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.series_info_tabs.tabs[1].contextual_help,
|
sonarr_data.series_info_tabs.tabs[1]
|
||||||
Some(build_context_clue_string(&SERIES_HISTORY_CONTEXT_CLUES))
|
.contextual_help
|
||||||
|
.unwrap(),
|
||||||
|
&SERIES_HISTORY_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.series_info_tabs.tabs[1].config, None);
|
assert_eq!(sonarr_data.series_info_tabs.tabs[1].config, None);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,13 @@ pub struct EditIndexerParams {
|
|||||||
pub clear_tags: bool,
|
pub clear_tags: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct KeybindingItem {
|
||||||
|
pub key: String,
|
||||||
|
pub alt_key: String,
|
||||||
|
pub desc: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct HostConfig {
|
pub struct HostConfig {
|
||||||
|
|||||||
+37
-9
@@ -4,15 +4,16 @@ use std::sync::atomic::Ordering;
|
|||||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::style::{Style, Stylize};
|
use ratatui::style::{Style, Stylize};
|
||||||
use ratatui::text::{Line, Text};
|
use ratatui::text::{Line, Text};
|
||||||
use ratatui::widgets::Clear;
|
|
||||||
use ratatui::widgets::Paragraph;
|
use ratatui::widgets::Paragraph;
|
||||||
use ratatui::widgets::Tabs;
|
use ratatui::widgets::Tabs;
|
||||||
use ratatui::widgets::Wrap;
|
use ratatui::widgets::Wrap;
|
||||||
|
use ratatui::widgets::{Clear, Row};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
use sonarr_ui::SonarrUi;
|
use sonarr_ui::SonarrUi;
|
||||||
use utils::layout_block;
|
use utils::layout_block;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_models::KeybindingItem;
|
||||||
use crate::models::{HorizontallyScrollableText, Route, TabState};
|
use crate::models::{HorizontallyScrollableText, Route, TabState};
|
||||||
use crate::ui::radarr_ui::RadarrUi;
|
use crate::ui::radarr_ui::RadarrUi;
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
@@ -22,6 +23,7 @@ use crate::ui::utils::{
|
|||||||
unstyled_title_block,
|
unstyled_title_block,
|
||||||
};
|
};
|
||||||
use crate::ui::widgets::input_box::InputBox;
|
use crate::ui::widgets::input_box::InputBox;
|
||||||
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
use crate::ui::widgets::popup::Size;
|
use crate::ui::widgets::popup::Size;
|
||||||
|
|
||||||
mod builtin_themes;
|
mod builtin_themes;
|
||||||
@@ -79,6 +81,10 @@ pub fn ui(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.keymapping_table.is_some() {
|
||||||
|
draw_help_popup(f, app);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_header_row(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_header_row(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
@@ -88,7 +94,7 @@ fn draw_header_row(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.flex(Flex::SpaceBetween)
|
.flex(Flex::SpaceBetween)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
let help_text = Text::from(app.server_tabs.get_active_tab_help().help());
|
let help_text = Text::from("<?> to open help".help());
|
||||||
|
|
||||||
let titles = app
|
let titles = app
|
||||||
.server_tabs
|
.server_tabs
|
||||||
@@ -138,6 +144,34 @@ pub fn draw_popup(
|
|||||||
popup_fn(f, app, popup_area);
|
popup_fn(f, app, popup_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_help_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
|
let (percent_x, percent_y) = Size::LongNarrowTable.to_percent();
|
||||||
|
let table_area = centered_rect(percent_x, percent_y, f.area());
|
||||||
|
let keymap_row_mapping = |keymap: &KeybindingItem| {
|
||||||
|
Row::new(vec![
|
||||||
|
ratatui::widgets::Cell::from(keymap.key.clone()),
|
||||||
|
ratatui::widgets::Cell::from(keymap.alt_key.clone()),
|
||||||
|
ratatui::widgets::Cell::from(keymap.desc.clone()),
|
||||||
|
])
|
||||||
|
.primary()
|
||||||
|
};
|
||||||
|
let keymapping_table = ManagarrTable::new(
|
||||||
|
Some(app.keymapping_table.as_mut().unwrap()),
|
||||||
|
keymap_row_mapping,
|
||||||
|
)
|
||||||
|
.block(title_block("Keybindings"))
|
||||||
|
.loading(app.is_loading)
|
||||||
|
.headers(["Key", "Alt Key", "Description"])
|
||||||
|
.constraints([
|
||||||
|
Constraint::Ratio(1, 3),
|
||||||
|
Constraint::Ratio(1, 3),
|
||||||
|
Constraint::Ratio(1, 3),
|
||||||
|
]);
|
||||||
|
f.render_widget(Clear, table_area);
|
||||||
|
f.render_widget(background_block(), table_area);
|
||||||
|
f.render_widget(keymapping_table, table_area);
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_tabs(f: &mut Frame<'_>, area: Rect, title: &str, tab_state: &TabState) -> Rect {
|
fn draw_tabs(f: &mut Frame<'_>, area: Rect, title: &str, tab_state: &TabState) -> Rect {
|
||||||
if title.is_empty() {
|
if title.is_empty() {
|
||||||
f.render_widget(layout_block().default(), area);
|
f.render_widget(layout_block().default(), area);
|
||||||
@@ -148,8 +182,6 @@ fn draw_tabs(f: &mut Frame<'_>, area: Rect, title: &str, tab_state: &TabState) -
|
|||||||
let [header_area, content_area] = Layout::vertical([Constraint::Length(1), Constraint::Fill(0)])
|
let [header_area, content_area] = Layout::vertical([Constraint::Length(1), Constraint::Fill(0)])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
let [tabs_area, help_area] =
|
|
||||||
Layout::horizontal([Constraint::Percentage(45), Constraint::Fill(0)]).areas(header_area);
|
|
||||||
|
|
||||||
let titles = tab_state
|
let titles = tab_state
|
||||||
.tabs
|
.tabs
|
||||||
@@ -159,12 +191,8 @@ fn draw_tabs(f: &mut Frame<'_>, area: Rect, title: &str, tab_state: &TabState) -
|
|||||||
.block(borderless_block())
|
.block(borderless_block())
|
||||||
.highlight_style(Style::new().secondary())
|
.highlight_style(Style::new().secondary())
|
||||||
.select(tab_state.index);
|
.select(tab_state.index);
|
||||||
let help = Paragraph::new(Text::from(tab_state.get_active_tab_help().help()))
|
|
||||||
.block(borderless_block())
|
|
||||||
.right_aligned();
|
|
||||||
|
|
||||||
f.render_widget(tabs, tabs_area);
|
f.render_widget(tabs, header_area);
|
||||||
f.render_widget(help, help_area);
|
|
||||||
|
|
||||||
content_area
|
content_area
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,11 +82,6 @@ fn draw_blocklist_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
} else {
|
} else {
|
||||||
app.data.radarr_data.blocklist.current_selection().clone()
|
app.data.radarr_data.blocklist.current_selection().clone()
|
||||||
};
|
};
|
||||||
let blocklist_table_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let blocklist_row_mapping = |blocklist_item: &BlocklistItem| {
|
let blocklist_row_mapping = |blocklist_item: &BlocklistItem| {
|
||||||
let BlocklistItem {
|
let BlocklistItem {
|
||||||
@@ -136,7 +131,6 @@ fn draw_blocklist_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(blocklist_table_footer)
|
|
||||||
.sorting(active_radarr_block == ActiveRadarrBlock::BlocklistSortPrompt)
|
.sorting(active_radarr_block == ActiveRadarrBlock::BlocklistSortPrompt)
|
||||||
.headers([
|
.headers([
|
||||||
"Movie Title",
|
"Movie Title",
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ use ratatui::text::{Line, Text};
|
|||||||
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES};
|
|
||||||
use crate::app::radarr::radarr_context_clues::COLLECTION_DETAILS_CONTEXT_CLUES;
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::radarr_models::CollectionMovie;
|
use crate::models::radarr_models::CollectionMovie;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
@@ -144,10 +142,6 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
"No"
|
"No"
|
||||||
};
|
};
|
||||||
let minimum_availability = collection_selection.minimum_availability.to_display_str();
|
let minimum_availability = collection_selection.minimum_availability.to_display_str();
|
||||||
let help_footer = format!(
|
|
||||||
"<↑↓> scroll table | {}",
|
|
||||||
build_context_clue_string(&COLLECTION_DETAILS_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
|
|
||||||
let collection_description = Text::from(vec![
|
let collection_description = Text::from(vec![
|
||||||
Line::from(vec![
|
Line::from(vec![
|
||||||
@@ -191,7 +185,6 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
.block(layout_block_top_border_with_title(title_style("Movies")))
|
.block(layout_block_top_border_with_title(title_style("Movies")))
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer_alignment(Alignment::Center)
|
.footer_alignment(Alignment::Center)
|
||||||
.footer(Some(help_footer))
|
|
||||||
.headers([
|
.headers([
|
||||||
"✔",
|
"✔",
|
||||||
"Title",
|
"Title",
|
||||||
@@ -220,11 +213,10 @@ fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let title_block = title_block("Overview");
|
let title_block = title_block("Overview");
|
||||||
f.render_widget(title_block, area);
|
f.render_widget(title_block, area);
|
||||||
|
|
||||||
let [paragraph_area, help_area] =
|
let [paragraph_area] = Layout::vertical([Constraint::Percentage(95)])
|
||||||
Layout::vertical([Constraint::Percentage(95), Constraint::Length(1)])
|
.flex(Flex::SpaceBetween)
|
||||||
.flex(Flex::SpaceBetween)
|
.margin(1)
|
||||||
.margin(1)
|
.areas(area);
|
||||||
.areas(area);
|
|
||||||
let overview = Text::from(
|
let overview = Text::from(
|
||||||
app
|
app
|
||||||
.data
|
.data
|
||||||
@@ -235,15 +227,10 @@ fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.overview,
|
.overview,
|
||||||
)
|
)
|
||||||
.default();
|
.default();
|
||||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
|
||||||
|
|
||||||
let paragraph = Paragraph::new(overview)
|
let paragraph = Paragraph::new(overview)
|
||||||
.block(borderless_block())
|
.block(borderless_block())
|
||||||
.wrap(Wrap { trim: false });
|
.wrap(Wrap { trim: false });
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(borderless_block())
|
|
||||||
.centered();
|
|
||||||
|
|
||||||
f.render_widget(paragraph, paragraph_area);
|
f.render_widget(paragraph, paragraph_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use ratatui::layout::{Constraint, Layout, Rect};
|
use ratatui::layout::{Constraint, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
use ratatui::widgets::ListItem;
|
||||||
use ratatui::widgets::{ListItem, Paragraph};
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
|
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
@@ -102,7 +100,7 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>
|
|||||||
let selected_minimum_availability = minimum_availability_list.current_selection();
|
let selected_minimum_availability = minimum_availability_list.current_selection();
|
||||||
let selected_quality_profile = quality_profile_list.current_selection();
|
let selected_quality_profile = quality_profile_list.current_selection();
|
||||||
|
|
||||||
let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, root_folder_area, search_on_add_area, _, buttons_area, help_area] =
|
let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, root_folder_area, search_on_add_area, _, buttons_area] =
|
||||||
Layout::vertical([
|
Layout::vertical([
|
||||||
Constraint::Length(6),
|
Constraint::Length(6),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
@@ -112,7 +110,6 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>
|
|||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
@@ -120,8 +117,6 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>
|
|||||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
.areas(buttons_area);
|
.areas(buttons_area);
|
||||||
|
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
let prompt_paragraph = layout_paragraph_borderless(&collection_overview);
|
let prompt_paragraph = layout_paragraph_borderless(&collection_overview);
|
||||||
let monitored_checkbox = Checkbox::new("Monitored")
|
let monitored_checkbox = Checkbox::new("Monitored")
|
||||||
.highlighted(selected_block == ActiveRadarrBlock::EditCollectionToggleMonitored)
|
.highlighted(selected_block == ActiveRadarrBlock::EditCollectionToggleMonitored)
|
||||||
@@ -163,7 +158,6 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>
|
|||||||
f.render_widget(search_on_add_checkbox, search_on_add_area);
|
f.render_widget(search_on_add_checkbox, search_on_add_area);
|
||||||
f.render_widget(save_button, save_area);
|
f.render_widget(save_button, save_area);
|
||||||
f.render_widget(cancel_button, cancel_area);
|
f.render_widget(cancel_button, cancel_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_edit_collection_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
fn draw_edit_collection_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
|
|||||||
@@ -66,11 +66,6 @@ pub(super) fn draw_collections(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
};
|
};
|
||||||
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
||||||
let content = Some(&mut app.data.radarr_data.collections);
|
let content = Some(&mut app.data.radarr_data.collections);
|
||||||
let collections_table_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let collection_row_mapping = |collection: &Collection| {
|
let collection_row_mapping = |collection: &Collection| {
|
||||||
let number_of_movies = collection.movies.as_ref().unwrap_or(&Vec::new()).len();
|
let number_of_movies = collection.movies.as_ref().unwrap_or(&Vec::new()).len();
|
||||||
collection.title.scroll_left_or_reset(
|
collection.title.scroll_left_or_reset(
|
||||||
@@ -112,7 +107,6 @@ pub(super) fn draw_collections(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
|| app.data.radarr_data.movies.is_empty()
|
|| app.data.radarr_data.movies.is_empty()
|
||||||
|| app.data.radarr_data.quality_profile_map.is_empty(),
|
|| app.data.radarr_data.quality_profile_map.is_empty(),
|
||||||
)
|
)
|
||||||
.footer(collections_table_footer)
|
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.sorting(active_radarr_block == ActiveRadarrBlock::CollectionsSortPrompt)
|
.sorting(active_radarr_block == ActiveRadarrBlock::CollectionsSortPrompt)
|
||||||
.searching(active_radarr_block == ActiveRadarrBlock::SearchCollection)
|
.searching(active_radarr_block == ActiveRadarrBlock::SearchCollection)
|
||||||
|
|||||||
@@ -72,11 +72,6 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
} else {
|
} else {
|
||||||
app.data.radarr_data.downloads.current_selection().clone()
|
app.data.radarr_data.downloads.current_selection().clone()
|
||||||
};
|
};
|
||||||
let downloads_table_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let downloads_row_mapping = |download_record: &DownloadRecord| {
|
let downloads_row_mapping = |download_record: &DownloadRecord| {
|
||||||
let DownloadRecord {
|
let DownloadRecord {
|
||||||
@@ -125,7 +120,6 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(downloads_table_footer)
|
|
||||||
.headers([
|
.headers([
|
||||||
"Title",
|
"Title",
|
||||||
"Percent Complete",
|
"Percent Complete",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
@@ -14,8 +13,6 @@ use crate::ui::widgets::loading_block::LoadingBlock;
|
|||||||
use crate::ui::widgets::popup::Size;
|
use crate::ui::widgets::popup::Size;
|
||||||
use crate::ui::{draw_popup, DrawUi};
|
use crate::ui::{draw_popup, DrawUi};
|
||||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
|
||||||
use ratatui::widgets::Paragraph;
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -45,22 +42,15 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let highlight_yes_no = selected_block == ActiveRadarrBlock::EditIndexerConfirmPrompt;
|
let highlight_yes_no = selected_block == ActiveRadarrBlock::EditIndexerConfirmPrompt;
|
||||||
let edit_indexer_modal_option = &app.data.radarr_data.edit_indexer_modal;
|
let edit_indexer_modal_option = &app.data.radarr_data.edit_indexer_modal;
|
||||||
let protocol = &app.data.radarr_data.indexers.current_selection().protocol;
|
let protocol = &app.data.radarr_data.indexers.current_selection().protocol;
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
|
|
||||||
if edit_indexer_modal_option.is_some() {
|
if edit_indexer_modal_option.is_some() {
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
let edit_indexer_modal = edit_indexer_modal_option.as_ref().unwrap();
|
let edit_indexer_modal = edit_indexer_modal_option.as_ref().unwrap();
|
||||||
|
|
||||||
let [_, settings_area, _, buttons_area, help_area] = Layout::vertical([
|
let [settings_area, buttons_area] =
|
||||||
Constraint::Fill(1),
|
Layout::vertical([Constraint::Fill(1), Constraint::Length(3)])
|
||||||
Constraint::Length(18),
|
.margin(1)
|
||||||
Constraint::Fill(1),
|
.areas(area);
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
|
||||||
.margin(1)
|
|
||||||
.areas(area);
|
|
||||||
let [left_side_area, right_side_area] =
|
let [left_side_area, right_side_area] =
|
||||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
@@ -169,7 +159,6 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
f.render_widget(interactive_search_checkbox, interactive_search_area);
|
f.render_widget(interactive_search_checkbox, interactive_search_area);
|
||||||
f.render_widget(save_button, save_area);
|
f.render_widget(save_button, save_area);
|
||||||
f.render_widget(cancel_button, cancel_area);
|
f.render_widget(cancel_button, cancel_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
|
||||||
use ratatui::widgets::Paragraph;
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS,
|
ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS,
|
||||||
@@ -52,22 +49,15 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
|||||||
let selected_block = app.data.radarr_data.selected_block.get_active_block();
|
let selected_block = app.data.radarr_data.selected_block.get_active_block();
|
||||||
let highlight_yes_no = selected_block == ActiveRadarrBlock::IndexerSettingsConfirmPrompt;
|
let highlight_yes_no = selected_block == ActiveRadarrBlock::IndexerSettingsConfirmPrompt;
|
||||||
let indexer_settings_option = &app.data.radarr_data.indexer_settings;
|
let indexer_settings_option = &app.data.radarr_data.indexer_settings;
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
|
|
||||||
if indexer_settings_option.is_some() {
|
if indexer_settings_option.is_some() {
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
let indexer_settings = indexer_settings_option.as_ref().unwrap();
|
let indexer_settings = indexer_settings_option.as_ref().unwrap();
|
||||||
|
|
||||||
let [_, settings_area, _, buttons_area, help_area] = Layout::vertical([
|
let [settings_area, buttons_area] =
|
||||||
Constraint::Fill(1),
|
Layout::vertical([Constraint::Fill(1), Constraint::Length(3)])
|
||||||
Constraint::Length(15),
|
.margin(1)
|
||||||
Constraint::Fill(1),
|
.areas(area);
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
|
||||||
.margin(1)
|
|
||||||
.areas(area);
|
|
||||||
let [left_side_area, right_side_area] =
|
let [left_side_area, right_side_area] =
|
||||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
@@ -167,7 +157,6 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
|||||||
f.render_widget(allow_hardcoded_subs_checkbox, allow_hardcoded_subs_area);
|
f.render_widget(allow_hardcoded_subs_checkbox, allow_hardcoded_subs_area);
|
||||||
f.render_widget(save_button, save_area);
|
f.render_widget(save_button, save_area);
|
||||||
f.render_widget(cancel_button, cancel_area);
|
f.render_widget(cancel_button, cancel_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
} else {
|
} else {
|
||||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,17 +157,11 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
])
|
])
|
||||||
.primary()
|
.primary()
|
||||||
};
|
};
|
||||||
let indexers_table_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let indexers_table = ManagarrTable::new(
|
let indexers_table = ManagarrTable::new(
|
||||||
Some(&mut app.data.radarr_data.indexers),
|
Some(&mut app.data.radarr_data.indexers),
|
||||||
indexers_row_mapping,
|
indexers_row_mapping,
|
||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.footer(indexers_table_footer)
|
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.headers([
|
.headers([
|
||||||
"Indexer",
|
"Indexer",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
@@ -43,10 +42,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
IndexerTestResultModalItem::default()
|
IndexerTestResultModalItem::default()
|
||||||
};
|
};
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
let help_footer = format!(
|
|
||||||
"<↑↓> scroll | {}",
|
|
||||||
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
let test_results_row_mapping = |result: &IndexerTestResultModalItem| {
|
let test_results_row_mapping = |result: &IndexerTestResultModalItem| {
|
||||||
result.validation_failures.scroll_left_or_reset(
|
result.validation_failures.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 86),
|
get_width_from_percentage(area, 86),
|
||||||
@@ -72,7 +67,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
test_results_row_mapping,
|
test_results_row_mapping,
|
||||||
)
|
)
|
||||||
.loading(is_loading)
|
.loading(is_loading)
|
||||||
.footer(Some(help_footer))
|
|
||||||
.footer_alignment(Alignment::Center)
|
.footer_alignment(Alignment::Center)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Constraint, Layout, Rect};
|
use ratatui::layout::{Constraint, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
use ratatui::widgets::{Cell, ListItem, Row};
|
||||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{
|
|
||||||
build_context_clue_string, BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
|
||||||
};
|
|
||||||
use crate::app::radarr::radarr_context_clues::ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES;
|
|
||||||
use crate::models::radarr_models::AddMovieSearchResult;
|
use crate::models::radarr_models::AddMovieSearchResult;
|
||||||
use crate::models::servarr_data::radarr::modals::AddMovieModal;
|
use crate::models::servarr_data::radarr::modals::AddMovieModal;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ADD_MOVIE_BLOCKS};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ADD_MOVIE_BLOCKS};
|
||||||
@@ -16,8 +11,7 @@ use crate::models::Route;
|
|||||||
use crate::ui::radarr_ui::collections::CollectionsUi;
|
use crate::ui::radarr_ui::collections::CollectionsUi;
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{
|
use crate::ui::utils::{
|
||||||
borderless_block, get_width_from_percentage, layout_block, layout_paragraph_borderless,
|
get_width_from_percentage, layout_block, layout_paragraph_borderless, title_block_centered,
|
||||||
title_block_centered,
|
|
||||||
};
|
};
|
||||||
use crate::ui::widgets::button::Button;
|
use crate::ui::widgets::button::Button;
|
||||||
use crate::ui::widgets::input_box::InputBox;
|
use crate::ui::widgets::input_box::InputBox;
|
||||||
@@ -83,13 +77,10 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
AddMovieSearchResult::default()
|
AddMovieSearchResult::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let [search_box_area, results_area, help_area] = Layout::vertical([
|
let [search_box_area, results_area] =
|
||||||
Constraint::Length(3),
|
Layout::vertical([Constraint::Length(3), Constraint::Fill(0)])
|
||||||
Constraint::Fill(0),
|
.margin(1)
|
||||||
Constraint::Length(3),
|
.areas(area);
|
||||||
])
|
|
||||||
.margin(1)
|
|
||||||
.areas(area);
|
|
||||||
let block_content = &app.data.radarr_data.add_movie_search.as_ref().unwrap().text;
|
let block_content = &app.data.radarr_data.add_movie_search.as_ref().unwrap().text;
|
||||||
let offset = app
|
let offset = app
|
||||||
.data
|
.data
|
||||||
@@ -164,27 +155,17 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let search_box = InputBox::new(block_content)
|
let search_box = InputBox::new(block_content)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.block(title_block_centered("Add Movie"));
|
.block(title_block_centered("Add Movie"));
|
||||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(borderless_block())
|
|
||||||
.centered();
|
|
||||||
|
|
||||||
search_box.show_cursor(f, search_box_area);
|
search_box.show_cursor(f, search_box_area);
|
||||||
f.render_widget(layout_block().default(), results_area);
|
f.render_widget(layout_block().default(), results_area);
|
||||||
f.render_widget(search_box, search_box_area);
|
f.render_widget(search_box, search_box_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::AddMovieEmptySearchResults => {
|
ActiveRadarrBlock::AddMovieEmptySearchResults => {
|
||||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(borderless_block())
|
|
||||||
.centered();
|
|
||||||
let error_message = Message::new("No movies found matching your query!");
|
let error_message = Message::new("No movies found matching your query!");
|
||||||
let error_message_popup = Popup::new(error_message).size(Size::Message);
|
let error_message_popup = Popup::new(error_message).size(Size::Message);
|
||||||
|
|
||||||
f.render_widget(layout_block().default(), results_area);
|
f.render_widget(layout_block().default(), results_area);
|
||||||
f.render_widget(error_message_popup, f.area());
|
f.render_widget(error_message_popup, f.area());
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::AddMovieSearchResults
|
ActiveRadarrBlock::AddMovieSearchResults
|
||||||
| ActiveRadarrBlock::AddMoviePrompt
|
| ActiveRadarrBlock::AddMoviePrompt
|
||||||
@@ -194,11 +175,6 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
| ActiveRadarrBlock::AddMovieSelectRootFolder
|
| ActiveRadarrBlock::AddMovieSelectRootFolder
|
||||||
| ActiveRadarrBlock::AddMovieAlreadyInLibrary
|
| ActiveRadarrBlock::AddMovieAlreadyInLibrary
|
||||||
| ActiveRadarrBlock::AddMovieTagsInput => {
|
| ActiveRadarrBlock::AddMovieTagsInput => {
|
||||||
let help_text =
|
|
||||||
Text::from(build_context_clue_string(&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(borderless_block())
|
|
||||||
.centered();
|
|
||||||
let search_results_table = ManagarrTable::new(
|
let search_results_table = ManagarrTable::new(
|
||||||
app.data.radarr_data.add_searched_movies.as_mut(),
|
app.data.radarr_data.add_searched_movies.as_mut(),
|
||||||
search_results_row_mapping,
|
search_results_row_mapping,
|
||||||
@@ -225,7 +201,6 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
f.render_widget(search_results_table, results_area);
|
f.render_widget(search_results_table, results_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@@ -322,7 +297,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
f.render_widget(title_block_centered(&title), area);
|
f.render_widget(title_block_centered(&title), area);
|
||||||
|
|
||||||
let [paragraph_area, root_folder_area, monitor_area, min_availability_area, quality_profile_area, tags_area, _, buttons_area, help_area] =
|
let [paragraph_area, root_folder_area, monitor_area, min_availability_area, quality_profile_area, tags_area, _, buttons_area] =
|
||||||
Layout::vertical([
|
Layout::vertical([
|
||||||
Constraint::Length(6),
|
Constraint::Length(6),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
@@ -332,16 +307,12 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
|
|
||||||
let prompt_paragraph = layout_paragraph_borderless(&prompt);
|
let prompt_paragraph = layout_paragraph_borderless(&prompt);
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
f.render_widget(prompt_paragraph, paragraph_area);
|
f.render_widget(prompt_paragraph, paragraph_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
|
|
||||||
let [add_area, cancel_area] =
|
let [add_area, cancel_area] =
|
||||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ use std::sync::atomic::Ordering;
|
|||||||
|
|
||||||
use ratatui::layout::{Constraint, Rect};
|
use ratatui::layout::{Constraint, Rect};
|
||||||
use ratatui::prelude::Layout;
|
use ratatui::prelude::Layout;
|
||||||
use ratatui::text::Text;
|
use ratatui::widgets::ListItem;
|
||||||
use ratatui::widgets::{ListItem, Paragraph};
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::radarr::modals::EditMovieModal;
|
use crate::models::servarr_data::radarr::modals::EditMovieModal;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
@@ -94,7 +92,7 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
let selected_minimum_availability = minimum_availability_list.current_selection();
|
let selected_minimum_availability = minimum_availability_list.current_selection();
|
||||||
let selected_quality_profile = quality_profile_list.current_selection();
|
let selected_quality_profile = quality_profile_list.current_selection();
|
||||||
|
|
||||||
let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, path_area, tags_area, _, buttons_area, help_area] =
|
let [paragraph_area, monitored_area, min_availability_area, quality_profile_area, path_area, tags_area, _, buttons_area] =
|
||||||
Layout::vertical([
|
Layout::vertical([
|
||||||
Constraint::Length(6),
|
Constraint::Length(6),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
@@ -104,7 +102,6 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
@@ -112,8 +109,6 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
.areas(buttons_area);
|
.areas(buttons_area);
|
||||||
|
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
let prompt_paragraph = layout_paragraph_borderless(&movie_overview);
|
let prompt_paragraph = layout_paragraph_borderless(&movie_overview);
|
||||||
let monitored_checkbox = Checkbox::new("Monitored")
|
let monitored_checkbox = Checkbox::new("Monitored")
|
||||||
.checked(monitored.unwrap_or_default())
|
.checked(monitored.unwrap_or_default())
|
||||||
@@ -164,7 +159,6 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
f.render_widget(quality_profile_drop_down_button, quality_profile_area);
|
f.render_widget(quality_profile_drop_down_button, quality_profile_area);
|
||||||
f.render_widget(save_button, save_area);
|
f.render_widget(save_button, save_area);
|
||||||
f.render_widget(cancel_button, cancel_area);
|
f.render_widget(cancel_button, cancel_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_edit_movie_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
fn draw_edit_movie_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
|
|||||||
@@ -85,11 +85,6 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let tags_map = &app.data.radarr_data.tags_map;
|
let tags_map = &app.data.radarr_data.tags_map;
|
||||||
let downloads_vec = &app.data.radarr_data.downloads.items;
|
let downloads_vec = &app.data.radarr_data.downloads.items;
|
||||||
let content = Some(&mut app.data.radarr_data.movies);
|
let content = Some(&mut app.data.radarr_data.movies);
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let library_table_row_mapping = |movie: &Movie| {
|
let library_table_row_mapping = |movie: &Movie| {
|
||||||
movie.title.scroll_left_or_reset(
|
movie.title.scroll_left_or_reset(
|
||||||
@@ -143,7 +138,6 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let library_table = ManagarrTable::new(content, library_table_row_mapping)
|
let library_table = ManagarrTable::new(content, library_table_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
|
||||||
.sorting(active_radarr_block == ActiveRadarrBlock::MoviesSortPrompt)
|
.sorting(active_radarr_block == ActiveRadarrBlock::MoviesSortPrompt)
|
||||||
.searching(active_radarr_block == ActiveRadarrBlock::SearchMovie)
|
.searching(active_radarr_block == ActiveRadarrBlock::SearchMovie)
|
||||||
.search_produced_empty_results(active_radarr_block == ActiveRadarrBlock::SearchMovieError)
|
.search_produced_empty_results(active_radarr_block == ActiveRadarrBlock::SearchMovieError)
|
||||||
|
|||||||
@@ -259,18 +259,12 @@ fn draw_movie_history(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
])
|
])
|
||||||
.primary()
|
.primary()
|
||||||
};
|
};
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.movie_info_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let history_table = ManagarrTable::new(
|
let history_table = ManagarrTable::new(
|
||||||
Some(&mut movie_details_modal.movie_history),
|
Some(&mut movie_details_modal.movie_history),
|
||||||
history_row_mapping,
|
history_row_mapping,
|
||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
|
||||||
.headers(["Source Title", "Event Type", "Languages", "Quality", "Date"])
|
.headers(["Source Title", "Event Type", "Languages", "Quality", "Date"])
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Percentage(34),
|
Constraint::Percentage(34),
|
||||||
@@ -301,14 +295,8 @@ fn draw_movie_cast(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.primary()
|
.primary()
|
||||||
};
|
};
|
||||||
let content = Some(&mut movie_details_modal.movie_cast);
|
let content = Some(&mut movie_details_modal.movie_cast);
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.movie_info_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let cast_table = ManagarrTable::new(content, cast_row_mapping)
|
let cast_table = ManagarrTable::new(content, cast_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.footer(help_footer)
|
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.headers(["Cast Member", "Character"])
|
.headers(["Cast Member", "Character"])
|
||||||
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
|
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
|
||||||
@@ -344,17 +332,11 @@ fn draw_movie_crew(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.primary()
|
.primary()
|
||||||
};
|
};
|
||||||
let content = Some(&mut movie_details_modal.movie_crew);
|
let content = Some(&mut movie_details_modal.movie_crew);
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.movie_info_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let crew_table = ManagarrTable::new(content, crew_row_mapping)
|
let crew_table = ManagarrTable::new(content, crew_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.headers(["Crew Member", "Job", "Department"])
|
.headers(["Crew Member", "Job", "Department"])
|
||||||
.constraints(iter::repeat_n(Constraint::Ratio(1, 3), 3))
|
.constraints(iter::repeat_n(Constraint::Ratio(1, 3), 3));
|
||||||
.footer(help_footer);
|
|
||||||
|
|
||||||
f.render_widget(crew_table, area);
|
f.render_widget(crew_table, area);
|
||||||
}
|
}
|
||||||
@@ -383,11 +365,6 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
};
|
};
|
||||||
let current_route = app.get_current_route();
|
let current_route = app.get_current_route();
|
||||||
let mut default_movie_details_modal = MovieDetailsModal::default();
|
let mut default_movie_details_modal = MovieDetailsModal::default();
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.movie_info_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let content = Some(
|
let content = Some(
|
||||||
&mut app
|
&mut app
|
||||||
.data
|
.data
|
||||||
@@ -464,7 +441,6 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let releases_table = ManagarrTable::new(content, releases_row_mapping)
|
let releases_table = ManagarrTable::new(content, releases_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading || is_empty)
|
.loading(app.is_loading || is_empty)
|
||||||
.footer(help_footer)
|
|
||||||
.sorting(active_radarr_block == ActiveRadarrBlock::ManualSearchSortPrompt)
|
.sorting(active_radarr_block == ActiveRadarrBlock::ManualSearchSortPrompt)
|
||||||
.headers([
|
.headers([
|
||||||
"Source", "Age", "⛔", "Title", "Indexer", "Size", "Peers", "Language", "Quality",
|
"Source", "Age", "⛔", "Title", "Indexer", "Size", "Peers", "Language", "Quality",
|
||||||
|
|||||||
@@ -59,11 +59,6 @@ impl DrawUi for RootFoldersUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let root_folders_row_mapping = |root_folders: &RootFolder| {
|
let root_folders_row_mapping = |root_folders: &RootFolder| {
|
||||||
let RootFolder {
|
let RootFolder {
|
||||||
path,
|
path,
|
||||||
@@ -94,7 +89,6 @@ fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
|
||||||
.headers(["Path", "Free Space", "Unmapped Folders"])
|
.headers(["Path", "Free Space", "Unmapped Folders"])
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Ratio(3, 5),
|
Constraint::Ratio(3, 5),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use chrono::Utc;
|
|||||||
use ratatui::layout::Layout;
|
use ratatui::layout::Layout;
|
||||||
use ratatui::style::Style;
|
use ratatui::style::Style;
|
||||||
use ratatui::text::{Span, Text};
|
use ratatui::text::{Span, Text};
|
||||||
use ratatui::widgets::{Cell, Paragraph, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Rect},
|
layout::{Constraint, Rect},
|
||||||
widgets::ListItem,
|
widgets::ListItem,
|
||||||
@@ -17,9 +17,7 @@ use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
|||||||
use crate::models::servarr_models::QueueEvent;
|
use crate::models::servarr_models::QueueEvent;
|
||||||
use crate::ui::radarr_ui::system::system_details_ui::SystemDetailsUi;
|
use crate::ui::radarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{
|
use crate::ui::utils::{convert_to_minutes_hours_days, style_log_list_item};
|
||||||
convert_to_minutes_hours_days, layout_block_top_border, style_log_list_item,
|
|
||||||
};
|
|
||||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
use crate::ui::widgets::selectable_list::SelectableList;
|
use crate::ui::widgets::selectable_list::SelectableList;
|
||||||
@@ -72,12 +70,8 @@ impl DrawUi for SystemUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
let [activities_area, logs_area, help_area] = Layout::vertical([
|
let [activities_area, logs_area] =
|
||||||
Constraint::Ratio(1, 2),
|
Layout::vertical([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(area);
|
||||||
Constraint::Ratio(1, 2),
|
|
||||||
Constraint::Min(2),
|
|
||||||
])
|
|
||||||
.areas(area);
|
|
||||||
|
|
||||||
let [tasks_area, events_area] =
|
let [tasks_area, events_area] =
|
||||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(activities_area);
|
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(activities_area);
|
||||||
@@ -85,7 +79,6 @@ fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
draw_tasks(f, app, tasks_area);
|
draw_tasks(f, app, tasks_area);
|
||||||
draw_queued_events(f, app, events_area);
|
draw_queued_events(f, app, events_area);
|
||||||
draw_logs(f, app, logs_area);
|
draw_logs(f, app, logs_area);
|
||||||
draw_help(f, app, help_area);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
@@ -188,26 +181,6 @@ fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
f.render_widget(logs_box, area);
|
f.render_widget(logs_box, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_help(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|
||||||
let help_text = Text::from(
|
|
||||||
format!(
|
|
||||||
" {}",
|
|
||||||
app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help()
|
|
||||||
.unwrap()
|
|
||||||
)
|
|
||||||
.help(),
|
|
||||||
);
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(layout_block_top_border())
|
|
||||||
.left_aligned();
|
|
||||||
|
|
||||||
f.render_widget(help_paragraph, area);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct TaskProps {
|
pub(super) struct TaskProps {
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
pub(super) interval: String,
|
pub(super) interval: String,
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ use ratatui::text::{Span, Text};
|
|||||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES};
|
|
||||||
use crate::app::radarr::radarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES;
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::radarr_models::RadarrTask;
|
use crate::models::radarr_models::RadarrTask;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS};
|
||||||
@@ -59,17 +57,10 @@ impl DrawUi for SystemDetailsUi {
|
|||||||
|
|
||||||
fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
let block = title_block("Log Details");
|
let block = title_block("Log Details");
|
||||||
let help_footer = format!(
|
|
||||||
"<↑↓←→> scroll | {}",
|
|
||||||
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
|
|
||||||
if app.data.radarr_data.log_details.items.is_empty() {
|
if app.data.radarr_data.log_details.items.is_empty() {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading)
|
let popup = Popup::new(loading).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
return;
|
return;
|
||||||
@@ -82,16 +73,12 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||||
})
|
})
|
||||||
.block(borderless_block());
|
.block(borderless_block());
|
||||||
let popup = Popup::new(logs_list)
|
let popup = Popup::new(logs_list).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
let help_footer = Some(build_context_clue_string(&SYSTEM_TASKS_CONTEXT_CLUES));
|
|
||||||
let tasks_row_mapping = |task: &RadarrTask| {
|
let tasks_row_mapping = |task: &RadarrTask| {
|
||||||
let task_props = extract_task_props(task);
|
let task_props = extract_task_props(task);
|
||||||
|
|
||||||
@@ -107,7 +94,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping)
|
let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping)
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.footer(help_footer)
|
|
||||||
.footer_alignment(Alignment::Center)
|
.footer_alignment(Alignment::Center)
|
||||||
.headers(TASK_TABLE_HEADERS)
|
.headers(TASK_TABLE_HEADERS)
|
||||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||||
@@ -136,10 +122,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
let help_footer = format!(
|
|
||||||
"<↑↓> scroll | {}",
|
|
||||||
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
let updates = app.data.radarr_data.updates.get_text();
|
let updates = app.data.radarr_data.updates.get_text();
|
||||||
let block = title_block("Updates");
|
let block = title_block("Updates");
|
||||||
|
|
||||||
@@ -147,18 +129,12 @@ fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
let updates_paragraph = Paragraph::new(Text::from(updates))
|
let updates_paragraph = Paragraph::new(Text::from(updates))
|
||||||
.block(borderless_block())
|
.block(borderless_block())
|
||||||
.scroll((app.data.radarr_data.updates.offset, 0));
|
.scroll((app.data.radarr_data.updates.offset, 0));
|
||||||
let popup = Popup::new(updates_paragraph)
|
let popup = Popup::new(updates_paragraph).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
} else {
|
} else {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading)
|
let popup = Popup::new(loading).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,12 +77,6 @@ impl DrawUi for BlocklistUi {
|
|||||||
|
|
||||||
fn draw_blocklist_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_blocklist_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let blocklist_table_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let blocklist_row_mapping = |blocklist_item: &BlocklistItem| {
|
let blocklist_row_mapping = |blocklist_item: &BlocklistItem| {
|
||||||
let BlocklistItem {
|
let BlocklistItem {
|
||||||
source_title,
|
source_title,
|
||||||
@@ -115,7 +109,6 @@ fn draw_blocklist_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(blocklist_table_footer)
|
|
||||||
.sorting(active_sonarr_block == ActiveSonarrBlock::BlocklistSortPrompt)
|
.sorting(active_sonarr_block == ActiveSonarrBlock::BlocklistSortPrompt)
|
||||||
.headers([
|
.headers([
|
||||||
"Series Title",
|
"Series Title",
|
||||||
|
|||||||
@@ -72,11 +72,6 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
} else {
|
} else {
|
||||||
app.data.sonarr_data.downloads.current_selection().clone()
|
app.data.sonarr_data.downloads.current_selection().clone()
|
||||||
};
|
};
|
||||||
let downloads_table_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let downloads_row_mapping = |download_record: &DownloadRecord| {
|
let downloads_row_mapping = |download_record: &DownloadRecord| {
|
||||||
let DownloadRecord {
|
let DownloadRecord {
|
||||||
@@ -130,7 +125,6 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(downloads_table_footer)
|
|
||||||
.headers([
|
.headers([
|
||||||
"Title",
|
"Title",
|
||||||
"Percent Complete",
|
"Percent Complete",
|
||||||
|
|||||||
@@ -55,12 +55,6 @@ fn draw_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
app.data.sonarr_data.history.current_selection().clone()
|
app.data.sonarr_data.history.current_selection().clone()
|
||||||
};
|
};
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let history_table_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
||||||
let SonarrHistoryItem {
|
let SonarrHistoryItem {
|
||||||
source_title,
|
source_title,
|
||||||
@@ -96,7 +90,6 @@ fn draw_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
ManagarrTable::new(Some(&mut app.data.sonarr_data.history), history_row_mapping)
|
ManagarrTable::new(Some(&mut app.data.sonarr_data.history), history_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(history_table_footer)
|
|
||||||
.sorting(active_sonarr_block == ActiveSonarrBlock::HistorySortPrompt)
|
.sorting(active_sonarr_block == ActiveSonarrBlock::HistorySortPrompt)
|
||||||
.searching(active_sonarr_block == ActiveSonarrBlock::SearchHistory)
|
.searching(active_sonarr_block == ActiveSonarrBlock::SearchHistory)
|
||||||
.search_produced_empty_results(active_sonarr_block == ActiveSonarrBlock::SearchHistoryError)
|
.search_produced_empty_results(active_sonarr_block == ActiveSonarrBlock::SearchHistoryError)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
@@ -14,8 +13,6 @@ use crate::ui::widgets::loading_block::LoadingBlock;
|
|||||||
use crate::ui::widgets::popup::Size;
|
use crate::ui::widgets::popup::Size;
|
||||||
use crate::ui::{draw_popup, DrawUi};
|
use crate::ui::{draw_popup, DrawUi};
|
||||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
|
||||||
use ratatui::widgets::Paragraph;
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -45,22 +42,15 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let highlight_yes_no = selected_block == ActiveSonarrBlock::EditIndexerConfirmPrompt;
|
let highlight_yes_no = selected_block == ActiveSonarrBlock::EditIndexerConfirmPrompt;
|
||||||
let edit_indexer_modal_option = &app.data.sonarr_data.edit_indexer_modal;
|
let edit_indexer_modal_option = &app.data.sonarr_data.edit_indexer_modal;
|
||||||
let protocol = &app.data.sonarr_data.indexers.current_selection().protocol;
|
let protocol = &app.data.sonarr_data.indexers.current_selection().protocol;
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
|
|
||||||
if edit_indexer_modal_option.is_some() {
|
if edit_indexer_modal_option.is_some() {
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
let edit_indexer_modal = edit_indexer_modal_option.as_ref().unwrap();
|
let edit_indexer_modal = edit_indexer_modal_option.as_ref().unwrap();
|
||||||
|
|
||||||
let [_, settings_area, _, buttons_area, help_area] = Layout::vertical([
|
let [settings_area, buttons_area] =
|
||||||
Constraint::Fill(1),
|
Layout::vertical([Constraint::Fill(1), Constraint::Length(3)])
|
||||||
Constraint::Length(18),
|
.margin(1)
|
||||||
Constraint::Fill(1),
|
.areas(area);
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
|
||||||
.margin(1)
|
|
||||||
.areas(area);
|
|
||||||
let [left_side_area, right_side_area] =
|
let [left_side_area, right_side_area] =
|
||||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
@@ -169,7 +159,6 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
f.render_widget(interactive_search_checkbox, interactive_search_area);
|
f.render_widget(interactive_search_checkbox, interactive_search_area);
|
||||||
f.render_widget(save_button, save_area);
|
f.render_widget(save_button, save_area);
|
||||||
f.render_widget(cancel_button, cancel_area);
|
f.render_widget(cancel_button, cancel_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
|
||||||
use ratatui::widgets::Paragraph;
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{
|
use crate::models::servarr_data::sonarr::sonarr_data::{
|
||||||
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
|
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
|
||||||
@@ -44,14 +41,12 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
|||||||
let selected_block = app.data.sonarr_data.selected_block.get_active_block();
|
let selected_block = app.data.sonarr_data.selected_block.get_active_block();
|
||||||
let highlight_yes_no = selected_block == ActiveSonarrBlock::IndexerSettingsConfirmPrompt;
|
let highlight_yes_no = selected_block == ActiveSonarrBlock::IndexerSettingsConfirmPrompt;
|
||||||
let indexer_settings_option = &app.data.sonarr_data.indexer_settings;
|
let indexer_settings_option = &app.data.sonarr_data.indexer_settings;
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
|
|
||||||
if indexer_settings_option.is_some() {
|
if indexer_settings_option.is_some() {
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
let indexer_settings = indexer_settings_option.as_ref().unwrap();
|
let indexer_settings = indexer_settings_option.as_ref().unwrap();
|
||||||
|
|
||||||
let [_, min_age_area, retention_area, max_size_area, rss_sync_area, _, buttons_area, help_area] =
|
let [_, min_age_area, retention_area, max_size_area, rss_sync_area, _, buttons_area] =
|
||||||
Layout::vertical([
|
Layout::vertical([
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
@@ -60,7 +55,6 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
|||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
@@ -112,7 +106,6 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
|||||||
|
|
||||||
f.render_widget(save_button, save_area);
|
f.render_widget(save_button, save_area);
|
||||||
f.render_widget(cancel_button, cancel_area);
|
f.render_widget(cancel_button, cancel_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
} else {
|
} else {
|
||||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,17 +157,11 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
])
|
])
|
||||||
.primary()
|
.primary()
|
||||||
};
|
};
|
||||||
let indexers_table_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let indexers_table = ManagarrTable::new(
|
let indexers_table = ManagarrTable::new(
|
||||||
Some(&mut app.data.sonarr_data.indexers),
|
Some(&mut app.data.sonarr_data.indexers),
|
||||||
indexers_row_mapping,
|
indexers_row_mapping,
|
||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.footer(indexers_table_footer)
|
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.headers([
|
.headers([
|
||||||
"Indexer",
|
"Indexer",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
@@ -41,10 +40,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
IndexerTestResultModalItem::default()
|
IndexerTestResultModalItem::default()
|
||||||
};
|
};
|
||||||
f.render_widget(title_block("Test All Indexers"), area);
|
f.render_widget(title_block("Test All Indexers"), area);
|
||||||
let help_footer = format!(
|
|
||||||
"<↑↓> scroll | {}",
|
|
||||||
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
let test_results_row_mapping = |result: &IndexerTestResultModalItem| {
|
let test_results_row_mapping = |result: &IndexerTestResultModalItem| {
|
||||||
result.validation_failures.scroll_left_or_reset(
|
result.validation_failures.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 86),
|
get_width_from_percentage(area, 86),
|
||||||
@@ -70,7 +65,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
test_results_row_mapping,
|
test_results_row_mapping,
|
||||||
)
|
)
|
||||||
.loading(is_loading)
|
.loading(is_loading)
|
||||||
.footer(Some(help_footer))
|
|
||||||
.footer_alignment(Alignment::Center)
|
.footer_alignment(Alignment::Center)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Constraint, Layout, Rect};
|
use ratatui::layout::{Constraint, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
use ratatui::widgets::{Cell, ListItem, Row};
|
||||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{
|
|
||||||
build_context_clue_string, BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
|
||||||
};
|
|
||||||
use crate::app::sonarr::sonarr_context_clues::ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES;
|
|
||||||
use crate::models::servarr_data::sonarr::modals::AddSeriesModal;
|
use crate::models::servarr_data::sonarr::modals::AddSeriesModal;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ADD_SERIES_BLOCKS};
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ADD_SERIES_BLOCKS};
|
||||||
use crate::models::sonarr_models::AddSeriesSearchResult;
|
use crate::models::sonarr_models::AddSeriesSearchResult;
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{
|
use crate::ui::utils::{
|
||||||
borderless_block, get_width_from_percentage, layout_block, layout_paragraph_borderless,
|
get_width_from_percentage, layout_block, layout_paragraph_borderless, title_block_centered,
|
||||||
title_block_centered,
|
|
||||||
};
|
};
|
||||||
use crate::ui::widgets::button::Button;
|
use crate::ui::widgets::button::Button;
|
||||||
use crate::ui::widgets::checkbox::Checkbox;
|
use crate::ui::widgets::checkbox::Checkbox;
|
||||||
@@ -78,13 +72,10 @@ fn draw_add_series_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
AddSeriesSearchResult::default()
|
AddSeriesSearchResult::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let [search_box_area, results_area, help_area] = Layout::vertical([
|
let [search_box_area, results_area] =
|
||||||
Constraint::Length(3),
|
Layout::vertical([Constraint::Length(3), Constraint::Fill(0)])
|
||||||
Constraint::Fill(0),
|
.margin(1)
|
||||||
Constraint::Length(3),
|
.areas(area);
|
||||||
])
|
|
||||||
.margin(1)
|
|
||||||
.areas(area);
|
|
||||||
let block_content = &app
|
let block_content = &app
|
||||||
.data
|
.data
|
||||||
.sonarr_data
|
.sonarr_data
|
||||||
@@ -150,27 +141,17 @@ fn draw_add_series_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let search_box = InputBox::new(block_content)
|
let search_box = InputBox::new(block_content)
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.block(title_block_centered("Add Series"));
|
.block(title_block_centered("Add Series"));
|
||||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(borderless_block())
|
|
||||||
.centered();
|
|
||||||
|
|
||||||
search_box.show_cursor(f, search_box_area);
|
search_box.show_cursor(f, search_box_area);
|
||||||
f.render_widget(layout_block().default(), results_area);
|
f.render_widget(layout_block().default(), results_area);
|
||||||
f.render_widget(search_box, search_box_area);
|
f.render_widget(search_box, search_box_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
ActiveSonarrBlock::AddSeriesEmptySearchResults => {
|
ActiveSonarrBlock::AddSeriesEmptySearchResults => {
|
||||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(borderless_block())
|
|
||||||
.centered();
|
|
||||||
let error_message = Message::new("No series found matching your query!");
|
let error_message = Message::new("No series found matching your query!");
|
||||||
let error_message_popup = Popup::new(error_message).size(Size::Message);
|
let error_message_popup = Popup::new(error_message).size(Size::Message);
|
||||||
|
|
||||||
f.render_widget(layout_block().default(), results_area);
|
f.render_widget(layout_block().default(), results_area);
|
||||||
f.render_widget(error_message_popup, f.area());
|
f.render_widget(error_message_popup, f.area());
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
ActiveSonarrBlock::AddSeriesSearchResults
|
ActiveSonarrBlock::AddSeriesSearchResults
|
||||||
| ActiveSonarrBlock::AddSeriesPrompt
|
| ActiveSonarrBlock::AddSeriesPrompt
|
||||||
@@ -181,11 +162,6 @@ fn draw_add_series_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
| ActiveSonarrBlock::AddSeriesSelectRootFolder
|
| ActiveSonarrBlock::AddSeriesSelectRootFolder
|
||||||
| ActiveSonarrBlock::AddSeriesAlreadyInLibrary
|
| ActiveSonarrBlock::AddSeriesAlreadyInLibrary
|
||||||
| ActiveSonarrBlock::AddSeriesTagsInput => {
|
| ActiveSonarrBlock::AddSeriesTagsInput => {
|
||||||
let help_text =
|
|
||||||
Text::from(build_context_clue_string(&ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(borderless_block())
|
|
||||||
.centered();
|
|
||||||
let search_results_table = ManagarrTable::new(
|
let search_results_table = ManagarrTable::new(
|
||||||
app.data.sonarr_data.add_searched_series.as_mut(),
|
app.data.sonarr_data.add_searched_series.as_mut(),
|
||||||
search_results_row_mapping,
|
search_results_row_mapping,
|
||||||
@@ -206,7 +182,6 @@ fn draw_add_series_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
f.render_widget(search_results_table, results_area);
|
f.render_widget(search_results_table, results_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@@ -297,7 +272,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
f.render_widget(title_block_centered(&title), area);
|
f.render_widget(title_block_centered(&title), area);
|
||||||
|
|
||||||
let [paragraph_area, root_folder_area, monitor_area, quality_profile_area, language_profile_area, series_type_area, season_folder_area, tags_area, _, buttons_area, help_area] =
|
let [paragraph_area, root_folder_area, monitor_area, quality_profile_area, language_profile_area, series_type_area, season_folder_area, tags_area, _, buttons_area] =
|
||||||
Layout::vertical([
|
Layout::vertical([
|
||||||
Constraint::Length(6),
|
Constraint::Length(6),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
@@ -309,16 +284,12 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
|
|
||||||
let prompt_paragraph = layout_paragraph_borderless(&prompt);
|
let prompt_paragraph = layout_paragraph_borderless(&prompt);
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
f.render_widget(prompt_paragraph, paragraph_area);
|
f.render_widget(prompt_paragraph, paragraph_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
|
|
||||||
let [add_area, cancel_area] =
|
let [add_area, cancel_area] =
|
||||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ use std::sync::atomic::Ordering;
|
|||||||
|
|
||||||
use ratatui::layout::{Constraint, Rect};
|
use ratatui::layout::{Constraint, Rect};
|
||||||
use ratatui::prelude::Layout;
|
use ratatui::prelude::Layout;
|
||||||
use ratatui::text::Text;
|
use ratatui::widgets::ListItem;
|
||||||
use ratatui::widgets::{ListItem, Paragraph};
|
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::sonarr::modals::EditSeriesModal;
|
use crate::models::servarr_data::sonarr::modals::EditSeriesModal;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{
|
use crate::models::servarr_data::sonarr::sonarr_data::{
|
||||||
@@ -107,7 +105,7 @@ fn draw_edit_series_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, ar
|
|||||||
let selected_quality_profile = quality_profile_list.current_selection();
|
let selected_quality_profile = quality_profile_list.current_selection();
|
||||||
let selected_language_profile = language_profile_list.current_selection();
|
let selected_language_profile = language_profile_list.current_selection();
|
||||||
|
|
||||||
let [paragraph_area, monitored_area, season_folder_area, quality_profile_area, language_profile_area, series_type_area, path_area, tags_area, _, buttons_area, help_area] =
|
let [paragraph_area, monitored_area, season_folder_area, quality_profile_area, language_profile_area, series_type_area, path_area, tags_area, _, buttons_area] =
|
||||||
Layout::vertical([
|
Layout::vertical([
|
||||||
Constraint::Length(6),
|
Constraint::Length(6),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
@@ -119,7 +117,6 @@ fn draw_edit_series_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, ar
|
|||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
@@ -127,8 +124,6 @@ fn draw_edit_series_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, ar
|
|||||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
.areas(buttons_area);
|
.areas(buttons_area);
|
||||||
|
|
||||||
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
let prompt_paragraph = layout_paragraph_borderless(&series_overview);
|
let prompt_paragraph = layout_paragraph_borderless(&series_overview);
|
||||||
let monitored_checkbox = Checkbox::new("Monitored")
|
let monitored_checkbox = Checkbox::new("Monitored")
|
||||||
.checked(monitored.unwrap_or_default())
|
.checked(monitored.unwrap_or_default())
|
||||||
@@ -189,7 +184,6 @@ fn draw_edit_series_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, ar
|
|||||||
f.render_widget(series_type_drop_down_button, series_type_area);
|
f.render_widget(series_type_drop_down_button, series_type_area);
|
||||||
f.render_widget(save_button, save_area);
|
f.render_widget(save_button, save_area);
|
||||||
f.render_widget(cancel_button, cancel_area);
|
f.render_widget(cancel_button, cancel_area);
|
||||||
f.render_widget(help_paragraph, help_area);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_edit_series_select_series_type_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
fn draw_edit_series_select_series_type_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
|
|||||||
@@ -257,9 +257,6 @@ fn draw_episode_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
.current_selection()
|
.current_selection()
|
||||||
.clone()
|
.clone()
|
||||||
};
|
};
|
||||||
let episode_history_table_footer = episode_details_modal
|
|
||||||
.episode_details_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
||||||
let SonarrHistoryItem {
|
let SonarrHistoryItem {
|
||||||
@@ -306,7 +303,6 @@ fn draw_episode_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
ManagarrTable::new(Some(&mut episode_history_table), history_row_mapping)
|
ManagarrTable::new(Some(&mut episode_history_table), history_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(episode_history_table_footer)
|
|
||||||
.headers(["Source Title", "Event Type", "Language", "Quality", "Date"])
|
.headers(["Source Title", "Event Type", "Language", "Quality", "Date"])
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Percentage(40),
|
Constraint::Percentage(40),
|
||||||
@@ -397,9 +393,6 @@ fn draw_episode_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
episode_details_modal.episode_releases.is_empty(),
|
episode_details_modal.episode_releases.is_empty(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let episode_release_table_footer = episode_details_modal
|
|
||||||
.episode_details_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let episode_release_row_mapping = |release: &SonarrRelease| {
|
let episode_release_row_mapping = |release: &SonarrRelease| {
|
||||||
@@ -483,7 +476,6 @@ fn draw_episode_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading || is_empty)
|
.loading(app.is_loading || is_empty)
|
||||||
.footer(episode_release_table_footer)
|
|
||||||
.sorting(active_sonarr_block == ActiveSonarrBlock::ManualEpisodeSearchSortPrompt)
|
.sorting(active_sonarr_block == ActiveSonarrBlock::ManualEpisodeSearchSortPrompt)
|
||||||
.headers([
|
.headers([
|
||||||
"Source", "Age", "⛔", "Title", "Indexer", "Size", "Peers", "Language", "Quality",
|
"Source", "Age", "⛔", "Title", "Indexer", "Size", "Peers", "Language", "Quality",
|
||||||
|
|||||||
@@ -90,11 +90,6 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let language_profile_map = &app.data.sonarr_data.language_profiles_map;
|
let language_profile_map = &app.data.sonarr_data.language_profiles_map;
|
||||||
let tags_map = &app.data.sonarr_data.tags_map;
|
let tags_map = &app.data.sonarr_data.tags_map;
|
||||||
let content = Some(&mut app.data.sonarr_data.series);
|
let content = Some(&mut app.data.sonarr_data.series);
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
let series_table_row_mapping = |series: &Series| {
|
let series_table_row_mapping = |series: &Series| {
|
||||||
series.title.scroll_left_or_reset(
|
series.title.scroll_left_or_reset(
|
||||||
@@ -154,7 +149,6 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let series_table = ManagarrTable::new(content, series_table_row_mapping)
|
let series_table = ManagarrTable::new(content, series_table_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
|
||||||
.sorting(active_sonarr_block == ActiveSonarrBlock::SeriesSortPrompt)
|
.sorting(active_sonarr_block == ActiveSonarrBlock::SeriesSortPrompt)
|
||||||
.searching(active_sonarr_block == ActiveSonarrBlock::SearchSeries)
|
.searching(active_sonarr_block == ActiveSonarrBlock::SearchSeries)
|
||||||
.filtering(active_sonarr_block == ActiveSonarrBlock::FilterSeries)
|
.filtering(active_sonarr_block == ActiveSonarrBlock::FilterSeries)
|
||||||
|
|||||||
@@ -149,14 +149,6 @@ pub fn draw_season_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
fn draw_episodes_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_episodes_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.season_details_modal
|
|
||||||
.as_ref()
|
|
||||||
.expect("Season details modal is unpopulated")
|
|
||||||
.season_details_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let episode_files = app
|
let episode_files = app
|
||||||
.data
|
.data
|
||||||
.sonarr_data
|
.sonarr_data
|
||||||
@@ -222,7 +214,6 @@ fn draw_episodes_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let season_table = ManagarrTable::new(content, episode_row_mapping)
|
let season_table = ManagarrTable::new(content, episode_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
|
||||||
.searching(is_searching)
|
.searching(is_searching)
|
||||||
.search_produced_empty_results(active_sonarr_block == ActiveSonarrBlock::SearchEpisodesError)
|
.search_produced_empty_results(active_sonarr_block == ActiveSonarrBlock::SearchEpisodesError)
|
||||||
.headers([
|
.headers([
|
||||||
@@ -261,9 +252,6 @@ fn draw_season_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.current_selection()
|
.current_selection()
|
||||||
.clone()
|
.clone()
|
||||||
};
|
};
|
||||||
let season_history_table_footer = season_details_modal
|
|
||||||
.season_details_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
||||||
@@ -308,7 +296,6 @@ fn draw_season_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
ManagarrTable::new(Some(&mut season_history_table), history_row_mapping)
|
ManagarrTable::new(Some(&mut season_history_table), history_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(season_history_table_footer)
|
|
||||||
.sorting(active_sonarr_block == ActiveSonarrBlock::SeasonHistorySortPrompt)
|
.sorting(active_sonarr_block == ActiveSonarrBlock::SeasonHistorySortPrompt)
|
||||||
.searching(active_sonarr_block == ActiveSonarrBlock::SearchSeasonHistory)
|
.searching(active_sonarr_block == ActiveSonarrBlock::SearchSeasonHistory)
|
||||||
.search_produced_empty_results(
|
.search_produced_empty_results(
|
||||||
@@ -363,9 +350,6 @@ fn draw_season_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
season_details_modal.season_releases.is_empty(),
|
season_details_modal.season_releases.is_empty(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let season_release_table_footer = season_details_modal
|
|
||||||
.season_details_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let season_release_row_mapping = |release: &SonarrRelease| {
|
let season_release_row_mapping = |release: &SonarrRelease| {
|
||||||
@@ -444,7 +428,6 @@ fn draw_season_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
ManagarrTable::new(Some(&mut season_release_table), season_release_row_mapping)
|
ManagarrTable::new(Some(&mut season_release_table), season_release_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading || is_empty)
|
.loading(app.is_loading || is_empty)
|
||||||
.footer(season_release_table_footer)
|
|
||||||
.sorting(active_sonarr_block == ActiveSonarrBlock::ManualSeasonSearchSortPrompt)
|
.sorting(active_sonarr_block == ActiveSonarrBlock::ManualSeasonSearchSortPrompt)
|
||||||
.headers([
|
.headers([
|
||||||
"Source", "Age", "⛔", "Title", "Indexer", "Size", "Peers", "Language", "Quality",
|
"Source", "Age", "⛔", "Title", "Indexer", "Size", "Peers", "Language", "Quality",
|
||||||
|
|||||||
@@ -229,11 +229,6 @@ pub fn draw_series_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let content = Some(&mut app.data.sonarr_data.seasons);
|
let content = Some(&mut app.data.sonarr_data.seasons);
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.series_info_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let season_row_mapping = |season: &Season| {
|
let season_row_mapping = |season: &Season| {
|
||||||
let Season {
|
let Season {
|
||||||
title,
|
title,
|
||||||
@@ -279,7 +274,6 @@ fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let season_table = ManagarrTable::new(content, season_row_mapping)
|
let season_table = ManagarrTable::new(content, season_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
|
||||||
.searching(is_searching)
|
.searching(is_searching)
|
||||||
.search_produced_empty_results(active_sonarr_block == ActiveSonarrBlock::SearchSeasonError)
|
.search_produced_empty_results(active_sonarr_block == ActiveSonarrBlock::SearchSeasonError)
|
||||||
.headers(["Monitored", "Season", "Episode Count", "Size on Disk"])
|
.headers(["Monitored", "Season", "Episode Count", "Size on Disk"])
|
||||||
@@ -306,11 +300,6 @@ fn draw_series_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
} else {
|
} else {
|
||||||
series_history.current_selection().clone()
|
series_history.current_selection().clone()
|
||||||
};
|
};
|
||||||
let series_history_table_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.series_info_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
|
|
||||||
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
let history_row_mapping = |history_item: &SonarrHistoryItem| {
|
||||||
@@ -349,7 +338,6 @@ fn draw_series_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
ManagarrTable::new(Some(&mut series_history_table), history_row_mapping)
|
ManagarrTable::new(Some(&mut series_history_table), history_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(series_history_table_footer)
|
|
||||||
.sorting(active_sonarr_block == ActiveSonarrBlock::SeriesHistorySortPrompt)
|
.sorting(active_sonarr_block == ActiveSonarrBlock::SeriesHistorySortPrompt)
|
||||||
.searching(active_sonarr_block == ActiveSonarrBlock::SearchSeriesHistory)
|
.searching(active_sonarr_block == ActiveSonarrBlock::SearchSeriesHistory)
|
||||||
.search_produced_empty_results(
|
.search_produced_empty_results(
|
||||||
|
|||||||
@@ -59,11 +59,6 @@ impl DrawUi for RootFoldersUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
let help_footer = app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help();
|
|
||||||
let root_folders_row_mapping = |root_folders: &RootFolder| {
|
let root_folders_row_mapping = |root_folders: &RootFolder| {
|
||||||
let RootFolder {
|
let RootFolder {
|
||||||
path,
|
path,
|
||||||
@@ -94,7 +89,6 @@ fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
|
||||||
.headers(["Path", "Free Space", "Unmapped Folders"])
|
.headers(["Path", "Free Space", "Unmapped Folders"])
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Ratio(3, 5),
|
Constraint::Ratio(3, 5),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use chrono::Utc;
|
|||||||
use ratatui::layout::Layout;
|
use ratatui::layout::Layout;
|
||||||
use ratatui::style::Style;
|
use ratatui::style::Style;
|
||||||
use ratatui::text::{Span, Text};
|
use ratatui::text::{Span, Text};
|
||||||
use ratatui::widgets::{Cell, Paragraph, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Rect},
|
layout::{Constraint, Rect},
|
||||||
widgets::ListItem,
|
widgets::ListItem,
|
||||||
@@ -17,9 +17,7 @@ use crate::models::servarr_models::QueueEvent;
|
|||||||
use crate::models::sonarr_models::SonarrTask;
|
use crate::models::sonarr_models::SonarrTask;
|
||||||
use crate::ui::sonarr_ui::system::system_details_ui::SystemDetailsUi;
|
use crate::ui::sonarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{
|
use crate::ui::utils::{convert_to_minutes_hours_days, style_log_list_item};
|
||||||
convert_to_minutes_hours_days, layout_block_top_border, style_log_list_item,
|
|
||||||
};
|
|
||||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
use crate::ui::widgets::selectable_list::SelectableList;
|
use crate::ui::widgets::selectable_list::SelectableList;
|
||||||
@@ -66,12 +64,8 @@ impl DrawUi for SystemUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
let [activities_area, logs_area, help_area] = Layout::vertical([
|
let [activities_area, logs_area] =
|
||||||
Constraint::Ratio(1, 2),
|
Layout::vertical([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(area);
|
||||||
Constraint::Ratio(1, 2),
|
|
||||||
Constraint::Min(2),
|
|
||||||
])
|
|
||||||
.areas(area);
|
|
||||||
|
|
||||||
let [tasks_area, events_area] =
|
let [tasks_area, events_area] =
|
||||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(activities_area);
|
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(activities_area);
|
||||||
@@ -79,7 +73,6 @@ fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
draw_tasks(f, app, tasks_area);
|
draw_tasks(f, app, tasks_area);
|
||||||
draw_queued_events(f, app, events_area);
|
draw_queued_events(f, app, events_area);
|
||||||
draw_logs(f, app, logs_area);
|
draw_logs(f, app, logs_area);
|
||||||
draw_help(f, app, help_area);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
@@ -181,26 +174,6 @@ fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
f.render_widget(logs_box, area);
|
f.render_widget(logs_box, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_help(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|
||||||
let help_text = Text::from(
|
|
||||||
format!(
|
|
||||||
" {}",
|
|
||||||
app
|
|
||||||
.data
|
|
||||||
.sonarr_data
|
|
||||||
.main_tabs
|
|
||||||
.get_active_tab_contextual_help()
|
|
||||||
.unwrap()
|
|
||||||
)
|
|
||||||
.help(),
|
|
||||||
);
|
|
||||||
let help_paragraph = Paragraph::new(help_text)
|
|
||||||
.block(layout_block_top_border())
|
|
||||||
.left_aligned();
|
|
||||||
|
|
||||||
f.render_widget(help_paragraph, area);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct TaskProps {
|
pub(super) struct TaskProps {
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
pub(super) interval: String,
|
pub(super) interval: String,
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ use ratatui::text::{Span, Text};
|
|||||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES};
|
|
||||||
use crate::app::sonarr::sonarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES;
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS};
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS};
|
||||||
use crate::models::sonarr_models::SonarrTask;
|
use crate::models::sonarr_models::SonarrTask;
|
||||||
@@ -59,17 +57,10 @@ impl DrawUi for SystemDetailsUi {
|
|||||||
|
|
||||||
fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
let block = title_block("Log Details");
|
let block = title_block("Log Details");
|
||||||
let help_footer = format!(
|
|
||||||
"<↑↓←→> scroll | {}",
|
|
||||||
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
|
|
||||||
if app.data.sonarr_data.log_details.items.is_empty() {
|
if app.data.sonarr_data.log_details.items.is_empty() {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading)
|
let popup = Popup::new(loading).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
return;
|
return;
|
||||||
@@ -82,16 +73,12 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||||
})
|
})
|
||||||
.block(borderless_block());
|
.block(borderless_block());
|
||||||
let popup = Popup::new(logs_list)
|
let popup = Popup::new(logs_list).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
let help_footer = Some(build_context_clue_string(&SYSTEM_TASKS_CONTEXT_CLUES));
|
|
||||||
let tasks_row_mapping = |task: &SonarrTask| {
|
let tasks_row_mapping = |task: &SonarrTask| {
|
||||||
let task_props = extract_task_props(task);
|
let task_props = extract_task_props(task);
|
||||||
|
|
||||||
@@ -106,7 +93,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.sonarr_data.tasks), tasks_row_mapping)
|
let tasks_table = ManagarrTable::new(Some(&mut app.data.sonarr_data.tasks), tasks_row_mapping)
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.footer(help_footer)
|
|
||||||
.footer_alignment(Alignment::Center)
|
.footer_alignment(Alignment::Center)
|
||||||
.headers(TASK_TABLE_HEADERS)
|
.headers(TASK_TABLE_HEADERS)
|
||||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||||
@@ -135,10 +121,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
let help_footer = format!(
|
|
||||||
"<↑↓> scroll | {}",
|
|
||||||
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
|
||||||
);
|
|
||||||
let updates = app.data.sonarr_data.updates.get_text();
|
let updates = app.data.sonarr_data.updates.get_text();
|
||||||
let block = title_block("Updates");
|
let block = title_block("Updates");
|
||||||
|
|
||||||
@@ -146,18 +128,12 @@ fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
let updates_paragraph = Paragraph::new(Text::from(updates))
|
let updates_paragraph = Paragraph::new(Text::from(updates))
|
||||||
.block(borderless_block())
|
.block(borderless_block())
|
||||||
.scroll((app.data.sonarr_data.updates.offset, 0));
|
.scroll((app.data.sonarr_data.updates.offset, 0));
|
||||||
let popup = Popup::new(updates_paragraph)
|
let popup = Popup::new(updates_paragraph).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
} else {
|
} else {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading)
|
let popup = Popup::new(loading).size(Size::Large).block(block);
|
||||||
.size(Size::Large)
|
|
||||||
.block(block)
|
|
||||||
.footer(&help_footer);
|
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{layout_paragraph_borderless, title_block_centered};
|
use crate::ui::utils::{layout_paragraph_borderless, title_block_centered};
|
||||||
use crate::ui::widgets::button::Button;
|
use crate::ui::widgets::button::Button;
|
||||||
@@ -6,7 +5,6 @@ use crate::ui::widgets::checkbox::Checkbox;
|
|||||||
use derive_setters::Setters;
|
use derive_setters::Setters;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
|
||||||
use ratatui::widgets::{Paragraph, Widget};
|
use ratatui::widgets::{Paragraph, Widget};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
@@ -40,16 +38,12 @@ impl ConfirmationPrompt<'_> {
|
|||||||
|
|
||||||
fn render_confirmation_prompt_with_checkboxes(self, area: Rect, buf: &mut Buffer) {
|
fn render_confirmation_prompt_with_checkboxes(self, area: Rect, buf: &mut Buffer) {
|
||||||
title_block_centered(self.title).render(area, buf);
|
title_block_centered(self.title).render(area, buf);
|
||||||
let help_text =
|
|
||||||
Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
|
|
||||||
if let Some(checkboxes) = self.checkboxes {
|
if let Some(checkboxes) = self.checkboxes {
|
||||||
let mut constraints = vec![
|
let mut constraints = vec![
|
||||||
Constraint::Length(4),
|
Constraint::Length(4),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
];
|
];
|
||||||
constraints.splice(
|
constraints.splice(
|
||||||
1..1,
|
1..1,
|
||||||
@@ -61,7 +55,6 @@ impl ConfirmationPrompt<'_> {
|
|||||||
.areas(chunks[checkboxes.len() + 2]);
|
.areas(chunks[checkboxes.len() + 2]);
|
||||||
|
|
||||||
layout_paragraph_borderless(self.prompt).render(chunks[0], buf);
|
layout_paragraph_borderless(self.prompt).render(chunks[0], buf);
|
||||||
help_paragraph.render(chunks[checkboxes.len() + 3], buf);
|
|
||||||
|
|
||||||
checkboxes
|
checkboxes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -83,38 +76,30 @@ impl ConfirmationPrompt<'_> {
|
|||||||
|
|
||||||
fn render_confirmation_prompt(self, area: Rect, buf: &mut Buffer) {
|
fn render_confirmation_prompt(self, area: Rect, buf: &mut Buffer) {
|
||||||
title_block_centered(self.title).render(area, buf);
|
title_block_centered(self.title).render(area, buf);
|
||||||
let help_text =
|
|
||||||
Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
|
||||||
let help_paragraph = Paragraph::new(help_text).centered();
|
|
||||||
|
|
||||||
let [prompt_area, buttons_area] = if let Some(content_paragraph) = self.content {
|
let [prompt_area, buttons_area] = if let Some(content_paragraph) = self.content {
|
||||||
let [prompt_area, content_area, _, buttons_area, help_area] = Layout::vertical([
|
let [prompt_area, content_area, _, buttons_area] = Layout::vertical([
|
||||||
Constraint::Length(4),
|
Constraint::Length(4),
|
||||||
Constraint::Length(7),
|
Constraint::Length(7),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
Constraint::Length(1),
|
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
|
|
||||||
content_paragraph.render(content_area, buf);
|
content_paragraph.render(content_area, buf);
|
||||||
help_paragraph.render(help_area, buf);
|
|
||||||
|
|
||||||
[prompt_area, buttons_area]
|
[prompt_area, buttons_area]
|
||||||
} else {
|
} else {
|
||||||
let [prompt_area, buttons_area, _, help_area] = Layout::vertical([
|
let [prompt_area, _, buttons_area] = Layout::vertical([
|
||||||
Constraint::Percentage(72),
|
Constraint::Percentage(72),
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Fill(0),
|
Constraint::Fill(0),
|
||||||
Constraint::Min(1),
|
Constraint::Length(3),
|
||||||
])
|
])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.flex(Flex::SpaceBetween)
|
.flex(Flex::SpaceBetween)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
|
|
||||||
help_paragraph.render(help_area, buf);
|
|
||||||
|
|
||||||
[prompt_area, buttons_area]
|
[prompt_area, buttons_area]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::utils::{background_block, centered_rect};
|
||||||
use crate::ui::utils::{background_block, borderless_block, centered_rect};
|
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::{Constraint, Layout, Rect};
|
use ratatui::layout::{Constraint, Layout, Rect};
|
||||||
use ratatui::widgets::{Block, Clear, Paragraph, Widget, WidgetRef};
|
use ratatui::widgets::{Block, Clear, Widget, WidgetRef};
|
||||||
|
|
||||||
use super::input_box::InputBox;
|
use super::input_box::InputBox;
|
||||||
|
|
||||||
@@ -39,17 +38,10 @@ impl<'a> InputBoxPopup<'a> {
|
|||||||
Clear.render(popup_area, buf);
|
Clear.render(popup_area, buf);
|
||||||
background_block().render(popup_area, buf);
|
background_block().render(popup_area, buf);
|
||||||
|
|
||||||
let [text_box_area, help_area] =
|
let [text_box_area] = Layout::vertical([Constraint::Length(3)])
|
||||||
Layout::vertical([Constraint::Length(3), Constraint::Length(1)])
|
.margin(1)
|
||||||
.margin(1)
|
.areas(popup_area);
|
||||||
.areas(popup_area);
|
|
||||||
self.input_box.render_ref(text_box_area, buf);
|
self.input_box.render_ref(text_box_area, buf);
|
||||||
|
|
||||||
let help = Paragraph::new("<esc> cancel")
|
|
||||||
.help()
|
|
||||||
.centered()
|
|
||||||
.block(borderless_block());
|
|
||||||
help.render(help_area, buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-29
@@ -1,9 +1,7 @@
|
|||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::utils::{background_block, centered_rect};
|
||||||
use crate::ui::utils::{background_block, centered_rect, layout_block_top_border};
|
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::{Constraint, Layout, Rect};
|
use ratatui::layout::Rect;
|
||||||
use ratatui::prelude::Text;
|
use ratatui::widgets::{Block, Clear, Widget};
|
||||||
use ratatui::widgets::{Block, Clear, Paragraph, Widget};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "popup_tests.rs"]
|
#[path = "popup_tests.rs"]
|
||||||
@@ -25,6 +23,7 @@ pub enum Size {
|
|||||||
XLarge,
|
XLarge,
|
||||||
XXLarge,
|
XXLarge,
|
||||||
Long,
|
Long,
|
||||||
|
LongNarrowTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Size {
|
impl Size {
|
||||||
@@ -45,6 +44,7 @@ impl Size {
|
|||||||
Size::XLarge => (83, 83),
|
Size::XLarge => (83, 83),
|
||||||
Size::XXLarge => (90, 90),
|
Size::XXLarge => (90, 90),
|
||||||
Size::Long => (65, 75),
|
Size::Long => (65, 75),
|
||||||
|
Size::LongNarrowTable => (55, 85),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,6 @@ pub struct Popup<'a, T: Widget> {
|
|||||||
percent_x: u16,
|
percent_x: u16,
|
||||||
percent_y: u16,
|
percent_y: u16,
|
||||||
block: Option<Block<'a>>,
|
block: Option<Block<'a>>,
|
||||||
footer: Option<&'a str>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Widget> Popup<'a, T> {
|
impl<'a, T: Widget> Popup<'a, T> {
|
||||||
@@ -64,7 +63,6 @@ impl<'a, T: Widget> Popup<'a, T> {
|
|||||||
percent_x: 0,
|
percent_x: 0,
|
||||||
percent_y: 0,
|
percent_y: 0,
|
||||||
block: None,
|
block: None,
|
||||||
footer: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,11 +84,6 @@ impl<'a, T: Widget> Popup<'a, T> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn footer(mut self, footer: &'a str) -> Self {
|
|
||||||
self.footer = Some(footer);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_popup(self, area: Rect, buf: &mut Buffer) {
|
fn render_popup(self, area: Rect, buf: &mut Buffer) {
|
||||||
let mut popup_area = centered_rect(self.percent_x, self.percent_y, area);
|
let mut popup_area = centered_rect(self.percent_x, self.percent_y, area);
|
||||||
let height = if popup_area.height < 3 {
|
let height = if popup_area.height < 3 {
|
||||||
@@ -109,23 +102,7 @@ impl<'a, T: Widget> Popup<'a, T> {
|
|||||||
block.render(popup_area, buf);
|
block.render(popup_area, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
let content_area = if let Some(footer) = self.footer {
|
self.widget.render(popup_area, buf);
|
||||||
let [content_area, help_footer_area] =
|
|
||||||
Layout::vertical([Constraint::Fill(0), Constraint::Length(2)])
|
|
||||||
.margin(1)
|
|
||||||
.areas(popup_area);
|
|
||||||
|
|
||||||
Paragraph::new(Text::from(format!(" {footer}").help()))
|
|
||||||
.block(layout_block_top_border())
|
|
||||||
.left_aligned()
|
|
||||||
.render(help_footer_area, buf);
|
|
||||||
|
|
||||||
content_area
|
|
||||||
} else {
|
|
||||||
popup_area
|
|
||||||
};
|
|
||||||
|
|
||||||
self.widget.render(content_area, buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ mod tests {
|
|||||||
assert_eq!(Size::XLarge.to_percent(), (83, 83));
|
assert_eq!(Size::XLarge.to_percent(), (83, 83));
|
||||||
assert_eq!(Size::XXLarge.to_percent(), (90, 90));
|
assert_eq!(Size::XXLarge.to_percent(), (90, 90));
|
||||||
assert_eq!(Size::Long.to_percent(), (65, 75));
|
assert_eq!(Size::Long.to_percent(), (65, 75));
|
||||||
|
assert_eq!(Size::LongNarrowTable.to_percent(), (55, 85));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -31,7 +32,6 @@ mod tests {
|
|||||||
assert_eq!(popup.percent_x, 0);
|
assert_eq!(popup.percent_x, 0);
|
||||||
assert_eq!(popup.percent_y, 0);
|
assert_eq!(popup.percent_y, 0);
|
||||||
assert_eq!(popup.block, None);
|
assert_eq!(popup.block, None);
|
||||||
assert_eq!(popup.footer, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -42,7 +42,6 @@ mod tests {
|
|||||||
assert_eq!(popup.percent_y, 40);
|
assert_eq!(popup.percent_y, 40);
|
||||||
assert_eq!(popup.widget, Block::new());
|
assert_eq!(popup.widget, Block::new());
|
||||||
assert_eq!(popup.block, None);
|
assert_eq!(popup.block, None);
|
||||||
assert_eq!(popup.footer, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -53,7 +52,6 @@ mod tests {
|
|||||||
assert_eq!(popup.percent_y, 50);
|
assert_eq!(popup.percent_y, 50);
|
||||||
assert_eq!(popup.widget, Block::new());
|
assert_eq!(popup.widget, Block::new());
|
||||||
assert_eq!(popup.block, None);
|
assert_eq!(popup.block, None);
|
||||||
assert_eq!(popup.footer, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -64,17 +62,5 @@ mod tests {
|
|||||||
assert_eq!(popup.widget, Block::new());
|
assert_eq!(popup.widget, Block::new());
|
||||||
assert_eq!(popup.percent_x, 0);
|
assert_eq!(popup.percent_x, 0);
|
||||||
assert_eq!(popup.percent_y, 0);
|
assert_eq!(popup.percent_y, 0);
|
||||||
assert_eq!(popup.footer, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_popup_footer() {
|
|
||||||
let popup = Popup::new(Block::new()).footer("footer");
|
|
||||||
|
|
||||||
assert_eq!(popup.footer, Some("footer"));
|
|
||||||
assert_eq!(popup.widget, Block::new());
|
|
||||||
assert_eq!(popup.percent_x, 0);
|
|
||||||
assert_eq!(popup.percent_y, 0);
|
|
||||||
assert_eq!(popup.block, None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user