Fully added filtering and searching!
This commit is contained in:
+24
-5
@@ -16,6 +16,7 @@ pub struct RadarrData {
|
|||||||
pub version: String,
|
pub version: String,
|
||||||
pub start_time: DateTime<Utc>,
|
pub start_time: DateTime<Utc>,
|
||||||
pub movies: StatefulTable<Movie>,
|
pub movies: StatefulTable<Movie>,
|
||||||
|
pub filtered_movies: StatefulTable<Movie>,
|
||||||
pub downloads: StatefulTable<DownloadRecord>,
|
pub downloads: StatefulTable<DownloadRecord>,
|
||||||
pub quality_profile_map: HashMap<u64, String>,
|
pub quality_profile_map: HashMap<u64, String>,
|
||||||
pub movie_details: ScrollableText,
|
pub movie_details: ScrollableText,
|
||||||
@@ -26,6 +27,7 @@ pub struct RadarrData {
|
|||||||
pub movie_cast: StatefulTable<Credit>,
|
pub movie_cast: StatefulTable<Credit>,
|
||||||
pub movie_crew: StatefulTable<Credit>,
|
pub movie_crew: StatefulTable<Credit>,
|
||||||
pub collections: StatefulTable<Collection>,
|
pub collections: StatefulTable<Collection>,
|
||||||
|
pub filtered_collections: StatefulTable<Collection>,
|
||||||
pub collection_movies: StatefulTable<CollectionMovie>,
|
pub collection_movies: StatefulTable<CollectionMovie>,
|
||||||
pub main_tabs: TabState,
|
pub main_tabs: TabState,
|
||||||
pub movie_info_tabs: TabState,
|
pub movie_info_tabs: TabState,
|
||||||
@@ -43,6 +45,8 @@ impl RadarrData {
|
|||||||
self.is_searching = false;
|
self.is_searching = false;
|
||||||
self.search = String::default();
|
self.search = String::default();
|
||||||
self.filter = String::default();
|
self.filter = String::default();
|
||||||
|
self.filtered_movies = StatefulTable::default();
|
||||||
|
self.filtered_collections = StatefulTable::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_movie_info_tabs(&mut self) {
|
pub fn reset_movie_info_tabs(&mut self) {
|
||||||
@@ -68,6 +72,7 @@ impl Default for RadarrData {
|
|||||||
version: String::default(),
|
version: String::default(),
|
||||||
start_time: DateTime::default(),
|
start_time: DateTime::default(),
|
||||||
movies: StatefulTable::default(),
|
movies: StatefulTable::default(),
|
||||||
|
filtered_movies: StatefulTable::default(),
|
||||||
downloads: StatefulTable::default(),
|
downloads: StatefulTable::default(),
|
||||||
quality_profile_map: HashMap::default(),
|
quality_profile_map: HashMap::default(),
|
||||||
file_details: String::default(),
|
file_details: String::default(),
|
||||||
@@ -78,6 +83,7 @@ impl Default for RadarrData {
|
|||||||
movie_cast: StatefulTable::default(),
|
movie_cast: StatefulTable::default(),
|
||||||
movie_crew: StatefulTable::default(),
|
movie_crew: StatefulTable::default(),
|
||||||
collections: StatefulTable::default(),
|
collections: StatefulTable::default(),
|
||||||
|
filtered_collections: StatefulTable::default(),
|
||||||
collection_movies: StatefulTable::default(),
|
collection_movies: StatefulTable::default(),
|
||||||
search: String::default(),
|
search: String::default(),
|
||||||
filter: String::default(),
|
filter: String::default(),
|
||||||
@@ -86,7 +92,7 @@ impl Default for RadarrData {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Library".to_owned(),
|
title: "Library".to_owned(),
|
||||||
route: ActiveRadarrBlock::Movies.into(),
|
route: ActiveRadarrBlock::Movies.into(),
|
||||||
help: "<↑↓> scroll | <s> search | <f> filter | <enter> details | ←→ change tab "
|
help: "<↑↓> scroll | <s> search | <f> filter | <enter> details | <esc> cancel filter | ←→ change tab "
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
},
|
},
|
||||||
TabRoute {
|
TabRoute {
|
||||||
@@ -97,7 +103,7 @@ impl Default for RadarrData {
|
|||||||
TabRoute {
|
TabRoute {
|
||||||
title: "Collections".to_owned(),
|
title: "Collections".to_owned(),
|
||||||
route: ActiveRadarrBlock::Collections.into(),
|
route: ActiveRadarrBlock::Collections.into(),
|
||||||
help: "<↑↓> scroll | <s> search | <f> filter | <enter> details | ←→ change tab "
|
help: "<↑↓> scroll | <s> search | <f> filter | <enter> details | <esc> cancel filter | ←→ change tab "
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
@@ -245,14 +251,27 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn populate_movie_collection_table(&mut self) {
|
async fn populate_movie_collection_table(&mut self) {
|
||||||
self.data.radarr_data.collection_movies.set_items(
|
let collection_movies = if !self.data.radarr_data.filtered_collections.items.is_empty() {
|
||||||
|
self
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.filtered_collections
|
||||||
|
.current_selection_clone()
|
||||||
|
.movies
|
||||||
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
self
|
self
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
.current_selection_clone()
|
.current_selection_clone()
|
||||||
.movies
|
.movies
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default()
|
||||||
);
|
};
|
||||||
|
self
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.collection_movies
|
||||||
|
.set_items(collection_movies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use regex::Regex;
|
|
||||||
|
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::app::radarr::ActiveRadarrBlock;
|
use crate::app::radarr::ActiveRadarrBlock;
|
||||||
use crate::handlers::radarr_handlers::collection_details_handler::CollectionDetailsHandler;
|
use crate::handlers::radarr_handlers::collection_details_handler::CollectionDetailsHandler;
|
||||||
use crate::handlers::radarr_handlers::movie_details_handler::MovieDetailsHandler;
|
use crate::handlers::radarr_handlers::movie_details_handler::MovieDetailsHandler;
|
||||||
use crate::handlers::{handle_clear_errors, KeyEventHandler};
|
use crate::handlers::{handle_clear_errors, KeyEventHandler};
|
||||||
use crate::models::radarr_models::Movie;
|
use crate::models::radarr_models::{Collection, Movie};
|
||||||
use crate::models::Scrollable;
|
use crate::models::Scrollable;
|
||||||
use crate::utils::strip_non_alphanumeric_characters;
|
use crate::utils::strip_non_alphanumeric_characters;
|
||||||
use crate::{App, Key};
|
use crate::{App, Key};
|
||||||
@@ -55,16 +53,15 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
|||||||
fn handle_scroll_up(&mut self) {
|
fn handle_scroll_up(&mut self) {
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::Collections => {
|
ActiveRadarrBlock::Collections => {
|
||||||
if !self.app.data.radarr_data.filter.is_empty() {
|
if !self
|
||||||
self
|
.app
|
||||||
.app
|
.data
|
||||||
.data
|
.radarr_data
|
||||||
.radarr_data
|
.filtered_collections
|
||||||
.collections
|
.items
|
||||||
.scroll_up_with_filter(|&collection| {
|
.is_empty()
|
||||||
strip_non_alphanumeric_characters(&collection.title)
|
{
|
||||||
.starts_with(&self.app.data.radarr_data.filter)
|
self.app.data.radarr_data.filtered_collections.scroll_up();
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
self.app.data.radarr_data.collections.scroll_up()
|
self.app.data.radarr_data.collections.scroll_up()
|
||||||
}
|
}
|
||||||
@@ -73,16 +70,8 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
|||||||
self.app.data.radarr_data.collection_movies.scroll_up()
|
self.app.data.radarr_data.collection_movies.scroll_up()
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::Movies => {
|
ActiveRadarrBlock::Movies => {
|
||||||
if !self.app.data.radarr_data.filter.is_empty() {
|
if !self.app.data.radarr_data.filtered_movies.items.is_empty() {
|
||||||
self
|
self.app.data.radarr_data.filtered_movies.scroll_up();
|
||||||
.app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.movies
|
|
||||||
.scroll_up_with_filter(|&movie| {
|
|
||||||
strip_non_alphanumeric_characters(&movie.title)
|
|
||||||
.starts_with(&self.app.data.radarr_data.filter)
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
self.app.data.radarr_data.movies.scroll_up()
|
self.app.data.radarr_data.movies.scroll_up()
|
||||||
}
|
}
|
||||||
@@ -95,16 +84,15 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
|||||||
fn handle_scroll_down(&mut self) {
|
fn handle_scroll_down(&mut self) {
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::Collections => {
|
ActiveRadarrBlock::Collections => {
|
||||||
if !self.app.data.radarr_data.filter.is_empty() {
|
if !self
|
||||||
self
|
.app
|
||||||
.app
|
.data
|
||||||
.data
|
.radarr_data
|
||||||
.radarr_data
|
.filtered_collections
|
||||||
.collections
|
.items
|
||||||
.scroll_down_with_filter(|&collection| {
|
.is_empty()
|
||||||
strip_non_alphanumeric_characters(&collection.title)
|
{
|
||||||
.starts_with(&self.app.data.radarr_data.filter)
|
self.app.data.radarr_data.filtered_collections.scroll_down();
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
self.app.data.radarr_data.collections.scroll_down()
|
self.app.data.radarr_data.collections.scroll_down()
|
||||||
}
|
}
|
||||||
@@ -113,16 +101,8 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
|||||||
self.app.data.radarr_data.collection_movies.scroll_down()
|
self.app.data.radarr_data.collection_movies.scroll_down()
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::Movies => {
|
ActiveRadarrBlock::Movies => {
|
||||||
if !self.app.data.radarr_data.filter.is_empty() {
|
if !self.app.data.radarr_data.filtered_movies.items.is_empty() {
|
||||||
self
|
self.app.data.radarr_data.filtered_movies.scroll_down();
|
||||||
.app
|
|
||||||
.data
|
|
||||||
.radarr_data
|
|
||||||
.movies
|
|
||||||
.scroll_down_with_filter(|&movie| {
|
|
||||||
strip_non_alphanumeric_characters(&movie.title)
|
|
||||||
.starts_with(&self.app.data.radarr_data.filter)
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
self.app.data.radarr_data.movies.scroll_down()
|
self.app.data.radarr_data.movies.scroll_down()
|
||||||
}
|
}
|
||||||
@@ -192,7 +172,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
|||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.position(|movie| {
|
.position(|movie| {
|
||||||
strip_non_alphanumeric_characters(&movie.title).starts_with(&search_string)
|
strip_non_alphanumeric_characters(&movie.title).contains(&search_string)
|
||||||
});
|
});
|
||||||
|
|
||||||
self.app.data.radarr_data.is_searching = false;
|
self.app.data.radarr_data.is_searching = false;
|
||||||
@@ -220,7 +200,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
|||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.position(|collection| {
|
.position(|collection| {
|
||||||
strip_non_alphanumeric_characters(&collection.title).starts_with(&search_string)
|
strip_non_alphanumeric_characters(&collection.title).contains(&search_string)
|
||||||
});
|
});
|
||||||
|
|
||||||
self.app.data.radarr_data.is_searching = false;
|
self.app.data.radarr_data.is_searching = false;
|
||||||
@@ -236,47 +216,63 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::FilterMovies => {
|
ActiveRadarrBlock::FilterMovies => {
|
||||||
self.app.data.radarr_data.filter =
|
let filter = strip_non_alphanumeric_characters(
|
||||||
strip_non_alphanumeric_characters(&self.app.data.radarr_data.filter);
|
&self
|
||||||
let filter_string = &self.app.data.radarr_data.filter;
|
.app
|
||||||
let filter_matches = self
|
.data
|
||||||
.app
|
.radarr_data
|
||||||
.data
|
.filter
|
||||||
.radarr_data
|
.drain(..)
|
||||||
.movies
|
.collect::<String>(),
|
||||||
.items
|
);
|
||||||
|
let movie_list = self.app.data.radarr_data.movies.items.clone();
|
||||||
|
let filter_matches = movie_list
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&movie| {
|
.filter(|&movie| strip_non_alphanumeric_characters(&movie.title).contains(&filter))
|
||||||
strip_non_alphanumeric_characters(&movie.title).starts_with(filter_string)
|
.map(|movie| movie.to_owned())
|
||||||
})
|
.collect::<Vec<Movie>>();
|
||||||
.count();
|
|
||||||
|
|
||||||
self.app.data.radarr_data.is_searching = false;
|
self.app.data.radarr_data.is_searching = false;
|
||||||
|
|
||||||
if filter_matches > 0 {
|
if !filter_matches.is_empty() {
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.filtered_movies
|
||||||
|
.set_items(filter_matches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::FilterCollections => {
|
ActiveRadarrBlock::FilterCollections => {
|
||||||
self.app.data.radarr_data.filter =
|
let filter = strip_non_alphanumeric_characters(
|
||||||
strip_non_alphanumeric_characters(&self.app.data.radarr_data.filter);
|
&self
|
||||||
let filter_string = &self.app.data.radarr_data.filter;
|
.app
|
||||||
let filter_matches = self
|
.data
|
||||||
.app
|
.radarr_data
|
||||||
.data
|
.filter
|
||||||
.radarr_data
|
.drain(..)
|
||||||
.collections
|
.collect::<String>(),
|
||||||
.items
|
);
|
||||||
|
let collection_list = self.app.data.radarr_data.collections.items.clone();
|
||||||
|
let filter_matches = collection_list
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&collection| {
|
.filter(|&collection| {
|
||||||
strip_non_alphanumeric_characters(&collection.title).starts_with(filter_string)
|
strip_non_alphanumeric_characters(&collection.title).contains(&filter)
|
||||||
})
|
})
|
||||||
.count();
|
.map(|collection| collection.to_owned())
|
||||||
|
.collect::<Vec<Collection>>();
|
||||||
|
|
||||||
self.app.data.radarr_data.is_searching = false;
|
self.app.data.radarr_data.is_searching = false;
|
||||||
|
|
||||||
if filter_matches > 0 {
|
if !filter_matches.is_empty() {
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.filtered_collections
|
||||||
|
.set_items(filter_matches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|||||||
+3
-51
@@ -1,6 +1,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
|
||||||
|
use log::debug;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tui::widgets::TableState;
|
use tui::widgets::TableState;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ impl<T> Default for StatefulTable<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + PartialEq + Eq> StatefulTable<T> {
|
impl<T: Clone + PartialEq + Eq + Debug> StatefulTable<T> {
|
||||||
pub fn set_items(&mut self, items: Vec<T>) {
|
pub fn set_items(&mut self, items: Vec<T>) {
|
||||||
let items_len = items.len();
|
let items_len = items.len();
|
||||||
self.items = items;
|
self.items = items;
|
||||||
@@ -69,55 +70,6 @@ impl<T: Clone + PartialEq + Eq> StatefulTable<T> {
|
|||||||
pub fn select_index(&mut self, index: Option<usize>) {
|
pub fn select_index(&mut self, index: Option<usize>) {
|
||||||
self.state.select(index);
|
self.state.select(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_up_with_filter<F>(&mut self, filter: F)
|
|
||||||
where
|
|
||||||
F: FnMut(&&T) -> bool,
|
|
||||||
{
|
|
||||||
let filtered_list: Vec<&T> = self.items.iter().filter(filter).collect();
|
|
||||||
|
|
||||||
let element_position = filtered_list
|
|
||||||
.iter()
|
|
||||||
.position(|&item| item == self.current_selection())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if element_position == 0 {
|
|
||||||
let selected_index = self
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.position(|item| item == filtered_list[filtered_list.len()]);
|
|
||||||
self.select_index(selected_index);
|
|
||||||
} else {
|
|
||||||
let selected_index = self
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.position(|item| item == filtered_list[element_position - 1]);
|
|
||||||
self.select_index(selected_index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scroll_down_with_filter<F>(&mut self, filter: F)
|
|
||||||
where
|
|
||||||
F: FnMut(&&T) -> bool,
|
|
||||||
{
|
|
||||||
let filtered_list: Vec<&T> = self.items.iter().filter(filter).collect();
|
|
||||||
|
|
||||||
let element_position = filtered_list
|
|
||||||
.iter()
|
|
||||||
.position(|&item| item == self.current_selection())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if element_position + 1 > filtered_list.len() {
|
|
||||||
let selected_index = self.items.iter().position(|item| item == filtered_list[0]);
|
|
||||||
self.select_index(selected_index);
|
|
||||||
} else {
|
|
||||||
let selected_index = self
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.position(|item| item == filtered_list[element_position + 1]);
|
|
||||||
self.select_index(selected_index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Scrollable for StatefulTable<T> {
|
impl<T> Scrollable for StatefulTable<T> {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use indoc::formatdoc;
|
use indoc::formatdoc;
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
@@ -195,6 +197,7 @@ impl<'a> Network<'a> {
|
|||||||
|
|
||||||
let status = get_movie_status(has_file, &app.data.radarr_data.downloads.items, id);
|
let status = get_movie_status(has_file, &app.data.radarr_data.downloads.items, id);
|
||||||
let collection = collection.unwrap_or_default();
|
let collection = collection.unwrap_or_default();
|
||||||
|
debug!("title: {:?}", title);
|
||||||
|
|
||||||
app.data.radarr_data.movie_details = ScrollableText::with_string(formatdoc!(
|
app.data.radarr_data.movie_details = ScrollableText::with_string(formatdoc!(
|
||||||
"Title: {}
|
"Title: {}
|
||||||
@@ -394,18 +397,42 @@ impl<'a> Network<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn extract_movie_id(&self) -> u64 {
|
async fn extract_movie_id(&self) -> u64 {
|
||||||
self
|
if !self
|
||||||
.app
|
.app
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.filtered_movies
|
||||||
.current_selection()
|
.items
|
||||||
.id
|
.is_empty()
|
||||||
.clone()
|
{
|
||||||
.as_u64()
|
self
|
||||||
.unwrap()
|
.app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.filtered_movies
|
||||||
|
.current_selection()
|
||||||
|
.id
|
||||||
|
.clone()
|
||||||
|
.as_u64()
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.movies
|
||||||
|
.current_selection()
|
||||||
|
.id
|
||||||
|
.clone()
|
||||||
|
.as_u64()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn append_movie_id_param(&self, resource: &str) -> String {
|
async fn append_movie_id_param(&self, resource: &str) -> String {
|
||||||
|
|||||||
+2
-4
@@ -201,18 +201,16 @@ pub struct TableProps<'a, T> {
|
|||||||
pub constraints: Vec<Constraint>,
|
pub constraints: Vec<Constraint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_table<'a, B, T, F, S>(
|
fn draw_table<'a, B, T, F>(
|
||||||
f: &mut Frame<'_, B>,
|
f: &mut Frame<'_, B>,
|
||||||
content_area: Rect,
|
content_area: Rect,
|
||||||
block: Block,
|
block: Block,
|
||||||
table_props: TableProps<'a, T>,
|
table_props: TableProps<'a, T>,
|
||||||
row_mapper: F,
|
row_mapper: F,
|
||||||
filter_fn: S,
|
|
||||||
is_loading: bool,
|
is_loading: bool,
|
||||||
) where
|
) where
|
||||||
B: Backend,
|
B: Backend,
|
||||||
F: Fn(&T) -> Row<'a>,
|
F: Fn(&T) -> Row<'a>,
|
||||||
S: FnMut(&&T) -> bool,
|
|
||||||
{
|
{
|
||||||
let TableProps {
|
let TableProps {
|
||||||
content,
|
content,
|
||||||
@@ -221,7 +219,7 @@ fn draw_table<'a, B, T, F, S>(
|
|||||||
} = table_props;
|
} = table_props;
|
||||||
|
|
||||||
if !content.items.is_empty() {
|
if !content.items.is_empty() {
|
||||||
let rows = content.items.iter().filter(filter_fn).map(row_mapper);
|
let rows = content.items.iter().map(row_mapper);
|
||||||
|
|
||||||
let headers = Row::new(table_headers)
|
let headers = Row::new(table_headers)
|
||||||
.style(style_default_bold())
|
.style(style_default_bold())
|
||||||
|
|||||||
@@ -46,7 +46,15 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
|
|||||||
content_area,
|
content_area,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
let collection_selection = app.data.radarr_data.collections.current_selection();
|
let collection_selection = if !app.data.radarr_data.filtered_collections.items.is_empty() {
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.radarr_data
|
||||||
|
.filtered_collections
|
||||||
|
.current_selection()
|
||||||
|
} else {
|
||||||
|
app.data.radarr_data.collections.current_selection()
|
||||||
|
};
|
||||||
let quality_profile = app
|
let quality_profile = app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
@@ -150,7 +158,6 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
|
|||||||
])
|
])
|
||||||
.style(style_primary())
|
.style(style_primary())
|
||||||
},
|
},
|
||||||
|_| true,
|
|
||||||
app.is_loading,
|
app.is_loading,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-30
@@ -2,7 +2,6 @@ use std::iter;
|
|||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
|
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
use regex::Regex;
|
|
||||||
use tui::backend::Backend;
|
use tui::backend::Backend;
|
||||||
use tui::layout::{Alignment, Constraint, Rect};
|
use tui::layout::{Alignment, Constraint, Rect};
|
||||||
use tui::style::{Color, Style};
|
use tui::style::{Color, Style};
|
||||||
@@ -13,7 +12,7 @@ use tui::Frame;
|
|||||||
use crate::app::radarr::{ActiveRadarrBlock, RadarrData};
|
use crate::app::radarr::{ActiveRadarrBlock, RadarrData};
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::logos::RADARR_LOGO;
|
use crate::logos::RADARR_LOGO;
|
||||||
use crate::models::radarr_models::{Collection, DiskSpace, DownloadRecord, Movie};
|
use crate::models::radarr_models::{DiskSpace, DownloadRecord, Movie};
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup;
|
use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup;
|
||||||
use crate::ui::radarr_ui::movie_details_ui::draw_movie_info;
|
use crate::ui::radarr_ui::movie_details_ui::draw_movie_info;
|
||||||
@@ -84,15 +83,12 @@ pub(super) fn draw_radarr_context_row<B: Backend>(f: &mut Frame<'_, B>, app: &Ap
|
|||||||
fn draw_library<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
fn draw_library<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
||||||
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
||||||
let downloads_vec = &app.data.radarr_data.downloads.items;
|
let downloads_vec = &app.data.radarr_data.downloads.items;
|
||||||
let filter_fn: Box<dyn FnMut(&&Movie) -> bool> = if !app.data.radarr_data.filter.is_empty() {
|
let content = if !app.data.radarr_data.filtered_movies.items.is_empty()
|
||||||
Box::new(|&movie| {
|
&& !app.data.radarr_data.is_searching
|
||||||
Regex::new(r"[^a-zA-Z0-9\s]")
|
{
|
||||||
.unwrap()
|
&mut app.data.radarr_data.filtered_movies
|
||||||
.replace_all(&movie.title.to_lowercase(), "")
|
|
||||||
.starts_with(&app.data.radarr_data.filter)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Box::new(|_| true)
|
&mut app.data.radarr_data.movies
|
||||||
};
|
};
|
||||||
|
|
||||||
draw_table(
|
draw_table(
|
||||||
@@ -100,7 +96,7 @@ fn draw_library<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
|||||||
area,
|
area,
|
||||||
layout_block_top_border(),
|
layout_block_top_border(),
|
||||||
TableProps {
|
TableProps {
|
||||||
content: &mut app.data.radarr_data.movies,
|
content,
|
||||||
table_headers: vec![
|
table_headers: vec![
|
||||||
"Title",
|
"Title",
|
||||||
"Year",
|
"Year",
|
||||||
@@ -141,7 +137,6 @@ fn draw_library<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
|||||||
])
|
])
|
||||||
.style(determine_row_style(downloads_vec, movie))
|
.style(determine_row_style(downloads_vec, movie))
|
||||||
},
|
},
|
||||||
filter_fn,
|
|
||||||
app.is_loading,
|
app.is_loading,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -166,19 +161,23 @@ fn draw_search_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|
|||||||
|
|
||||||
f.render_widget(input, chunks[0]);
|
f.render_widget(input, chunks[0]);
|
||||||
} else {
|
} else {
|
||||||
let block_title = match app.get_current_route() {
|
let (block_title, block_content) = match app.get_current_route() {
|
||||||
Route::Radarr(active_radarr_block) => match active_radarr_block {
|
Route::Radarr(active_radarr_block) => match active_radarr_block {
|
||||||
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => "Search",
|
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
|
||||||
ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterCollections => "Filter",
|
("Search", app.data.radarr_data.search.as_str())
|
||||||
_ => "",
|
}
|
||||||
|
ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterCollections => {
|
||||||
|
("Filter", app.data.radarr_data.filter.as_str())
|
||||||
|
}
|
||||||
|
_ => ("", ""),
|
||||||
},
|
},
|
||||||
_ => "",
|
_ => ("", ""),
|
||||||
};
|
};
|
||||||
|
|
||||||
let input = Paragraph::new(app.data.radarr_data.search.as_ref())
|
let input = Paragraph::new(block_content)
|
||||||
.style(style_default())
|
.style(style_default())
|
||||||
.block(title_block(block_title));
|
.block(title_block(block_title));
|
||||||
show_cursor(f, chunks[0], &app.data.radarr_data.search);
|
show_cursor(f, chunks[0], block_content);
|
||||||
|
|
||||||
f.render_widget(input, chunks[0]);
|
f.render_widget(input, chunks[0]);
|
||||||
}
|
}
|
||||||
@@ -275,29 +274,25 @@ fn draw_downloads<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
|||||||
])
|
])
|
||||||
.style(style_primary())
|
.style(style_primary())
|
||||||
},
|
},
|
||||||
|_| true,
|
|
||||||
app.is_loading,
|
app.is_loading,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
|
||||||
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
let quality_profile_map = &app.data.radarr_data.quality_profile_map;
|
||||||
let filter_fn: Box<dyn FnMut(&&Collection) -> bool> = if !app.data.radarr_data.filter.is_empty() {
|
let content = if !app.data.radarr_data.filtered_collections.items.is_empty()
|
||||||
Box::new(|&collection| {
|
&& !app.data.radarr_data.is_searching
|
||||||
Regex::new(r"[^a-zA-Z0-9\s]")
|
{
|
||||||
.unwrap()
|
&mut app.data.radarr_data.filtered_collections
|
||||||
.replace_all(&collection.title.to_lowercase(), "")
|
|
||||||
.starts_with(&app.data.radarr_data.filter)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Box::new(|_| true)
|
&mut app.data.radarr_data.collections
|
||||||
};
|
};
|
||||||
draw_table(
|
draw_table(
|
||||||
f,
|
f,
|
||||||
area,
|
area,
|
||||||
layout_block_top_border(),
|
layout_block_top_border(),
|
||||||
TableProps {
|
TableProps {
|
||||||
content: &mut app.data.radarr_data.collections,
|
content,
|
||||||
table_headers: vec![
|
table_headers: vec![
|
||||||
"Collection",
|
"Collection",
|
||||||
"Search on Add?",
|
"Search on Add?",
|
||||||
@@ -324,7 +319,6 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
|
|||||||
])
|
])
|
||||||
.style(style_primary())
|
.style(style_primary())
|
||||||
},
|
},
|
||||||
filter_fn,
|
|
||||||
app.is_loading,
|
app.is_loading,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,6 @@ fn draw_movie_history<B: Backend>(
|
|||||||
])
|
])
|
||||||
.style(style_success())
|
.style(style_success())
|
||||||
},
|
},
|
||||||
|_| true,
|
|
||||||
app.is_loading,
|
app.is_loading,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -229,7 +228,6 @@ fn draw_movie_cast<B: Backend>(
|
|||||||
])
|
])
|
||||||
.style(style_success())
|
.style(style_success())
|
||||||
},
|
},
|
||||||
|_| true,
|
|
||||||
app.is_loading,
|
app.is_loading,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -264,7 +262,6 @@ fn draw_movie_crew<B: Backend>(
|
|||||||
])
|
])
|
||||||
.style(style_success())
|
.style(style_success())
|
||||||
},
|
},
|
||||||
|_| true,
|
|
||||||
app.is_loading,
|
app.is_loading,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -206,6 +206,6 @@ pub fn line_gauge_with_label(title: &str, ratio: f64) -> LineGauge {
|
|||||||
.label(Spans::from(format!("{}: {:.0}%", title, ratio * 100.0)))
|
.label(Spans::from(format!("{}: {:.0}%", title, ratio * 100.0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_cursor<B: Backend>(f: &mut Frame<'_, B>, area: Rect, string: &String) {
|
pub fn show_cursor<B: Backend>(f: &mut Frame<'_, B>, area: Rect, string: &str) {
|
||||||
f.set_cursor(area.x + string.len() as u16 + 1, area.y + 1);
|
f.set_cursor(area.x + string.len() as u16 + 1, area.y + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user