Added horizontal scrolling for long movie titles, a refresh key, and fixed the network issues so that network requests are sent once every 20 seconds by default to not stress out the server.

This commit is contained in:
2023-08-08 10:50:06 -06:00
parent 7f3dd18478
commit 514fd2244a
12 changed files with 337 additions and 205 deletions
+5
View File
@@ -20,6 +20,7 @@ generate_keybindings! {
sort, sort,
edit, edit,
refresh, refresh,
update,
home, home,
end, end,
delete, delete,
@@ -78,6 +79,10 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
key: Key::Char('r'), key: Key::Char('r'),
desc: "Refresh", desc: "Refresh",
}, },
update: KeyBinding {
key: Key::Char('u'),
desc: "Update All",
},
home: KeyBinding { home: KeyBinding {
key: Key::Home, key: Key::Home,
desc: "Home", desc: "Home",
+2 -9
View File
@@ -1,5 +1,3 @@
use std::time::Duration;
use anyhow::anyhow; use anyhow::anyhow;
use log::{debug, error}; use log::{debug, error};
use reqwest::Client; use reqwest::Client;
@@ -28,7 +26,6 @@ pub struct App {
pub ticks_until_scroll: u64, pub ticks_until_scroll: u64,
pub tick_count: u64, pub tick_count: u64,
pub last_tick: Instant, pub last_tick: Instant,
pub network_tick_frequency: Duration,
pub is_routing: bool, pub is_routing: bool,
pub is_loading: bool, pub is_loading: bool,
pub should_refresh: bool, pub should_refresh: bool,
@@ -137,10 +134,9 @@ impl Default for App {
]), ]),
client: Client::new(), client: Client::new(),
title: "Managarr", title: "Managarr",
tick_until_poll: 50, tick_until_poll: 400,
ticks_until_scroll: 4, ticks_until_scroll: 4,
tick_count: 0, tick_count: 0,
network_tick_frequency: Duration::from_secs(20),
last_tick: Instant::now(), last_tick: Instant::now(),
is_loading: false, is_loading: false,
is_routing: false, is_routing: false,
@@ -181,8 +177,6 @@ impl Default for RadarrConfig {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::time::Duration;
use anyhow::anyhow; use anyhow::anyhow;
use pretty_assertions::{assert_eq, assert_str_eq}; use pretty_assertions::{assert_eq, assert_str_eq};
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -220,10 +214,9 @@ mod tests {
] ]
); );
assert_str_eq!(app.title, "Managarr"); assert_str_eq!(app.title, "Managarr");
assert_eq!(app.tick_until_poll, 50); assert_eq!(app.tick_until_poll, 400);
assert_eq!(app.ticks_until_scroll, 4); assert_eq!(app.ticks_until_scroll, 4);
assert_eq!(app.tick_count, 0); assert_eq!(app.tick_count, 0);
assert_eq!(app.network_tick_frequency, Duration::from_secs(20));
assert!(!app.is_loading); assert!(!app.is_loading);
assert!(!app.is_routing); assert!(!app.is_routing);
assert!(!app.should_refresh); assert!(!app.should_refresh);
+19 -25
View File
@@ -1,5 +1,3 @@
use std::time::Duration;
use bimap::BiMap; use bimap::BiMap;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@@ -218,7 +216,7 @@ impl Default for RadarrData {
title: "Library".to_owned(), title: "Library".to_owned(),
route: ActiveRadarrBlock::Movies.into(), route: ActiveRadarrBlock::Movies.into(),
help: String::default(), help: String::default(),
contextual_help: Some("<a> add | <e> edit | <s> search | <f> filter | <r> refresh | <enter> details | <esc> cancel filter | <del> delete" contextual_help: Some("<a> add | <e> edit | <s> search | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter | <del> delete"
.to_owned()), .to_owned()),
}, },
TabRoute { TabRoute {
@@ -231,7 +229,7 @@ impl Default for RadarrData {
title: "Collections".to_owned(), title: "Collections".to_owned(),
route: ActiveRadarrBlock::Collections.into(), route: ActiveRadarrBlock::Collections.into(),
help: String::default(), help: String::default(),
contextual_help: Some("<s> search | <f> filter | <r> refresh | <enter> details | <esc> cancel filter" contextual_help: Some("<s> search | <f> filter | <r> refresh | <u> update all | <enter> details | <esc> cancel filter"
.to_owned()), .to_owned()),
}, },
]), ]),
@@ -239,37 +237,37 @@ impl Default for RadarrData {
TabRoute { TabRoute {
title: "Details".to_owned(), title: "Details".to_owned(),
route: ActiveRadarrBlock::MovieDetails.into(), route: ActiveRadarrBlock::MovieDetails.into(),
help: "<r> refresh | <e> edit | <s> auto search | <esc> close".to_owned(), help: "<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close".to_owned(),
contextual_help: None contextual_help: None
}, },
TabRoute { TabRoute {
title: "History".to_owned(), title: "History".to_owned(),
route: ActiveRadarrBlock::MovieHistory.into(), route: ActiveRadarrBlock::MovieHistory.into(),
help: "<r> refresh | <e> edit | <s> auto search | <esc> close".to_owned(), help: "<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close".to_owned(),
contextual_help: None contextual_help: None
}, },
TabRoute { TabRoute {
title: "File".to_owned(), title: "File".to_owned(),
route: ActiveRadarrBlock::FileInfo.into(), route: ActiveRadarrBlock::FileInfo.into(),
help: "<r> refresh | <e> edit | <s> auto search | <esc> close".to_owned(), help: "<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close".to_owned(),
contextual_help: None, contextual_help: None,
}, },
TabRoute { TabRoute {
title: "Cast".to_owned(), title: "Cast".to_owned(),
route: ActiveRadarrBlock::Cast.into(), route: ActiveRadarrBlock::Cast.into(),
help: "<r> refresh | <e> edit | <s> auto search | <esc> close".to_owned(), help: "<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close".to_owned(),
contextual_help: None, contextual_help: None,
}, },
TabRoute { TabRoute {
title: "Crew".to_owned(), title: "Crew".to_owned(),
route: ActiveRadarrBlock::Crew.into(), route: ActiveRadarrBlock::Crew.into(),
help: "<r> refresh | <e> edit | <s> auto search | <esc> close".to_owned(), help: "<r> refresh | <u> update | <e> edit | <s> auto search | <esc> close".to_owned(),
contextual_help: None, contextual_help: None,
}, },
TabRoute { TabRoute {
title: "Manual Search".to_owned(), title: "Manual Search".to_owned(),
route: ActiveRadarrBlock::ManualSearch.into(), route: ActiveRadarrBlock::ManualSearch.into(),
help: "<r> refresh | <e> edit | <o> sort | <s> auto search | <esc> close".to_owned(), help: "<r> refresh | <u> update | <e> edit | <o> sort | <s> auto search | <esc> close".to_owned(),
contextual_help: Some("<enter> details".to_owned()) contextual_help: Some("<enter> details".to_owned())
} }
]), ]),
@@ -313,10 +311,10 @@ pub enum ActiveRadarrBlock {
MovieDetails, MovieDetails,
MovieHistory, MovieHistory,
Movies, Movies,
RefreshAndScanPrompt, UpdateAndScanPrompt,
RefreshAllCollectionsPrompt, UpdateAllCollectionsPrompt,
RefreshAllMoviesPrompt, UpdateAllMoviesPrompt,
RefreshDownloadsPrompt, UpdateDownloadsPrompt,
SearchMovie, SearchMovie,
SearchCollection, SearchCollection,
ViewMovieOverview, ViewMovieOverview,
@@ -349,7 +347,7 @@ pub const MOVIE_DETAILS_BLOCKS: [ActiveRadarrBlock; 10] = [
ActiveRadarrBlock::Cast, ActiveRadarrBlock::Cast,
ActiveRadarrBlock::Crew, ActiveRadarrBlock::Crew,
ActiveRadarrBlock::AutomaticallySearchMoviePrompt, ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
ActiveRadarrBlock::RefreshAndScanPrompt, ActiveRadarrBlock::UpdateAndScanPrompt,
ActiveRadarrBlock::ManualSearch, ActiveRadarrBlock::ManualSearch,
ActiveRadarrBlock::ManualSearchSortPrompt, ActiveRadarrBlock::ManualSearchSortPrompt,
ActiveRadarrBlock::ManualSearchConfirmPrompt, ActiveRadarrBlock::ManualSearchConfirmPrompt,
@@ -540,13 +538,7 @@ impl App {
self.dispatch_by_radarr_block(&active_radarr_block).await; self.dispatch_by_radarr_block(&active_radarr_block).await;
} }
if self.is_routing if self.is_routing || self.tick_count % self.tick_until_poll == 0 {
|| self
.network_tick_frequency
.checked_sub(self.last_tick.elapsed())
.unwrap_or_else(|| Duration::from_secs(0))
.is_zero()
{
self.refresh_metadata().await; self.refresh_metadata().await;
self.dispatch_by_radarr_block(&active_radarr_block).await; self.dispatch_by_radarr_block(&active_radarr_block).await;
} }
@@ -559,6 +551,9 @@ impl App {
self self
.dispatch_network_event(RadarrEvent::GetTags.into()) .dispatch_network_event(RadarrEvent::GetTags.into())
.await; .await;
self
.dispatch_network_event(RadarrEvent::GetDownloads.into())
.await;
} }
async fn populate_movie_collection_table(&mut self) { async fn populate_movie_collection_table(&mut self) {
@@ -1019,8 +1014,6 @@ mod tests {
} }
mod radarr_tests { mod radarr_tests {
use std::time::Duration;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -1456,7 +1449,8 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_radarr_on_tick_network_tick_frequency() { async fn test_radarr_on_tick_network_tick_frequency() {
let (mut app, mut sync_network_rx) = construct_app_unit(); let (mut app, mut sync_network_rx) = construct_app_unit();
app.network_tick_frequency = Duration::from_secs(0); app.tick_count = 2;
app.tick_until_poll = 2;
app app
.radarr_on_tick(ActiveRadarrBlock::Downloads, false) .radarr_on_tick(ActiveRadarrBlock::Downloads, false)
+164 -86
View File
@@ -231,9 +231,9 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
} }
ActiveRadarrBlock::DeleteMoviePrompt ActiveRadarrBlock::DeleteMoviePrompt
| ActiveRadarrBlock::DeleteDownloadPrompt | ActiveRadarrBlock::DeleteDownloadPrompt
| ActiveRadarrBlock::RefreshAllMoviesPrompt | ActiveRadarrBlock::UpdateAllMoviesPrompt
| ActiveRadarrBlock::RefreshAllCollectionsPrompt | ActiveRadarrBlock::UpdateAllCollectionsPrompt
| ActiveRadarrBlock::RefreshDownloadsPrompt => handle_prompt_toggle(self.app, self.key), | ActiveRadarrBlock::UpdateDownloadsPrompt => handle_prompt_toggle(self.app, self.key),
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => { ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
handle_text_box_left_right_keys!(self, self.key, self.app.data.radarr_data.search) handle_text_box_left_right_keys!(self, self.key, self.app.data.radarr_data.search)
} }
@@ -256,7 +256,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
if self.app.data.radarr_data.filtered_movies.items.is_empty() { if self.app.data.radarr_data.filtered_movies.items.is_empty() {
let selected_index = self let selected_index = self
.search_table(&self.app.data.radarr_data.movies.items.clone(), |movie| { .search_table(&self.app.data.radarr_data.movies.items.clone(), |movie| {
&movie.title &movie.title.text
}); });
self self
.app .app
@@ -267,7 +267,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
} else { } else {
let selected_index = self.search_table( let selected_index = self.search_table(
&self.app.data.radarr_data.filtered_movies.items.clone(), &self.app.data.radarr_data.filtered_movies.items.clone(),
|movie| &movie.title, |movie| &movie.title.text,
); );
self self
.app .app
@@ -288,7 +288,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
{ {
let selected_index = self.search_table( let selected_index = self.search_table(
&self.app.data.radarr_data.collections.items.clone(), &self.app.data.radarr_data.collections.items.clone(),
|collection| &collection.title, |collection| &collection.title.text,
); );
self self
.app .app
@@ -299,7 +299,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
} else { } else {
let selected_index = self.search_table( let selected_index = self.search_table(
&self.app.data.radarr_data.filtered_collections.items.clone(), &self.app.data.radarr_data.filtered_collections.items.clone(),
|collection| &collection.title, |collection| &collection.title.text,
); );
self self
.app .app
@@ -312,7 +312,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
ActiveRadarrBlock::FilterMovies => { ActiveRadarrBlock::FilterMovies => {
let filtered_movies = self let filtered_movies = self
.filter_table(&self.app.data.radarr_data.movies.items.clone(), |movie| { .filter_table(&self.app.data.radarr_data.movies.items.clone(), |movie| {
&movie.title &movie.title.text
}); });
if !filtered_movies.is_empty() { if !filtered_movies.is_empty() {
@@ -327,7 +327,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
ActiveRadarrBlock::FilterCollections => { ActiveRadarrBlock::FilterCollections => {
let filtered_collections = self.filter_table( let filtered_collections = self.filter_table(
&self.app.data.radarr_data.collections.items.clone(), &self.app.data.radarr_data.collections.items.clone(),
|collection| &collection.title, |collection| &collection.title.text,
); );
if !filtered_collections.is_empty() { if !filtered_collections.is_empty() {
@@ -353,23 +353,23 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
ActiveRadarrBlock::RefreshAllMoviesPrompt => { ActiveRadarrBlock::UpdateAllMoviesPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAllMovies);
} }
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
ActiveRadarrBlock::RefreshDownloadsPrompt => { ActiveRadarrBlock::UpdateDownloadsPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::RefreshDownloads); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateDownloads);
} }
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
ActiveRadarrBlock::RefreshAllCollectionsPrompt => { ActiveRadarrBlock::UpdateAllCollectionsPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::RefreshCollections); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateCollections);
} }
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
@@ -392,9 +392,9 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
} }
ActiveRadarrBlock::DeleteMoviePrompt ActiveRadarrBlock::DeleteMoviePrompt
| ActiveRadarrBlock::DeleteDownloadPrompt | ActiveRadarrBlock::DeleteDownloadPrompt
| ActiveRadarrBlock::RefreshAllMoviesPrompt | ActiveRadarrBlock::UpdateAllMoviesPrompt
| ActiveRadarrBlock::RefreshAllCollectionsPrompt | ActiveRadarrBlock::UpdateAllCollectionsPrompt
| ActiveRadarrBlock::RefreshDownloadsPrompt => { | ActiveRadarrBlock::UpdateDownloadsPrompt => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
self.app.data.radarr_data.prompt_confirm = false; self.app.data.radarr_data.prompt_confirm = false;
} }
@@ -441,18 +441,28 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into()); .push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into());
self.app.should_ignore_quit_key = true; self.app.should_ignore_quit_key = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::RefreshAllMoviesPrompt.into()); .pop_and_push_navigation_stack((*self.active_radarr_block).into());
} }
_ => (), _ => (),
}, },
ActiveRadarrBlock::Downloads => match self.key { ActiveRadarrBlock::Downloads => match self.key {
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateDownloadsPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::RefreshDownloadsPrompt.into()); .pop_and_push_navigation_stack((*self.active_radarr_block).into());
} }
_ => (), _ => (),
}, },
@@ -471,10 +481,15 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
self.app.data.radarr_data.is_filtering = true; self.app.data.radarr_data.is_filtering = true;
self.app.should_ignore_quit_key = true; self.app.should_ignore_quit_key = true;
} }
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::RefreshAllCollectionsPrompt.into()); .pop_and_push_navigation_stack((*self.active_radarr_block).into());
} }
_ => (), _ => (),
}, },
@@ -614,6 +629,7 @@ mod tests {
use crate::handlers::radarr_handlers::RadarrHandler; use crate::handlers::radarr_handlers::RadarrHandler;
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::{Collection, Movie}; use crate::models::radarr_models::{Collection, Movie};
use crate::models::HorizontallyScrollableText;
use crate::{extended_stateful_iterable_vec, test_handler_delegation}; use crate::{extended_stateful_iterable_vec, test_handler_delegation};
mod test_handle_scroll_up_and_down { mod test_handle_scroll_up_and_down {
@@ -628,40 +644,44 @@ mod tests {
test_collections_scroll, test_collections_scroll,
RadarrHandler, RadarrHandler,
collections, collections,
Collection, simple_stateful_iterable_vec!(Collection, HorizontallyScrollableText),
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
None, None,
title title,
to_string
); );
test_iterable_scroll!( test_iterable_scroll!(
test_filtered_collections_scroll, test_filtered_collections_scroll,
RadarrHandler, RadarrHandler,
filtered_collections, filtered_collections,
Collection, simple_stateful_iterable_vec!(Collection, HorizontallyScrollableText),
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
None, None,
title title,
to_string
); );
test_iterable_scroll!( test_iterable_scroll!(
test_movies_scroll, test_movies_scroll,
RadarrHandler, RadarrHandler,
movies, movies,
Movie, simple_stateful_iterable_vec!(Movie, HorizontallyScrollableText),
ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies,
None, None,
title title,
to_string
); );
test_iterable_scroll!( test_iterable_scroll!(
test_filtered_movies_scroll, test_filtered_movies_scroll,
RadarrHandler, RadarrHandler,
filtered_movies, filtered_movies,
Movie, simple_stateful_iterable_vec!(Movie, HorizontallyScrollableText),
ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies,
None, None,
title title,
to_string
); );
test_iterable_scroll!( test_iterable_scroll!(
@@ -689,40 +709,44 @@ mod tests {
test_collections_home_end, test_collections_home_end,
RadarrHandler, RadarrHandler,
collections, collections,
Collection, extended_stateful_iterable_vec!(Collection, HorizontallyScrollableText),
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
None, None,
title title,
to_string
); );
test_iterable_home_and_end!( test_iterable_home_and_end!(
test_filtered_collections_home_end, test_filtered_collections_home_end,
RadarrHandler, RadarrHandler,
filtered_collections, filtered_collections,
Collection, extended_stateful_iterable_vec!(Collection, HorizontallyScrollableText),
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
None, None,
title title,
to_string
); );
test_iterable_home_and_end!( test_iterable_home_and_end!(
test_movies_home_end, test_movies_home_end,
RadarrHandler, RadarrHandler,
movies, movies,
Movie, extended_stateful_iterable_vec!(Movie, HorizontallyScrollableText),
ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies,
None, None,
title title,
to_string
); );
test_iterable_home_and_end!( test_iterable_home_and_end!(
test_filtered_movies_home_end, test_filtered_movies_home_end,
RadarrHandler, RadarrHandler,
filtered_movies, filtered_movies,
Movie, extended_stateful_iterable_vec!(Movie, HorizontallyScrollableText),
ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies,
None, None,
title title,
to_string
); );
test_iterable_home_and_end!( test_iterable_home_and_end!(
@@ -851,9 +875,9 @@ mod tests {
#[values( #[values(
ActiveRadarrBlock::DeleteMoviePrompt, ActiveRadarrBlock::DeleteMoviePrompt,
ActiveRadarrBlock::DeleteDownloadPrompt, ActiveRadarrBlock::DeleteDownloadPrompt,
ActiveRadarrBlock::RefreshAllMoviesPrompt, ActiveRadarrBlock::UpdateAllMoviesPrompt,
ActiveRadarrBlock::RefreshAllCollectionsPrompt, ActiveRadarrBlock::UpdateAllCollectionsPrompt,
ActiveRadarrBlock::RefreshDownloadsPrompt ActiveRadarrBlock::UpdateDownloadsPrompt
)] )]
active_radarr_block: ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key, #[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
@@ -917,7 +941,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.movies .movies
.set_items(extended_stateful_iterable_vec!(Movie)); .set_items(extended_stateful_iterable_vec!(
Movie,
HorizontallyScrollableText
));
app.data.radarr_data.search = "Test 2".to_owned().into(); app.data.radarr_data.search = "Test 2".to_owned().into();
RadarrHandler::with( RadarrHandler::with(
@@ -929,7 +956,7 @@ mod tests {
.handle(); .handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.movies.current_selection().title, app.data.radarr_data.movies.current_selection().title.text,
"Test 2" "Test 2"
); );
} }
@@ -941,7 +968,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.filtered_movies .filtered_movies
.set_items(extended_stateful_iterable_vec!(Movie)); .set_items(extended_stateful_iterable_vec!(
Movie,
HorizontallyScrollableText
));
app.data.radarr_data.search = "Test 2".to_owned().into(); app.data.radarr_data.search = "Test 2".to_owned().into();
RadarrHandler::with( RadarrHandler::with(
@@ -958,7 +988,8 @@ mod tests {
.radarr_data .radarr_data
.filtered_movies .filtered_movies
.current_selection() .current_selection()
.title, .title
.text,
"Test 2" "Test 2"
); );
} }
@@ -970,7 +1001,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.collections .collections
.set_items(extended_stateful_iterable_vec!(Collection)); .set_items(extended_stateful_iterable_vec!(
Collection,
HorizontallyScrollableText
));
app.data.radarr_data.search = "Test 2".to_owned().into(); app.data.radarr_data.search = "Test 2".to_owned().into();
RadarrHandler::with( RadarrHandler::with(
@@ -982,7 +1016,13 @@ mod tests {
.handle(); .handle();
assert_str_eq!( assert_str_eq!(
app.data.radarr_data.collections.current_selection().title, app
.data
.radarr_data
.collections
.current_selection()
.title
.text,
"Test 2" "Test 2"
); );
} }
@@ -994,7 +1034,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.filtered_collections .filtered_collections
.set_items(extended_stateful_iterable_vec!(Collection)); .set_items(extended_stateful_iterable_vec!(
Collection,
HorizontallyScrollableText
));
app.data.radarr_data.search = "Test 2".to_owned().into(); app.data.radarr_data.search = "Test 2".to_owned().into();
RadarrHandler::with( RadarrHandler::with(
@@ -1011,7 +1054,8 @@ mod tests {
.radarr_data .radarr_data
.filtered_collections .filtered_collections
.current_selection() .current_selection()
.title, .title
.text,
"Test 2" "Test 2"
); );
} }
@@ -1023,7 +1067,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.movies .movies
.set_items(extended_stateful_iterable_vec!(Movie)); .set_items(extended_stateful_iterable_vec!(
Movie,
HorizontallyScrollableText
));
app.data.radarr_data.filter = "Test".to_owned().into(); app.data.radarr_data.filter = "Test".to_owned().into();
RadarrHandler::with( RadarrHandler::with(
@@ -1041,7 +1088,8 @@ mod tests {
.radarr_data .radarr_data
.filtered_movies .filtered_movies
.current_selection() .current_selection()
.title, .title
.text,
"Test 1" "Test 1"
); );
} }
@@ -1053,7 +1101,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.collections .collections
.set_items(extended_stateful_iterable_vec!(Collection)); .set_items(extended_stateful_iterable_vec!(
Collection,
HorizontallyScrollableText
));
app.data.radarr_data.filter = "Test".to_owned().into(); app.data.radarr_data.filter = "Test".to_owned().into();
RadarrHandler::with( RadarrHandler::with(
@@ -1071,7 +1122,8 @@ mod tests {
.radarr_data .radarr_data
.filtered_collections .filtered_collections
.current_selection() .current_selection()
.title, .title
.text,
"Test 1" "Test 1"
); );
} }
@@ -1089,18 +1141,18 @@ mod tests {
)] )]
#[case( #[case(
ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies,
ActiveRadarrBlock::RefreshAllMoviesPrompt, ActiveRadarrBlock::UpdateAllMoviesPrompt,
RadarrEvent::UpdateAllMovies RadarrEvent::UpdateAllMovies
)] )]
#[case( #[case(
ActiveRadarrBlock::Downloads, ActiveRadarrBlock::Downloads,
ActiveRadarrBlock::RefreshDownloadsPrompt, ActiveRadarrBlock::UpdateDownloadsPrompt,
RadarrEvent::RefreshDownloads RadarrEvent::UpdateDownloads
)] )]
#[case( #[case(
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
ActiveRadarrBlock::RefreshAllCollectionsPrompt, ActiveRadarrBlock::UpdateAllCollectionsPrompt,
RadarrEvent::RefreshCollections RadarrEvent::UpdateCollections
)] )]
fn test_prompt_confirm_submit( fn test_prompt_confirm_submit(
#[case] base_route: ActiveRadarrBlock, #[case] base_route: ActiveRadarrBlock,
@@ -1125,14 +1177,11 @@ mod tests {
#[rstest] #[rstest]
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::DeleteMoviePrompt)] #[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::DeleteMoviePrompt)]
#[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::DeleteDownloadPrompt)] #[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::DeleteDownloadPrompt)]
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::RefreshAllMoviesPrompt)] #[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::UpdateAllMoviesPrompt)]
#[case( #[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::UpdateDownloadsPrompt)]
ActiveRadarrBlock::Downloads,
ActiveRadarrBlock::RefreshDownloadsPrompt
)]
#[case( #[case(
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
ActiveRadarrBlock::RefreshAllCollectionsPrompt ActiveRadarrBlock::UpdateAllCollectionsPrompt
)] )]
fn test_prompt_decline_submit( fn test_prompt_decline_submit(
#[case] base_route: ActiveRadarrBlock, #[case] base_route: ActiveRadarrBlock,
@@ -1203,15 +1252,12 @@ mod tests {
#[rstest] #[rstest]
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::DeleteMoviePrompt)] #[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::DeleteMoviePrompt)]
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::RefreshAllMoviesPrompt)] #[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::UpdateAllMoviesPrompt)]
#[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::DeleteDownloadPrompt)] #[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::DeleteDownloadPrompt)]
#[case( #[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::UpdateDownloadsPrompt)]
ActiveRadarrBlock::Downloads,
ActiveRadarrBlock::RefreshDownloadsPrompt
)]
#[case( #[case(
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
ActiveRadarrBlock::RefreshAllCollectionsPrompt ActiveRadarrBlock::UpdateAllCollectionsPrompt
)] )]
fn test_prompt_blocks_esc( fn test_prompt_blocks_esc(
#[case] base_block: ActiveRadarrBlock, #[case] base_block: ActiveRadarrBlock,
@@ -1337,21 +1383,40 @@ mod tests {
} }
#[rstest] #[rstest]
#[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::RefreshAllMoviesPrompt)] #[case(ActiveRadarrBlock::Movies, ActiveRadarrBlock::UpdateAllMoviesPrompt)]
#[case( #[case(ActiveRadarrBlock::Downloads, ActiveRadarrBlock::UpdateDownloadsPrompt)]
ActiveRadarrBlock::Downloads,
ActiveRadarrBlock::RefreshDownloadsPrompt
)]
#[case( #[case(
ActiveRadarrBlock::Collections, ActiveRadarrBlock::Collections,
ActiveRadarrBlock::RefreshAllCollectionsPrompt ActiveRadarrBlock::UpdateAllCollectionsPrompt
)] )]
fn test_refresh_key( fn test_update_key(
#[case] active_radarr_block: ActiveRadarrBlock, #[case] active_radarr_block: ActiveRadarrBlock,
#[case] expected_radarr_block: ActiveRadarrBlock, #[case] expected_radarr_block: ActiveRadarrBlock,
) { ) {
let mut app = App::default(); let mut app = App::default();
RadarrHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
&mut app,
&active_radarr_block,
&None,
)
.handle();
assert_eq!(app.get_current_route(), &expected_radarr_block.into());
}
#[rstest]
fn test_refresh_key(
#[values(
ActiveRadarrBlock::Movies,
ActiveRadarrBlock::Collections,
ActiveRadarrBlock::Downloads
)]
active_radarr_block: ActiveRadarrBlock,
) {
let mut app = App::default();
RadarrHandler::with( RadarrHandler::with(
&DEFAULT_KEYBINDINGS.refresh.key, &DEFAULT_KEYBINDINGS.refresh.key,
&mut app, &mut app,
@@ -1360,7 +1425,8 @@ mod tests {
) )
.handle(); .handle();
assert_eq!(app.get_current_route(), &expected_radarr_block.into()); assert_eq!(app.get_current_route(), &active_radarr_block.into());
assert!(app.is_routing);
} }
#[rstest] #[rstest]
@@ -1433,7 +1499,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.movies .movies
.set_items(extended_stateful_iterable_vec!(Movie)); .set_items(extended_stateful_iterable_vec!(
Movie,
HorizontallyScrollableText
));
app.data.radarr_data.search = "Test 2".to_owned().into(); app.data.radarr_data.search = "Test 2".to_owned().into();
app.data.radarr_data.is_searching = true; app.data.radarr_data.is_searching = true;
app.should_ignore_quit_key = true; app.should_ignore_quit_key = true;
@@ -1447,7 +1516,7 @@ mod tests {
&ActiveRadarrBlock::SearchMovie, &ActiveRadarrBlock::SearchMovie,
&None, &None,
) )
.search_table(movies, |movie| &movie.title); .search_table(movies, |movie| &movie.title.text);
assert_eq!(index, Some(1)); assert_eq!(index, Some(1));
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
@@ -1463,7 +1532,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.movies .movies
.set_items(extended_stateful_iterable_vec!(Movie)); .set_items(extended_stateful_iterable_vec!(
Movie,
HorizontallyScrollableText
));
app.data.radarr_data.search = "Test 5".to_owned().into(); app.data.radarr_data.search = "Test 5".to_owned().into();
app.data.radarr_data.is_searching = true; app.data.radarr_data.is_searching = true;
app.should_ignore_quit_key = true; app.should_ignore_quit_key = true;
@@ -1477,7 +1549,7 @@ mod tests {
&ActiveRadarrBlock::SearchMovie, &ActiveRadarrBlock::SearchMovie,
&None, &None,
) )
.search_table(movies, |movie| &movie.title); .search_table(movies, |movie| &movie.title.text);
assert_eq!(index, None); assert_eq!(index, None);
assert_eq!( assert_eq!(
@@ -1496,7 +1568,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.movies .movies
.set_items(extended_stateful_iterable_vec!(Movie)); .set_items(extended_stateful_iterable_vec!(
Movie,
HorizontallyScrollableText
));
app.data.radarr_data.filter = "Test 2".to_owned().into(); app.data.radarr_data.filter = "Test 2".to_owned().into();
app.data.radarr_data.is_searching = true; app.data.radarr_data.is_searching = true;
app.should_ignore_quit_key = true; app.should_ignore_quit_key = true;
@@ -1510,10 +1585,10 @@ mod tests {
&ActiveRadarrBlock::FilterMovies, &ActiveRadarrBlock::FilterMovies,
&None, &None,
) )
.filter_table(movies, |movie| &movie.title); .filter_table(movies, |movie| &movie.title.text);
assert_eq!(filter_matches.len(), 1); assert_eq!(filter_matches.len(), 1);
assert_str_eq!(filter_matches[0].title, "Test 2"); assert_str_eq!(filter_matches[0].title.text, "Test 2");
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
assert!(!app.data.radarr_data.is_filtering); assert!(!app.data.radarr_data.is_filtering);
assert!(!app.should_ignore_quit_key); assert!(!app.should_ignore_quit_key);
@@ -1527,7 +1602,10 @@ mod tests {
.data .data
.radarr_data .radarr_data
.movies .movies
.set_items(extended_stateful_iterable_vec!(Movie)); .set_items(extended_stateful_iterable_vec!(
Movie,
HorizontallyScrollableText
));
app.data.radarr_data.filter = "Test 5".to_owned().into(); app.data.radarr_data.filter = "Test 5".to_owned().into();
app.data.radarr_data.is_filtering = true; app.data.radarr_data.is_filtering = true;
app.should_ignore_quit_key = true; app.should_ignore_quit_key = true;
@@ -1541,7 +1619,7 @@ mod tests {
&ActiveRadarrBlock::FilterMovies, &ActiveRadarrBlock::FilterMovies,
&None, &None,
) )
.filter_table(movies, |movie| &movie.title); .filter_table(movies, |movie| &movie.title.text);
assert!(filter_matches.is_empty()); assert!(filter_matches.is_empty());
assert_eq!( assert_eq!(
@@ -1590,7 +1668,7 @@ mod tests {
ActiveRadarrBlock::Cast, ActiveRadarrBlock::Cast,
ActiveRadarrBlock::Crew, ActiveRadarrBlock::Crew,
ActiveRadarrBlock::AutomaticallySearchMoviePrompt, ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
ActiveRadarrBlock::RefreshAndScanPrompt, ActiveRadarrBlock::UpdateAndScanPrompt,
ActiveRadarrBlock::ManualSearch, ActiveRadarrBlock::ManualSearch,
ActiveRadarrBlock::ManualSearchConfirmPrompt ActiveRadarrBlock::ManualSearchConfirmPrompt
)] )]
@@ -127,7 +127,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for MovieDetailsHandler<'a> {
_ => (), _ => (),
}, },
ActiveRadarrBlock::AutomaticallySearchMoviePrompt ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::RefreshAndScanPrompt | ActiveRadarrBlock::UpdateAndScanPrompt
| ActiveRadarrBlock::ManualSearchConfirmPrompt => handle_prompt_toggle(self.app, self.key), | ActiveRadarrBlock::ManualSearchConfirmPrompt => handle_prompt_toggle(self.app, self.key),
_ => (), _ => (),
} }
@@ -143,9 +143,9 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for MovieDetailsHandler<'a> {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
} }
ActiveRadarrBlock::RefreshAndScanPrompt => { ActiveRadarrBlock::UpdateAndScanPrompt => {
if self.app.data.radarr_data.prompt_confirm { if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::RefreshAndScan); self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan);
} }
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
@@ -201,7 +201,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for MovieDetailsHandler<'a> {
self.app.data.radarr_data.reset_movie_info_tabs(); self.app.data.radarr_data.reset_movie_info_tabs();
} }
ActiveRadarrBlock::AutomaticallySearchMoviePrompt ActiveRadarrBlock::AutomaticallySearchMoviePrompt
| ActiveRadarrBlock::RefreshAndScanPrompt | ActiveRadarrBlock::UpdateAndScanPrompt
| ActiveRadarrBlock::ManualSearchConfirmPrompt | ActiveRadarrBlock::ManualSearchConfirmPrompt
| ActiveRadarrBlock::ManualSearchSortPrompt => { | ActiveRadarrBlock::ManualSearchSortPrompt => {
self.app.pop_navigation_stack(); self.app.pop_navigation_stack();
@@ -236,10 +236,15 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for MovieDetailsHandler<'a> {
self.app.data.radarr_data.populate_edit_movie_fields(); self.app.data.radarr_data.populate_edit_movie_fields();
self.app.data.radarr_data.selected_block = ActiveRadarrBlock::EditMovieToggleMonitored; self.app.data.radarr_data.selected_block = ActiveRadarrBlock::EditMovieToggleMonitored;
} }
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
self
.app
.push_navigation_stack(ActiveRadarrBlock::UpdateAndScanPrompt.into());
}
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => { _ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
self self
.app .app
.push_navigation_stack(ActiveRadarrBlock::RefreshAndScanPrompt.into()); .pop_and_push_navigation_stack((*self.active_radarr_block).into());
} }
_ if *key == DEFAULT_KEYBINDINGS.sort.key => { _ if *key == DEFAULT_KEYBINDINGS.sort.key => {
self self
@@ -534,7 +539,7 @@ mod tests {
fn test_left_right_prompt_toggle( fn test_left_right_prompt_toggle(
#[values( #[values(
ActiveRadarrBlock::AutomaticallySearchMoviePrompt, ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
ActiveRadarrBlock::RefreshAndScanPrompt, ActiveRadarrBlock::UpdateAndScanPrompt,
ActiveRadarrBlock::ManualSearchConfirmPrompt ActiveRadarrBlock::ManualSearchConfirmPrompt
)] )]
active_radarr_block: ActiveRadarrBlock, active_radarr_block: ActiveRadarrBlock,
@@ -628,7 +633,7 @@ mod tests {
ActiveRadarrBlock::AutomaticallySearchMoviePrompt, ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
RadarrEvent::TriggerAutomaticSearch RadarrEvent::TriggerAutomaticSearch
)] )]
#[case(ActiveRadarrBlock::RefreshAndScanPrompt, RadarrEvent::RefreshAndScan)] #[case(ActiveRadarrBlock::UpdateAndScanPrompt, RadarrEvent::UpdateAndScan)]
#[case( #[case(
ActiveRadarrBlock::ManualSearchConfirmPrompt, ActiveRadarrBlock::ManualSearchConfirmPrompt,
RadarrEvent::DownloadRelease RadarrEvent::DownloadRelease
@@ -659,7 +664,7 @@ mod tests {
fn test_movie_info_prompt_decline_submit( fn test_movie_info_prompt_decline_submit(
#[values( #[values(
ActiveRadarrBlock::AutomaticallySearchMoviePrompt, ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
ActiveRadarrBlock::RefreshAndScanPrompt, ActiveRadarrBlock::UpdateAndScanPrompt,
ActiveRadarrBlock::ManualSearchConfirmPrompt ActiveRadarrBlock::ManualSearchConfirmPrompt
)] )]
prompt_block: ActiveRadarrBlock, prompt_block: ActiveRadarrBlock,
@@ -748,7 +753,7 @@ mod tests {
fn test_movie_info_prompts_esc( fn test_movie_info_prompts_esc(
#[values( #[values(
ActiveRadarrBlock::AutomaticallySearchMoviePrompt, ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
ActiveRadarrBlock::RefreshAndScanPrompt, ActiveRadarrBlock::UpdateAndScanPrompt,
ActiveRadarrBlock::ManualSearchConfirmPrompt, ActiveRadarrBlock::ManualSearchConfirmPrompt,
ActiveRadarrBlock::ManualSearchSortPrompt ActiveRadarrBlock::ManualSearchSortPrompt
)] )]
@@ -849,6 +854,34 @@ mod tests {
); );
} }
#[rstest]
fn test_update_key(
#[values(
ActiveRadarrBlock::MovieDetails,
ActiveRadarrBlock::MovieHistory,
ActiveRadarrBlock::FileInfo,
ActiveRadarrBlock::Cast,
ActiveRadarrBlock::Crew,
ActiveRadarrBlock::ManualSearch
)]
active_radarr_block: ActiveRadarrBlock,
) {
let mut app = App::default();
MovieDetailsHandler::with(
&DEFAULT_KEYBINDINGS.update.key,
&mut app,
&active_radarr_block,
&None,
)
.handle();
assert_eq!(
app.get_current_route(),
&ActiveRadarrBlock::UpdateAndScanPrompt.into()
);
}
#[rstest] #[rstest]
fn test_refresh_key( fn test_refresh_key(
#[values( #[values(
@@ -871,10 +904,8 @@ mod tests {
) )
.handle(); .handle();
assert_eq!( assert_eq!(app.get_current_route(), &active_radarr_block.into());
app.get_current_route(), assert!(app.is_routing);
&ActiveRadarrBlock::RefreshAndScanPrompt.into()
);
} }
} }
+6 -7
View File
@@ -219,9 +219,8 @@ impl HorizontallyScrollableText {
} }
pub fn scroll_left_or_reset(&self, width: usize, is_current_selection: bool, can_scroll: bool) { pub fn scroll_left_or_reset(&self, width: usize, is_current_selection: bool, can_scroll: bool) {
if can_scroll { if can_scroll && is_current_selection && self.text.len() >= width {
if is_current_selection && self.text.len() >= width && *self.offset.borrow() < self.text.len() if *self.offset.borrow() < self.text.len() {
{
self.scroll_left(); self.scroll_left();
} else { } else {
self.reset_offset(); self.reset_offset();
@@ -563,19 +562,19 @@ mod tests {
horizontally_scrollable_text.scroll_left_or_reset(width, false, true); horizontally_scrollable_text.scroll_left_or_reset(width, false, true);
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0); assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
horizontally_scrollable_text.scroll_left_or_reset(width, true, false); horizontally_scrollable_text.scroll_left_or_reset(width, true, false);
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0); assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
horizontally_scrollable_text.scroll_left_or_reset(width, true, true); horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1); assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
horizontally_scrollable_text.scroll_left_or_reset(test_text.len(), false, true); horizontally_scrollable_text.scroll_left_or_reset(test_text.len(), false, true);
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0); assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
} }
#[test] #[test]
+2 -2
View File
@@ -36,7 +36,7 @@ pub struct RootFolder {
pub struct Movie { pub struct Movie {
#[derivative(Default(value = "Number::from(0)"))] #[derivative(Default(value = "Number::from(0)"))]
pub id: Number, pub id: Number,
pub title: String, pub title: HorizontallyScrollableText,
pub original_language: Language, pub original_language: Language,
#[derivative(Default(value = "Number::from(0)"))] #[derivative(Default(value = "Number::from(0)"))]
pub size_on_disk: Number, pub size_on_disk: Number,
@@ -84,7 +84,7 @@ pub struct CollectionMovie {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Collection { pub struct Collection {
#[serde(default)] #[serde(default)]
pub title: String, pub title: HorizontallyScrollableText,
pub root_folder_path: Option<String>, pub root_folder_path: Option<String>,
pub search_on_add: bool, pub search_on_add: bool,
pub overview: Option<String>, pub overview: Option<String>,
+32 -32
View File
@@ -37,12 +37,12 @@ pub enum RadarrEvent {
GetStatus, GetStatus,
GetTags, GetTags,
HealthCheck, HealthCheck,
RefreshAndScan,
RefreshCollections,
RefreshDownloads,
SearchNewMovie, SearchNewMovie,
TriggerAutomaticSearch, TriggerAutomaticSearch,
UpdateAllMovies, UpdateAllMovies,
UpdateAndScan,
UpdateCollections,
UpdateDownloads,
} }
impl RadarrEvent { impl RadarrEvent {
@@ -65,10 +65,10 @@ impl RadarrEvent {
RadarrEvent::GetStatus => "/system/status", RadarrEvent::GetStatus => "/system/status",
RadarrEvent::GetTags => "/tag", RadarrEvent::GetTags => "/tag",
RadarrEvent::TriggerAutomaticSearch RadarrEvent::TriggerAutomaticSearch
| RadarrEvent::RefreshAndScan | RadarrEvent::UpdateAndScan
| RadarrEvent::UpdateAllMovies | RadarrEvent::UpdateAllMovies
| RadarrEvent::RefreshDownloads | RadarrEvent::UpdateDownloads
| RadarrEvent::RefreshCollections => "/command", | RadarrEvent::UpdateCollections => "/command",
RadarrEvent::HealthCheck => "/health", RadarrEvent::HealthCheck => "/health",
} }
} }
@@ -101,12 +101,12 @@ impl<'a> Network<'a> {
RadarrEvent::GetStatus => self.get_status().await, RadarrEvent::GetStatus => self.get_status().await,
RadarrEvent::GetTags => self.get_tags().await, RadarrEvent::GetTags => self.get_tags().await,
RadarrEvent::HealthCheck => self.get_healthcheck().await, RadarrEvent::HealthCheck => self.get_healthcheck().await,
RadarrEvent::RefreshAndScan => self.refresh_and_scan().await,
RadarrEvent::RefreshCollections => self.refresh_collections().await,
RadarrEvent::RefreshDownloads => self.refresh_downloads().await,
RadarrEvent::SearchNewMovie => self.search_movie().await, RadarrEvent::SearchNewMovie => self.search_movie().await,
RadarrEvent::TriggerAutomaticSearch => self.trigger_automatic_search().await, RadarrEvent::TriggerAutomaticSearch => self.trigger_automatic_search().await,
RadarrEvent::UpdateAllMovies => self.update_all_movies().await, RadarrEvent::UpdateAllMovies => self.update_all_movies().await,
RadarrEvent::UpdateAndScan => self.update_and_scan().await,
RadarrEvent::UpdateCollections => self.update_collections().await,
RadarrEvent::UpdateDownloads => self.update_downloads().await,
} }
} }
@@ -258,9 +258,9 @@ impl<'a> Network<'a> {
.await; .await;
} }
async fn refresh_and_scan(&self) { async fn update_and_scan(&self) {
let movie_id = self.extract_movie_id().await; let movie_id = self.extract_movie_id().await;
info!("Refreshing and scanning movie with id: {}", movie_id); info!("Updating and scanning movie with id: {}", movie_id);
let body = MovieCommandBody { let body = MovieCommandBody {
name: "RefreshMovie".to_owned(), name: "RefreshMovie".to_owned(),
movie_ids: vec![movie_id], movie_ids: vec![movie_id],
@@ -268,7 +268,7 @@ impl<'a> Network<'a> {
let request_props = self let request_props = self
.radarr_request_props_from( .radarr_request_props_from(
RadarrEvent::RefreshAndScan.resource(), RadarrEvent::UpdateAndScan.resource(),
RequestMethod::Post, RequestMethod::Post,
Some(body), Some(body),
) )
@@ -299,15 +299,15 @@ impl<'a> Network<'a> {
.await; .await;
} }
async fn refresh_downloads(&self) { async fn update_downloads(&self) {
info!("Refreshing downloads"); info!("Updating downloads");
let body = CommandBody { let body = CommandBody {
name: "RefreshMonitoredDownloads".to_owned(), name: "RefreshMonitoredDownloads".to_owned(),
}; };
let request_props = self let request_props = self
.radarr_request_props_from( .radarr_request_props_from(
RadarrEvent::RefreshDownloads.resource(), RadarrEvent::UpdateDownloads.resource(),
RequestMethod::Post, RequestMethod::Post,
Some(body), Some(body),
) )
@@ -318,15 +318,15 @@ impl<'a> Network<'a> {
.await; .await;
} }
async fn refresh_collections(&self) { async fn update_collections(&self) {
info!("Refreshing collections"); info!("Updating collections");
let body = CommandBody { let body = CommandBody {
name: "RefreshCollections".to_owned(), name: "RefreshCollections".to_owned(),
}; };
let request_props = self let request_props = self
.radarr_request_props_from( .radarr_request_props_from(
RadarrEvent::RefreshCollections.resource(), RadarrEvent::UpdateCollections.resource(),
RequestMethod::Post, RequestMethod::Post,
Some(body), Some(body),
) )
@@ -1176,10 +1176,10 @@ mod test {
fn test_resource_command( fn test_resource_command(
#[values( #[values(
RadarrEvent::TriggerAutomaticSearch, RadarrEvent::TriggerAutomaticSearch,
RadarrEvent::RefreshAndScan, RadarrEvent::UpdateAndScan,
RadarrEvent::UpdateAllMovies, RadarrEvent::UpdateAllMovies,
RadarrEvent::RefreshDownloads, RadarrEvent::UpdateDownloads,
RadarrEvent::RefreshCollections RadarrEvent::UpdateCollections
)] )]
event: RadarrEvent, event: RadarrEvent,
) { ) {
@@ -1484,7 +1484,7 @@ mod test {
} }
#[tokio::test] #[tokio::test]
async fn test_handle_refresh_and_scan_event() { async fn test_handle_update_and_scan_event() {
let (async_server, app_arc, _server) = mock_radarr_api( let (async_server, app_arc, _server) = mock_radarr_api(
RequestMethod::Post, RequestMethod::Post,
Some(json!({ Some(json!({
@@ -1492,7 +1492,7 @@ mod test {
"movieIds": [ 1 ] "movieIds": [ 1 ]
})), })),
None, None,
RadarrEvent::RefreshAndScan.resource(), RadarrEvent::UpdateAndScan.resource(),
) )
.await; .await;
app_arc app_arc
@@ -1505,7 +1505,7 @@ mod test {
let network = Network::new(reqwest::Client::new(), &app_arc); let network = Network::new(reqwest::Client::new(), &app_arc);
network network
.handle_radarr_event(RadarrEvent::RefreshAndScan) .handle_radarr_event(RadarrEvent::UpdateAndScan)
.await; .await;
async_server.assert_async().await; async_server.assert_async().await;
@@ -1533,40 +1533,40 @@ mod test {
} }
#[tokio::test] #[tokio::test]
async fn test_handle_refresh_downloads_event() { async fn test_handle_update_downloads_event() {
let (async_server, app_arc, _server) = mock_radarr_api( let (async_server, app_arc, _server) = mock_radarr_api(
RequestMethod::Post, RequestMethod::Post,
Some(json!({ Some(json!({
"name": "RefreshMonitoredDownloads" "name": "RefreshMonitoredDownloads"
})), })),
None, None,
RadarrEvent::RefreshDownloads.resource(), RadarrEvent::UpdateDownloads.resource(),
) )
.await; .await;
let network = Network::new(reqwest::Client::new(), &app_arc); let network = Network::new(reqwest::Client::new(), &app_arc);
network network
.handle_radarr_event(RadarrEvent::RefreshDownloads) .handle_radarr_event(RadarrEvent::UpdateDownloads)
.await; .await;
async_server.assert_async().await; async_server.assert_async().await;
} }
#[tokio::test] #[tokio::test]
async fn test_handle_refresh_collections_event() { async fn test_handle_update_collections_event() {
let (async_server, app_arc, _server) = mock_radarr_api( let (async_server, app_arc, _server) = mock_radarr_api(
RequestMethod::Post, RequestMethod::Post,
Some(json!({ Some(json!({
"name": "RefreshCollections" "name": "RefreshCollections"
})), })),
None, None,
RadarrEvent::RefreshCollections.resource(), RadarrEvent::UpdateCollections.resource(),
) )
.await; .await;
let network = Network::new(reqwest::Client::new(), &app_arc); let network = Network::new(reqwest::Client::new(), &app_arc);
network network
.handle_radarr_event(RadarrEvent::RefreshCollections) .handle_radarr_event(RadarrEvent::UpdateCollections)
.await; .await;
async_server.assert_async().await; async_server.assert_async().await;
@@ -2562,7 +2562,7 @@ mod test {
fn collection() -> Collection { fn collection() -> Collection {
Collection { Collection {
title: "Test Collection".to_owned(), title: "Test Collection".to_owned().into(),
root_folder_path: None, root_folder_path: None,
search_on_add: true, search_on_add: true,
overview: Some("Collection blah blah blah".to_owned()), overview: Some("Collection blah blah blah".to_owned()),
@@ -2574,7 +2574,7 @@ mod test {
fn movie() -> Movie { fn movie() -> Movie {
Movie { Movie {
id: Number::from(1), id: Number::from(1),
title: "Test".to_owned(), title: "Test".to_owned().into(),
original_language: language(), original_language: language(),
size_on_disk: Number::from(3543348019u64), size_on_disk: Number::from(3543348019u64),
status: "Downloaded".to_owned(), status: "Downloaded".to_owned(),
+5 -3
View File
@@ -92,9 +92,11 @@ fn draw_error<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
.style(style_failure()) .style(style_failure())
.borders(Borders::ALL); .borders(Borders::ALL);
if app.error.text.len() > area.width as usize { app.error.scroll_left_or_reset(
app.error.scroll_left(); area.width as usize,
} true,
app.tick_count % app.ticks_until_scroll == 0,
);
let mut text = Text::from(app.error.to_string()); let mut text = Text::from(app.error.to_string());
text.patch_style(style_failure()); text.patch_style(style_failure());
+1 -1
View File
@@ -108,7 +108,7 @@ pub(super) fn draw_collection_details<B: Backend>(
.block(borderless_block()) .block(borderless_block())
.alignment(Alignment::Center); .alignment(Alignment::Center);
f.render_widget(title_block(&collection_selection.title), content_area); f.render_widget(title_block(&collection_selection.title.text), content_area);
f.render_widget(description_paragraph, chunks[0]); f.render_widget(description_paragraph, chunks[0]);
f.render_widget(help_paragraph, chunks[2]); f.render_widget(help_paragraph, chunks[2]);
+52 -18
View File
@@ -15,7 +15,7 @@ use crate::app::radarr::{
}; };
use crate::app::App; use crate::app::App;
use crate::logos::RADARR_LOGO; use crate::logos::RADARR_LOGO;
use crate::models::radarr_models::{DiskSpace, DownloadRecord, Movie}; use crate::models::radarr_models::{Collection, DiskSpace, DownloadRecord, Movie};
use crate::models::Route; use crate::models::Route;
use crate::ui::radarr_ui::add_movie_ui::draw_add_movie_search_popup; use crate::ui::radarr_ui::add_movie_ui::draw_add_movie_search_popup;
use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup; use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup;
@@ -123,26 +123,26 @@ pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, ar
draw_downloads, draw_downloads,
draw_delete_download_prompt, draw_delete_download_prompt,
), ),
ActiveRadarrBlock::RefreshDownloadsPrompt => draw_prompt_popup_over( ActiveRadarrBlock::UpdateDownloadsPrompt => draw_prompt_popup_over(
f, f,
app, app,
content_rect, content_rect,
draw_downloads, draw_downloads,
draw_refresh_downloads_prompt, draw_update_downloads_prompt,
), ),
ActiveRadarrBlock::RefreshAllMoviesPrompt => draw_prompt_popup_over( ActiveRadarrBlock::UpdateAllMoviesPrompt => draw_prompt_popup_over(
f, f,
app, app,
content_rect, content_rect,
draw_library, draw_library,
draw_refresh_all_movies_prompt, draw_update_all_movies_prompt,
), ),
ActiveRadarrBlock::RefreshAllCollectionsPrompt => draw_prompt_popup_over( ActiveRadarrBlock::UpdateAllCollectionsPrompt => draw_prompt_popup_over(
f, f,
app, app,
content_rect, content_rect,
draw_collections, draw_collections,
draw_refresh_all_collections_prompt, draw_update_all_collections_prompt,
), ),
_ => (), _ => (),
} }
@@ -157,6 +157,18 @@ 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 current_selection = if !app.data.radarr_data.filtered_movies.items.is_empty() {
app
.data
.radarr_data
.filtered_movies
.current_selection()
.clone()
} else if !app.data.radarr_data.movies.items.is_empty() {
app.data.radarr_data.movies.current_selection().clone()
} else {
Movie::default()
};
let quality_profile_map = &app.data.radarr_data.quality_profile_map; let quality_profile_map = &app.data.radarr_data.quality_profile_map;
let tags_map = &app.data.radarr_data.tags_map; let tags_map = &app.data.radarr_data.tags_map;
let downloads_vec = &app.data.radarr_data.downloads.items; let downloads_vec = &app.data.radarr_data.downloads.items;
@@ -205,6 +217,11 @@ fn draw_library<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
.get_active_tab_contextual_help(), .get_active_tab_contextual_help(),
}, },
|movie| { |movie| {
movie.title.scroll_left_or_reset(
get_width_from_percentage(area, 27),
*movie == current_selection,
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.as_u64().unwrap()); let (hours, minutes) = convert_runtime(movie.runtime.as_u64().unwrap());
let file_size: f64 = convert_to_gb(movie.size_on_disk.as_u64().unwrap()); let file_size: f64 = convert_to_gb(movie.size_on_disk.as_u64().unwrap());
@@ -226,7 +243,7 @@ fn draw_library<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
.join(", "); .join(", ");
Row::new(vec![ Row::new(vec![
Cell::from(movie.title.to_owned()), 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!("{}h {}m", hours, minutes)), Cell::from(format!("{}h {}m", hours, minutes)),
@@ -243,7 +260,7 @@ fn draw_library<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
); );
} }
fn draw_refresh_all_movies_prompt<B: Backend>( fn draw_update_all_movies_prompt<B: Backend>(
f: &mut Frame<'_, B>, f: &mut Frame<'_, B>,
app: &mut App, app: &mut App,
prompt_area: Rect, prompt_area: Rect,
@@ -251,13 +268,13 @@ fn draw_refresh_all_movies_prompt<B: Backend>(
draw_prompt_box( draw_prompt_box(
f, f,
prompt_area, prompt_area,
"Refresh All Movies", "Update All Movies",
"Do you want to refresh info and scan your disks for all of your movies?", "Do you want to update info and scan your disks for all of your movies?",
&app.data.radarr_data.prompt_confirm, &app.data.radarr_data.prompt_confirm,
); );
} }
fn draw_refresh_downloads_prompt<B: Backend>( fn draw_update_downloads_prompt<B: Backend>(
f: &mut Frame<'_, B>, f: &mut Frame<'_, B>,
app: &mut App, app: &mut App,
prompt_area: Rect, prompt_area: Rect,
@@ -265,13 +282,13 @@ fn draw_refresh_downloads_prompt<B: Backend>(
draw_prompt_box( draw_prompt_box(
f, f,
prompt_area, prompt_area,
"Refresh Downloads", "Update Downloads",
"Do you want to refresh your downloads?", "Do you want to update your downloads?",
&app.data.radarr_data.prompt_confirm, &app.data.radarr_data.prompt_confirm,
); );
} }
fn draw_refresh_all_collections_prompt<B: Backend>( fn draw_update_all_collections_prompt<B: Backend>(
f: &mut Frame<'_, B>, f: &mut Frame<'_, B>,
app: &mut App, app: &mut App,
prompt_area: Rect, prompt_area: Rect,
@@ -279,8 +296,8 @@ fn draw_refresh_all_collections_prompt<B: Backend>(
draw_prompt_box( draw_prompt_box(
f, f,
prompt_area, prompt_area,
"Refresh All Collections", "Update All Collections",
"Do you want to refresh all of your collections?", "Do you want to update all of your collections?",
&app.data.radarr_data.prompt_confirm, &app.data.radarr_data.prompt_confirm,
); );
} }
@@ -496,6 +513,18 @@ fn draw_downloads<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) { fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let current_selection = if !app.data.radarr_data.filtered_collections.items.is_empty() {
app
.data
.radarr_data
.filtered_collections
.current_selection()
.clone()
} else if !app.data.radarr_data.collections.items.is_empty() {
app.data.radarr_data.collections.current_selection().clone()
} else {
Collection::default()
};
let quality_profile_map = &app.data.radarr_data.quality_profile_map; let quality_profile_map = &app.data.radarr_data.quality_profile_map;
let content = if !app.data.radarr_data.filtered_collections.items.is_empty() let content = if !app.data.radarr_data.filtered_collections.items.is_empty()
&& !app.data.radarr_data.is_filtering && !app.data.radarr_data.is_filtering
@@ -526,9 +555,14 @@ fn draw_collections<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
}, },
|collection| { |collection| {
let number_of_movies = collection.movies.clone().unwrap_or_default().len(); let number_of_movies = collection.movies.clone().unwrap_or_default().len();
collection.title.scroll_left_or_reset(
get_width_from_percentage(area, 100 / 5),
*collection == current_selection,
app.tick_count % app.ticks_until_scroll == 0,
);
Row::new(vec![ Row::new(vec![
Cell::from(collection.title.to_owned()), Cell::from(collection.title.to_string()),
Cell::from(collection.search_on_add.to_string()), Cell::from(collection.search_on_add.to_string()),
Cell::from(number_of_movies.to_string()), Cell::from(number_of_movies.to_string()),
Cell::from(collection.root_folder_path.clone().unwrap_or_default()), Cell::from(collection.root_folder_path.clone().unwrap_or_default()),
+5 -9
View File
@@ -39,12 +39,12 @@ pub(super) fn draw_movie_info_popup<B: Backend>(f: &mut Frame<'_, B>, app: &mut
draw_movie_info, draw_movie_info,
draw_search_movie_prompt, draw_search_movie_prompt,
), ),
ActiveRadarrBlock::RefreshAndScanPrompt => draw_prompt_popup_over( ActiveRadarrBlock::UpdateAndScanPrompt => draw_prompt_popup_over(
f, f,
app, app,
content_area, content_area,
draw_movie_info, draw_movie_info,
draw_refresh_and_scan_prompt, draw_update_and_scan_prompt,
), ),
ActiveRadarrBlock::ManualSearchSortPrompt => draw_drop_down_popup( ActiveRadarrBlock::ManualSearchSortPrompt => draw_drop_down_popup(
f, f,
@@ -102,17 +102,13 @@ fn draw_search_movie_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, pro
); );
} }
fn draw_refresh_and_scan_prompt<B: Backend>( fn draw_update_and_scan_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, prompt_area: Rect) {
f: &mut Frame<'_, B>,
app: &mut App,
prompt_area: Rect,
) {
draw_prompt_box( draw_prompt_box(
f, f,
prompt_area, prompt_area,
"Refresh and Scan", "Update and Scan",
format!( format!(
"Do you want to trigger a refresh and disk scan for the movie: {}?", "Do you want to trigger an update and disk scan for the movie: {}?",
app.data.radarr_data.movies.current_selection().title app.data.radarr_data.movies.current_selection().title
) )
.as_str(), .as_str(),