refactor: Simplified both the table_handler macro and the stateful_table implementation

This commit is contained in:
2025-12-04 11:34:45 -07:00
parent 659023d561
commit 71240373c0
14 changed files with 192 additions and 419 deletions
@@ -38,7 +38,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
let blocklist_table_handling_config = let blocklist_table_handling_config =
TableHandlingConfig::new(ActiveRadarrBlock::Blocklist.into()) TableHandlingConfig::new(ActiveRadarrBlock::Blocklist.into())
.sorting_block(ActiveRadarrBlock::BlocklistSortPrompt.into()) .sorting_block(ActiveRadarrBlock::BlocklistSortPrompt.into())
.sort_by_fn(|a: &BlocklistItem, b: &BlocklistItem| a.id.cmp(&b.id))
.sort_options(blocklist_sorting_options()); .sort_options(blocklist_sorting_options());
if !self.handle_blocklist_table_events(blocklist_table_handling_config) { if !self.handle_blocklist_table_events(blocklist_table_handling_config) {
@@ -42,7 +42,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
let collections_table_handling_config = let collections_table_handling_config =
TableHandlingConfig::new(ActiveRadarrBlock::Collections.into()) TableHandlingConfig::new(ActiveRadarrBlock::Collections.into())
.sorting_block(ActiveRadarrBlock::CollectionsSortPrompt.into()) .sorting_block(ActiveRadarrBlock::CollectionsSortPrompt.into())
.sort_by_fn(|a: &Collection, b: &Collection| a.id.cmp(&b.id))
.sort_options(collections_sorting_options()) .sort_options(collections_sorting_options())
.searching_block(ActiveRadarrBlock::SearchCollection.into()) .searching_block(ActiveRadarrBlock::SearchCollection.into())
.search_error_block(ActiveRadarrBlock::SearchCollectionError.into()) .search_error_block(ActiveRadarrBlock::SearchCollectionError.into())
@@ -7,7 +7,6 @@ use crate::models::servarr_data::radarr::modals::AddMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, ActiveRadarrBlock,
}; };
use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Scrollable}; use crate::models::{BlockSelectionState, Scrollable};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{ use crate::{
@@ -35,7 +34,7 @@ impl AddMovieHandler<'_, '_> {
.radarr_data .radarr_data
.add_searched_movies .add_searched_movies
.as_mut() .as_mut()
.unwrap_or(&mut StatefulTable::default()), .expect("add_searched_movies should be initialized"),
AddMovieSearchResult AddMovieSearchResult
); );
@@ -44,7 +44,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
fn handle(&mut self) { fn handle(&mut self) {
let movie_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Movies.into()) let movie_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Movies.into())
.sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into()) .sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into())
.sort_by_fn(|a: &Movie, b: &Movie| a.id.cmp(&b.id))
.sort_options(movies_sorting_options()) .sort_options(movies_sorting_options())
.searching_block(ActiveRadarrBlock::SearchMovie.into()) .searching_block(ActiveRadarrBlock::SearchMovie.into())
.search_error_block(ActiveRadarrBlock::SearchMovieError.into()) .search_error_block(ActiveRadarrBlock::SearchMovieError.into())
@@ -38,7 +38,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for BlocklistHandler<'a,
let blocklist_table_handling_config = let blocklist_table_handling_config =
TableHandlingConfig::new(ActiveSonarrBlock::Blocklist.into()) TableHandlingConfig::new(ActiveSonarrBlock::Blocklist.into())
.sorting_block(ActiveSonarrBlock::BlocklistSortPrompt.into()) .sorting_block(ActiveSonarrBlock::BlocklistSortPrompt.into())
.sort_by_fn(|a: &BlocklistItem, b: &BlocklistItem| a.id.cmp(&b.id))
.sort_options(blocklist_sorting_options()); .sort_options(blocklist_sorting_options());
if !self.handle_blocklist_table_events(blocklist_table_handling_config) { if !self.handle_blocklist_table_events(blocklist_table_handling_config) {
@@ -33,7 +33,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for HistoryHandler<'a, '
fn handle(&mut self) { fn handle(&mut self) {
let history_table_handling_config = TableHandlingConfig::new(ActiveSonarrBlock::History.into()) let history_table_handling_config = TableHandlingConfig::new(ActiveSonarrBlock::History.into())
.sorting_block(ActiveSonarrBlock::HistorySortPrompt.into()) .sorting_block(ActiveSonarrBlock::HistorySortPrompt.into())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.sort_options(history_sorting_options()) .sort_options(history_sorting_options())
.searching_block(ActiveSonarrBlock::SearchHistory.into()) .searching_block(ActiveSonarrBlock::SearchHistory.into())
.search_error_block(ActiveSonarrBlock::SearchHistoryError.into()) .search_error_block(ActiveSonarrBlock::SearchHistoryError.into())
@@ -5,7 +5,6 @@ use crate::models::servarr_data::sonarr::sonarr_data::{
ADD_SERIES_BLOCKS, ADD_SERIES_SELECTION_BLOCKS, ActiveSonarrBlock, ADD_SERIES_BLOCKS, ADD_SERIES_SELECTION_BLOCKS, ActiveSonarrBlock,
}; };
use crate::models::sonarr_models::{AddSeriesBody, AddSeriesOptions, AddSeriesSearchResult}; use crate::models::sonarr_models::{AddSeriesBody, AddSeriesOptions, AddSeriesSearchResult};
use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Scrollable}; use crate::models::{BlockSelectionState, Scrollable};
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
use crate::{ use crate::{
@@ -33,7 +32,7 @@ impl AddSeriesHandler<'_, '_> {
.sonarr_data .sonarr_data
.add_searched_series .add_searched_series
.as_mut() .as_mut()
.unwrap_or(&mut StatefulTable::default()), .expect("add_searched_series should be initialized"),
AddSeriesSearchResult AddSeriesSearchResult
); );
@@ -55,7 +55,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, '
fn handle(&mut self) { fn handle(&mut self) {
let series_table_handling_config = TableHandlingConfig::new(ActiveSonarrBlock::Series.into()) let series_table_handling_config = TableHandlingConfig::new(ActiveSonarrBlock::Series.into())
.sorting_block(ActiveSonarrBlock::SeriesSortPrompt.into()) .sorting_block(ActiveSonarrBlock::SeriesSortPrompt.into())
.sort_by_fn(|a: &Series, b: &Series| a.id.cmp(&b.id))
.sort_options(series_sorting_options()) .sort_options(series_sorting_options())
.searching_block(ActiveSonarrBlock::SearchSeries.into()) .searching_block(ActiveSonarrBlock::SearchSeries.into())
.search_error_block(ActiveSonarrBlock::SearchSeriesError.into()) .search_error_block(ActiveSonarrBlock::SearchSeriesError.into())
@@ -115,7 +115,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler
TableHandlingConfig::new(ActiveSonarrBlock::SeasonHistory.into()) TableHandlingConfig::new(ActiveSonarrBlock::SeasonHistory.into())
.sorting_block(ActiveSonarrBlock::SeasonHistorySortPrompt.into()) .sorting_block(ActiveSonarrBlock::SeasonHistorySortPrompt.into())
.sort_options(history_sorting_options()) .sort_options(history_sorting_options())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.searching_block(ActiveSonarrBlock::SearchSeasonHistory.into()) .searching_block(ActiveSonarrBlock::SearchSeasonHistory.into())
.search_error_block(ActiveSonarrBlock::SearchSeasonHistoryError.into()) .search_error_block(ActiveSonarrBlock::SearchSeasonHistoryError.into())
.search_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text) .search_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text)
@@ -71,7 +71,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
TableHandlingConfig::new(ActiveSonarrBlock::SeriesHistory.into()) TableHandlingConfig::new(ActiveSonarrBlock::SeriesHistory.into())
.sorting_block(ActiveSonarrBlock::SeriesHistorySortPrompt.into()) .sorting_block(ActiveSonarrBlock::SeriesHistorySortPrompt.into())
.sort_options(history_sorting_options()) .sort_options(history_sorting_options())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.searching_block(ActiveSonarrBlock::SearchSeriesHistory.into()) .searching_block(ActiveSonarrBlock::SearchSeriesHistory.into())
.search_error_block(ActiveSonarrBlock::SearchSeriesHistoryError.into()) .search_error_block(ActiveSonarrBlock::SearchSeriesHistoryError.into())
.search_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text) .search_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text)
+120 -187
View File
@@ -1,7 +1,6 @@
use crate::models::Route; use crate::models::Route;
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use derive_setters::Setters; use derive_setters::Setters;
use std::cmp::Ordering;
use std::fmt::Debug; use std::fmt::Debug;
#[cfg(test)] #[cfg(test)]
@@ -18,8 +17,6 @@ where
#[setters(strip_option)] #[setters(strip_option)]
pub sort_options: Option<Vec<SortOption<T>>>, pub sort_options: Option<Vec<SortOption<T>>>,
#[setters(strip_option)] #[setters(strip_option)]
pub sort_by_fn: Option<fn(&T, &T) -> Ordering>,
#[setters(strip_option)]
pub searching_block: Option<Route>, pub searching_block: Option<Route>,
#[setters(strip_option)] #[setters(strip_option)]
pub search_error_block: Option<Route>, pub search_error_block: Option<Route>,
@@ -56,16 +53,12 @@ macro_rules! handle_table_events {
_ if $crate::matches_key!(submit, $self.key) => $self.[<handle_ $name _table_submit>](config), _ if $crate::matches_key!(submit, $self.key) => $self.[<handle_ $name _table_submit>](config),
_ if $crate::matches_key!(esc, $self.key) => $self.[<handle_ $name _table_esc>](config), _ if $crate::matches_key!(esc, $self.key) => $self.[<handle_ $name _table_esc>](config),
_ if config.searching_block.is_some() _ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
.as_ref()
.expect("searching_block must be configured for this table") =>
{ {
$self.[<handle_ $name _table_search_box_input>]() $self.[<handle_ $name _table_search_box_input>]()
} }
_ if config.filtering_block.is_some() _ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
.as_ref()
.expect("filtering_block must be configured for this table") =>
{ {
$self.[<handle_ $name _table_filter_box_input>]() $self.[<handle_ $name _table_filter_box_input>]()
} }
@@ -91,11 +84,11 @@ macro_rules! handle_table_events {
true true
} }
_ if config.sorting_block.is_some() _ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
.as_ref()
.expect("sorting_block must be configured for this table") =>
{ {
$table.sort.as_mut().unwrap().scroll_up(); if let Some(ref mut sort) = $table.sort {
sort.scroll_up();
}
true true
} }
_ => false, _ => false,
@@ -111,27 +104,12 @@ macro_rules! handle_table_events {
true true
} }
_ if config.sorting_block.is_some() _ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
.as_ref()
.expect("sorting_block must be configured for this table") =>
{ {
$table if let Some(ref mut sort) = $table.sort {
.sort sort
.as_mut()
.unwrap()
.scroll_down(); .scroll_down();
true
} }
_ => false,
}
}
fn [<handle_ $name _table_page_down>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
use $crate::models::Paginated;
match $self.app.get_current_route() {
_ if config.table_block == $self.app.get_current_route() => {
$table.page_down();
true true
} }
_ => false, _ => false,
@@ -141,12 +119,22 @@ macro_rules! handle_table_events {
fn [<handle_ $name _table_page_up>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool { fn [<handle_ $name _table_page_up>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
use $crate::models::Paginated; use $crate::models::Paginated;
match $self.app.get_current_route() { if config.table_block == $self.app.get_current_route() {
_ if config.table_block == $self.app.get_current_route() => {
$table.page_up(); $table.page_up();
true true
} else {
false
} }
_ => false, }
fn [<handle_ $name _table_page_down>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
use $crate::models::Paginated;
if config.table_block == $self.app.get_current_route() {
$table.page_down();
true
} else {
false
} }
} }
@@ -159,39 +147,27 @@ macro_rules! handle_table_events {
true true
} }
_ if config.sorting_block.is_some() _ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
.as_ref()
.expect("sorting_block must be configured for this table") =>
{ {
$table if let Some(ref mut sort) = $table.sort {
.sort sort.scroll_to_top();
.as_mut() }
.unwrap()
.scroll_to_top();
true true
} }
_ if config.searching_block.is_some() _ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
.as_ref()
.expect("searching_block must be configured for this table") =>
{ {
$table if let Some(ref mut search) = $table.search {
.search search.scroll_home();
.as_mut() }
.unwrap()
.scroll_home();
true true
} }
_ if config.filtering_block.is_some() _ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
.as_ref()
.expect("filtering_block must be configured for this table") =>
{ {
$table if let Some(ref mut filter) = $table.filter {
.filter filter.scroll_home();
.as_mut() }
.unwrap()
.scroll_home();
true true
} }
_ => false, _ => false,
@@ -207,39 +183,27 @@ macro_rules! handle_table_events {
true true
} }
_ if config.sorting_block.is_some() _ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
.as_ref()
.expect("sorting_block must be configured for this table") =>
{ {
$table if let Some(ref mut sort) = $table.sort {
.sort sort.scroll_to_bottom();
.as_mut() }
.unwrap()
.scroll_to_bottom();
true true
} }
_ if config.searching_block.is_some() _ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
.as_ref()
.expect("searching_block must be configured for this table") =>
{ {
$table if let Some(ref mut search) = $table.search {
.search search.reset_offset();
.as_mut() }
.unwrap()
.reset_offset();
true true
} }
_ if config.filtering_block.is_some() _ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
.as_ref()
.expect("filtering_block must be configured for this table") =>
{ {
$table if let Some(ref mut filter) = $table.filter {
.filter filter.reset_offset();
.as_mut() }
.unwrap()
.reset_offset();
true true
} }
_ => false, _ => false,
@@ -249,27 +213,19 @@ macro_rules! handle_table_events {
fn [<handle_ $name _table_left_right>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool { fn [<handle_ $name _table_left_right>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
match $self.app.get_current_route() { match $self.app.get_current_route() {
_ if config.searching_block.is_some() _ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
.as_ref()
.expect("searching_block must be configured for this table") =>
{ {
$crate::handle_text_box_left_right_keys!( if let Some(ref mut search) = $table.search {
$self, $crate::handle_text_box_left_right_keys!($self, $self.key, search);
$self.key, }
$table.search.as_mut().unwrap()
);
true true
} }
_ if config.filtering_block.is_some() _ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
.as_ref()
.expect("filtering_block must be configured for this table") =>
{ {
$crate::handle_text_box_left_right_keys!( if let Some(ref mut filter) = $table.filter {
$self, $crate::handle_text_box_left_right_keys!($self, $self.key, filter);
$self.key, }
$table.filter.as_mut().unwrap()
);
true true
} }
_ => false, _ => false,
@@ -279,23 +235,14 @@ macro_rules! handle_table_events {
fn [<handle_ $name _table_submit>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool { fn [<handle_ $name _table_submit>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
match $self.app.get_current_route() { match $self.app.get_current_route() {
_ if config.sorting_block.is_some() _ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
.as_ref()
.expect("sorting_block must be configured for this table") =>
{ {
if let Some(sort_by_fn) = config.sort_by_fn {
$table.items.sort_by(sort_by_fn);
}
$table.apply_sorting(); $table.apply_sorting();
$self.app.pop_navigation_stack(); $self.app.pop_navigation_stack();
true true
} }
_ if config.searching_block.is_some() _ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
.as_ref()
.expect("searching_block must be configured for this table") =>
{ {
$self.app.pop_navigation_stack(); $self.app.pop_navigation_stack();
$self.app.ignore_special_keys_for_textbox_input = false; $self.app.ignore_special_keys_for_textbox_input = false;
@@ -318,9 +265,7 @@ macro_rules! handle_table_events {
true true
} }
_ if config.filtering_block.is_some() _ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
.as_ref()
.expect("filtering_block must be configured for this table") =>
{ {
$self.app.pop_navigation_stack(); $self.app.pop_navigation_stack();
$self.app.ignore_special_keys_for_textbox_input = false; $self.app.ignore_special_keys_for_textbox_input = false;
@@ -349,21 +294,15 @@ macro_rules! handle_table_events {
fn [<handle_ $name _table_esc>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool { fn [<handle_ $name _table_esc>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
match $self.app.get_current_route() { match $self.app.get_current_route() {
_ if config.sorting_block.is_some() _ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
.as_ref()
.expect("sorting_block must be configured for this table") =>
{ {
$self.app.pop_navigation_stack(); $self.app.pop_navigation_stack();
true true
} }
_ if (config.searching_block.is_some() _ if (config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap())
.as_ref()
.expect("searching_block must be configured for this table"))
|| (config.search_error_block.is_some() || (config.search_error_block.is_some()
&& $self.app.get_current_route() == *config.search_error_block && $self.app.get_current_route() == *config.search_error_block.as_ref().unwrap()) =>
.as_ref()
.expect("search_error_block must be configured for this table")) =>
{ {
$self.app.pop_navigation_stack(); $self.app.pop_navigation_stack();
$table.reset_search(); $table.reset_search();
@@ -371,13 +310,9 @@ macro_rules! handle_table_events {
true true
} }
_ if (config.filtering_block.is_some() _ if (config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap())
.as_ref()
.expect("filtering_block must be configured for this table"))
|| (config.filter_error_block.is_some() || (config.filter_error_block.is_some()
&& $self.app.get_current_route() == *config.filter_error_block && $self.app.get_current_route() == *config.filter_error_block.as_ref().unwrap()) =>
.as_ref()
.expect("filter_error_block must be configured for this table")) =>
{ {
$self.app.pop_navigation_stack(); $self.app.pop_navigation_stack();
$table.reset_filter(); $table.reset_filter();
@@ -394,68 +329,67 @@ macro_rules! handle_table_events {
} }
} }
fn [<handle_ $name _table_filter_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if config.table_block == $self.app.get_current_route()) {
$self
.app
.push_navigation_stack(config.filtering_block.expect("Filtering block is undefined").into());
$table.reset_filter();
$table.filter = Some($crate::models::HorizontallyScrollableText::default());
$self.app.ignore_special_keys_for_textbox_input = true;
true
} else {
false
}
}
fn [<handle_ $name _table_search_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if config.table_block == $self.app.get_current_route()) {
$self
.app
.push_navigation_stack(config.searching_block.expect("Searching block is undefined"));
$table.search = Some($crate::models::HorizontallyScrollableText::default());
$self.app.ignore_special_keys_for_textbox_input = true;
true
} else {
false
}
}
fn [<handle_ $name _table_sort_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if config.table_block == $self.app.get_current_route()) {
$table.sorting(
config
.sort_options
.as_ref()
.expect("Sort options are undefined")
.clone(),
);
$self
.app
.push_navigation_stack(config.sorting_block.expect("Sorting block is undefined"));
true
} else {
false
}
}
fn [<handle_ $name _table_search_box_input>](&mut $self) -> bool { fn [<handle_ $name _table_search_box_input>](&mut $self) -> bool {
$crate::handle_text_box_keys!( let Some(ref mut search) = $table.search else {
$self, return false;
$self.key, };
$table.search.as_mut().unwrap()
); $crate::handle_text_box_keys!($self, $self.key, search);
true true
} }
fn [<handle_ $name _table_filter_box_input>](&mut $self) -> bool { fn [<handle_ $name _table_filter_box_input>](&mut $self) -> bool {
$crate::handle_text_box_keys!( let Some(ref mut filter) = $table.filter else {
$self, return false;
$self.key, };
$table.filter.as_mut().unwrap()
); $crate::handle_text_box_keys!($self, $self.key, filter);
true
}
fn [<handle_ $name _table_filter_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if $self.app.get_current_route() != config.table_block {
return false;
}
let Some(ref filtering_block) = config.filtering_block else {
return false;
};
let filter = $crate::models::HorizontallyScrollableText::default();
$table.filter = Some(filter);
$self.app.push_navigation_stack(*filtering_block);
$self.app.ignore_special_keys_for_textbox_input = true;
true
}
fn [<handle_ $name _table_search_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if $self.app.get_current_route() != config.table_block {
return false;
}
let Some(ref searching_block) = config.searching_block else {
return false;
};
let search = $crate::models::HorizontallyScrollableText::default();
$table.search = Some(search);
$self.app.push_navigation_stack(*searching_block);
$self.app.ignore_special_keys_for_textbox_input = true;
true
}
fn [<handle_ $name _table_sort_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if $self.app.get_current_route() != config.table_block {
return false;
}
let (Some(ref sorting_block), Some(sort_options)) = (config.sorting_block, config.sort_options.as_ref()) else {
return false;
};
$table.sorting(sort_options.clone());
$self.app.push_navigation_stack(*sorting_block);
true true
} }
} }
@@ -467,17 +401,16 @@ where
T: Clone + PartialEq + Eq + Debug + Default, T: Clone + PartialEq + Eq + Debug + Default,
{ {
pub fn new(table_block: Route) -> Self { pub fn new(table_block: Route) -> Self {
TableHandlingConfig { Self {
table_block,
sorting_block: None, sorting_block: None,
sort_options: None, sort_options: None,
sort_by_fn: None,
searching_block: None, searching_block: None,
search_error_block: None, search_error_block: None,
search_field_fn: None, search_field_fn: None,
filtering_block: None, filtering_block: None,
filter_error_block: None, filter_error_block: None,
filter_field_fn: None, filter_field_fn: None,
table_block,
} }
} }
} }
-1
View File
@@ -23,7 +23,6 @@ mod tests {
fn handle(&mut self) { fn handle(&mut self) {
let movie_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Movies.into()) let movie_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Movies.into())
.sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into()) .sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into())
.sort_by_fn(|a: &Movie, b: &Movie| a.id.cmp(&b.id))
.sort_options(sort_options()) .sort_options(sort_options())
.searching_block(ActiveRadarrBlock::SearchMovie.into()) .searching_block(ActiveRadarrBlock::SearchMovie.into())
.search_error_block(ActiveRadarrBlock::SearchMovieError.into()) .search_error_block(ActiveRadarrBlock::SearchMovieError.into())
+57 -206
View File
@@ -50,151 +50,46 @@ where
T: Clone + PartialEq + Eq + Debug, T: Clone + PartialEq + Eq + Debug,
{ {
fn scroll_down(&mut self) { fn scroll_down(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() { let items_len = self.active_items().len();
if filtered_items.is_empty() { if items_len == 0 {
return; return;
} }
match self let state = self.active_state_mut();
.filtered_state match state.selected() {
.as_ref() Some(i) if i >= items_len - 1 => state.select_first(),
.expect("filtered_state must exist when filtered_items exists") Some(_) => state.select_next(),
.selected() None => state.select_first(),
{
Some(i) => {
if i >= filtered_items.len() - 1 {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first();
} else {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_next();
} }
} }
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first(),
};
return;
}
if self.items.is_empty() {
return;
}
match self.state.selected() {
Some(i) => {
if i >= self.items.len() - 1 {
self.state.select_first();
} else {
self.state.select_next();
}
}
None => self.state.select_first(),
};
}
fn scroll_up(&mut self) { fn scroll_up(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() { let items_len = self.active_items().len();
if filtered_items.is_empty() { if items_len == 0 {
return; return;
} }
match self let state = self.active_state_mut();
.filtered_state match state.selected() {
.as_ref() Some(0) => state.select(Some(items_len - 1)),
.expect("filtered_state must exist when filtered_items exists") Some(_) => state.select_previous(),
.selected() None => state.select_first(),
{
Some(i) => {
if i == 0 {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select(Some(filtered_items.len() - 1));
} else {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_previous();
} }
} }
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first(),
};
return;
}
if self.items.is_empty() {
return;
}
match self.state.selected() {
Some(i) => {
if i == 0 {
self.state.select(Some(self.items.len() - 1));
} else {
self.state.select_previous();
}
}
None => self.state.select_first(),
};
}
fn scroll_to_top(&mut self) { fn scroll_to_top(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() { if self.active_items().is_empty() {
if filtered_items.is_empty() {
return; return;
} }
self.active_state_mut().select_first();
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first();
return;
}
if self.items.is_empty() {
return;
}
self.state.select_first();
} }
fn scroll_to_bottom(&mut self) { fn scroll_to_bottom(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() { let items_len = self.active_items().len();
if filtered_items.is_empty() { if items_len == 0 {
return; return;
} }
self.active_state_mut().select(Some(items_len - 1));
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select(Some(filtered_items.len() - 1));
return;
}
if self.items.is_empty() {
return;
}
self.state.select(Some(self.items.len() - 1));
} }
} }
@@ -203,89 +98,56 @@ where
T: Clone + PartialEq + Eq + Debug, T: Clone + PartialEq + Eq + Debug,
{ {
fn page_down(&mut self) { fn page_down(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() { let items_len = self.active_items().len();
if filtered_items.is_empty() { if items_len == 0 {
return; return;
} }
match self let state = self.active_state_mut();
.filtered_state match state.selected() {
.as_ref() Some(i) => state.select(Some(i.saturating_add(20) % (items_len - 1))),
.expect("filtered_state must exist when filtered_items exists") None => state.select_first(),
.selected()
{
Some(i) => {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select(Some(i.saturating_add(20) % (filtered_items.len() - 1)));
} }
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first(),
};
return;
}
if self.items.is_empty() {
return;
}
match self.state.selected() {
Some(i) => {
self
.state
.select(Some(i.saturating_add(20) % (self.items.len() - 1)));
}
None => self.state.select_first(),
};
} }
fn page_up(&mut self) { fn page_up(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() { let items_len = self.active_items().len();
if filtered_items.is_empty() { if items_len == 0 {
return; return;
} }
match self let state = self.active_state_mut();
.filtered_state match state.selected() {
.as_ref() Some(i) => {
.expect("filtered_state must exist when filtered_items exists") let len = items_len - 1;
.selected() state.select(Some((i + len - (20 % len)) % len));
}
None => state.select_last(),
}
}
}
impl<T> StatefulTable<T>
where
T: Clone + PartialEq + Eq + Debug,
{ {
Some(i) => { fn active_items(&self) -> &[T] {
let len = filtered_items.len() - 1;
self self
.filtered_state .filtered_items
.as_mut() .as_ref()
.expect("filtered_state must exist when filtered_items exists") .map_or(&self.items[..], |items| &items[..])
.select(Some((i + len - (20 % len)) % len));
}
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_last(),
};
return;
} }
if self.items.is_empty() { fn active_state_mut(&mut self) -> &mut TableState {
return; if let Some(ref mut filtered_state) = self.filtered_state {
filtered_state
} else {
&mut self.state
}
} }
match self.state.selected() { fn active_state(&self) -> &TableState {
Some(i) => { self.filtered_state.as_ref().unwrap_or(&self.state)
let len = self.items.len() - 1;
self.state.select(Some((i + len - (20 % len)) % len));
}
None => self.state.select_last(),
};
} }
} }
@@ -318,24 +180,13 @@ where
} }
pub fn select_index(&mut self, index: Option<usize>) { pub fn select_index(&mut self, index: Option<usize>) {
if let Some(filtered_state) = &mut self.filtered_state { self.active_state_mut().select(index);
filtered_state.select(index);
} else {
self.state.select(index);
}
} }
pub fn current_selection(&self) -> &T { pub fn current_selection(&self) -> &T {
if let Some(filtered_items) = &self.filtered_items { let items = self.active_items();
&filtered_items[self let index = self.active_state().selected().unwrap_or(0);
.filtered_state &items[index]
.as_ref()
.expect("filtered_state must exist when filtered_items exists")
.selected()
.unwrap_or(0)]
} else {
&self.items[self.state.selected().unwrap_or(0)]
}
} }
pub fn sorting(&mut self, sort_options: Vec<SortOption<T>>) { pub fn sorting(&mut self, sort_options: Vec<SortOption<T>>) {
+3 -3
View File
@@ -6,10 +6,10 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use anyhow::Result; use anyhow::Result;
use anyhow::{anyhow, Context}; use anyhow::{Context, anyhow};
use colored::Colorize; use colored::Colorize;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use log::{error, LevelFilter}; use log::{LevelFilter, error};
use log4rs::append::file::FileAppender; use log4rs::append::file::FileAppender;
use log4rs::config::{Appender, Root}; use log4rs::config::{Appender, Root};
use log4rs::encode::pattern::PatternEncoder; use log4rs::encode::pattern::PatternEncoder;
@@ -18,7 +18,7 @@ use reqwest::{Certificate, Client};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use crate::app::{log_and_print_error, App, AppConfig}; use crate::app::{App, AppConfig, log_and_print_error};
use crate::cli::{self, Command}; use crate::cli::{self, Command};
use crate::network::Network; use crate::network::Network;
use crate::ui::theme::ThemeDefinitionsWrapper; use crate::ui::theme::ThemeDefinitionsWrapper;