Added sorting to the main library table

This commit is contained in:
2024-02-13 23:00:35 -07:00
parent a982f610cb
commit 6cd24be5e4
13 changed files with 707 additions and 117 deletions
+2 -1
View File
@@ -5,12 +5,13 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
#[path = "radarr_context_clues_tests.rs"]
mod radarr_context_clues_tests;
pub static LIBRARY_CONTEXT_CLUES: [ContextClue; 9] = [
pub static LIBRARY_CONTEXT_CLUES: [ContextClue; 10] = [
(DEFAULT_KEYBINDINGS.add, DEFAULT_KEYBINDINGS.add.desc),
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(
DEFAULT_KEYBINDINGS.refresh,
DEFAULT_KEYBINDINGS.refresh.desc,
@@ -42,6 +42,11 @@ mod tests {
let (key_binding, description) = library_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.sort);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.sort.desc);
let (key_binding, description) = library_context_clues_iter.next().unwrap();
assert_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
@@ -1,24 +1,27 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest;
use std::cmp::Ordering;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::library::LibraryHandler;
use crate::handlers::radarr_handlers::library::{movies_sorting_options, LibraryHandler};
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::Movie;
use crate::models::radarr_models::{Language, Movie};
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, DELETE_MOVIE_BLOCKS, EDIT_MOVIE_BLOCKS, LIBRARY_BLOCKS,
MOVIE_DETAILS_BLOCKS,
};
use crate::models::stateful_table::SortOption;
use crate::models::HorizontallyScrollableText;
use crate::test_handler_delegation;
mod test_handle_scroll_up_and_down {
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
use pretty_assertions::assert_eq;
use super::*;
@@ -32,6 +35,51 @@ mod tests {
title,
to_string
);
#[rstest]
fn test_movies_sort_scroll(
#[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key,
) {
let movie_field_vec = sort_options();
let mut app = App::default();
app.data.radarr_data.movies.sorting(sort_options());
if key == Key::Up {
for i in (0..movie_field_vec.len()).rev() {
LibraryHandler::with(&key, &mut app, &ActiveRadarrBlock::MoviesSortPrompt, &None)
.handle();
assert_eq!(
app
.data
.radarr_data
.movies
.sort
.as_ref()
.unwrap()
.current_selection(),
&movie_field_vec[i]
);
}
} else {
for i in 0..movie_field_vec.len() {
LibraryHandler::with(&key, &mut app, &ActiveRadarrBlock::MoviesSortPrompt, &None)
.handle();
assert_eq!(
app
.data
.radarr_data
.movies
.sort
.as_ref()
.unwrap()
.current_selection(),
&movie_field_vec[(i + 1) % movie_field_vec.len()]
);
}
}
}
}
mod test_handle_home_end {
@@ -147,6 +195,53 @@ mod tests {
0
);
}
#[test]
fn test_movies_sort_home_end() {
let movie_field_vec = sort_options();
let mut app = App::default();
app.data.radarr_data.movies.sorting(sort_options());
LibraryHandler::with(
&DEFAULT_KEYBINDINGS.end.key,
&mut app,
&ActiveRadarrBlock::MoviesSortPrompt,
&None,
)
.handle();
assert_eq!(
app
.data
.radarr_data
.movies
.sort
.as_ref()
.unwrap()
.current_selection(),
&movie_field_vec[movie_field_vec.len() - 1]
);
LibraryHandler::with(
&DEFAULT_KEYBINDINGS.home.key,
&mut app,
&ActiveRadarrBlock::MoviesSortPrompt,
&None,
)
.handle();
assert_eq!(
app
.data
.radarr_data
.movies
.sort
.as_ref()
.unwrap()
.current_selection(),
&movie_field_vec[0]
);
}
}
mod test_handle_delete {
@@ -579,6 +674,30 @@ mod tests {
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
}
#[test]
fn test_movies_sort_prompt_submit() {
let mut app = App::default();
app.data.radarr_data.movies.sort_asc = true;
app.data.radarr_data.movies.sorting(sort_options());
app.data.radarr_data.movies.set_items(movies_vec());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::MoviesSortPrompt.into());
let mut expected_vec = movies_vec();
expected_vec.reverse();
LibraryHandler::with(
&SUBMIT_KEY,
&mut app,
&ActiveRadarrBlock::MoviesSortPrompt,
&None,
)
.handle();
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert_eq!(app.data.radarr_data.movies.items, expected_vec);
}
}
mod test_handle_esc {
@@ -902,6 +1021,29 @@ mod tests {
"h"
);
}
#[test]
fn test_sort_key() {
let mut app = App::default();
LibraryHandler::with(
&DEFAULT_KEYBINDINGS.sort.key,
&mut app,
&ActiveRadarrBlock::Movies,
&None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::MoviesSortPrompt.into()
);
assert_eq!(
app.data.radarr_data.movies.sort.as_ref().unwrap().items,
movies_sorting_options()
);
assert!(!app.data.radarr_data.movies.sort_asc);
}
}
#[rstest]
@@ -975,6 +1117,247 @@ mod tests {
);
}
#[test]
fn test_movies_sorting_options_title() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering = |a, b| {
a.title
.text
.to_lowercase()
.cmp(&b.title.text.to_lowercase())
};
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[0].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Title");
}
#[test]
fn test_movies_sorting_options_year() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering = |a, b| a.year.cmp(&b.year);
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[1].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Year");
}
#[test]
fn test_movies_sorting_options_studio() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering =
|a, b| a.studio.to_lowercase().cmp(&b.studio.to_lowercase());
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[2].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Studio");
}
#[test]
fn test_movies_sorting_options_runtime() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering = |a, b| a.runtime.cmp(&b.runtime);
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[3].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Runtime");
}
#[test]
fn test_movies_sorting_options_rating() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering = |a, b| {
a.certification
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(
&b.certification
.as_ref()
.unwrap_or(&String::new())
.to_lowercase(),
)
};
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[4].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Rating");
}
#[test]
fn test_movies_sorting_options_language() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering = |a, b| {
a.original_language
.name
.to_lowercase()
.cmp(&b.original_language.name.to_lowercase())
};
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[5].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Language");
}
#[test]
fn test_movies_sorting_options_size() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering =
|a, b| a.size_on_disk.cmp(&b.size_on_disk);
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[6].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Size");
}
#[test]
fn test_movies_sorting_options_quality() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering =
|a, b| a.quality_profile_id.cmp(&b.quality_profile_id);
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[7].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Quality");
}
#[test]
fn test_movies_sorting_options_monitored() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering = |a, b| a.monitored.cmp(&b.monitored);
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[8].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Monitored");
}
#[test]
fn test_movies_sorting_options_tags() {
let expected_cmp_fn: fn(&Movie, &Movie) -> Ordering = |a, b| {
let a_str = a
.tags
.iter()
.map(|tag| tag.as_i64().unwrap().to_string())
.collect::<Vec<String>>()
.join(",");
let b_str = b
.tags
.iter()
.map(|tag| tag.as_i64().unwrap().to_string())
.collect::<Vec<String>>()
.join(",");
a_str.cmp(&b_str)
};
let mut expected_movies_vec = movies_vec();
expected_movies_vec.sort_by(expected_cmp_fn);
let sort_option = movies_sorting_options()[9].clone();
let mut sorted_movies_vec = movies_vec();
sorted_movies_vec.sort_by(sort_option.cmp_fn.unwrap());
assert_eq!(sorted_movies_vec, expected_movies_vec);
assert_str_eq!(sort_option.name, "Tags");
}
fn movies_vec() -> Vec<Movie> {
vec![
Movie {
title: "test 1".into(),
original_language: Language {
name: "English".to_owned(),
},
size_on_disk: 1024,
studio: "Studio 1".to_owned(),
year: 2024,
monitored: false,
runtime: 12.into(),
quality_profile_id: 1,
certification: Some("PG-13".to_owned()),
tags: vec![1.into(), 2.into()],
..Movie::default()
},
Movie {
title: "test 2".into(),
original_language: Language {
name: "Chinese".to_owned(),
},
size_on_disk: 2048,
studio: "Studio 2".to_owned(),
year: 1998,
monitored: false,
runtime: 60.into(),
quality_profile_id: 2,
certification: Some("R".to_owned()),
tags: vec![1.into(), 3.into()],
..Movie::default()
},
Movie {
title: "test 3".into(),
original_language: Language {
name: "Japanese".to_owned(),
},
size_on_disk: 512,
studio: "studio 3".to_owned(),
year: 1954,
monitored: true,
runtime: 120.into(),
quality_profile_id: 3,
certification: Some("G".to_owned()),
tags: vec![2.into(), 3.into()],
..Movie::default()
},
]
}
fn sort_options() -> Vec<SortOption<Movie>> {
vec![SortOption {
name: "Test 1",
cmp_fn: Some(|a, b| {
a.title
.text
.to_lowercase()
.cmp(&b.title.text.to_lowercase())
}),
}]
}
#[test]
fn test_library_handler_accepts() {
let mut library_handler_blocks = Vec::new();
+141 -4
View File
@@ -8,9 +8,11 @@ use crate::handlers::radarr_handlers::library::edit_movie_handler::EditMovieHand
use crate::handlers::radarr_handlers::library::movie_details_handler::MovieDetailsHandler;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::Movie;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, DELETE_MOVIE_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, LIBRARY_BLOCKS,
};
use crate::models::stateful_table::SortOption;
use crate::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable};
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
@@ -79,14 +81,34 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
}
fn handle_scroll_up(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Movies {
self.app.data.radarr_data.movies.scroll_up()
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_up(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_up(),
_ => (),
}
}
fn handle_scroll_down(&mut self) {
if self.active_radarr_block == &ActiveRadarrBlock::Movies {
self.app.data.radarr_data.movies.scroll_down()
match self.active_radarr_block {
ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_down(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_down(),
_ => (),
}
}
@@ -115,6 +137,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
.unwrap()
.scroll_home();
}
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_to_top(),
_ => (),
}
}
@@ -140,6 +171,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
.as_mut()
.unwrap()
.reset_offset(),
ActiveRadarrBlock::MoviesSortPrompt => self
.app
.data
.radarr_data
.movies
.sort
.as_mut()
.unwrap()
.scroll_to_bottom(),
_ => (),
}
}
@@ -226,6 +266,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::MoviesSortPrompt => {
self.app.data.radarr_data.movies.apply_sorting();
self.app.pop_navigation_stack();
}
_ => (),
}
}
@@ -300,6 +345,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self.app.should_refresh = true;
}
_ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self
.app
.data
.radarr_data
.movies
.sorting(movies_sorting_options());
self
.app
.push_navigation_stack(ActiveRadarrBlock::MoviesSortPrompt.into());
}
_ => (),
},
ActiveRadarrBlock::SearchMovie => {
@@ -320,3 +376,84 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
}
}
}
fn movies_sorting_options() -> Vec<SortOption<Movie>> {
vec![
SortOption {
name: "Title",
cmp_fn: Some(|a, b| {
a.title
.text
.to_lowercase()
.cmp(&b.title.text.to_lowercase())
}),
},
SortOption {
name: "Year",
cmp_fn: Some(|a, b| a.year.cmp(&b.year)),
},
SortOption {
name: "Studio",
cmp_fn: Some(|a, b| a.studio.to_lowercase().cmp(&b.studio.to_lowercase())),
},
SortOption {
name: "Runtime",
cmp_fn: Some(|a, b| a.runtime.cmp(&b.runtime)),
},
SortOption {
name: "Rating",
cmp_fn: Some(|a, b| {
a.certification
.as_ref()
.unwrap_or(&String::new())
.to_lowercase()
.cmp(
&b.certification
.as_ref()
.unwrap_or(&String::new())
.to_lowercase(),
)
}),
},
SortOption {
name: "Language",
cmp_fn: Some(|a, b| {
a.original_language
.name
.to_lowercase()
.cmp(&b.original_language.name.to_lowercase())
}),
},
SortOption {
name: "Size",
cmp_fn: Some(|a, b| a.size_on_disk.cmp(&b.size_on_disk)),
},
SortOption {
name: "Quality",
cmp_fn: Some(|a, b| a.quality_profile_id.cmp(&b.quality_profile_id)),
},
SortOption {
name: "Monitored",
cmp_fn: Some(|a, b| a.monitored.cmp(&b.monitored)),
},
SortOption {
name: "Tags",
cmp_fn: Some(|a, b| {
let a_str = a
.tags
.iter()
.map(|tag| tag.as_i64().unwrap().to_string())
.collect::<Vec<String>>()
.join(",");
let b_str = b
.tags
.iter()
.map(|tag| tag.as_i64().unwrap().to_string())
.collect::<Vec<String>>()
.join(",");
a_str.cmp(&b_str)
}),
},
]
}
@@ -469,11 +469,16 @@ fn releases_sorting_options() -> Vec<SortOption<Release>> {
},
SortOption {
name: "Title",
cmp_fn: Some(|a, b| a.title.text.cmp(&b.title.text)),
cmp_fn: Some(|a, b| {
a.title
.text
.to_lowercase()
.cmp(&b.title.text.to_lowercase())
}),
},
SortOption {
name: "Indexer",
cmp_fn: Some(|a, b| a.indexer.cmp(&b.indexer)),
cmp_fn: Some(|a, b| a.indexer.to_lowercase().cmp(&b.indexer.to_lowercase())),
},
SortOption {
name: "Size",
@@ -1108,8 +1108,12 @@ mod tests {
#[test]
fn test_releases_sorting_options_title() {
let expected_cmp_fn: fn(&Release, &Release) -> Ordering =
|a, b| a.title.text.cmp(&b.title.text);
let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| {
a.title
.text
.to_lowercase()
.cmp(&b.title.text.to_lowercase())
};
let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn);
@@ -1123,7 +1127,8 @@ mod tests {
#[test]
fn test_releases_sorting_options_indexer() {
let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| a.indexer.cmp(&b.indexer);
let expected_cmp_fn: fn(&Release, &Release) -> Ordering =
|a, b| a.indexer.to_lowercase().cmp(&b.indexer.to_lowercase());
let mut expected_releases_vec = release_vec();
expected_releases_vec.sort_by(expected_cmp_fn);
@@ -1237,8 +1242,8 @@ mod tests {
let release_b = Release {
protocol: "Protocol B".to_owned(),
age: 2,
title: HorizontallyScrollableText::from("Title B"),
indexer: "Indexer B".to_owned(),
title: HorizontallyScrollableText::from("title B"),
indexer: "indexer B".to_owned(),
size: 2,
rejected: false,
seeders: Some(Number::from(2)),
@@ -270,6 +270,7 @@ pub enum ActiveRadarrBlock {
MovieHistory,
#[default]
Movies,
MoviesSortPrompt,
RootFolders,
System,
SystemLogs,
@@ -289,8 +290,9 @@ pub enum ActiveRadarrBlock {
ViewMovieOverview,
}
pub static LIBRARY_BLOCKS: [ActiveRadarrBlock; 6] = [
pub static LIBRARY_BLOCKS: [ActiveRadarrBlock; 7] = [
ActiveRadarrBlock::Movies,
ActiveRadarrBlock::MoviesSortPrompt,
ActiveRadarrBlock::SearchMovie,
ActiveRadarrBlock::SearchMovieError,
ActiveRadarrBlock::FilterMovies,
@@ -256,8 +256,9 @@ mod tests {
#[test]
fn test_library_blocks_contents() {
assert_eq!(LIBRARY_BLOCKS.len(), 6);
assert_eq!(LIBRARY_BLOCKS.len(), 7);
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::Movies));
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::MoviesSortPrompt));
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::SearchMovie));
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::SearchMovieError));
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::FilterMovies));
+6
View File
@@ -218,8 +218,14 @@ where
}
pub fn apply_sorting(&mut self) {
self.apply_sorting_toggle(true);
}
pub fn apply_sorting_toggle(&mut self, toggle_dir: bool) {
if let Some(sort_options) = &mut self.sort {
if toggle_dir {
self.sort_asc = !self.sort_asc;
}
let selected_sort_option = sort_options.current_selection();
let mut items = self.filtered_items.as_ref().unwrap_or(&self.items).clone();
if let Some(cmp_fn) = selected_sort_option.cmp_fn {
+51 -12
View File
@@ -268,18 +268,18 @@ mod tests {
}
#[test]
fn test_stateful_table_apply_sorting_no_op_no_sort_options() {
fn test_stateful_table_apply_sorting_toggle_no_op_no_sort_options() {
let mut stateful_table = create_test_stateful_table();
let expected_items = stateful_table.items.clone();
stateful_table.apply_sorting();
stateful_table.apply_sorting_toggle(true);
assert_eq!(stateful_table.items, expected_items);
assert!(!stateful_table.sort_asc);
}
#[test]
fn test_stateful_table_apply_sorting_no_op_no_cmp_fn() {
fn test_stateful_table_apply_sorting_toggle_no_op_no_cmp_fn() {
let mut stateful_table = create_test_stateful_table();
stateful_table.sorting(vec![SortOption {
name: "Test 1",
@@ -287,14 +287,14 @@ mod tests {
}]);
let expected_items = stateful_table.items.clone();
stateful_table.apply_sorting();
stateful_table.apply_sorting_toggle(true);
assert_eq!(stateful_table.items, expected_items);
assert!(stateful_table.sort_asc);
}
#[test]
fn test_filtered_stateful_table_apply_sorting_no_op_no_cmp_fn() {
fn test_filtered_stateful_table_apply_sorting_toggle_no_op_no_cmp_fn() {
let mut filtered_stateful_table = create_test_filtered_stateful_table();
filtered_stateful_table.sorting(vec![SortOption {
name: "Test 1",
@@ -306,7 +306,7 @@ mod tests {
.unwrap()
.clone();
filtered_stateful_table.apply_sorting();
filtered_stateful_table.apply_sorting_toggle(true);
assert_eq!(
*filtered_stateful_table.filtered_items.as_ref().unwrap(),
@@ -316,7 +316,7 @@ mod tests {
}
#[test]
fn test_stateful_table_apply_sorting() {
fn test_stateful_table_apply_sorting_toggles_direction() {
let mut stateful_table = create_test_stateful_table();
stateful_table.sorting(vec![SortOption {
name: "Test 1",
@@ -325,12 +325,12 @@ mod tests {
let mut expected_items = stateful_table.items.clone();
expected_items.sort();
stateful_table.apply_sorting();
stateful_table.apply_sorting_toggle(true);
assert_eq!(stateful_table.items, expected_items);
assert!(stateful_table.sort_asc);
stateful_table.apply_sorting();
stateful_table.apply_sorting_toggle(true);
expected_items.reverse();
assert_eq!(stateful_table.items, expected_items);
@@ -338,7 +338,46 @@ mod tests {
}
#[test]
fn test_filtered_stateful_table_apply_sorting() {
fn test_stateful_table_apply_sorting_toggle() {
let mut stateful_table = create_test_stateful_table();
stateful_table.sorting(vec![SortOption {
name: "Test 1",
cmp_fn: Some(|a, b| a.cmp(b)),
}]);
let mut expected_items = stateful_table.items.clone();
expected_items.sort();
stateful_table.apply_sorting_toggle(true);
assert_eq!(stateful_table.items, expected_items);
assert!(stateful_table.sort_asc);
stateful_table.apply_sorting_toggle(true);
expected_items.reverse();
assert_eq!(stateful_table.items, expected_items);
assert!(!stateful_table.sort_asc);
}
#[test]
fn test_stateful_table_apply_sorting_toggle_false_doesnt_toggle_direction() {
let mut stateful_table = create_test_stateful_table();
stateful_table.sorting(vec![SortOption {
name: "Test 1",
cmp_fn: Some(|a, b| a.cmp(b)),
}]);
let mut expected_items = stateful_table.items.clone();
expected_items.sort();
expected_items.reverse();
stateful_table.apply_sorting_toggle(false);
assert_eq!(stateful_table.items, expected_items);
assert!(!stateful_table.sort_asc);
}
#[test]
fn test_filtered_stateful_table_apply_sorting_toggle() {
let mut filtered_stateful_table = create_test_filtered_stateful_table();
filtered_stateful_table.sorting(vec![SortOption {
name: "Test 1",
@@ -351,7 +390,7 @@ mod tests {
.clone();
expected_items.sort();
filtered_stateful_table.apply_sorting();
filtered_stateful_table.apply_sorting_toggle(true);
assert_eq!(
*filtered_stateful_table.filtered_items.as_ref().unwrap(),
@@ -359,7 +398,7 @@ mod tests {
);
assert!(filtered_stateful_table.sort_asc);
filtered_stateful_table.apply_sorting();
filtered_stateful_table.apply_sorting_toggle(true);
expected_items.reverse();
assert_eq!(
+2 -1
View File
@@ -1191,7 +1191,8 @@ impl<'a, 'b> Network<'a, 'b> {
self
.handle_request::<(), Vec<Movie>>(request_props, |movie_vec, mut app| {
app.data.radarr_data.movies.set_items(movie_vec)
app.data.radarr_data.movies.set_items(movie_vec);
app.data.radarr_data.movies.apply_sorting_toggle(false);
})
.await;
}
+2
View File
@@ -301,6 +301,7 @@ mod test {
RadarrEvent::GetMovies.resource(),
)
.await;
app_arc.lock().await.data.radarr_data.movies.sort_asc = true;
let mut network = Network::new(&app_arc, CancellationToken::new());
network.handle_radarr_event(RadarrEvent::GetMovies).await;
@@ -310,6 +311,7 @@ mod test {
app_arc.lock().await.data.radarr_data.movies.items,
vec![movie()]
);
assert!(app_arc.lock().await.data.radarr_data.movies.sort_asc);
}
#[tokio::test]
+4 -1
View File
@@ -46,7 +46,7 @@ impl DrawUi for LibraryUi {
let route = *app.get_current_route();
let mut library_ui_matchers = |active_radarr_block: ActiveRadarrBlock| match active_radarr_block
{
ActiveRadarrBlock::Movies => draw_library(f, app, area),
ActiveRadarrBlock::Movies | ActiveRadarrBlock::MoviesSortPrompt => draw_library(f, app, area),
ActiveRadarrBlock::SearchMovie => draw_popup_over(
f,
app,
@@ -101,6 +101,7 @@ impl DrawUi for LibraryUi {
}
pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
let current_selection = if !app.data.radarr_data.movies.items.is_empty() {
app.data.radarr_data.movies.current_selection().clone()
} else {
@@ -163,6 +164,7 @@ pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
.block(layout_block_top_border())
.loading(app.is_loading)
.footer(help_footer)
.sorting(active_radarr_block == ActiveRadarrBlock::MoviesSortPrompt)
.headers([
"Title",
"Year",
@@ -189,6 +191,7 @@ pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
]);
f.render_widget(library_table, area);
}
}
fn draw_update_all_movies_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {