feat(ui): Sonarr support for the series details popup
This commit is contained in:
Generated
+20
@@ -489,12 +489,30 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_setters"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.89",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "destructure_traitobject"
|
name = "destructure_traitobject"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
|
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deunicode"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff"
|
name = "diff"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@@ -1319,6 +1337,8 @@ dependencies = [
|
|||||||
"crossterm",
|
"crossterm",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"derivative",
|
"derivative",
|
||||||
|
"derive_setters",
|
||||||
|
"deunicode",
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"human-panic",
|
"human-panic",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ async-trait = "0.1.83"
|
|||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
managarr-tree-widget = "0.24.0"
|
managarr-tree-widget = "0.24.0"
|
||||||
indicatif = "0.17.9"
|
indicatif = "0.17.9"
|
||||||
|
derive_setters = "0.1.6"
|
||||||
|
deunicode = "1.6.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "2.0.16"
|
assert_cmd = "2.0.16"
|
||||||
|
|||||||
@@ -196,7 +196,13 @@ impl<'a> App<'a> {
|
|||||||
.current_selection()
|
.current_selection()
|
||||||
.clone()
|
.clone()
|
||||||
.seasons
|
.seasons
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut season| {
|
||||||
|
season.title = Some(format!("Season {}", season.season_number));
|
||||||
|
season
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
self.data.sonarr_data.seasons.set_items(seasons);
|
self.data.sonarr_data.seasons.set_items(seasons);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,24 @@ pub static HISTORY_CONTEXT_CLUES: [ContextClue; 6] = [
|
|||||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
|
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub static SERIES_HISTORY_CONTEXT_CLUES: [ContextClue; 9] = [
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "details"),
|
||||||
|
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
||||||
|
(
|
||||||
|
DEFAULT_KEYBINDINGS.auto_search,
|
||||||
|
DEFAULT_KEYBINDINGS.auto_search.desc,
|
||||||
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.update, DEFAULT_KEYBINDINGS.update.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.esc, "cancel filter/close"),
|
||||||
|
];
|
||||||
|
|
||||||
pub static SEASON_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
pub static SEASON_DETAILS_CONTEXT_CLUES: [ContextClue; 5] = [
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ mod tests {
|
|||||||
HISTORY_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXTUAL_CONTEXT_CLUES,
|
HISTORY_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXTUAL_CONTEXT_CLUES,
|
||||||
MANUAL_EPISODE_SEARCH_CONTEXT_CLUES, MANUAL_SEASON_SEARCH_CONTEXT_CLUES,
|
MANUAL_EPISODE_SEARCH_CONTEXT_CLUES, MANUAL_SEASON_SEARCH_CONTEXT_CLUES,
|
||||||
SEASON_DETAILS_CONTEXT_CLUES, SERIES_CONTEXT_CLUES, SERIES_DETAILS_CONTEXT_CLUES,
|
SEASON_DETAILS_CONTEXT_CLUES, SERIES_CONTEXT_CLUES, SERIES_DETAILS_CONTEXT_CLUES,
|
||||||
SYSTEM_TASKS_CONTEXT_CLUES,
|
SERIES_HISTORY_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,6 +86,57 @@ mod tests {
|
|||||||
assert_eq!(series_context_clues_iter.next(), None);
|
assert_eq!(series_context_clues_iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_series_history_context_clues() {
|
||||||
|
let mut series_history_context_clues_iter = SERIES_HISTORY_CONTEXT_CLUES.iter();
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.edit);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.edit.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.submit);
|
||||||
|
assert_str_eq!(*description, "details");
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.search);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.search.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.filter);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.filter.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.auto_search);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.auto_search.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.update);
|
||||||
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.update.desc);
|
||||||
|
|
||||||
|
let (key_binding, description) = series_history_context_clues_iter.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.esc);
|
||||||
|
assert_str_eq!(*description, "cancel filter/close");
|
||||||
|
assert_eq!(series_history_context_clues_iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_history_context_clues() {
|
fn test_history_context_clues() {
|
||||||
let mut history_context_clues_iter = HISTORY_CONTEXT_CLUES.iter();
|
let mut history_context_clues_iter = HISTORY_CONTEXT_CLUES.iter();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
mod sonarr_tests {
|
mod sonarr_tests {
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -572,6 +572,13 @@ mod tests {
|
|||||||
app.populate_seasons_table().await;
|
app.populate_seasons_table().await;
|
||||||
|
|
||||||
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
||||||
|
assert_str_eq!(
|
||||||
|
app.data.sonarr_data.seasons.items[0]
|
||||||
|
.title
|
||||||
|
.as_ref()
|
||||||
|
.unwrap(),
|
||||||
|
"Season 0"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -585,6 +592,13 @@ mod tests {
|
|||||||
app.populate_seasons_table().await;
|
app.populate_seasons_table().await;
|
||||||
|
|
||||||
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
assert!(!app.data.sonarr_data.seasons.items.is_empty());
|
||||||
|
assert_str_eq!(
|
||||||
|
app.data.sonarr_data.seasons.items[0]
|
||||||
|
.title
|
||||||
|
.as_ref()
|
||||||
|
.unwrap(),
|
||||||
|
"Season 0"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_app_unit<'a>() -> (App<'a>, mpsc::Receiver<NetworkEvent>) {
|
fn construct_app_unit<'a>() -> (App<'a>, mpsc::Receiver<NetworkEvent>) {
|
||||||
|
|||||||
@@ -323,6 +323,11 @@ mod test_utils {
|
|||||||
macro_rules! test_handler_delegation {
|
macro_rules! test_handler_delegation {
|
||||||
($handler:ident, $base:expr, $active_block:expr) => {
|
($handler:ident, $base:expr, $active_block:expr) => {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
let mut series_history = $crate::models::stateful_table::StatefulTable::default();
|
||||||
|
series_history.set_items(vec![
|
||||||
|
$crate::models::sonarr_models::SonarrHistoryItem::default(),
|
||||||
|
]);
|
||||||
|
app.data.sonarr_data.series_history = Some(series_history);
|
||||||
app.push_navigation_stack($base.into());
|
app.push_navigation_stack($base.into());
|
||||||
app.push_navigation_stack($active_block.into());
|
app.push_navigation_stack($active_block.into());
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
|
|||||||
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
|
_ if key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||||
self
|
self
|
||||||
.app
|
.app
|
||||||
.pop_and_push_navigation_stack((self.active_radarr_block).into());
|
.pop_and_push_navigation_stack(self.active_radarr_block.into());
|
||||||
}
|
}
|
||||||
_ if key == DEFAULT_KEYBINDINGS.sort.key => {
|
_ if key == DEFAULT_KEYBINDINGS.sort.key => {
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -1785,6 +1785,7 @@ mod tests {
|
|||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.is_loading = true;
|
app.is_loading = true;
|
||||||
app.push_navigation_stack(active_radarr_block.into());
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
app.is_routing = false;
|
||||||
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal {
|
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal {
|
||||||
movie_details: ScrollableText::with_string("test".to_owned()),
|
movie_details: ScrollableText::with_string("test".to_owned()),
|
||||||
..MovieDetailsModal::default()
|
..MovieDetailsModal::default()
|
||||||
@@ -1799,7 +1800,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(app.get_current_route(), active_radarr_block.into());
|
assert_eq!(app.get_current_route(), active_radarr_block.into());
|
||||||
assert!(app.is_routing);
|
assert!(!app.is_routing);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for HistoryHandler<'a, '
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn history_sorting_options() -> Vec<SortOption<SonarrHistoryItem>> {
|
pub(in crate::handlers::sonarr_handlers) fn history_sorting_options() -> Vec<SortOption<SonarrHistoryItem>> {
|
||||||
vec![
|
vec![
|
||||||
SortOption {
|
SortOption {
|
||||||
name: "Source Title",
|
name: "Source Title",
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ mod tests {
|
|||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
use crate::handlers::sonarr_handlers::library::{series_sorting_options, LibraryHandler};
|
use crate::handlers::sonarr_handlers::library::{series_sorting_options, LibraryHandler};
|
||||||
use crate::handlers::KeyEventHandler;
|
use crate::handlers::KeyEventHandler;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ADD_SERIES_BLOCKS, DELETE_SERIES_BLOCKS, EDIT_SERIES_BLOCKS, LIBRARY_BLOCKS, SERIES_DETAILS_BLOCKS};
|
||||||
ActiveSonarrBlock, ADD_SERIES_BLOCKS, DELETE_SERIES_BLOCKS, EDIT_SERIES_BLOCKS, LIBRARY_BLOCKS,
|
|
||||||
};
|
|
||||||
use crate::models::sonarr_models::{Series, SeriesStatus, SeriesType};
|
use crate::models::sonarr_models::{Series, SeriesStatus, SeriesType};
|
||||||
use crate::models::stateful_table::SortOption;
|
use crate::models::stateful_table::SortOption;
|
||||||
use crate::models::HorizontallyScrollableText;
|
use crate::models::HorizontallyScrollableText;
|
||||||
@@ -615,6 +613,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_search_series_submit() {
|
fn test_search_series_submit() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
app.should_ignore_quit_key = true;
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::SearchSeries.into());
|
app.push_navigation_stack(ActiveSonarrBlock::SearchSeries.into());
|
||||||
app
|
app
|
||||||
@@ -629,6 +628,7 @@ mod tests {
|
|||||||
|
|
||||||
LibraryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::SearchSeries, None).handle();
|
LibraryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::SearchSeries, None).handle();
|
||||||
|
|
||||||
|
assert!(!app.should_ignore_quit_key);
|
||||||
assert_str_eq!(
|
assert_str_eq!(
|
||||||
app.data.sonarr_data.series.current_selection().title.text,
|
app.data.sonarr_data.series.current_selection().title.text,
|
||||||
"Test 2"
|
"Test 2"
|
||||||
@@ -639,6 +639,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_search_series_submit_error_on_no_search_hits() {
|
fn test_search_series_submit_error_on_no_search_hits() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
app.should_ignore_quit_key = true;
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::SearchSeries.into());
|
app.push_navigation_stack(ActiveSonarrBlock::SearchSeries.into());
|
||||||
app
|
app
|
||||||
@@ -653,6 +654,7 @@ mod tests {
|
|||||||
|
|
||||||
LibraryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::SearchSeries, None).handle();
|
LibraryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::SearchSeries, None).handle();
|
||||||
|
|
||||||
|
assert!(!app.should_ignore_quit_key);
|
||||||
assert_str_eq!(
|
assert_str_eq!(
|
||||||
app.data.sonarr_data.series.current_selection().title.text,
|
app.data.sonarr_data.series.current_selection().title.text,
|
||||||
"Test 1"
|
"Test 1"
|
||||||
@@ -666,6 +668,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_search_filtered_series_submit() {
|
fn test_search_filtered_series_submit() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
app.should_ignore_quit_key = true;
|
||||||
app
|
app
|
||||||
.data
|
.data
|
||||||
.sonarr_data
|
.sonarr_data
|
||||||
@@ -685,6 +688,7 @@ mod tests {
|
|||||||
|
|
||||||
LibraryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::SearchSeries, None).handle();
|
LibraryHandler::with(SUBMIT_KEY, &mut app, ActiveSonarrBlock::SearchSeries, None).handle();
|
||||||
|
|
||||||
|
assert!(!app.should_ignore_quit_key);
|
||||||
assert_str_eq!(
|
assert_str_eq!(
|
||||||
app.data.sonarr_data.series.current_selection().title.text,
|
app.data.sonarr_data.series.current_selection().title.text,
|
||||||
"Test 2"
|
"Test 2"
|
||||||
@@ -695,6 +699,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_filter_series_submit() {
|
fn test_filter_series_submit() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
app.should_ignore_quit_key = true;
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::FilterSeries.into());
|
app.push_navigation_stack(ActiveSonarrBlock::FilterSeries.into());
|
||||||
app
|
app
|
||||||
@@ -732,6 +737,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_filter_series_submit_error_on_no_filter_matches() {
|
fn test_filter_series_submit_error_on_no_filter_matches() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
app.should_ignore_quit_key = true;
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
||||||
app.push_navigation_stack(ActiveSonarrBlock::FilterSeries.into());
|
app.push_navigation_stack(ActiveSonarrBlock::FilterSeries.into());
|
||||||
app
|
app
|
||||||
@@ -946,7 +952,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_key_char {
|
mod test_handle_key_char {
|
||||||
use bimap::BiMap;
|
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
@@ -1465,27 +1470,30 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[rstest]
|
#[rstest]
|
||||||
// fn test_delegates_series_details_blocks_to_series_details_handler(
|
fn test_delegates_series_details_blocks_to_series_details_handler(
|
||||||
// #[values(
|
#[values(
|
||||||
// ActiveSonarrBlock::SeriesDetails,
|
ActiveSonarrBlock::SeriesDetails,
|
||||||
// ActiveSonarrBlock::SeriesHistory,
|
ActiveSonarrBlock::SeriesHistory,
|
||||||
// ActiveSonarrBlock::FileInfo,
|
ActiveSonarrBlock::SearchSeason,
|
||||||
// ActiveSonarrBlock::Cast,
|
ActiveSonarrBlock::SearchSeasonError,
|
||||||
// ActiveSonarrBlock::Crew,
|
ActiveSonarrBlock::UpdateAndScanSeriesPrompt,
|
||||||
// ActiveSonarrBlock::AutomaticallySearchSeriesPrompt,
|
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt,
|
||||||
// ActiveSonarrBlock::UpdateAndScanPrompt,
|
ActiveSonarrBlock::SearchSeriesHistory,
|
||||||
// ActiveSonarrBlock::ManualSearch,
|
ActiveSonarrBlock::SearchSeriesHistoryError,
|
||||||
// ActiveSonarrBlock::ManualSearchConfirmPrompt
|
ActiveSonarrBlock::FilterSeriesHistory,
|
||||||
// )]
|
ActiveSonarrBlock::FilterSeriesHistoryError,
|
||||||
// active_sonarr_block: ActiveSonarrBlock,
|
ActiveSonarrBlock::SeriesHistorySortPrompt,
|
||||||
// ) {
|
ActiveSonarrBlock::SeriesHistoryDetails
|
||||||
// test_handler_delegation!(
|
)]
|
||||||
// LibraryHandler,
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
// ActiveSonarrBlock::Series,
|
) {
|
||||||
// active_sonarr_block
|
test_handler_delegation!(
|
||||||
// );
|
LibraryHandler,
|
||||||
// }
|
ActiveSonarrBlock::Series,
|
||||||
|
active_sonarr_block
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_delegates_edit_series_blocks_to_edit_series_handler(
|
fn test_delegates_edit_series_blocks_to_edit_series_handler(
|
||||||
@@ -1711,6 +1719,7 @@ mod tests {
|
|||||||
library_handler_blocks.extend(ADD_SERIES_BLOCKS);
|
library_handler_blocks.extend(ADD_SERIES_BLOCKS);
|
||||||
library_handler_blocks.extend(DELETE_SERIES_BLOCKS);
|
library_handler_blocks.extend(DELETE_SERIES_BLOCKS);
|
||||||
library_handler_blocks.extend(EDIT_SERIES_BLOCKS);
|
library_handler_blocks.extend(EDIT_SERIES_BLOCKS);
|
||||||
|
library_handler_blocks.extend(SERIES_DETAILS_BLOCKS);
|
||||||
|
|
||||||
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
|
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
|
||||||
if library_handler_blocks.contains(&active_sonarr_block) {
|
if library_handler_blocks.contains(&active_sonarr_block) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::handle_change_tab_left_right_keys;
|
use super::handle_change_tab_left_right_keys;
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::handlers::sonarr_handlers::library::series_details_handler::SeriesDetailsHandler;
|
||||||
|
|
||||||
mod add_series_handler;
|
mod add_series_handler;
|
||||||
mod delete_series_handler;
|
mod delete_series_handler;
|
||||||
@@ -29,6 +30,7 @@ mod delete_series_handler;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "library_handler_tests.rs"]
|
#[path = "library_handler_tests.rs"]
|
||||||
mod library_handler_tests;
|
mod library_handler_tests;
|
||||||
|
mod series_details_handler;
|
||||||
|
|
||||||
pub(super) struct LibraryHandler<'a, 'b> {
|
pub(super) struct LibraryHandler<'a, 'b> {
|
||||||
key: Key,
|
key: Key,
|
||||||
@@ -51,6 +53,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, '
|
|||||||
EditSeriesHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
|
EditSeriesHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
|
||||||
.handle();
|
.handle();
|
||||||
}
|
}
|
||||||
|
_ if SeriesDetailsHandler::accepts(self.active_sonarr_block) => {
|
||||||
|
SeriesDetailsHandler::with(self.key, self.app, self.active_sonarr_block, self.context)
|
||||||
|
.handle();
|
||||||
|
}
|
||||||
_ => self.handle_key_event(),
|
_ => self.handle_key_event(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,6 +65,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, '
|
|||||||
AddSeriesHandler::accepts(active_block)
|
AddSeriesHandler::accepts(active_block)
|
||||||
|| DeleteSeriesHandler::accepts(active_block)
|
|| DeleteSeriesHandler::accepts(active_block)
|
||||||
|| EditSeriesHandler::accepts(active_block)
|
|| EditSeriesHandler::accepts(active_block)
|
||||||
|
|| SeriesDetailsHandler::accepts(active_block)
|
||||||
|| LIBRARY_BLOCKS.contains(&active_block)
|
|| LIBRARY_BLOCKS.contains(&active_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,610 @@
|
|||||||
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handle_text_box_keys;
|
||||||
|
use crate::handlers::sonarr_handlers::history::history_sorting_options;
|
||||||
|
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{
|
||||||
|
ActiveSonarrBlock, EDIT_SERIES_SELECTION_BLOCKS, SERIES_DETAILS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable};
|
||||||
|
use crate::network::sonarr_network::SonarrEvent;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "series_details_handler_tests.rs"]
|
||||||
|
mod series_details_handler_tests;
|
||||||
|
|
||||||
|
pub(super) struct SeriesDetailsHandler<'a, 'b> {
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_sonarr_block: ActiveSonarrBlock,
|
||||||
|
_context: Option<ActiveSonarrBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler<'a, 'b> {
|
||||||
|
fn accepts(active_block: ActiveSonarrBlock) -> bool {
|
||||||
|
SERIES_DETAILS_BLOCKS.contains(&active_block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with(
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_block: ActiveSonarrBlock,
|
||||||
|
_context: Option<ActiveSonarrBlock>,
|
||||||
|
) -> SeriesDetailsHandler<'a, 'b> {
|
||||||
|
SeriesDetailsHandler {
|
||||||
|
key,
|
||||||
|
app,
|
||||||
|
active_sonarr_block: active_block,
|
||||||
|
_context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> Key {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
if self.active_sonarr_block == ActiveSonarrBlock::SeriesHistory {
|
||||||
|
!self.app.is_loading && self.app.data.sonarr_data.series_history.is_some()
|
||||||
|
} else {
|
||||||
|
!self.app.is_loading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_up(&mut self) {
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SeriesDetails => self.app.data.sonarr_data.seasons.scroll_up(),
|
||||||
|
ActiveSonarrBlock::SeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_up(),
|
||||||
|
ActiveSonarrBlock::SeriesHistorySortPrompt => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.sort
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_up(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_down(&mut self) {
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SeriesDetails => self.app.data.sonarr_data.seasons.scroll_down(),
|
||||||
|
ActiveSonarrBlock::SeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_down(),
|
||||||
|
ActiveSonarrBlock::SeriesHistorySortPrompt => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.sort
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_down(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_home(&mut self) {
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SeriesDetails => self.app.data.sonarr_data.seasons.scroll_to_top(),
|
||||||
|
ActiveSonarrBlock::SearchSeason => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.seasons
|
||||||
|
.search
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_home(),
|
||||||
|
ActiveSonarrBlock::SearchSeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.search
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_home(),
|
||||||
|
ActiveSonarrBlock::FilterSeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.filter
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_home(),
|
||||||
|
ActiveSonarrBlock::SeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_to_top(),
|
||||||
|
ActiveSonarrBlock::SeriesHistorySortPrompt => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.sort
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_to_top(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_end(&mut self) {
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SeriesDetails => self.app.data.sonarr_data.seasons.scroll_to_bottom(),
|
||||||
|
ActiveSonarrBlock::SearchSeason => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.seasons
|
||||||
|
.search
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.reset_offset(),
|
||||||
|
ActiveSonarrBlock::SearchSeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.search
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.reset_offset(),
|
||||||
|
ActiveSonarrBlock::FilterSeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.filter
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.reset_offset(),
|
||||||
|
ActiveSonarrBlock::SeriesHistory => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_to_bottom(),
|
||||||
|
ActiveSonarrBlock::SeriesHistorySortPrompt => self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.sort
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_to_bottom(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_delete(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_left_right_action(&mut self) {
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SeriesDetails | ActiveSonarrBlock::SeriesHistory => match self.key {
|
||||||
|
_ if self.key == DEFAULT_KEYBINDINGS.left.key => {
|
||||||
|
self.app.data.sonarr_data.series_info_tabs.previous();
|
||||||
|
self.app.pop_and_push_navigation_stack(
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_info_tabs
|
||||||
|
.get_active_route(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ if self.key == DEFAULT_KEYBINDINGS.right.key => {
|
||||||
|
self.app.data.sonarr_data.series_info_tabs.next();
|
||||||
|
self.app.pop_and_push_navigation_stack(
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_info_tabs
|
||||||
|
.get_active_route(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
ActiveSonarrBlock::UpdateAndScanSeriesPrompt
|
||||||
|
| ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
|
||||||
|
handle_prompt_toggle(self.app, self.key)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_submit(&mut self) {
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SeriesDetails if !self.app.data.sonarr_data.seasons.is_empty() => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::SeasonDetails.into());
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SeriesHistory
|
||||||
|
if !self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_ref()
|
||||||
|
.expect("Series history should be Some")
|
||||||
|
.is_empty() =>
|
||||||
|
{
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::SeriesHistoryDetails.into());
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
|
||||||
|
if self.app.data.sonarr_data.prompt_confirm {
|
||||||
|
self.app.data.sonarr_data.prompt_confirm_action =
|
||||||
|
Some(SonarrEvent::TriggerAutomaticSeriesSearch(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
|
||||||
|
if self.app.data.sonarr_data.prompt_confirm {
|
||||||
|
self.app.data.sonarr_data.prompt_confirm_action =
|
||||||
|
Some(SonarrEvent::UpdateAndScanSeries(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SearchSeason => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
|
||||||
|
if self.app.data.sonarr_data.seasons.search.is_some() {
|
||||||
|
let has_match = self.app.data.sonarr_data.seasons.apply_search(|season| {
|
||||||
|
season
|
||||||
|
.title
|
||||||
|
.as_ref()
|
||||||
|
.expect("Season was not populated with title in handlers")
|
||||||
|
});
|
||||||
|
|
||||||
|
if !has_match {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::SearchSeasonError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SearchSeriesHistory => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
|
||||||
|
if self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.search
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
let has_match = self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.apply_search(|history_item| &history_item.source_title.text);
|
||||||
|
|
||||||
|
if !has_match {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::SearchSeriesHistoryError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::FilterSeriesHistory => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
|
||||||
|
if self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.filter
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
let has_matches = self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.apply_filter(|history_item| &history_item.source_title.text);
|
||||||
|
|
||||||
|
if !has_matches {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::FilterSeriesHistoryError.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SeriesHistorySortPrompt => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.items
|
||||||
|
.sort_by(|a, b| a.id.cmp(&b.id));
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.apply_sorting();
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_esc(&mut self) {
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SearchSeason | ActiveSonarrBlock::SearchSeasonError => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.data.sonarr_data.seasons.reset_search();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SearchSeriesHistory | ActiveSonarrBlock::SearchSeriesHistoryError => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.reset_search();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::FilterSeriesHistory | ActiveSonarrBlock::FilterSeriesHistoryError => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.reset_filter();
|
||||||
|
self.app.should_ignore_quit_key = false;
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::UpdateAndScanSeriesPrompt
|
||||||
|
| ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.data.sonarr_data.prompt_confirm = false;
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SeriesHistoryDetails | ActiveSonarrBlock::SeriesHistorySortPrompt => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SeriesHistory => {
|
||||||
|
if self.app.data.sonarr_data.series_history.as_ref().expect("Series history is not populated").filtered_items.is_some() {
|
||||||
|
self.app.data.sonarr_data.series_history.as_mut().expect("Series history is not populated").reset_filter();
|
||||||
|
} else {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.data.sonarr_data.reset_series_info_tabs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SeriesDetails => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.data.sonarr_data.reset_series_info_tabs();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_char_key_event(&mut self) {
|
||||||
|
let key = self.key;
|
||||||
|
match self.active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::SeriesDetails => match self.key {
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.refresh.key => self
|
||||||
|
.app
|
||||||
|
.pop_and_push_navigation_stack(self.active_sonarr_block.into()),
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.search.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::SearchSeason.into());
|
||||||
|
self.app.data.sonarr_data.seasons.search = Some(HorizontallyScrollableText::default());
|
||||||
|
self.app.should_ignore_quit_key = true;
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.auto_search.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::AutomaticallySearchSeriesPrompt.into());
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.update.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::UpdateAndScanSeriesPrompt.into());
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
|
||||||
|
self.app.push_navigation_stack(
|
||||||
|
(
|
||||||
|
ActiveSonarrBlock::EditSeriesPrompt,
|
||||||
|
Some(self.active_sonarr_block),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
self.app.data.sonarr_data.edit_series_modal = Some((&self.app.data.sonarr_data).into());
|
||||||
|
self.app.data.sonarr_data.selected_block =
|
||||||
|
BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
ActiveSonarrBlock::SeriesHistory => match self.key {
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.refresh.key => self
|
||||||
|
.app
|
||||||
|
.pop_and_push_navigation_stack(self.active_sonarr_block.into()),
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.auto_search.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::AutomaticallySearchSeriesPrompt.into());
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.search.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::SearchSeriesHistory.into());
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.expect("Series history should be populated")
|
||||||
|
.search = Some(HorizontallyScrollableText::default());
|
||||||
|
self.app.should_ignore_quit_key = true;
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.filter.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::FilterSeriesHistory.into());
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.expect("Series history should be populated")
|
||||||
|
.reset_filter();
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.expect("Series history should be populated")
|
||||||
|
.filter = Some(HorizontallyScrollableText::default());
|
||||||
|
self.app.should_ignore_quit_key = true;
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.sort.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.series_history
|
||||||
|
.as_mut()
|
||||||
|
.expect("Series history should be populated")
|
||||||
|
.sorting(history_sorting_options());
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::SeriesHistorySortPrompt.into());
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.edit.key => {
|
||||||
|
self.app.push_navigation_stack(
|
||||||
|
(
|
||||||
|
ActiveSonarrBlock::EditSeriesPrompt,
|
||||||
|
Some(self.active_sonarr_block),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
self.app.data.sonarr_data.edit_series_modal = Some((&self.app.data.sonarr_data).into());
|
||||||
|
self.app.data.sonarr_data.selected_block =
|
||||||
|
BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
|
||||||
|
}
|
||||||
|
_ if key == DEFAULT_KEYBINDINGS.update.key => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveSonarrBlock::UpdateAndScanSeriesPrompt.into());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
ActiveSonarrBlock::SearchSeason => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
key,
|
||||||
|
self.app.data.sonarr_data.seasons.search.as_mut().unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::SearchSeriesHistory => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
key,
|
||||||
|
self.app.data.sonarr_data.series_history.as_mut().expect("Series history should be populated").search.as_mut().unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::FilterSeriesHistory => {
|
||||||
|
handle_text_box_keys!(
|
||||||
|
self,
|
||||||
|
key,
|
||||||
|
self.app.data.sonarr_data.series_history.as_mut().expect("Series history should be populated").filter.as_mut().unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
|
||||||
|
if key == DEFAULT_KEYBINDINGS.confirm.key {
|
||||||
|
self.app.data.sonarr_data.prompt_confirm = true;
|
||||||
|
self.app.data.sonarr_data.prompt_confirm_action =
|
||||||
|
Some(SonarrEvent::TriggerAutomaticSeriesSearch(None));
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
|
||||||
|
if self.app.data.sonarr_data.prompt_confirm {
|
||||||
|
self.app.data.sonarr_data.prompt_confirm_action =
|
||||||
|
Some(SonarrEvent::UpdateAndScanSeries(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -7,15 +7,15 @@ mod utils {
|
|||||||
($handler:ident, $block:expr, $context:expr) => {
|
($handler:ident, $block:expr, $context:expr) => {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
let mut sonarr_data = SonarrData {
|
let mut sonarr_data = SonarrData {
|
||||||
quality_profile_map: BiMap::from_iter([
|
quality_profile_map: bimap::BiMap::from_iter([
|
||||||
(2222, "HD - 1080p".to_owned()),
|
(2222, "HD - 1080p".to_owned()),
|
||||||
(1111, "Any".to_owned()),
|
(1111, "Any".to_owned()),
|
||||||
]),
|
]),
|
||||||
language_profiles_map: BiMap::from_iter([
|
language_profiles_map: bimap::BiMap::from_iter([
|
||||||
(2222, "English".to_owned()),
|
(2222, "English".to_owned()),
|
||||||
(1111, "Any".to_owned()),
|
(1111, "Any".to_owned()),
|
||||||
]),
|
]),
|
||||||
tags_map: BiMap::from_iter([(1, "test".to_owned())]),
|
tags_map: bimap::BiMap::from_iter([(1, "test".to_owned())]),
|
||||||
..create_test_sonarr_data()
|
..create_test_sonarr_data()
|
||||||
};
|
};
|
||||||
sonarr_data.series.set_items(vec![Series {
|
sonarr_data.series.set_items(vec![Series {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
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,
|
||||||
|
SERIES_HISTORY_CONTEXT_CLUES,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
models::{
|
models::{
|
||||||
@@ -80,6 +81,12 @@ impl<'a> SonarrData<'a> {
|
|||||||
self.delete_series_files = false;
|
self.delete_series_files = false;
|
||||||
self.add_list_exclusion = false;
|
self.add_list_exclusion = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_series_info_tabs(&mut self) {
|
||||||
|
self.series_history = None;
|
||||||
|
self.seasons = StatefulTable::default();
|
||||||
|
self.series_info_tabs.index = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for SonarrData<'a> {
|
impl<'a> Default for SonarrData<'a> {
|
||||||
@@ -174,7 +181,7 @@ impl<'a> Default for SonarrData<'a> {
|
|||||||
title: "History",
|
title: "History",
|
||||||
route: ActiveSonarrBlock::SeriesHistory.into(),
|
route: ActiveSonarrBlock::SeriesHistory.into(),
|
||||||
help: String::new(),
|
help: String::new(),
|
||||||
contextual_help: Some(build_context_clue_string(&HISTORY_CONTEXT_CLUES)),
|
contextual_help: Some(build_context_clue_string(&SERIES_HISTORY_CONTEXT_CLUES)),
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
@@ -304,12 +311,13 @@ pub static LIBRARY_BLOCKS: [ActiveSonarrBlock; 7] = [
|
|||||||
ActiveSonarrBlock::UpdateAllSeriesPrompt,
|
ActiveSonarrBlock::UpdateAllSeriesPrompt,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static SERIES_DETAILS_BLOCKS: [ActiveSonarrBlock; 11] = [
|
pub static SERIES_DETAILS_BLOCKS: [ActiveSonarrBlock; 12] = [
|
||||||
ActiveSonarrBlock::SeriesDetails,
|
ActiveSonarrBlock::SeriesDetails,
|
||||||
ActiveSonarrBlock::SeriesHistory,
|
ActiveSonarrBlock::SeriesHistory,
|
||||||
ActiveSonarrBlock::SearchSeason,
|
ActiveSonarrBlock::SearchSeason,
|
||||||
ActiveSonarrBlock::SearchSeasonError,
|
ActiveSonarrBlock::SearchSeasonError,
|
||||||
ActiveSonarrBlock::UpdateAndScanSeriesPrompt,
|
ActiveSonarrBlock::UpdateAndScanSeriesPrompt,
|
||||||
|
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt,
|
||||||
ActiveSonarrBlock::SearchSeriesHistory,
|
ActiveSonarrBlock::SearchSeriesHistory,
|
||||||
ActiveSonarrBlock::SearchSeriesHistoryError,
|
ActiveSonarrBlock::SearchSeriesHistoryError,
|
||||||
ActiveSonarrBlock::FilterSeriesHistory,
|
ActiveSonarrBlock::FilterSeriesHistory,
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ mod tests {
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
|
||||||
|
use crate::app::sonarr::sonarr_context_clues::SERIES_HISTORY_CONTEXT_CLUES;
|
||||||
|
use crate::models::sonarr_models::{Season, SonarrHistoryItem};
|
||||||
|
use crate::models::stateful_table::StatefulTable;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
context_clues::{
|
context_clues::{
|
||||||
@@ -56,6 +59,24 @@ mod tests {
|
|||||||
assert!(!sonarr_data.add_list_exclusion);
|
assert!(!sonarr_data.add_list_exclusion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reset_series_info_tabs() {
|
||||||
|
let mut series_history = StatefulTable::default();
|
||||||
|
series_history.set_items(vec![SonarrHistoryItem::default()]);
|
||||||
|
let mut sonarr_data = SonarrData {
|
||||||
|
series_history: Some(series_history),
|
||||||
|
..SonarrData::default()
|
||||||
|
};
|
||||||
|
sonarr_data.seasons.set_items(vec![Season::default()]);
|
||||||
|
sonarr_data.series_info_tabs.index = 1;
|
||||||
|
|
||||||
|
sonarr_data.reset_series_info_tabs();
|
||||||
|
|
||||||
|
assert!(sonarr_data.series_history.is_none());
|
||||||
|
assert!(sonarr_data.seasons.is_empty());
|
||||||
|
assert_eq!(sonarr_data.series_info_tabs.index, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sonarr_data_defaults() {
|
fn test_sonarr_data_defaults() {
|
||||||
let sonarr_data = SonarrData::default();
|
let sonarr_data = SonarrData::default();
|
||||||
@@ -195,7 +216,7 @@ mod tests {
|
|||||||
assert!(sonarr_data.series_info_tabs.tabs[1].help.is_empty());
|
assert!(sonarr_data.series_info_tabs.tabs[1].help.is_empty());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.series_info_tabs.tabs[1].contextual_help,
|
sonarr_data.series_info_tabs.tabs[1].contextual_help,
|
||||||
Some(build_context_clue_string(&HISTORY_CONTEXT_CLUES))
|
Some(build_context_clue_string(&SERIES_HISTORY_CONTEXT_CLUES))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -570,12 +591,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_series_details_blocks_contents() {
|
fn test_series_details_blocks_contents() {
|
||||||
assert_eq!(SERIES_DETAILS_BLOCKS.len(), 11);
|
assert_eq!(SERIES_DETAILS_BLOCKS.len(), 12);
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SeriesDetails));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SeriesDetails));
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SeriesHistory));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SeriesHistory));
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeason));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeason));
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeasonError));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeasonError));
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::UpdateAndScanSeriesPrompt));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::UpdateAndScanSeriesPrompt));
|
||||||
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::AutomaticallySearchSeriesPrompt));
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeriesHistory));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeriesHistory));
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeriesHistoryError));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::SearchSeriesHistoryError));
|
||||||
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::FilterSeriesHistory));
|
assert!(SERIES_DETAILS_BLOCKS.contains(&ActiveSonarrBlock::FilterSeriesHistory));
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ pub mod utils {
|
|||||||
|
|
||||||
let mut seasons = StatefulTable::default();
|
let mut seasons = StatefulTable::default();
|
||||||
seasons.set_items(vec![Season::default()]);
|
seasons.set_items(vec![Season::default()]);
|
||||||
|
let mut series_history = StatefulTable::default();
|
||||||
|
series_history.set_items(vec![SonarrHistoryItem::default()]);
|
||||||
|
|
||||||
let mut sonarr_data = SonarrData {
|
let mut sonarr_data = SonarrData {
|
||||||
delete_series_files: true,
|
delete_series_files: true,
|
||||||
@@ -39,6 +41,7 @@ pub mod utils {
|
|||||||
add_series_search: Some("test search".into()),
|
add_series_search: Some("test search".into()),
|
||||||
edit_root_folder: Some("test path".into()),
|
edit_root_folder: Some("test path".into()),
|
||||||
seasons,
|
seasons,
|
||||||
|
series_history: Some(series_history),
|
||||||
season_details_modal: Some(season_details_modal),
|
season_details_modal: Some(season_details_modal),
|
||||||
add_searched_series: Some(StatefulTable::default()),
|
add_searched_series: Some(StatefulTable::default()),
|
||||||
..SonarrData::default()
|
..SonarrData::default()
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ impl Eq for Rating {}
|
|||||||
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Season {
|
pub struct Season {
|
||||||
|
pub title: Option<String>,
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub season_number: i64,
|
pub season_number: i64,
|
||||||
pub monitored: bool,
|
pub monitored: bool,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod test {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bimap::BiMap;
|
use bimap::BiMap;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::DateTime;
|
||||||
use indoc::formatdoc;
|
use indoc::formatdoc;
|
||||||
use mockito::{Matcher, Server};
|
use mockito::{Matcher, Server};
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
@@ -5237,8 +5237,7 @@ mod test {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
|
||||||
let date_time = DateTime::from(DateTime::parse_from_rfc3339("2023-02-25T20:16:43Z").unwrap())
|
let date_time = DateTime::from(DateTime::parse_from_rfc3339("2023-02-25T20:16:43Z").unwrap());
|
||||||
as DateTime<Utc>;
|
|
||||||
|
|
||||||
if let SonarrSerdeable::SystemStatus(status) = network
|
if let SonarrSerdeable::SystemStatus(status) = network
|
||||||
.handle_sonarr_event(SonarrEvent::GetStatus)
|
.handle_sonarr_event(SonarrEvent::GetStatus)
|
||||||
@@ -6874,6 +6873,7 @@ mod test {
|
|||||||
|
|
||||||
fn season() -> Season {
|
fn season() -> Season {
|
||||||
Season {
|
Season {
|
||||||
|
title: None,
|
||||||
season_number: 1,
|
season_number: 1,
|
||||||
monitored: true,
|
monitored: true,
|
||||||
statistics: season_statistics(),
|
statistics: season_statistics(),
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ impl DrawUi for MovieDetailsUi {
|
|||||||
.prompt(&prompt)
|
.prompt(&prompt)
|
||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_movie_info(f, app, content_area);
|
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
f.area(),
|
f.area(),
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ impl DrawUi for EditSeriesUi {
|
|||||||
}
|
}
|
||||||
_ if SERIES_DETAILS_BLOCKS.contains(&context) => {
|
_ if SERIES_DETAILS_BLOCKS.contains(&context) => {
|
||||||
draw_popup_over_ui::<SeriesDetailsUi>(f, app, area, draw_library, Size::Large);
|
draw_popup_over_ui::<SeriesDetailsUi>(f, app, area, draw_library, Size::Large);
|
||||||
draw_popup(f, app, draw_edit_series_prompt, Size::Medium);
|
draw_popup(f, app, draw_edit_series_prompt, Size::Long);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ mod tests {
|
|||||||
use crate::ui::DrawUi;
|
use crate::ui::DrawUi;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use ratatui::widgets::{Cell, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use rstest::rstest;
|
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::models::sonarr_models::{Season, SeasonStatistics};
|
||||||
use crate::{
|
use crate::{
|
||||||
models::sonarr_models::{Series, SeriesStatistics},
|
models::sonarr_models::Series,
|
||||||
ui::sonarr_ui::library::decorate_series_row_with_style,
|
ui::sonarr_ui::library::decorate_series_row_with_style,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,45 +38,193 @@ mod tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[test]
|
||||||
#[case(SeriesStatus::Ended, None, RowStyle::Missing)]
|
fn test_decorate_row_with_style_downloaded_when_ended_and_all_monitored_episodes_are_present(
|
||||||
#[case(SeriesStatus::Ended, Some(59.0), RowStyle::Missing)]
|
|
||||||
#[case(SeriesStatus::Ended, Some(100.0), RowStyle::Downloaded)]
|
|
||||||
#[case(SeriesStatus::Continuing, None, RowStyle::Missing)]
|
|
||||||
#[case(SeriesStatus::Continuing, Some(59.0), RowStyle::Missing)]
|
|
||||||
#[case(SeriesStatus::Continuing, Some(100.0), RowStyle::Unreleased)]
|
|
||||||
#[case(SeriesStatus::Upcoming, None, RowStyle::Unreleased)]
|
|
||||||
#[case(SeriesStatus::Deleted, None, RowStyle::Missing)]
|
|
||||||
fn test_decorate_series_row_with_style(
|
|
||||||
#[case] series_status: SeriesStatus,
|
|
||||||
#[case] percent_of_episodes: Option<f64>,
|
|
||||||
#[case] expected_row_style: RowStyle,
|
|
||||||
) {
|
) {
|
||||||
let mut series = Series {
|
let seasons = vec![
|
||||||
status: series_status,
|
Season {
|
||||||
|
monitored: false,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 1,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
Season {
|
||||||
|
monitored: true,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 3,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Ended,
|
||||||
|
seasons: Some(seasons),
|
||||||
..Series::default()
|
..Series::default()
|
||||||
};
|
};
|
||||||
if let Some(percentage) = percent_of_episodes {
|
|
||||||
series.statistics = Some(SeriesStatistics {
|
|
||||||
percent_of_episodes: percentage,
|
|
||||||
..SeriesStatistics::default()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
let style = decorate_series_row_with_style(&series, row.clone());
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
match expected_row_style {
|
assert_eq!(style, row.downloaded());
|
||||||
RowStyle::Downloaded => assert_eq!(style, row.downloaded()),
|
|
||||||
RowStyle::Missing => assert_eq!(style, row.missing()),
|
|
||||||
RowStyle::Unreleased => assert_eq!(style, row.unreleased()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RowStyle {
|
#[test]
|
||||||
Downloaded,
|
fn test_decorate_row_with_style_missing_when_ended_and_episodes_are_missing() {
|
||||||
Missing,
|
let seasons = vec![
|
||||||
Unreleased,
|
Season {
|
||||||
|
monitored: true,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 1,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
Season {
|
||||||
|
monitored: true,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 3,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Ended,
|
||||||
|
seasons: Some(seasons),
|
||||||
|
..Series::default()
|
||||||
|
};
|
||||||
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
|
assert_eq!(style, row.missing());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decorate_row_with_style_indeterminate_when_ended_and_seasons_is_empty() {
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Ended,
|
||||||
|
..Series::default()
|
||||||
|
};
|
||||||
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
|
assert_eq!(style, row.indeterminate());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decorate_row_with_style_unreleased_when_continuing_and_all_monitored_episodes_are_present(
|
||||||
|
) {
|
||||||
|
let seasons = vec![
|
||||||
|
Season {
|
||||||
|
monitored: false,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 1,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
Season {
|
||||||
|
monitored: true,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 3,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Continuing,
|
||||||
|
seasons: Some(seasons),
|
||||||
|
..Series::default()
|
||||||
|
};
|
||||||
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
|
assert_eq!(style, row.unreleased());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decorate_row_with_style_missing_when_continuing_and_episodes_are_missing() {
|
||||||
|
let seasons = vec![
|
||||||
|
Season {
|
||||||
|
monitored: true,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 1,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
Season {
|
||||||
|
monitored: true,
|
||||||
|
statistics: SeasonStatistics {
|
||||||
|
episode_count: 3,
|
||||||
|
total_episode_count: 3,
|
||||||
|
..SeasonStatistics::default()
|
||||||
|
},
|
||||||
|
..Season::default()
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Continuing,
|
||||||
|
seasons: Some(seasons),
|
||||||
|
..Series::default()
|
||||||
|
};
|
||||||
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
|
assert_eq!(style, row.missing());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decorate_row_with_style_indeterminate_when_continuing_and_seasons_is_empty() {
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Continuing,
|
||||||
|
..Series::default()
|
||||||
|
};
|
||||||
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
|
assert_eq!(style, row.indeterminate());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decorate_row_with_style_unreleased_when_upcoming() {
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Upcoming,
|
||||||
|
..Series::default()
|
||||||
|
};
|
||||||
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
|
assert_eq!(style, row.unreleased());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decorate_row_with_style_defaults_to_indeterminate() {
|
||||||
|
let series = Series {
|
||||||
|
status: SeriesStatus::Deleted,
|
||||||
|
..Series::default()
|
||||||
|
};
|
||||||
|
let row = Row::new(vec![Cell::from("test".to_owned())]);
|
||||||
|
|
||||||
|
let style = decorate_series_row_with_style(&series, row.clone());
|
||||||
|
|
||||||
|
assert_eq!(style, row.indeterminate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,35 +61,6 @@ impl DrawUi for LibraryUi {
|
|||||||
| ActiveSonarrBlock::SearchSeriesError
|
| ActiveSonarrBlock::SearchSeriesError
|
||||||
| ActiveSonarrBlock::FilterSeries
|
| ActiveSonarrBlock::FilterSeries
|
||||||
| ActiveSonarrBlock::FilterSeriesError => draw_library(f, app, area),
|
| ActiveSonarrBlock::FilterSeriesError => draw_library(f, app, area),
|
||||||
// ActiveSonarrBlock::SearchSeries => draw_popup_over(
|
|
||||||
// f,
|
|
||||||
// app,
|
|
||||||
// area,
|
|
||||||
// draw_library,
|
|
||||||
// draw_library_search_box,
|
|
||||||
// Size::InputBox,
|
|
||||||
// ),
|
|
||||||
// ActiveSonarrBlock::SearchSeriesError => {
|
|
||||||
// let popup = Popup::new(Message::new("Series not found!")).size(Size::Message);
|
|
||||||
|
|
||||||
// draw_library(f, app, area);
|
|
||||||
// f.render_widget(popup, f.area());
|
|
||||||
// }
|
|
||||||
// ActiveSonarrBlock::FilterSeries => draw_popup_over(
|
|
||||||
// f,
|
|
||||||
// app,
|
|
||||||
// area,
|
|
||||||
// draw_library,
|
|
||||||
// draw_filter_series_box,
|
|
||||||
// Size::InputBox,
|
|
||||||
// ),
|
|
||||||
// ActiveSonarrBlock::FilterSeriesError => {
|
|
||||||
// let popup = Popup::new(Message::new("No series found matching the given filter!"))
|
|
||||||
// .size(Size::Message);
|
|
||||||
|
|
||||||
// draw_library(f, app, area);
|
|
||||||
// f.render_widget(popup, f.area());
|
|
||||||
// }
|
|
||||||
ActiveSonarrBlock::UpdateAllSeriesPrompt => {
|
ActiveSonarrBlock::UpdateAllSeriesPrompt => {
|
||||||
let confirmation_prompt = ConfirmationPrompt::new()
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
.title("Update All Series")
|
.title("Update All Series")
|
||||||
@@ -234,24 +205,36 @@ pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
fn decorate_series_row_with_style<'a>(series: &Series, row: Row<'a>) -> Row<'a> {
|
fn decorate_series_row_with_style<'a>(series: &Series, row: Row<'a>) -> Row<'a> {
|
||||||
match series.status {
|
match series.status {
|
||||||
SeriesStatus::Ended => {
|
SeriesStatus::Ended => {
|
||||||
if let Some(ref stats) = series.statistics {
|
if let Some(ref seasons) = series.seasons {
|
||||||
if stats.percent_of_episodes == 100.0 {
|
return if seasons
|
||||||
return row.downloaded();
|
.iter()
|
||||||
|
.filter(|season| season.monitored)
|
||||||
|
.all(|season| season.statistics.episode_count == season.statistics.total_episode_count)
|
||||||
|
{
|
||||||
|
row.downloaded()
|
||||||
|
} else {
|
||||||
|
row.missing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row.missing()
|
row.indeterminate()
|
||||||
}
|
}
|
||||||
SeriesStatus::Continuing => {
|
SeriesStatus::Continuing => {
|
||||||
if let Some(ref stats) = series.statistics {
|
if let Some(ref seasons) = series.seasons {
|
||||||
if stats.percent_of_episodes == 100.0 {
|
return if seasons
|
||||||
return row.unreleased();
|
.iter()
|
||||||
}
|
.filter(|season| season.monitored)
|
||||||
|
.all(|season| season.statistics.episode_count == season.statistics.total_episode_count)
|
||||||
|
{
|
||||||
|
row.unreleased()
|
||||||
|
} else {
|
||||||
|
row.missing()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
row.missing()
|
row.indeterminate()
|
||||||
}
|
}
|
||||||
SeriesStatus::Upcoming => row.unreleased(),
|
SeriesStatus::Upcoming => row.unreleased(),
|
||||||
_ => row.missing(),
|
_ => row.indeterminate(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
use deunicode::deunicode;
|
||||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
use ratatui::layout::{Alignment, Constraint, 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::{Cell, Paragraph, Row, Wrap};
|
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SERIES_DETAILS_BLOCKS};
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SERIES_DETAILS_BLOCKS};
|
||||||
@@ -67,6 +69,20 @@ impl DrawUi for SeriesDetailsUi {
|
|||||||
draw_series_details(f, app, content_area);
|
draw_series_details(f, app, content_area);
|
||||||
|
|
||||||
match active_sonarr_block {
|
match active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
|
||||||
|
let prompt = format!(
|
||||||
|
"Do you want to trigger an automatic search of your indexers for all monitored episode(s) for the series: {}", app.data.sonarr_data.series.current_selection().title
|
||||||
|
);
|
||||||
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
|
.title("Automatic Series Search")
|
||||||
|
.prompt(&prompt)
|
||||||
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
|
}
|
||||||
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
|
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
|
||||||
let prompt = format!(
|
let prompt = format!(
|
||||||
"Do you want to trigger an update and disk scan for the series: {}?",
|
"Do you want to trigger an update and disk scan for the series: {}?",
|
||||||
@@ -83,14 +99,7 @@ impl DrawUi for SeriesDetailsUi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ActiveSonarrBlock::SeriesHistoryDetails => {
|
ActiveSonarrBlock::SeriesHistoryDetails => {
|
||||||
draw_popup_over(
|
draw_history_item_details_popup(f, app, popup_area);
|
||||||
f,
|
|
||||||
app,
|
|
||||||
popup_area,
|
|
||||||
draw_series_history_table,
|
|
||||||
draw_history_item_details_popup,
|
|
||||||
Size::Small,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
@@ -129,19 +138,25 @@ pub fn draw_series_description(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
.get_by_left(¤t_selection.language_profile_id)
|
.get_by_left(¤t_selection.language_profile_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
let overview = Regex::new(r"[\r\n\t]")
|
||||||
|
.unwrap()
|
||||||
|
.replace_all(
|
||||||
|
&deunicode(
|
||||||
|
current_selection
|
||||||
|
.overview
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&String::new()),
|
||||||
|
),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let mut series_description = vec![
|
let mut series_description = vec![
|
||||||
Line::from(vec![
|
Line::from(vec![
|
||||||
"Title: ".primary().bold(),
|
"Title: ".primary().bold(),
|
||||||
current_selection.title.text.clone().primary().bold(),
|
current_selection.title.text.clone().primary().bold(),
|
||||||
]),
|
]),
|
||||||
Line::from(vec![
|
Line::from(vec!["Overview: ".primary().bold(), overview.default()]),
|
||||||
"Overview: ".primary().bold(),
|
|
||||||
current_selection
|
|
||||||
.overview
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.default(),
|
|
||||||
]),
|
|
||||||
Line::from(vec![
|
Line::from(vec![
|
||||||
"Network: ".primary().bold(),
|
"Network: ".primary().bold(),
|
||||||
current_selection
|
current_selection
|
||||||
@@ -194,7 +209,7 @@ pub fn draw_series_description(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
|
|
||||||
let description_paragraph = Paragraph::new(series_description)
|
let description_paragraph = Paragraph::new(series_description)
|
||||||
.block(borderless_block())
|
.block(borderless_block())
|
||||||
.wrap(Wrap { trim: false });
|
.wrap(Wrap { trim: true });
|
||||||
f.render_widget(description_paragraph, area);
|
f.render_widget(description_paragraph, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,9 +235,10 @@ fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.get_active_tab_contextual_help();
|
.get_active_tab_contextual_help();
|
||||||
let season_row_mapping = |season: &Season| {
|
let season_row_mapping = |season: &Season| {
|
||||||
let Season {
|
let Season {
|
||||||
season_number,
|
title,
|
||||||
monitored,
|
monitored,
|
||||||
statistics,
|
statistics,
|
||||||
|
..
|
||||||
} = season;
|
} = season;
|
||||||
let SeasonStatistics {
|
let SeasonStatistics {
|
||||||
episode_count,
|
episode_count,
|
||||||
@@ -235,7 +251,7 @@ fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
let row = Row::new(vec![
|
let row = Row::new(vec![
|
||||||
Cell::from(season_monitored.to_owned()),
|
Cell::from(season_monitored.to_owned()),
|
||||||
Cell::from(format!("Season {}", season_number)),
|
Cell::from(title.clone().unwrap()),
|
||||||
Cell::from(format!("{}/{}", episode_count, total_episode_count)),
|
Cell::from(format!("{}/{}", episode_count, total_episode_count)),
|
||||||
Cell::from(format!("{size:.2} GB")),
|
Cell::from(format!("{size:.2} GB")),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ where
|
|||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new() -> T;
|
fn new() -> T;
|
||||||
fn awaiting_import(self) -> T;
|
fn awaiting_import(self) -> T;
|
||||||
|
fn indeterminate(self) -> T;
|
||||||
fn default(self) -> T;
|
fn default(self) -> T;
|
||||||
fn downloaded(self) -> T;
|
fn downloaded(self) -> T;
|
||||||
fn downloading(self) -> T;
|
fn downloading(self) -> T;
|
||||||
@@ -44,6 +45,10 @@ where
|
|||||||
self.fg(COLOR_ORANGE)
|
self.fg(COLOR_ORANGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn indeterminate(self) -> T {
|
||||||
|
self.fg(COLOR_ORANGE)
|
||||||
|
}
|
||||||
|
|
||||||
fn default(self) -> T {
|
fn default(self) -> T {
|
||||||
self.white()
|
self.white()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_style_indeterminate() {
|
||||||
|
assert_eq!(
|
||||||
|
Style::new().indeterminate(),
|
||||||
|
Style::new().fg(COLOR_ORANGE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_style_default() {
|
fn test_style_default() {
|
||||||
assert_eq!(Style::new().default(), Style::new().white());
|
assert_eq!(Style::new().default(), Style::new().white());
|
||||||
|
|||||||
@@ -1,50 +1,26 @@
|
|||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{layout_block, style_block_highlight};
|
use crate::ui::utils::{layout_block, style_block_highlight};
|
||||||
|
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::prelude::{Style, Text, Widget};
|
use ratatui::prelude::{Style, Text, Widget};
|
||||||
use ratatui::style::Styled;
|
use ratatui::style::Styled;
|
||||||
use ratatui::widgets::Paragraph;
|
use ratatui::widgets::Paragraph;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[derive(Default, Setters)]
|
||||||
#[path = "button_tests.rs"]
|
|
||||||
mod button_tests;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Button<'a> {
|
pub struct Button<'a> {
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
|
#[setters(strip_option)]
|
||||||
label: Option<&'a str>,
|
label: Option<&'a str>,
|
||||||
|
#[setters(strip_option)]
|
||||||
icon: Option<&'a str>,
|
icon: Option<&'a str>,
|
||||||
|
#[setters(into)]
|
||||||
style: Style,
|
style: Style,
|
||||||
|
#[setters(rename = "selected")]
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Button<'a> {
|
impl<'a> Button<'a> {
|
||||||
pub fn title(mut self, title: &'a str) -> Button<'a> {
|
|
||||||
self.title = title;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn label(mut self, label: &'a str) -> Button<'a> {
|
|
||||||
self.label = Some(label);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon(mut self, icon: &'a str) -> Button<'a> {
|
|
||||||
self.icon = Some(icon);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn style<S: Into<Style>>(mut self, style: S) -> Button<'a> {
|
|
||||||
self.style = style.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selected(mut self, is_selected: bool) -> Button<'a> {
|
|
||||||
self.is_selected = is_selected;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_button_with_icon(self, area: Rect, buf: &mut Buffer) {
|
fn render_button_with_icon(self, area: Rect, buf: &mut Buffer) {
|
||||||
let [title_area, icon_area] = Layout::horizontal([
|
let [title_area, icon_area] = Layout::horizontal([
|
||||||
Constraint::Length(self.title.len() as u16),
|
Constraint::Length(self.title.len() as u16),
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::ui::widgets::button::Button;
|
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
|
||||||
use ratatui::style::{Style, Stylize};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_title() {
|
|
||||||
let button = Button::default().title("Title");
|
|
||||||
|
|
||||||
assert_str_eq!(button.title, "Title");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_label() {
|
|
||||||
let button = Button::default().label("Label");
|
|
||||||
|
|
||||||
assert_eq!(button.label, Some("Label"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_icon() {
|
|
||||||
let button = Button::default().icon("Icon");
|
|
||||||
|
|
||||||
assert_eq!(button.icon, Some("Icon"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_style() {
|
|
||||||
let button = Button::default().style(Style::new().bold());
|
|
||||||
|
|
||||||
assert_eq!(button.style, Style::new().bold());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_selected() {
|
|
||||||
let button = Button::default().selected(true);
|
|
||||||
|
|
||||||
assert!(button.is_selected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use derive_setters::Setters;
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{borderless_block, layout_block, style_block_highlight};
|
use crate::ui::utils::{borderless_block, layout_block, style_block_highlight};
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
@@ -6,14 +7,13 @@ use ratatui::prelude::Text;
|
|||||||
use ratatui::style::Stylize;
|
use ratatui::style::Stylize;
|
||||||
use ratatui::widgets::{Paragraph, Widget};
|
use ratatui::widgets::{Paragraph, Widget};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[derive(PartialEq, Debug, Copy, Clone, Setters)]
|
||||||
#[path = "checkbox_tests.rs"]
|
|
||||||
mod checkbox_tests;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
|
||||||
pub struct Checkbox<'a> {
|
pub struct Checkbox<'a> {
|
||||||
|
#[setters(skip)]
|
||||||
label: &'a str,
|
label: &'a str,
|
||||||
|
#[setters(rename = "checked")]
|
||||||
is_checked: bool,
|
is_checked: bool,
|
||||||
|
#[setters(rename = "highlighted")]
|
||||||
is_highlighted: bool,
|
is_highlighted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,16 +26,6 @@ impl<'a> Checkbox<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn checked(mut self, is_checked: bool) -> Checkbox<'a> {
|
|
||||||
self.is_checked = is_checked;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn highlighted(mut self, is_selected: bool) -> Checkbox<'a> {
|
|
||||||
self.is_highlighted = is_selected;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_checkbox(self, area: Rect, buf: &mut Buffer) {
|
fn render_checkbox(self, area: Rect, buf: &mut Buffer) {
|
||||||
let check = if self.is_checked { "✔" } else { "" };
|
let check = if self.is_checked { "✔" } else { "" };
|
||||||
let [label_area, checkbox_area] =
|
let [label_area, checkbox_area] =
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::ui::widgets::checkbox::Checkbox;
|
|
||||||
use pretty_assertions::assert_str_eq;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_checkbox_new() {
|
|
||||||
let checkbox = Checkbox::new("test");
|
|
||||||
|
|
||||||
assert_str_eq!(checkbox.label, "test");
|
|
||||||
assert!(!checkbox.is_checked);
|
|
||||||
assert!(!checkbox.is_highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_checkbox_checked() {
|
|
||||||
let checkbox = Checkbox::new("test").checked(true);
|
|
||||||
|
|
||||||
assert_str_eq!(checkbox.label, "test");
|
|
||||||
assert!(checkbox.is_checked);
|
|
||||||
assert!(!checkbox.is_highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_checkbox_highlighted() {
|
|
||||||
let checkbox = Checkbox::new("test").highlighted(true);
|
|
||||||
|
|
||||||
assert_str_eq!(checkbox.label, "test");
|
|
||||||
assert!(!checkbox.is_checked);
|
|
||||||
assert!(checkbox.is_highlighted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,15 +8,19 @@ use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
|||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Paragraph, Widget};
|
use ratatui::widgets::{Paragraph, Widget};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use derive_setters::Setters;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "confirmation_prompt_tests.rs"]
|
#[path = "confirmation_prompt_tests.rs"]
|
||||||
mod confirmation_prompt_tests;
|
mod confirmation_prompt_tests;
|
||||||
|
|
||||||
|
#[derive(Setters)]
|
||||||
pub struct ConfirmationPrompt<'a> {
|
pub struct ConfirmationPrompt<'a> {
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
prompt: &'a str,
|
prompt: &'a str,
|
||||||
|
#[setters(strip_option)]
|
||||||
content: Option<Paragraph<'a>>,
|
content: Option<Paragraph<'a>>,
|
||||||
|
#[setters(strip_option)]
|
||||||
checkboxes: Option<Vec<Checkbox<'a>>>,
|
checkboxes: Option<Vec<Checkbox<'a>>>,
|
||||||
yes_no_value: bool,
|
yes_no_value: bool,
|
||||||
yes_no_highlighted: bool,
|
yes_no_highlighted: bool,
|
||||||
@@ -34,36 +38,6 @@ impl<'a> ConfirmationPrompt<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn title(mut self, title: &'a str) -> Self {
|
|
||||||
self.title = title;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prompt(mut self, prompt: &'a str) -> Self {
|
|
||||||
self.prompt = prompt;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn content(mut self, content: Paragraph<'a>) -> Self {
|
|
||||||
self.content = Some(content);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn checkboxes(mut self, checkboxes: Vec<Checkbox<'a>>) -> Self {
|
|
||||||
self.checkboxes = Some(checkboxes);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn yes_no_value(mut self, yes_highlighted: bool) -> Self {
|
|
||||||
self.yes_no_value = yes_highlighted;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn yes_no_highlighted(mut self, yes_highlighted: bool) -> Self {
|
|
||||||
self.yes_no_highlighted = yes_highlighted;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
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 =
|
let help_text =
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::ui::widgets::checkbox::Checkbox;
|
|
||||||
use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt;
|
use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt;
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use ratatui::widgets::Paragraph;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_confirmation_prompt_new() {
|
fn test_confirmation_prompt_new() {
|
||||||
@@ -16,78 +14,4 @@ mod tests {
|
|||||||
assert!(!confirmation_prompt.yes_no_value);
|
assert!(!confirmation_prompt.yes_no_value);
|
||||||
assert!(confirmation_prompt.yes_no_highlighted);
|
assert!(confirmation_prompt.yes_no_highlighted);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_confirmation_prompt_title() {
|
|
||||||
let confirmation_prompt = ConfirmationPrompt::new().title("title");
|
|
||||||
|
|
||||||
assert_str_eq!(confirmation_prompt.title, "title");
|
|
||||||
assert_str_eq!(confirmation_prompt.prompt, "");
|
|
||||||
assert_eq!(confirmation_prompt.content, None);
|
|
||||||
assert_eq!(confirmation_prompt.checkboxes, None);
|
|
||||||
assert!(!confirmation_prompt.yes_no_value);
|
|
||||||
assert!(confirmation_prompt.yes_no_highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_confirmation_prompt_prompt() {
|
|
||||||
let confirmation_prompt = ConfirmationPrompt::new().prompt("prompt");
|
|
||||||
|
|
||||||
assert_str_eq!(confirmation_prompt.prompt, "prompt");
|
|
||||||
assert_str_eq!(confirmation_prompt.title, "");
|
|
||||||
assert_eq!(confirmation_prompt.content, None);
|
|
||||||
assert_eq!(confirmation_prompt.checkboxes, None);
|
|
||||||
assert!(!confirmation_prompt.yes_no_value);
|
|
||||||
assert!(confirmation_prompt.yes_no_highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_confirmation_prompt_content() {
|
|
||||||
let content = Paragraph::new("content");
|
|
||||||
let confirmation_prompt = ConfirmationPrompt::new().content(content.clone());
|
|
||||||
|
|
||||||
assert_eq!(confirmation_prompt.content, Some(content));
|
|
||||||
assert_str_eq!(confirmation_prompt.title, "");
|
|
||||||
assert_str_eq!(confirmation_prompt.prompt, "");
|
|
||||||
assert_eq!(confirmation_prompt.checkboxes, None);
|
|
||||||
assert!(!confirmation_prompt.yes_no_value);
|
|
||||||
assert!(confirmation_prompt.yes_no_highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_confirmation_prompt_checkboxes() {
|
|
||||||
let checkboxes = vec![Checkbox::new("test").highlighted(true).checked(false)];
|
|
||||||
let confirmation_prompt = ConfirmationPrompt::new().checkboxes(checkboxes.clone());
|
|
||||||
|
|
||||||
assert_eq!(confirmation_prompt.checkboxes, Some(checkboxes));
|
|
||||||
assert_str_eq!(confirmation_prompt.title, "");
|
|
||||||
assert_str_eq!(confirmation_prompt.prompt, "");
|
|
||||||
assert_eq!(confirmation_prompt.content, None);
|
|
||||||
assert!(!confirmation_prompt.yes_no_value);
|
|
||||||
assert!(confirmation_prompt.yes_no_highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_confirmation_prompt_yes_no_value() {
|
|
||||||
let confirmation_prompt = ConfirmationPrompt::new().yes_no_value(true);
|
|
||||||
|
|
||||||
assert!(confirmation_prompt.yes_no_value);
|
|
||||||
assert_str_eq!(confirmation_prompt.title, "");
|
|
||||||
assert_str_eq!(confirmation_prompt.prompt, "");
|
|
||||||
assert_eq!(confirmation_prompt.content, None);
|
|
||||||
assert_eq!(confirmation_prompt.checkboxes, None);
|
|
||||||
assert!(confirmation_prompt.yes_no_highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_confirmation_prompt_yes_no_highlighted() {
|
|
||||||
let confirmation_prompt = ConfirmationPrompt::new().yes_no_highlighted(false);
|
|
||||||
|
|
||||||
assert!(!confirmation_prompt.yes_no_highlighted);
|
|
||||||
assert_str_eq!(confirmation_prompt.title, "");
|
|
||||||
assert_str_eq!(confirmation_prompt.prompt, "");
|
|
||||||
assert_eq!(confirmation_prompt.content, None);
|
|
||||||
assert_eq!(confirmation_prompt.checkboxes, None);
|
|
||||||
assert!(!confirmation_prompt.yes_no_value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use derive_setters::Setters;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::{Constraint, Layout, Position, Rect};
|
use ratatui::layout::{Constraint, Layout, Position, Rect};
|
||||||
use ratatui::prelude::Text;
|
use ratatui::prelude::Text;
|
||||||
@@ -12,16 +13,20 @@ use crate::ui::utils::{borderless_block, layout_block};
|
|||||||
#[path = "input_box_tests.rs"]
|
#[path = "input_box_tests.rs"]
|
||||||
mod input_box_tests;
|
mod input_box_tests;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Setters)]
|
||||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||||
pub struct InputBox<'a> {
|
pub struct InputBox<'a> {
|
||||||
content: &'a str,
|
content: &'a str,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
#[setters(into)]
|
||||||
style: Style,
|
style: Style,
|
||||||
block: Block<'a>,
|
block: Block<'a>,
|
||||||
|
#[setters(strip_option)]
|
||||||
label: Option<&'a str>,
|
label: Option<&'a str>,
|
||||||
cursor_after_string: bool,
|
cursor_after_string: bool,
|
||||||
|
#[setters(rename = "highlighted", strip_option)]
|
||||||
is_highlighted: Option<bool>,
|
is_highlighted: Option<bool>,
|
||||||
|
#[setters(rename = "selected", strip_option)]
|
||||||
is_selected: Option<bool>,
|
is_selected: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,41 +44,6 @@ impl<'a> InputBox<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style<S: Into<Style>>(mut self, style: S) -> InputBox<'a> {
|
|
||||||
self.style = style.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn block(mut self, block: Block<'a>) -> InputBox<'a> {
|
|
||||||
self.block = block;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn label(mut self, label: &'a str) -> InputBox<'a> {
|
|
||||||
self.label = Some(label);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn offset(mut self, offset: usize) -> InputBox<'a> {
|
|
||||||
self.offset = offset;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cursor_after_string(mut self, cursor_after_string: bool) -> InputBox<'a> {
|
|
||||||
self.cursor_after_string = cursor_after_string;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn highlighted(mut self, is_highlighted: bool) -> InputBox<'a> {
|
|
||||||
self.is_highlighted = Some(is_highlighted);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selected(mut self, is_selected: bool) -> InputBox<'a> {
|
|
||||||
self.is_selected = Some(is_selected);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_selected(&self) -> bool {
|
pub fn is_selected(&self) -> bool {
|
||||||
self.is_selected.unwrap_or_default()
|
self.is_selected.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,104 +20,6 @@ mod tests {
|
|||||||
assert_eq!(input_box.is_selected, None);
|
assert_eq!(input_box.is_selected, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_input_box_style() {
|
|
||||||
let input_box = InputBox::new("test").style(Style::new().highlight());
|
|
||||||
|
|
||||||
assert_eq!(input_box.style, Style::new().highlight());
|
|
||||||
assert_str_eq!(input_box.content, "test");
|
|
||||||
assert_eq!(input_box.offset, 0);
|
|
||||||
assert_eq!(input_box.block, layout_block());
|
|
||||||
assert_eq!(input_box.label, None);
|
|
||||||
assert!(input_box.cursor_after_string);
|
|
||||||
assert_eq!(input_box.is_highlighted, None);
|
|
||||||
assert_eq!(input_box.is_selected, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_input_box_block() {
|
|
||||||
let input_box = InputBox::new("test").block(layout_block().title("title"));
|
|
||||||
|
|
||||||
assert_eq!(input_box.block, layout_block().title("title"));
|
|
||||||
assert_str_eq!(input_box.content, "test");
|
|
||||||
assert_eq!(input_box.offset, 0);
|
|
||||||
assert_eq!(input_box.style, Style::new().default());
|
|
||||||
assert_eq!(input_box.label, None);
|
|
||||||
assert!(input_box.cursor_after_string);
|
|
||||||
assert_eq!(input_box.is_highlighted, None);
|
|
||||||
assert_eq!(input_box.is_selected, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_input_box_label() {
|
|
||||||
let input_box = InputBox::new("test").label("label");
|
|
||||||
|
|
||||||
assert_str_eq!(input_box.label.unwrap(), "label");
|
|
||||||
assert_str_eq!(input_box.content, "test");
|
|
||||||
assert_eq!(input_box.offset, 0);
|
|
||||||
assert_eq!(input_box.style, Style::new().default());
|
|
||||||
assert_eq!(input_box.block, layout_block());
|
|
||||||
assert!(input_box.cursor_after_string);
|
|
||||||
assert_eq!(input_box.is_highlighted, None);
|
|
||||||
assert_eq!(input_box.is_selected, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_input_box_offset() {
|
|
||||||
let input_box = InputBox::new("test").offset(1);
|
|
||||||
|
|
||||||
assert_eq!(input_box.offset, 1);
|
|
||||||
assert_str_eq!(input_box.content, "test");
|
|
||||||
assert_eq!(input_box.style, Style::new().default());
|
|
||||||
assert_eq!(input_box.block, layout_block());
|
|
||||||
assert_eq!(input_box.label, None);
|
|
||||||
assert!(input_box.cursor_after_string);
|
|
||||||
assert_eq!(input_box.is_highlighted, None);
|
|
||||||
assert_eq!(input_box.is_selected, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_input_box_cursor_after_string() {
|
|
||||||
let input_box = InputBox::new("test").cursor_after_string(false);
|
|
||||||
|
|
||||||
assert!(!input_box.cursor_after_string);
|
|
||||||
assert_str_eq!(input_box.content, "test");
|
|
||||||
assert_eq!(input_box.offset, 0);
|
|
||||||
assert_eq!(input_box.style, Style::new().default());
|
|
||||||
assert_eq!(input_box.block, layout_block());
|
|
||||||
assert_eq!(input_box.label, None);
|
|
||||||
assert_eq!(input_box.is_highlighted, None);
|
|
||||||
assert_eq!(input_box.is_selected, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_input_box_highlighted() {
|
|
||||||
let input_box = InputBox::new("test").highlighted(true);
|
|
||||||
|
|
||||||
assert_eq!(input_box.is_highlighted, Some(true));
|
|
||||||
assert_str_eq!(input_box.content, "test");
|
|
||||||
assert_eq!(input_box.offset, 0);
|
|
||||||
assert_eq!(input_box.style, Style::new().default());
|
|
||||||
assert_eq!(input_box.block, layout_block());
|
|
||||||
assert_eq!(input_box.label, None);
|
|
||||||
assert!(input_box.cursor_after_string);
|
|
||||||
assert_eq!(input_box.is_selected, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_input_box_selected() {
|
|
||||||
let input_box = InputBox::new("test").selected(true);
|
|
||||||
|
|
||||||
assert_eq!(input_box.is_selected, Some(true));
|
|
||||||
assert_str_eq!(input_box.content, "test");
|
|
||||||
assert_eq!(input_box.offset, 0);
|
|
||||||
assert_eq!(input_box.style, Style::new().default());
|
|
||||||
assert_eq!(input_box.block, layout_block());
|
|
||||||
assert_eq!(input_box.label, None);
|
|
||||||
assert!(input_box.cursor_after_string);
|
|
||||||
assert_eq!(input_box.is_highlighted, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_input_box_is_selected() {
|
fn test_input_box_is_selected() {
|
||||||
let input_box = InputBox::new("test").selected(true);
|
let input_box = InputBox::new("test").selected(true);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use ratatui::widgets::{Block, ListItem, Paragraph, Row, StatefulWidget, Table, W
|
|||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
use derive_setters::Setters;
|
||||||
use super::input_box_popup::InputBoxPopup;
|
use super::input_box_popup::InputBoxPopup;
|
||||||
use super::message::Message;
|
use super::message::Message;
|
||||||
use super::popup::Size;
|
use super::popup::Size;
|
||||||
@@ -21,24 +21,32 @@ use super::popup::Size;
|
|||||||
#[path = "managarr_table_tests.rs"]
|
#[path = "managarr_table_tests.rs"]
|
||||||
mod managarr_table_tests;
|
mod managarr_table_tests;
|
||||||
|
|
||||||
|
#[derive(Setters)]
|
||||||
pub struct ManagarrTable<'a, T, F>
|
pub struct ManagarrTable<'a, T, F>
|
||||||
where
|
where
|
||||||
F: Fn(&T) -> Row<'a>,
|
F: Fn(&T) -> Row<'a>,
|
||||||
T: Clone + PartialEq + Eq + Debug,
|
T: Clone + PartialEq + Eq + Debug,
|
||||||
{
|
{
|
||||||
|
#[setters(strip_option)]
|
||||||
content: Option<&'a mut StatefulTable<T>>,
|
content: Option<&'a mut StatefulTable<T>>,
|
||||||
|
#[setters(skip)]
|
||||||
table_headers: Vec<String>,
|
table_headers: Vec<String>,
|
||||||
|
#[setters(skip)]
|
||||||
constraints: Vec<Constraint>,
|
constraints: Vec<Constraint>,
|
||||||
row_mapper: F,
|
row_mapper: F,
|
||||||
footer: Option<String>,
|
footer: Option<String>,
|
||||||
footer_alignment: Alignment,
|
footer_alignment: Alignment,
|
||||||
block: Block<'a>,
|
block: Block<'a>,
|
||||||
margin: u16,
|
margin: u16,
|
||||||
|
#[setters(rename = "loading")]
|
||||||
is_loading: bool,
|
is_loading: bool,
|
||||||
highlight_rows: bool,
|
highlight_rows: bool,
|
||||||
|
#[setters(rename = "sorting")]
|
||||||
is_sorting: bool,
|
is_sorting: bool,
|
||||||
|
#[setters(rename = "searching")]
|
||||||
is_searching: bool,
|
is_searching: bool,
|
||||||
search_produced_empty_results: bool,
|
search_produced_empty_results: bool,
|
||||||
|
#[setters(rename = "filtering")]
|
||||||
is_filtering: bool,
|
is_filtering: bool,
|
||||||
filter_produced_empty_results: bool,
|
filter_produced_empty_results: bool,
|
||||||
search_box_content_length: usize,
|
search_box_content_length: usize,
|
||||||
@@ -107,61 +115,6 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn footer(mut self, footer: Option<String>) -> Self {
|
|
||||||
self.footer = footer;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn footer_alignment(mut self, alignment: Alignment) -> Self {
|
|
||||||
self.footer_alignment = alignment;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn block(mut self, block: Block<'a>) -> Self {
|
|
||||||
self.block = block;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn margin(mut self, margin: u16) -> Self {
|
|
||||||
self.margin = margin;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn loading(mut self, is_loading: bool) -> Self {
|
|
||||||
self.is_loading = is_loading;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn highlight_rows(mut self, highlight_rows: bool) -> Self {
|
|
||||||
self.highlight_rows = highlight_rows;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sorting(mut self, is_sorting: bool) -> Self {
|
|
||||||
self.is_sorting = is_sorting;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn searching(mut self, is_searching: bool) -> Self {
|
|
||||||
self.is_searching = is_searching;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn search_produced_empty_results(mut self, no_search_results: bool) -> Self {
|
|
||||||
self.search_produced_empty_results = no_search_results;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn filtering(mut self, is_filtering: bool) -> Self {
|
|
||||||
self.is_filtering = is_filtering;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn filter_produced_empty_results(mut self, no_filter_results: bool) -> Self {
|
|
||||||
self.filter_produced_empty_results = no_filter_results;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_table(self, area: Rect, buf: &mut Buffer) {
|
fn render_table(self, area: Rect, buf: &mut Buffer) {
|
||||||
let table_headers = self.parse_headers();
|
let table_headers = self.parse_headers();
|
||||||
let table_area = if let Some(ref footer) = self.footer {
|
let table_area = if let Some(ref footer) = self.footer {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ mod tests {
|
|||||||
use crate::models::stateful_list::StatefulList;
|
use crate::models::stateful_list::StatefulList;
|
||||||
use crate::models::stateful_table::{SortOption, StatefulTable};
|
use crate::models::stateful_table::{SortOption, StatefulTable};
|
||||||
use crate::models::{HorizontallyScrollableText, Scrollable};
|
use crate::models::{HorizontallyScrollableText, Scrollable};
|
||||||
use crate::ui::utils::layout_block;
|
|
||||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use ratatui::layout::{Alignment, Constraint};
|
use ratatui::layout::{Alignment, Constraint};
|
||||||
@@ -180,358 +179,6 @@ mod tests {
|
|||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
assert_eq!(managarr_table.filter_box_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_footer() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
let footer = "footer".to_owned();
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.footer(Some(footer.clone()));
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert_eq!(managarr_table.footer, Some(footer));
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_footer_alignment() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.footer_alignment(Alignment::Center);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Center);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_block() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.block(layout_block());
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert_eq!(managarr_table.block, layout_block());
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_margin() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)])).margin(1);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert_eq!(managarr_table.margin, 1);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_loading() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.loading(true);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert!(managarr_table.is_loading);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_highlight_rows() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.highlight_rows(false);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert!(!managarr_table.highlight_rows);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_sorting() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.sorting(true);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert!(managarr_table.is_sorting);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_is_searching() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.searching(true);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert!(managarr_table.is_searching);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_search_produced_empty_results() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.search_produced_empty_results(true);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert!(managarr_table.search_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_is_filtering() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.filtering(true);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert!(managarr_table.is_filtering);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_managarr_table_filter_produced_empty_results() {
|
|
||||||
let items = vec!["item1", "item2", "item3"];
|
|
||||||
let mut stateful_table = StatefulTable::default();
|
|
||||||
stateful_table.set_items(items.clone());
|
|
||||||
|
|
||||||
let managarr_table =
|
|
||||||
ManagarrTable::new(Some(&mut stateful_table), |&s| Row::new(vec![Cell::new(s)]))
|
|
||||||
.filter_produced_empty_results(true);
|
|
||||||
|
|
||||||
let row_mapper = managarr_table.row_mapper;
|
|
||||||
assert!(managarr_table.filter_produced_empty_results);
|
|
||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, Block::new());
|
|
||||||
assert_eq!(managarr_table.margin, 0);
|
|
||||||
assert!(!managarr_table.is_loading);
|
|
||||||
assert!(managarr_table.highlight_rows);
|
|
||||||
assert!(!managarr_table.is_sorting);
|
|
||||||
assert!(!managarr_table.is_searching);
|
|
||||||
assert!(!managarr_table.search_produced_empty_results);
|
|
||||||
assert!(!managarr_table.is_filtering);
|
|
||||||
assert_eq!(managarr_table.search_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.search_box_offset, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_content_length, 0);
|
|
||||||
assert_eq!(managarr_table.filter_box_offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_managarr_table_parse_headers() {
|
fn test_managarr_table_parse_headers() {
|
||||||
let items = vec!["item1", "item2", "item3"];
|
let items = vec!["item1", "item2", "item3"];
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::title_block_centered;
|
use crate::ui::utils::title_block_centered;
|
||||||
|
use derive_setters::Setters;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::{Alignment, Rect};
|
use ratatui::layout::{Alignment, Rect};
|
||||||
use ratatui::style::{Style, Stylize};
|
use ratatui::style::{Style, Stylize};
|
||||||
@@ -10,6 +11,7 @@ use ratatui::widgets::{Paragraph, Widget, Wrap};
|
|||||||
#[path = "message_tests.rs"]
|
#[path = "message_tests.rs"]
|
||||||
mod message_tests;
|
mod message_tests;
|
||||||
|
|
||||||
|
#[derive(Setters)]
|
||||||
pub struct Message<'a> {
|
pub struct Message<'a> {
|
||||||
text: Text<'a>,
|
text: Text<'a>,
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
@@ -30,21 +32,6 @@ impl<'a> Message<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn title(mut self, title: &'a str) -> Self {
|
|
||||||
self.title = title;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn style(mut self, style: Style) -> Self {
|
|
||||||
self.style = style;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alignment(mut self, alignment: Alignment) -> Self {
|
|
||||||
self.alignment = alignment;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_message(self, area: Rect, buf: &mut Buffer) {
|
fn render_message(self, area: Rect, buf: &mut Buffer) {
|
||||||
Paragraph::new(self.text)
|
Paragraph::new(self.text)
|
||||||
.style(self.style)
|
.style(self.style)
|
||||||
|
|||||||
@@ -18,42 +18,4 @@ mod tests {
|
|||||||
assert_eq!(message.style, Style::new().failure().bold());
|
assert_eq!(message.style, Style::new().failure().bold());
|
||||||
assert_eq!(message.alignment, Alignment::Center);
|
assert_eq!(message.alignment, Alignment::Center);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_message_title() {
|
|
||||||
let test_message = "This is a message";
|
|
||||||
let title = "Success";
|
|
||||||
|
|
||||||
let message = Message::new(test_message).title(title);
|
|
||||||
|
|
||||||
assert_str_eq!(message.title, title);
|
|
||||||
assert_eq!(message.text, Text::from(test_message));
|
|
||||||
assert_eq!(message.style, Style::new().failure().bold());
|
|
||||||
assert_eq!(message.alignment, Alignment::Center);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_message_style() {
|
|
||||||
let test_message = "This is a message";
|
|
||||||
let style = Style::new().success().bold();
|
|
||||||
|
|
||||||
let message = Message::new(test_message).style(style);
|
|
||||||
|
|
||||||
assert_eq!(message.style, style);
|
|
||||||
assert_eq!(message.text, Text::from(test_message));
|
|
||||||
assert_str_eq!(message.title, "Error");
|
|
||||||
assert_eq!(message.alignment, Alignment::Center);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_message_alignment() {
|
|
||||||
let test_message = "This is a message";
|
|
||||||
|
|
||||||
let message = Message::new(test_message).alignment(Alignment::Left);
|
|
||||||
|
|
||||||
assert_eq!(message.alignment, Alignment::Left);
|
|
||||||
assert_eq!(message.text, Text::from(test_message));
|
|
||||||
assert_str_eq!(message.title, "Error");
|
|
||||||
assert_eq!(message.style, Style::new().failure().bold());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user