Added the ability to view Radarr updates and remapped the events queue key to 'z'
This commit is contained in:
@@ -23,6 +23,7 @@ generate_keybindings! {
|
||||
tasks,
|
||||
refresh,
|
||||
update,
|
||||
queue,
|
||||
home,
|
||||
end,
|
||||
delete,
|
||||
@@ -91,7 +92,11 @@ pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
|
||||
},
|
||||
update: KeyBinding {
|
||||
key: Key::Char('u'),
|
||||
desc: "Update All",
|
||||
desc: "Update",
|
||||
},
|
||||
queue: KeyBinding {
|
||||
key: Key::Char('z'),
|
||||
desc: "Queue",
|
||||
},
|
||||
home: KeyBinding {
|
||||
key: Key::Home,
|
||||
|
||||
+13
-4
@@ -54,6 +54,7 @@ pub struct RadarrData<'a> {
|
||||
pub log_details: StatefulList<HorizontallyScrollableText>,
|
||||
pub tasks: StatefulTable<Task>,
|
||||
pub queued_events: StatefulTable<QueueEvent>,
|
||||
pub updates: ScrollableText,
|
||||
pub prompt_confirm_action: Option<RadarrEvent>,
|
||||
pub main_tabs: TabState,
|
||||
pub movie_info_tabs: TabState,
|
||||
@@ -278,6 +279,7 @@ impl<'a> Default for RadarrData<'a> {
|
||||
log_details: StatefulList::default(),
|
||||
tasks: StatefulTable::default(),
|
||||
queued_events: StatefulTable::default(),
|
||||
updates: ScrollableText::default(),
|
||||
prompt_confirm_action: None,
|
||||
search: HorizontallyScrollableText::default(),
|
||||
filter: HorizontallyScrollableText::default(),
|
||||
@@ -320,7 +322,7 @@ impl<'a> Default for RadarrData<'a> {
|
||||
title: "System",
|
||||
route: ActiveRadarrBlock::System.into(),
|
||||
help: "",
|
||||
contextual_help: Some("<t> open tasks | <u> open queue | <l> open logs | <r> refresh")
|
||||
contextual_help: Some("<t> open tasks | <z> open queue | <l> open logs | <u> open updates | <r> refresh")
|
||||
}
|
||||
]),
|
||||
movie_info_tabs: TabState::new(vec![
|
||||
@@ -418,9 +420,10 @@ pub enum ActiveRadarrBlock {
|
||||
RootFolders,
|
||||
System,
|
||||
SystemLogs,
|
||||
SystemQueue,
|
||||
SystemTasks,
|
||||
SystemTaskStartConfirmPrompt,
|
||||
SystemQueue,
|
||||
SystemUpdates,
|
||||
UpdateAndScanPrompt,
|
||||
UpdateAllCollectionsPrompt,
|
||||
UpdateAllMoviesPrompt,
|
||||
@@ -519,11 +522,12 @@ pub static DELETE_MOVIE_SELECTION_BLOCKS: [ActiveRadarrBlock; 3] = [
|
||||
ActiveRadarrBlock::DeleteMovieToggleAddListExclusion,
|
||||
ActiveRadarrBlock::DeleteMovieConfirmPrompt,
|
||||
];
|
||||
pub static SYSTEM_DETAILS_BLOCKS: [ActiveRadarrBlock; 4] = [
|
||||
pub static SYSTEM_DETAILS_BLOCKS: [ActiveRadarrBlock; 5] = [
|
||||
ActiveRadarrBlock::SystemLogs,
|
||||
ActiveRadarrBlock::SystemTasks,
|
||||
ActiveRadarrBlock::SystemQueue,
|
||||
ActiveRadarrBlock::SystemTasks,
|
||||
ActiveRadarrBlock::SystemTaskStartConfirmPrompt,
|
||||
ActiveRadarrBlock::SystemUpdates,
|
||||
];
|
||||
|
||||
impl From<ActiveRadarrBlock> for Route {
|
||||
@@ -580,6 +584,11 @@ impl<'a> App<'a> {
|
||||
.dispatch_network_event(RadarrEvent::GetLogs.into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::SystemUpdates => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetUpdates.into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::AddMovieSearchResults => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::SearchNewMovie.into())
|
||||
|
||||
+19
-1
@@ -291,6 +291,7 @@ mod tests {
|
||||
assert!(radarr_data.log_details.items.is_empty());
|
||||
assert!(radarr_data.tasks.items.is_empty());
|
||||
assert!(radarr_data.queued_events.items.is_empty());
|
||||
assert!(radarr_data.updates.get_text().is_empty());
|
||||
assert!(radarr_data.prompt_confirm_action.is_none());
|
||||
assert!(radarr_data.search.text.is_empty());
|
||||
assert!(radarr_data.filter.text.is_empty());
|
||||
@@ -355,7 +356,7 @@ mod tests {
|
||||
assert!(radarr_data.main_tabs.tabs[4].help.is_empty());
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[4].contextual_help,
|
||||
Some("<t> open tasks | <u> open queue | <l> open logs | <r> refresh")
|
||||
Some("<t> open tasks | <z> open queue | <l> open logs | <u> open updates | <r> refresh")
|
||||
);
|
||||
|
||||
assert_eq!(radarr_data.movie_info_tabs.tabs.len(), 6);
|
||||
@@ -708,6 +709,23 @@ mod tests {
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_system_updates_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::SystemUpdates)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetUpdates.into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_add_movie_search_results_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
@@ -238,6 +238,25 @@ mod test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_scrollable_text_scroll {
|
||||
($func:ident, $handler:ident, $data_ref:ident, $block:expr) => {
|
||||
#[test]
|
||||
fn $func() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.$data_ref = ScrollableText::with_string("Test 1\nTest 2".to_owned());
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.up.key, &mut app, &$block, &None).handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.$data_ref.offset, 0);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.down.key, &mut app, &$block, &None).handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.$data_ref.offset, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_iterable_home_and_end {
|
||||
($func:ident, $handler:ident, $data_ref:ident, $block:expr, $context:expr) => {
|
||||
@@ -373,6 +392,25 @@ mod test_utils {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_scrollable_text_home_and_end {
|
||||
($func:ident, $handler:ident, $data_ref:ident, $block:expr) => {
|
||||
#[test]
|
||||
fn $func() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.$data_ref = ScrollableText::with_string("Test 1\nTest 2".to_owned());
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.end.key, &mut app, &$block, &None).handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.$data_ref.offset, 1);
|
||||
|
||||
$handler::with(&DEFAULT_KEYBINDINGS.home.key, &mut app, &$block, &None).handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.$data_ref.offset, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_text_box_home_end_keys {
|
||||
($handler:ident, $block:expr, $field:ident) => {
|
||||
|
||||
@@ -574,7 +574,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
_ if *key == DEFAULT_KEYBINDINGS.refresh.key => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
|
||||
_ if *key == DEFAULT_KEYBINDINGS.queue.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::SystemQueue.into());
|
||||
@@ -596,6 +596,11 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
|
||||
}
|
||||
_ if *key == DEFAULT_KEYBINDINGS.update.key => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveRadarrBlock::AddRootFolderPrompt => {
|
||||
|
||||
@@ -23,35 +23,19 @@ mod tests {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::models::radarr_models::ReleaseField;
|
||||
use crate::{simple_stateful_iterable_vec, test_enum_scroll, test_iterable_scroll};
|
||||
use crate::{
|
||||
simple_stateful_iterable_vec, test_enum_scroll, test_iterable_scroll,
|
||||
test_scrollable_text_scroll,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_movie_details_scroll() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.movie_details = ScrollableText::with_string("Test 1\nTest 2".to_owned());
|
||||
|
||||
MovieDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.up.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::MovieDetails,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.movie_details.offset, 0);
|
||||
|
||||
MovieDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.down.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::MovieDetails,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.movie_details.offset, 1);
|
||||
}
|
||||
test_scrollable_text_scroll!(
|
||||
test_movie_details_scroll,
|
||||
MovieDetailsHandler,
|
||||
movie_details,
|
||||
ActiveRadarrBlock::MovieDetails
|
||||
);
|
||||
|
||||
test_iterable_scroll!(
|
||||
test_movie_history_scroll,
|
||||
@@ -113,35 +97,17 @@ mod tests {
|
||||
use crate::models::radarr_models::ReleaseField;
|
||||
use crate::{
|
||||
extended_stateful_iterable_vec, test_enum_home_and_end, test_iterable_home_and_end,
|
||||
test_scrollable_text_home_and_end,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_movie_details_home_end() {
|
||||
let mut app = App::default();
|
||||
app.data.radarr_data.movie_details = ScrollableText::with_string("Test 1\nTest 2".to_owned());
|
||||
|
||||
MovieDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.end.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::MovieDetails,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.movie_details.offset, 1);
|
||||
|
||||
MovieDetailsHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.home.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::MovieDetails,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.data.radarr_data.movie_details.offset, 0);
|
||||
}
|
||||
test_scrollable_text_home_and_end!(
|
||||
test_movie_details_home_end,
|
||||
MovieDetailsHandler,
|
||||
movie_details,
|
||||
ActiveRadarrBlock::MovieDetails
|
||||
);
|
||||
|
||||
test_iterable_home_and_end!(
|
||||
test_movie_history_home_end,
|
||||
|
||||
@@ -921,7 +921,7 @@ mod tests {
|
||||
ActiveRadarrBlock::Collections,
|
||||
ActiveRadarrBlock::UpdateAllCollectionsPrompt
|
||||
)]
|
||||
#[case(ActiveRadarrBlock::System, ActiveRadarrBlock::SystemQueue)]
|
||||
#[case(ActiveRadarrBlock::System, ActiveRadarrBlock::SystemUpdates)]
|
||||
fn test_update_key(
|
||||
#[case] active_radarr_block: ActiveRadarrBlock,
|
||||
#[case] expected_radarr_block: ActiveRadarrBlock,
|
||||
@@ -939,6 +939,24 @@ mod tests {
|
||||
assert_eq!(app.get_current_route(), &expected_radarr_block.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_queue_key() {
|
||||
let mut app = App::default();
|
||||
|
||||
RadarrHandler::with(
|
||||
&DEFAULT_KEYBINDINGS.queue.key,
|
||||
&mut app,
|
||||
&ActiveRadarrBlock::System,
|
||||
&None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
&ActiveRadarrBlock::SystemQueue.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_refresh_key(
|
||||
#[values(
|
||||
@@ -1252,7 +1270,8 @@ mod tests {
|
||||
ActiveRadarrBlock::System,
|
||||
ActiveRadarrBlock::SystemLogs,
|
||||
ActiveRadarrBlock::SystemTasks,
|
||||
ActiveRadarrBlock::SystemQueue
|
||||
ActiveRadarrBlock::SystemQueue,
|
||||
ActiveRadarrBlock::SystemUpdates
|
||||
)]
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
) {
|
||||
|
||||
@@ -40,6 +40,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::SystemLogs => self.app.data.radarr_data.log_details.scroll_up(),
|
||||
ActiveRadarrBlock::SystemTasks => self.app.data.radarr_data.tasks.scroll_up(),
|
||||
ActiveRadarrBlock::SystemUpdates => self.app.data.radarr_data.updates.scroll_up(),
|
||||
ActiveRadarrBlock::SystemQueue => self.app.data.radarr_data.queued_events.scroll_up(),
|
||||
_ => (),
|
||||
}
|
||||
@@ -49,6 +50,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::SystemLogs => self.app.data.radarr_data.log_details.scroll_down(),
|
||||
ActiveRadarrBlock::SystemTasks => self.app.data.radarr_data.tasks.scroll_down(),
|
||||
ActiveRadarrBlock::SystemUpdates => self.app.data.radarr_data.updates.scroll_down(),
|
||||
ActiveRadarrBlock::SystemQueue => self.app.data.radarr_data.queued_events.scroll_down(),
|
||||
_ => (),
|
||||
}
|
||||
@@ -58,6 +60,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::SystemLogs => self.app.data.radarr_data.log_details.scroll_to_top(),
|
||||
ActiveRadarrBlock::SystemTasks => self.app.data.radarr_data.tasks.scroll_to_top(),
|
||||
ActiveRadarrBlock::SystemUpdates => self.app.data.radarr_data.updates.scroll_to_top(),
|
||||
ActiveRadarrBlock::SystemQueue => self.app.data.radarr_data.queued_events.scroll_to_top(),
|
||||
_ => (),
|
||||
}
|
||||
@@ -67,6 +70,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::SystemLogs => self.app.data.radarr_data.log_details.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::SystemTasks => self.app.data.radarr_data.tasks.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::SystemUpdates => self.app.data.radarr_data.updates.scroll_to_bottom(),
|
||||
ActiveRadarrBlock::SystemQueue => self.app.data.radarr_data.queued_events.scroll_to_bottom(),
|
||||
_ => (),
|
||||
}
|
||||
@@ -126,12 +130,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
match self.active_radarr_block {
|
||||
ActiveRadarrBlock::SystemLogs
|
||||
| ActiveRadarrBlock::SystemTasks
|
||||
| ActiveRadarrBlock::SystemQueue => {
|
||||
ActiveRadarrBlock::SystemLogs => {
|
||||
self.app.data.radarr_data.reset_log_details_list();
|
||||
self.app.pop_navigation_stack()
|
||||
}
|
||||
ActiveRadarrBlock::SystemQueue
|
||||
| ActiveRadarrBlock::SystemTasks
|
||||
| ActiveRadarrBlock::SystemUpdates => self.app.pop_navigation_stack(),
|
||||
ActiveRadarrBlock::SystemTaskStartConfirmPrompt => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.radarr_data.prompt_confirm = false;
|
||||
|
||||
@@ -13,8 +13,8 @@ mod tests {
|
||||
mod test_handle_scroll_up_and_down {
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll};
|
||||
use crate::models::{HorizontallyScrollableText, ScrollableText};
|
||||
use crate::{simple_stateful_iterable_vec, test_iterable_scroll, test_scrollable_text_scroll};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -47,11 +47,20 @@ mod tests {
|
||||
None,
|
||||
name
|
||||
);
|
||||
|
||||
test_scrollable_text_scroll!(
|
||||
test_system_updates_scroll,
|
||||
SystemDetailsHandler,
|
||||
updates,
|
||||
ActiveRadarrBlock::SystemUpdates
|
||||
);
|
||||
}
|
||||
|
||||
mod test_handle_home_end {
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
|
||||
use crate::models::{HorizontallyScrollableText, ScrollableText};
|
||||
use crate::{
|
||||
extended_stateful_iterable_vec, test_iterable_home_and_end, test_scrollable_text_home_and_end,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -84,13 +93,21 @@ mod tests {
|
||||
None,
|
||||
name
|
||||
);
|
||||
|
||||
test_scrollable_text_home_and_end!(
|
||||
test_system_updates_home_end,
|
||||
SystemDetailsHandler,
|
||||
updates,
|
||||
ActiveRadarrBlock::SystemUpdates
|
||||
);
|
||||
}
|
||||
|
||||
mod test_handle_left_right_action {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_handle_log_details_left_right() {
|
||||
let active_radarr_block = ActiveRadarrBlock::SystemLogs;
|
||||
@@ -219,9 +236,10 @@ mod tests {
|
||||
}
|
||||
|
||||
mod test_handle_submit {
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
|
||||
use super::*;
|
||||
|
||||
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||
@@ -294,9 +312,10 @@ mod tests {
|
||||
}
|
||||
|
||||
mod test_handle_esc {
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
|
||||
use super::*;
|
||||
|
||||
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||
@@ -354,6 +373,23 @@ mod tests {
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_esc_system_updates() {
|
||||
let mut app = App::default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::System.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into());
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.queued_events
|
||||
.set_items(vec![QueueEvent::default()]);
|
||||
|
||||
SystemDetailsHandler::with(&ESC_KEY, &mut app, &ActiveRadarrBlock::SystemUpdates, &None)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_tasks_start_task_prompt_esc() {
|
||||
let mut app = App::default();
|
||||
@@ -387,7 +423,8 @@ mod tests {
|
||||
#[values(
|
||||
ActiveRadarrBlock::SystemLogs,
|
||||
ActiveRadarrBlock::SystemTasks,
|
||||
ActiveRadarrBlock::SystemQueue
|
||||
ActiveRadarrBlock::SystemQueue,
|
||||
ActiveRadarrBlock::SystemUpdates
|
||||
)]
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
) {
|
||||
|
||||
@@ -450,3 +450,21 @@ pub struct QueueEvent {
|
||||
pub ended: Option<DateTime<Utc>>,
|
||||
pub duration: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Update {
|
||||
pub version: String,
|
||||
pub release_date: DateTime<Utc>,
|
||||
pub installed: bool,
|
||||
pub latest: bool,
|
||||
pub installed_on: Option<DateTime<Utc>>,
|
||||
pub changes: UpdateChanges,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateChanges {
|
||||
pub new: Option<Vec<String>>,
|
||||
pub fixed: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::models::radarr_models::{
|
||||
AddMovieBody, AddMovieSearchResult, AddOptions, AddRootFolderBody, Collection, CollectionMovie,
|
||||
CommandBody, Credit, CreditType, DiskSpace, DownloadRecord, DownloadsResponse, LogResponse,
|
||||
Movie, MovieCommandBody, MovieHistoryItem, QualityProfile, QueueEvent, Release,
|
||||
ReleaseDownloadBody, RootFolder, SystemStatus, Tag, Task,
|
||||
ReleaseDownloadBody, RootFolder, SystemStatus, Tag, Task, Update,
|
||||
};
|
||||
use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText};
|
||||
use crate::network::{Network, NetworkEvent, RequestMethod, RequestProps};
|
||||
@@ -47,6 +47,7 @@ pub enum RadarrEvent {
|
||||
GetStatus,
|
||||
GetTags,
|
||||
GetTasks,
|
||||
GetUpdates,
|
||||
HealthCheck,
|
||||
SearchNewMovie,
|
||||
StartTask,
|
||||
@@ -80,6 +81,7 @@ impl RadarrEvent {
|
||||
RadarrEvent::GetStatus => "/system/status",
|
||||
RadarrEvent::GetTags => "/tag",
|
||||
RadarrEvent::GetTasks => "/system/task",
|
||||
RadarrEvent::GetUpdates => "/update",
|
||||
RadarrEvent::StartTask
|
||||
| RadarrEvent::GetQueuedEvents
|
||||
| RadarrEvent::TriggerAutomaticSearch
|
||||
@@ -124,6 +126,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
RadarrEvent::GetStatus => self.get_status().await,
|
||||
RadarrEvent::GetTags => self.get_tags().await,
|
||||
RadarrEvent::GetTasks => self.get_tasks().await,
|
||||
RadarrEvent::GetUpdates => self.get_updates().await,
|
||||
RadarrEvent::HealthCheck => self.get_healthcheck().await,
|
||||
RadarrEvent::SearchNewMovie => self.search_movie().await,
|
||||
RadarrEvent::StartTask => self.start_task().await,
|
||||
@@ -742,6 +745,97 @@ impl<'a, 'b> Network<'a, 'b> {
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn get_updates(&self) {
|
||||
info!("Fetching Radarr updates");
|
||||
|
||||
let request_props = self
|
||||
.radarr_request_props_from(
|
||||
RadarrEvent::GetUpdates.resource(),
|
||||
RequestMethod::Get,
|
||||
None::<()>,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Vec<Update>>(request_props, |updates_vec, mut app| {
|
||||
let latest_installed = if updates_vec
|
||||
.iter()
|
||||
.any(|update| update.latest && update.installed_on.is_some())
|
||||
{
|
||||
"already".to_owned()
|
||||
} else {
|
||||
"not".to_owned()
|
||||
};
|
||||
let updates = updates_vec
|
||||
.into_iter()
|
||||
.map(|update| {
|
||||
let install_status = if update.installed_on.is_some() {
|
||||
if update.installed {
|
||||
"(Currently Installed)".to_owned()
|
||||
} else {
|
||||
"(Previously Installed)".to_owned()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let vec_to_bullet_points = |vec: Vec<String>| {
|
||||
vec
|
||||
.iter()
|
||||
.map(|change| format!(" * {}", change))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
};
|
||||
|
||||
let mut update_info = formatdoc!(
|
||||
"{} - {} {}
|
||||
{}",
|
||||
update.version,
|
||||
update.release_date,
|
||||
install_status,
|
||||
"-".repeat(200)
|
||||
);
|
||||
|
||||
if let Some(new_changes) = update.changes.new {
|
||||
let changes = vec_to_bullet_points(new_changes);
|
||||
update_info = formatdoc!(
|
||||
"{}
|
||||
New:
|
||||
{}",
|
||||
update_info,
|
||||
changes
|
||||
)
|
||||
}
|
||||
|
||||
if let Some(fixes) = update.changes.fixed {
|
||||
let fixes = vec_to_bullet_points(fixes);
|
||||
update_info = formatdoc!(
|
||||
"{}
|
||||
Fixed:
|
||||
{}",
|
||||
update_info,
|
||||
fixes
|
||||
);
|
||||
}
|
||||
|
||||
update_info
|
||||
})
|
||||
.reduce(|version_1, version_2| format!("{}\n\n\n{}", version_1, version_2))
|
||||
.unwrap();
|
||||
|
||||
app.data.radarr_data.updates = ScrollableText::with_string(formatdoc!(
|
||||
"{}
|
||||
|
||||
{}",
|
||||
format!(
|
||||
"The latest version of Radarr is {} installed",
|
||||
latest_installed
|
||||
),
|
||||
updates
|
||||
));
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn add_tag(&self, tag: String) {
|
||||
info!("Adding a new Radarr tag");
|
||||
|
||||
|
||||
@@ -1065,6 +1065,95 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_updates_event() {
|
||||
let tasks_json = json!([{
|
||||
"version": "4.3.2.1",
|
||||
"releaseDate": "2023-04-15T02:02:53Z",
|
||||
"installed": true,
|
||||
"installedOn": "2023-04-15T02:02:53Z",
|
||||
"latest": true,
|
||||
"changes": {
|
||||
"new": [
|
||||
"Cool new thing"
|
||||
],
|
||||
"fixed": [
|
||||
"Some bugs killed"
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"version": "3.2.1.0",
|
||||
"releaseDate": "2023-04-15T02:02:53Z",
|
||||
"installed": false,
|
||||
"installedOn": "2023-04-15T02:02:53Z",
|
||||
"latest": false,
|
||||
"changes": {
|
||||
"new": [
|
||||
"Cool new thing (old)",
|
||||
"Other cool new thing (old)"
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"releaseDate": "2023-04-15T02:02:53Z",
|
||||
"installed": false,
|
||||
"latest": false,
|
||||
"changes": {
|
||||
"fixed": [
|
||||
"Killed bug 1",
|
||||
"Fixed bug 2"
|
||||
]
|
||||
},
|
||||
}]);
|
||||
let line_break = "-".repeat(200);
|
||||
let expected_text = ScrollableText::with_string(formatdoc!(
|
||||
"
|
||||
The latest version of Radarr is already installed
|
||||
|
||||
4.3.2.1 - 2023-04-15 02:02:53 UTC (Currently Installed)
|
||||
{}
|
||||
New:
|
||||
* Cool new thing
|
||||
Fixed:
|
||||
* Some bugs killed
|
||||
|
||||
|
||||
3.2.1.0 - 2023-04-15 02:02:53 UTC (Previously Installed)
|
||||
{}
|
||||
New:
|
||||
* Cool new thing (old)
|
||||
* Other cool new thing (old)
|
||||
|
||||
|
||||
2.1.0 - 2023-04-15 02:02:53 UTC
|
||||
{}
|
||||
Fixed:
|
||||
* Killed bug 1
|
||||
* Fixed bug 2",
|
||||
line_break.clone(),
|
||||
line_break.clone(),
|
||||
line_break
|
||||
));
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
RequestMethod::Get,
|
||||
None,
|
||||
Some(tasks_json),
|
||||
RadarrEvent::GetUpdates.resource(),
|
||||
)
|
||||
.await;
|
||||
let network = Network::new(reqwest::Client::new(), &app_arc);
|
||||
|
||||
network.handle_radarr_event(RadarrEvent::GetUpdates).await;
|
||||
|
||||
async_server.assert_async().await;
|
||||
assert_str_eq!(
|
||||
app_arc.lock().await.data.radarr_data.updates.get_text(),
|
||||
expected_text.get_text()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_add_tag() {
|
||||
let (async_server, app_arc, _server) = mock_radarr_api(
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
use tui::backend::Backend;
|
||||
use tui::layout::Rect;
|
||||
use tui::text::{Span, Text};
|
||||
use tui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||
use tui::Frame;
|
||||
|
||||
use crate::app::radarr::ActiveRadarrBlock;
|
||||
use crate::app::App;
|
||||
use crate::models::Route;
|
||||
@@ -9,13 +15,8 @@ use crate::ui::radarr_ui::system_ui::{
|
||||
use crate::ui::utils::{borderless_block, style_primary, title_block};
|
||||
use crate::ui::{
|
||||
draw_help, draw_large_popup_over, draw_list_box, draw_medium_popup_over, draw_prompt_box,
|
||||
draw_prompt_popup_over, draw_table, DrawUi, ListProps, TableProps,
|
||||
draw_prompt_popup_over, draw_table, loading, DrawUi, ListProps, TableProps,
|
||||
};
|
||||
use tui::backend::Backend;
|
||||
use tui::layout::Rect;
|
||||
use tui::text::{Span, Text};
|
||||
use tui::widgets::{Cell, ListItem, Row};
|
||||
use tui::Frame;
|
||||
|
||||
pub(super) struct SystemDetailsUi {}
|
||||
|
||||
@@ -42,6 +43,13 @@ impl DrawUi for SystemDetailsUi {
|
||||
draw_system_ui_layout,
|
||||
draw_queued_events,
|
||||
),
|
||||
ActiveRadarrBlock::SystemUpdates => draw_large_popup_over(
|
||||
f,
|
||||
app,
|
||||
content_rect,
|
||||
draw_system_ui_layout,
|
||||
draw_updates_popup,
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -125,3 +133,18 @@ fn draw_start_task_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, p
|
||||
app.data.radarr_data.prompt_confirm,
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_updates_popup<B: Backend>(f: &mut Frame<'_, B>, app: &mut App<'_>, area: Rect) {
|
||||
let updates = app.data.radarr_data.updates.get_text();
|
||||
let block = title_block("Updates");
|
||||
|
||||
if !updates.is_empty() {
|
||||
let updates_paragraph = Paragraph::new(Text::from(updates))
|
||||
.block(block)
|
||||
.scroll((app.data.radarr_data.updates.offset, 0));
|
||||
|
||||
f.render_widget(updates_paragraph, area);
|
||||
} else {
|
||||
loading(f, block, area, app.is_loading);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user