Added sorting to the main library table
This commit is contained in:
@@ -5,12 +5,13 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
|||||||
#[path = "radarr_context_clues_tests.rs"]
|
#[path = "radarr_context_clues_tests.rs"]
|
||||||
mod radarr_context_clues_tests;
|
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.add, DEFAULT_KEYBINDINGS.add.desc),
|
||||||
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
(DEFAULT_KEYBINDINGS.edit, DEFAULT_KEYBINDINGS.edit.desc),
|
||||||
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
|
||||||
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
|
||||||
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
(DEFAULT_KEYBINDINGS.filter, DEFAULT_KEYBINDINGS.filter.desc),
|
||||||
|
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ mod tests {
|
|||||||
|
|
||||||
let (key_binding, description) = library_context_clues_iter.next().unwrap();
|
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_eq!(*key_binding, DEFAULT_KEYBINDINGS.refresh);
|
||||||
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
assert_str_eq!(*description, DEFAULT_KEYBINDINGS.refresh.desc);
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pretty_assertions::assert_str_eq;
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::event::Key;
|
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::handlers::KeyEventHandler;
|
||||||
use crate::models::radarr_models::Movie;
|
use crate::models::radarr_models::{Language, Movie};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, DELETE_MOVIE_BLOCKS, EDIT_MOVIE_BLOCKS, LIBRARY_BLOCKS,
|
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, DELETE_MOVIE_BLOCKS, EDIT_MOVIE_BLOCKS, LIBRARY_BLOCKS,
|
||||||
MOVIE_DETAILS_BLOCKS,
|
MOVIE_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
|
use crate::models::stateful_table::SortOption;
|
||||||
use crate::models::HorizontallyScrollableText;
|
use crate::models::HorizontallyScrollableText;
|
||||||
use crate::test_handler_delegation;
|
use crate::test_handler_delegation;
|
||||||
|
|
||||||
mod test_handle_scroll_up_and_down {
|
mod test_handle_scroll_up_and_down {
|
||||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -32,6 +35,51 @@ mod tests {
|
|||||||
title,
|
title,
|
||||||
to_string
|
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 {
|
mod test_handle_home_end {
|
||||||
@@ -147,6 +195,53 @@ mod tests {
|
|||||||
0
|
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 {
|
mod test_handle_delete {
|
||||||
@@ -579,6 +674,30 @@ mod tests {
|
|||||||
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
|
||||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
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 {
|
mod test_handle_esc {
|
||||||
@@ -902,6 +1021,29 @@ mod tests {
|
|||||||
"h"
|
"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]
|
#[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]
|
#[test]
|
||||||
fn test_library_handler_accepts() {
|
fn test_library_handler_accepts() {
|
||||||
let mut library_handler_blocks = Vec::new();
|
let mut library_handler_blocks = Vec::new();
|
||||||
|
|||||||
@@ -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::radarr_handlers::library::movie_details_handler::MovieDetailsHandler;
|
||||||
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
|
||||||
|
|
||||||
|
use crate::models::radarr_models::Movie;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
ActiveRadarrBlock, DELETE_MOVIE_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, LIBRARY_BLOCKS,
|
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::models::{BlockSelectionState, HorizontallyScrollableText, Scrollable};
|
||||||
use crate::network::radarr_network::RadarrEvent;
|
use crate::network::radarr_network::RadarrEvent;
|
||||||
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
|
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) {
|
fn handle_scroll_up(&mut self) {
|
||||||
if self.active_radarr_block == &ActiveRadarrBlock::Movies {
|
match self.active_radarr_block {
|
||||||
self.app.data.radarr_data.movies.scroll_up()
|
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) {
|
fn handle_scroll_down(&mut self) {
|
||||||
if self.active_radarr_block == &ActiveRadarrBlock::Movies {
|
match self.active_radarr_block {
|
||||||
self.app.data.radarr_data.movies.scroll_down()
|
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()
|
.unwrap()
|
||||||
.scroll_home();
|
.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()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.reset_offset(),
|
.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();
|
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 => {
|
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||||
self.app.should_refresh = true;
|
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 => {
|
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 {
|
SortOption {
|
||||||
name: "Title",
|
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 {
|
SortOption {
|
||||||
name: "Indexer",
|
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 {
|
SortOption {
|
||||||
name: "Size",
|
name: "Size",
|
||||||
|
|||||||
@@ -1108,8 +1108,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_releases_sorting_options_title() {
|
fn test_releases_sorting_options_title() {
|
||||||
let expected_cmp_fn: fn(&Release, &Release) -> Ordering =
|
let expected_cmp_fn: fn(&Release, &Release) -> Ordering = |a, b| {
|
||||||
|a, b| a.title.text.cmp(&b.title.text);
|
a.title
|
||||||
|
.text
|
||||||
|
.to_lowercase()
|
||||||
|
.cmp(&b.title.text.to_lowercase())
|
||||||
|
};
|
||||||
let mut expected_releases_vec = release_vec();
|
let mut expected_releases_vec = release_vec();
|
||||||
expected_releases_vec.sort_by(expected_cmp_fn);
|
expected_releases_vec.sort_by(expected_cmp_fn);
|
||||||
|
|
||||||
@@ -1123,7 +1127,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_releases_sorting_options_indexer() {
|
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();
|
let mut expected_releases_vec = release_vec();
|
||||||
expected_releases_vec.sort_by(expected_cmp_fn);
|
expected_releases_vec.sort_by(expected_cmp_fn);
|
||||||
|
|
||||||
@@ -1237,8 +1242,8 @@ mod tests {
|
|||||||
let release_b = Release {
|
let release_b = Release {
|
||||||
protocol: "Protocol B".to_owned(),
|
protocol: "Protocol B".to_owned(),
|
||||||
age: 2,
|
age: 2,
|
||||||
title: HorizontallyScrollableText::from("Title B"),
|
title: HorizontallyScrollableText::from("title B"),
|
||||||
indexer: "Indexer B".to_owned(),
|
indexer: "indexer B".to_owned(),
|
||||||
size: 2,
|
size: 2,
|
||||||
rejected: false,
|
rejected: false,
|
||||||
seeders: Some(Number::from(2)),
|
seeders: Some(Number::from(2)),
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ pub enum ActiveRadarrBlock {
|
|||||||
MovieHistory,
|
MovieHistory,
|
||||||
#[default]
|
#[default]
|
||||||
Movies,
|
Movies,
|
||||||
|
MoviesSortPrompt,
|
||||||
RootFolders,
|
RootFolders,
|
||||||
System,
|
System,
|
||||||
SystemLogs,
|
SystemLogs,
|
||||||
@@ -289,8 +290,9 @@ pub enum ActiveRadarrBlock {
|
|||||||
ViewMovieOverview,
|
ViewMovieOverview,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static LIBRARY_BLOCKS: [ActiveRadarrBlock; 6] = [
|
pub static LIBRARY_BLOCKS: [ActiveRadarrBlock; 7] = [
|
||||||
ActiveRadarrBlock::Movies,
|
ActiveRadarrBlock::Movies,
|
||||||
|
ActiveRadarrBlock::MoviesSortPrompt,
|
||||||
ActiveRadarrBlock::SearchMovie,
|
ActiveRadarrBlock::SearchMovie,
|
||||||
ActiveRadarrBlock::SearchMovieError,
|
ActiveRadarrBlock::SearchMovieError,
|
||||||
ActiveRadarrBlock::FilterMovies,
|
ActiveRadarrBlock::FilterMovies,
|
||||||
|
|||||||
@@ -256,8 +256,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_library_blocks_contents() {
|
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::Movies));
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::MoviesSortPrompt));
|
||||||
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::SearchMovie));
|
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::SearchMovie));
|
||||||
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::SearchMovieError));
|
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::SearchMovieError));
|
||||||
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::FilterMovies));
|
assert!(LIBRARY_BLOCKS.contains(&ActiveRadarrBlock::FilterMovies));
|
||||||
|
|||||||
@@ -218,8 +218,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_sorting(&mut self) {
|
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 let Some(sort_options) = &mut self.sort {
|
||||||
self.sort_asc = !self.sort_asc;
|
if toggle_dir {
|
||||||
|
self.sort_asc = !self.sort_asc;
|
||||||
|
}
|
||||||
let selected_sort_option = sort_options.current_selection();
|
let selected_sort_option = sort_options.current_selection();
|
||||||
let mut items = self.filtered_items.as_ref().unwrap_or(&self.items).clone();
|
let mut items = self.filtered_items.as_ref().unwrap_or(&self.items).clone();
|
||||||
if let Some(cmp_fn) = selected_sort_option.cmp_fn {
|
if let Some(cmp_fn) = selected_sort_option.cmp_fn {
|
||||||
|
|||||||
@@ -268,18 +268,18 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 mut stateful_table = create_test_stateful_table();
|
||||||
let expected_items = stateful_table.items.clone();
|
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_eq!(stateful_table.items, expected_items);
|
||||||
assert!(!stateful_table.sort_asc);
|
assert!(!stateful_table.sort_asc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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();
|
let mut stateful_table = create_test_stateful_table();
|
||||||
stateful_table.sorting(vec![SortOption {
|
stateful_table.sorting(vec![SortOption {
|
||||||
name: "Test 1",
|
name: "Test 1",
|
||||||
@@ -287,14 +287,14 @@ mod tests {
|
|||||||
}]);
|
}]);
|
||||||
let expected_items = stateful_table.items.clone();
|
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_eq!(stateful_table.items, expected_items);
|
||||||
assert!(stateful_table.sort_asc);
|
assert!(stateful_table.sort_asc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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();
|
let mut filtered_stateful_table = create_test_filtered_stateful_table();
|
||||||
filtered_stateful_table.sorting(vec![SortOption {
|
filtered_stateful_table.sorting(vec![SortOption {
|
||||||
name: "Test 1",
|
name: "Test 1",
|
||||||
@@ -306,7 +306,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
filtered_stateful_table.apply_sorting();
|
filtered_stateful_table.apply_sorting_toggle(true);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*filtered_stateful_table.filtered_items.as_ref().unwrap(),
|
*filtered_stateful_table.filtered_items.as_ref().unwrap(),
|
||||||
@@ -316,7 +316,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stateful_table_apply_sorting() {
|
fn test_stateful_table_apply_sorting_toggles_direction() {
|
||||||
let mut stateful_table = create_test_stateful_table();
|
let mut stateful_table = create_test_stateful_table();
|
||||||
stateful_table.sorting(vec![SortOption {
|
stateful_table.sorting(vec![SortOption {
|
||||||
name: "Test 1",
|
name: "Test 1",
|
||||||
@@ -325,12 +325,12 @@ mod tests {
|
|||||||
let mut expected_items = stateful_table.items.clone();
|
let mut expected_items = stateful_table.items.clone();
|
||||||
expected_items.sort();
|
expected_items.sort();
|
||||||
|
|
||||||
stateful_table.apply_sorting();
|
stateful_table.apply_sorting_toggle(true);
|
||||||
|
|
||||||
assert_eq!(stateful_table.items, expected_items);
|
assert_eq!(stateful_table.items, expected_items);
|
||||||
assert!(stateful_table.sort_asc);
|
assert!(stateful_table.sort_asc);
|
||||||
|
|
||||||
stateful_table.apply_sorting();
|
stateful_table.apply_sorting_toggle(true);
|
||||||
|
|
||||||
expected_items.reverse();
|
expected_items.reverse();
|
||||||
assert_eq!(stateful_table.items, expected_items);
|
assert_eq!(stateful_table.items, expected_items);
|
||||||
@@ -338,7 +338,46 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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();
|
let mut filtered_stateful_table = create_test_filtered_stateful_table();
|
||||||
filtered_stateful_table.sorting(vec![SortOption {
|
filtered_stateful_table.sorting(vec![SortOption {
|
||||||
name: "Test 1",
|
name: "Test 1",
|
||||||
@@ -351,7 +390,7 @@ mod tests {
|
|||||||
.clone();
|
.clone();
|
||||||
expected_items.sort();
|
expected_items.sort();
|
||||||
|
|
||||||
filtered_stateful_table.apply_sorting();
|
filtered_stateful_table.apply_sorting_toggle(true);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*filtered_stateful_table.filtered_items.as_ref().unwrap(),
|
*filtered_stateful_table.filtered_items.as_ref().unwrap(),
|
||||||
@@ -359,7 +398,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(filtered_stateful_table.sort_asc);
|
assert!(filtered_stateful_table.sort_asc);
|
||||||
|
|
||||||
filtered_stateful_table.apply_sorting();
|
filtered_stateful_table.apply_sorting_toggle(true);
|
||||||
|
|
||||||
expected_items.reverse();
|
expected_items.reverse();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1191,7 +1191,8 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
|
|
||||||
self
|
self
|
||||||
.handle_request::<(), Vec<Movie>>(request_props, |movie_vec, mut app| {
|
.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;
|
.await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ mod test {
|
|||||||
RadarrEvent::GetMovies.resource(),
|
RadarrEvent::GetMovies.resource(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
app_arc.lock().await.data.radarr_data.movies.sort_asc = true;
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network.handle_radarr_event(RadarrEvent::GetMovies).await;
|
network.handle_radarr_event(RadarrEvent::GetMovies).await;
|
||||||
@@ -310,6 +311,7 @@ mod test {
|
|||||||
app_arc.lock().await.data.radarr_data.movies.items,
|
app_arc.lock().await.data.radarr_data.movies.items,
|
||||||
vec![movie()]
|
vec![movie()]
|
||||||
);
|
);
|
||||||
|
assert!(app_arc.lock().await.data.radarr_data.movies.sort_asc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl DrawUi for LibraryUi {
|
|||||||
let route = *app.get_current_route();
|
let route = *app.get_current_route();
|
||||||
let mut library_ui_matchers = |active_radarr_block: ActiveRadarrBlock| match active_radarr_block
|
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(
|
ActiveRadarrBlock::SearchMovie => draw_popup_over(
|
||||||
f,
|
f,
|
||||||
app,
|
app,
|
||||||
@@ -101,94 +101,97 @@ impl DrawUi for LibraryUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
let current_selection = if !app.data.radarr_data.movies.items.is_empty() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
app.data.radarr_data.movies.current_selection().clone()
|
let current_selection = if !app.data.radarr_data.movies.items.is_empty() {
|
||||||
} else {
|
app.data.radarr_data.movies.current_selection().clone()
|
||||||
Movie::default()
|
} else {
|
||||||
};
|
Movie::default()
|
||||||
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
};
|
||||||
let tags_map = &app.data.radarr_data.tags_map;
|
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
||||||
let downloads_vec = &app.data.radarr_data.downloads.items;
|
let tags_map = &app.data.radarr_data.tags_map;
|
||||||
let content = Some(&mut app.data.radarr_data.movies);
|
let downloads_vec = &app.data.radarr_data.downloads.items;
|
||||||
let help_footer = app
|
let content = Some(&mut app.data.radarr_data.movies);
|
||||||
.data
|
let help_footer = app
|
||||||
.radarr_data
|
.data
|
||||||
.main_tabs
|
.radarr_data
|
||||||
.get_active_tab_contextual_help();
|
.main_tabs
|
||||||
|
.get_active_tab_contextual_help();
|
||||||
|
|
||||||
let library_table_row_mapping = |movie: &Movie| {
|
let library_table_row_mapping = |movie: &Movie| {
|
||||||
movie.title.scroll_left_or_reset(
|
movie.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 27),
|
get_width_from_percentage(area, 27),
|
||||||
*movie == current_selection,
|
*movie == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count % app.ticks_until_scroll == 0,
|
||||||
);
|
);
|
||||||
let monitored = if movie.monitored { "🏷" } else { "" };
|
let monitored = if movie.monitored { "🏷" } else { "" };
|
||||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||||
let file_size: f64 = convert_to_gb(movie.size_on_disk);
|
let file_size: f64 = convert_to_gb(movie.size_on_disk);
|
||||||
let certification = movie.certification.clone().unwrap_or_default();
|
let certification = movie.certification.clone().unwrap_or_default();
|
||||||
let quality_profile = quality_profile_map
|
let quality_profile = quality_profile_map
|
||||||
.get_by_left(&movie.quality_profile_id)
|
.get_by_left(&movie.quality_profile_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let tags = movie
|
let tags = movie
|
||||||
.tags
|
.tags
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tag_id| {
|
.map(|tag_id| {
|
||||||
tags_map
|
tags_map
|
||||||
.get_by_left(&tag_id.as_i64().unwrap())
|
.get_by_left(&tag_id.as_i64().unwrap())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone()
|
.clone()
|
||||||
})
|
})
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
||||||
decorate_with_row_style(
|
decorate_with_row_style(
|
||||||
downloads_vec,
|
downloads_vec,
|
||||||
movie,
|
movie,
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
Cell::from(movie.title.to_string()),
|
Cell::from(movie.title.to_string()),
|
||||||
Cell::from(movie.year.to_string()),
|
Cell::from(movie.year.to_string()),
|
||||||
Cell::from(movie.studio.to_string()),
|
Cell::from(movie.studio.to_string()),
|
||||||
Cell::from(format!("{hours}h {minutes}m")),
|
Cell::from(format!("{hours}h {minutes}m")),
|
||||||
Cell::from(certification),
|
Cell::from(certification),
|
||||||
Cell::from(movie.original_language.name.to_owned()),
|
Cell::from(movie.original_language.name.to_owned()),
|
||||||
Cell::from(format!("{file_size:.2} GB")),
|
Cell::from(format!("{file_size:.2} GB")),
|
||||||
Cell::from(quality_profile),
|
Cell::from(quality_profile),
|
||||||
Cell::from(monitored.to_owned()),
|
Cell::from(monitored.to_owned()),
|
||||||
Cell::from(tags),
|
Cell::from(tags),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let library_table = ManagarrTable::new(content, library_table_row_mapping)
|
let library_table = ManagarrTable::new(content, library_table_row_mapping)
|
||||||
.block(layout_block_top_border())
|
.block(layout_block_top_border())
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer(help_footer)
|
.footer(help_footer)
|
||||||
.headers([
|
.sorting(active_radarr_block == ActiveRadarrBlock::MoviesSortPrompt)
|
||||||
"Title",
|
.headers([
|
||||||
"Year",
|
"Title",
|
||||||
"Studio",
|
"Year",
|
||||||
"Runtime",
|
"Studio",
|
||||||
"Rating",
|
"Runtime",
|
||||||
"Language",
|
"Rating",
|
||||||
"Size",
|
"Language",
|
||||||
"Quality Profile",
|
"Size",
|
||||||
"Monitored",
|
"Quality Profile",
|
||||||
"Tags",
|
"Monitored",
|
||||||
])
|
"Tags",
|
||||||
.constraints([
|
])
|
||||||
Constraint::Percentage(27),
|
.constraints([
|
||||||
Constraint::Percentage(4),
|
Constraint::Percentage(27),
|
||||||
Constraint::Percentage(17),
|
Constraint::Percentage(4),
|
||||||
Constraint::Percentage(6),
|
Constraint::Percentage(17),
|
||||||
Constraint::Percentage(6),
|
Constraint::Percentage(6),
|
||||||
Constraint::Percentage(6),
|
Constraint::Percentage(6),
|
||||||
Constraint::Percentage(6),
|
Constraint::Percentage(6),
|
||||||
Constraint::Percentage(10),
|
Constraint::Percentage(6),
|
||||||
Constraint::Percentage(6),
|
Constraint::Percentage(10),
|
||||||
Constraint::Percentage(12),
|
Constraint::Percentage(6),
|
||||||
]);
|
Constraint::Percentage(12),
|
||||||
|
]);
|
||||||
|
|
||||||
f.render_widget(library_table, area);
|
f.render_widget(library_table, area);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_update_all_movies_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
fn draw_update_all_movies_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
|||||||
Reference in New Issue
Block a user