Refactored the UI module and the handlers module to do a more chain-of-responsibility method to manage the UI's and handlers for different key events. Also, initial work for indexer settings as well

This commit is contained in:
2023-08-08 10:50:07 -06:00
parent 718613d59f
commit cf11527fef
67 changed files with 5255 additions and 2216 deletions
@@ -0,0 +1,340 @@
use std::cmp::Ordering;
use serde_json::Number;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::radarr::{ActiveRadarrBlock, EDIT_MOVIE_SELECTION_BLOCKS, MOVIE_DETAILS_BLOCKS};
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::{Language, Release, ReleaseField};
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::radarr_network::RadarrEvent;
#[cfg(test)]
#[path = "movie_details_handler_tests.rs"]
mod movie_details_handler_tests;
pub(super) struct MovieDetailsHandler<'a, 'b> {
key: &'a Key,
app: &'a mut App<'b>,
active_radarr_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<'a, 'b> {
fn accepts(active_block: &'a ActiveRadarrBlock) -> bool {
MOVIE_DETAILS_BLOCKS.contains(active_block)
}
fn with(
key: &'a Key,
app: &'a mut App<'b>,
active_block: &'a ActiveRadarrBlock,
_context: &'a Option<ActiveRadarrBlock>,
) -> MovieDetailsHandler<'a, 'b> {
MovieDetailsHandler {
key,
app,
active_radarr_block: active_block,
_context,
}
}
fn get_key(&self) -> &Key {
self.key
}
fn handle_scroll_up(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_up(),
ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_up(),
ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_up(),
ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_up(),
ActiveRadarrBlock::ManualSearch => self.app.data.radarr_data.movie_releases.scroll_up(),
ActiveRadarrBlock::ManualSearchSortPrompt => {
self.app.data.radarr_data.movie_releases_sort.scroll_up()
}
_ => (),
}
}
fn handle_scroll_down(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_down(),
ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_down(),
ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_down(),
ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_down(),
ActiveRadarrBlock::ManualSearch => self.app.data.radarr_data.movie_releases.scroll_down(),
ActiveRadarrBlock::ManualSearchSortPrompt => {
self.app.data.radarr_data.movie_releases_sort.scroll_down()
}
_ => (),
}
}
fn handle_home(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_to_top(),
ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_to_top(),
ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_to_top(),
ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_to_top(),
ActiveRadarrBlock::ManualSearch => self.app.data.radarr_data.movie_releases.scroll_to_top(),
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_releases_sort
.scroll_to_top(),
_ => (),
}
}
fn handle_end(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails => self.app.data.radarr_data.movie_details.scroll_to_bottom(),
ActiveRadarrBlock::MovieHistory => self.app.data.radarr_data.movie_history.scroll_to_bottom(),
ActiveRadarrBlock::Cast => self.app.data.radarr_data.movie_cast.scroll_to_bottom(),
ActiveRadarrBlock::Crew => self.app.data.radarr_data.movie_crew.scroll_to_bottom(),
ActiveRadarrBlock::ManualSearch => {
self.app.data.radarr_data.movie_releases.scroll_to_bottom()
}
ActiveRadarrBlock::ManualSearchSortPrompt => self
.app
.data
.radarr_data
.movie_releases_sort
.scroll_to_bottom(),
_ => (),
}
}
fn handle_delete(&mut self) {}
fn handle_left_right_action(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::ManualSearch => match self.key {
_ if *self.key == DEFAULT_KEYBINDINGS.left.key => {
self.app.data.radarr_data.movie_info_tabs.previous();
self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.movie_info_tabs.get_active_route(),
);
}
_ if *self.key == DEFAULT_KEYBINDINGS.right.key => {
self.app.data.radarr_data.movie_info_tabs.next();
self.app.pop_and_push_navigation_stack(
*self.app.data.radarr_data.movie_info_tabs.get_active_route(),
);
}
_ => (),
},
ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::UpdateAndScanPrompt
| ActiveRadarrBlock::ManualSearchConfirmPrompt => handle_prompt_toggle(self.app, self.key),
_ => (),
}
}
fn handle_submit(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::TriggerAutomaticSearch);
}
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::UpdateAndScanPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan);
}
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::ManualSearch => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::ManualSearchConfirmPrompt.into());
}
ActiveRadarrBlock::ManualSearchConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DownloadRelease);
}
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::ManualSearchSortPrompt => {
let movie_releases = self.app.data.radarr_data.movie_releases.items.clone();
let field = self
.app
.data
.radarr_data
.movie_releases_sort
.current_selection();
let sort_ascending = !self.app.data.radarr_data.sort_ascending.unwrap();
self.app.data.radarr_data.sort_ascending = Some(sort_ascending);
self
.app
.data
.radarr_data
.movie_releases
.set_items(sort_releases_by_selected_field(
movie_releases,
*field,
sort_ascending,
));
self.app.pop_navigation_stack();
}
_ => (),
}
}
fn handle_esc(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::ManualSearch => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.reset_movie_info_tabs();
}
ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::UpdateAndScanPrompt
| ActiveRadarrBlock::ManualSearchConfirmPrompt
| ActiveRadarrBlock::ManualSearchSortPrompt => {
self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false;
}
_ => (),
}
}
fn handle_char_key_event(&mut self) {
let key = self.key;
match *self.active_radarr_block {
ActiveRadarrBlock::MovieDetails
| ActiveRadarrBlock::MovieHistory
| ActiveRadarrBlock::FileInfo
| ActiveRadarrBlock::Cast
| ActiveRadarrBlock::Crew
| ActiveRadarrBlock::ManualSearch => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.search.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::AutomaticallySearchMoviePrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.edit.key => {
self.app.push_navigation_stack(
(
ActiveRadarrBlock::EditMoviePrompt,
Some(*self.active_radarr_block),
)
.into(),
);
self.app.data.radarr_data.populate_edit_movie_fields();
self.app.data.radarr_data.selected_block =
BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS);
}
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAndScanPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self
.app
.pop_and_push_navigation_stack((*self.active_radarr_block).into());
}
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.movie_releases_sort
.set_items(Vec::from_iter(ReleaseField::iter()));
let sort_ascending = self.app.data.radarr_data.sort_ascending;
self.app.data.radarr_data.sort_ascending =
Some(sort_ascending.is_some() && sort_ascending.unwrap());
self
.app
.push_navigation_stack(ActiveRadarrBlock::ManualSearchSortPrompt.into());
}
_ => (),
},
_ => (),
}
}
}
fn sort_releases_by_selected_field(
mut releases: Vec<Release>,
field: ReleaseField,
sort_ascending: bool,
) -> Vec<Release> {
let cmp_fn: fn(&Release, &Release) -> Ordering = match field {
ReleaseField::Source => |release_a, release_b| release_a.protocol.cmp(&release_b.protocol),
ReleaseField::Age => |release_a, release_b| release_a.age.as_u64().cmp(&release_b.age.as_u64()),
ReleaseField::Rejected => |release_a, release_b| release_a.rejected.cmp(&release_b.rejected),
ReleaseField::Title => |release_a, release_b| release_a.title.text.cmp(&release_b.title.text),
ReleaseField::Indexer => |release_a, release_b| release_a.indexer.cmp(&release_b.indexer),
ReleaseField::Size => |release_a, release_b| {
release_a
.size
.as_u64()
.as_ref()
.unwrap()
.cmp(release_b.size.as_u64().as_ref().unwrap())
},
ReleaseField::Peers => |release_a, release_b| {
let default_number = Number::from(i64::max_value());
let seeder_a = release_a
.seeders
.as_ref()
.unwrap_or(&default_number)
.as_u64()
.unwrap();
let seeder_b = release_b
.seeders
.as_ref()
.unwrap_or(&default_number)
.as_u64()
.unwrap();
seeder_a.cmp(&seeder_b)
},
ReleaseField::Language => |release_a, release_b| {
let default_language_vec = vec![Language {
name: "_".to_owned(),
}];
let language_a = &release_a
.languages
.as_ref()
.unwrap_or(&default_language_vec)[0];
let language_b = &release_b
.languages
.as_ref()
.unwrap_or(&default_language_vec)[0];
language_a.cmp(language_b)
},
ReleaseField::Quality => |release_a, release_b| release_a.quality.cmp(&release_b.quality),
};
if !sort_ascending {
releases.sort_by(|release_a, release_b| cmp_fn(release_a, release_b).reverse());
} else {
releases.sort_by(cmp_fn);
}
releases
}