From 7add62b245f8706010f1e8d6c0a668f26ea894fa Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Mon, 19 Jan 2026 14:50:01 -0700 Subject: [PATCH] fix: Sonarr network wasn't checking for the user to be using the sorting block when populating season details --- .../sonarr_network/library/episodes/mod.rs | 140 ++++-------------- .../episodes/sonarr_episodes_network_tests.rs | 5 +- .../sonarr_network/library/seasons/mod.rs | 51 +++---- .../seasons/sonarr_seasons_network_tests.rs | 88 +++++++++++ 4 files changed, 140 insertions(+), 144 deletions(-) diff --git a/src/network/sonarr_network/library/episodes/mod.rs b/src/network/sonarr_network/library/episodes/mod.rs index 4d1ea2f..518c872 100644 --- a/src/network/sonarr_network/library/episodes/mod.rs +++ b/src/network/sonarr_network/library/episodes/mod.rs @@ -1,4 +1,4 @@ -use crate::models::servarr_data::sonarr::modals::{EpisodeDetailsModal, SeasonDetailsModal}; +use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal; use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; use crate::models::servarr_models::Language; use crate::models::sonarr_models::{ @@ -65,10 +65,6 @@ impl Network<'_, '_> { app.get_current_route(), Route::Sonarr(ActiveSonarrBlock::EpisodesSortPrompt, _) ) { - if app.data.sonarr_data.season_details_modal.is_none() { - app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); - } - let season_episodes_vec = if !app.data.sonarr_data.seasons.is_empty() { let season_number = app .data @@ -85,22 +81,14 @@ impl Network<'_, '_> { episode_vec }; - app + let season_details_modal = app .data .sonarr_data .season_details_modal - .as_mut() - .unwrap() - .episodes - .set_items(season_episodes_vec); - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episodes - .apply_sorting_toggle(false); + .get_or_insert_default(); + + season_details_modal.episodes.set_items(season_episodes_vec); + season_details_modal.episodes.apply_sorting_toggle(false); } }) .await @@ -125,16 +113,13 @@ impl Network<'_, '_> { self .handle_request::<(), Vec>(request_props, |episode_file_vec, mut app| { - if app.data.sonarr_data.season_details_modal.is_none() { - app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); - } - - app + let season_details_modal = app .data .sonarr_data .season_details_modal - .as_mut() - .unwrap() + .get_or_insert_default(); + + season_details_modal .episode_files .set_items(episode_file_vec); }) @@ -156,50 +141,19 @@ impl Network<'_, '_> { self .handle_request::<(), SonarrHistoryWrapper>(request_props, |history_response, mut app| { - if app.data.sonarr_data.season_details_modal.is_none() { - app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); - } - - if app + let season_details_modal = app .data .sonarr_data .season_details_modal - .as_ref() - .unwrap() + .get_or_insert_default(); + let episode_details_modal = season_details_modal .episode_details_modal - .is_none() - { - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episode_details_modal = Some(EpisodeDetailsModal::default()); - } + .get_or_insert_default(); let mut history_vec = history_response.records; history_vec.sort_by(|a, b| a.id.cmp(&b.id)); - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episode_details_modal - .as_mut() - .unwrap() - .episode_history - .set_items(history_vec); - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episode_details_modal - .as_mut() - .unwrap() + episode_details_modal.episode_history.set_items(history_vec); + episode_details_modal .episode_history .apply_sorting_toggle(false); }) @@ -231,24 +185,6 @@ impl Network<'_, '_> { app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); } - if app - .data - .sonarr_data - .season_details_modal - .as_mut() - .expect("Season details modal is empty") - .episode_details_modal - .is_none() - { - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episode_details_modal = Some(EpisodeDetailsModal::default()); - } - let Episode { id, title, @@ -271,10 +207,9 @@ impl Network<'_, '_> { .sonarr_data .season_details_modal .as_mut() - .unwrap() + .expect("Season details modal is empty") .episode_details_modal - .as_mut() - .unwrap(); + .get_or_insert_default(); episode_details_modal.episode_details = ScrollableText::with_string(formatdoc!( " Title: {} @@ -366,42 +301,21 @@ impl Network<'_, '_> { self .handle_request::<(), Vec>(request_props, |release_vec, mut app| { - if app.data.sonarr_data.season_details_modal.is_none() { - app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); - } + let season_details_modal = app + .data + .sonarr_data + .season_details_modal + .get_or_insert_default(); + let episode_details_modal = season_details_modal + .episode_details_modal + .get_or_insert_default(); let episode_releases_vec = release_vec .into_iter() .filter(|release| !release.full_season) .collect(); - if app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episode_details_modal - .is_none() - { - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episode_details_modal = Some(EpisodeDetailsModal::default()); - } - - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episode_details_modal - .as_mut() - .unwrap() + episode_details_modal .episode_releases .set_items(episode_releases_vec); }) diff --git a/src/network/sonarr_network/library/episodes/sonarr_episodes_network_tests.rs b/src/network/sonarr_network/library/episodes/sonarr_episodes_network_tests.rs index 0a79044..62b2530 100644 --- a/src/network/sonarr_network/library/episodes/sonarr_episodes_network_tests.rs +++ b/src/network/sonarr_network/library/episodes/sonarr_episodes_network_tests.rs @@ -821,8 +821,7 @@ mod tests { .path("/1") .build_for(SonarrEvent::GetEpisodeDetails(1)) .await; - let mut episode_details_modal = EpisodeDetailsModal::default(); - episode_details_modal.episode_details_tabs.next(); + let episode_details_modal = EpisodeDetailsModal::default(); let mut season_details_modal = SeasonDetailsModal::default(); season_details_modal.episodes.set_items(vec![episode()]); season_details_modal.episode_details_modal = Some(episode_details_modal); @@ -868,7 +867,7 @@ mod tests { .unwrap() .episode_details_tabs .get_active_route(), - ActiveSonarrBlock::EpisodeHistory.into() + ActiveSonarrBlock::EpisodeDetails.into() ); assert_eq!(episode, response); diff --git a/src/network/sonarr_network/library/seasons/mod.rs b/src/network/sonarr_network/library/seasons/mod.rs index 7ed2912..33633d8 100644 --- a/src/network/sonarr_network/library/seasons/mod.rs +++ b/src/network/sonarr_network/library/seasons/mod.rs @@ -1,4 +1,5 @@ -use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal; +use crate::models::Route; +use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; use crate::models::sonarr_models::{SonarrCommandBody, SonarrHistoryItem, SonarrRelease}; use crate::network::sonarr_network::SonarrEvent; use crate::network::{Network, RequestMethod}; @@ -111,21 +112,18 @@ impl Network<'_, '_> { self .handle_request::<(), Vec>(request_props, |release_vec, mut app| { - if app.data.sonarr_data.season_details_modal.is_none() { - app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); - } + let season_details_modal = app + .data + .sonarr_data + .season_details_modal + .get_or_insert_default(); let season_releases_vec = release_vec .into_iter() .filter(|release| release.full_season) .collect(); - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() + season_details_modal .season_releases .set_items(season_releases_vec); }) @@ -147,28 +145,25 @@ impl Network<'_, '_> { self .handle_request::<(), Vec>(request_props, |history_items, mut app| { - if app.data.sonarr_data.season_details_modal.is_none() { - app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); - } + let is_sorting = matches!( + app.get_current_route(), + Route::Sonarr(ActiveSonarrBlock::SeasonHistorySortPrompt, _) + ); - let mut history_vec = history_items; - history_vec.sort_by(|a, b| a.id.cmp(&b.id)); - app + let season_details_modal = app .data .sonarr_data .season_details_modal - .as_mut() - .unwrap() - .season_history - .set_items(history_vec); - app - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .season_history - .apply_sorting_toggle(false); + .get_or_insert_default(); + + if !is_sorting { + let mut history_vec = history_items; + history_vec.sort_by(|a, b| a.id.cmp(&b.id)); + season_details_modal.season_history.set_items(history_vec); + season_details_modal + .season_history + .apply_sorting_toggle(false); + } }) .await } diff --git a/src/network/sonarr_network/library/seasons/sonarr_seasons_network_tests.rs b/src/network/sonarr_network/library/seasons/sonarr_seasons_network_tests.rs index 67e56ff..2610826 100644 --- a/src/network/sonarr_network/library/seasons/sonarr_seasons_network_tests.rs +++ b/src/network/sonarr_network/library/seasons/sonarr_seasons_network_tests.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal; + use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; use crate::models::sonarr_models::{SonarrHistoryItem, SonarrRelease, SonarrSerdeable}; use crate::network::NetworkResource; use crate::network::network_tests::test_utils::{MockServarrApi, test_network}; @@ -466,6 +467,93 @@ mod tests { assert_eq!(history, response); } + #[tokio::test] + async fn test_handle_get_sonarr_season_history_event_no_op_when_user_is_selecting_sort_option() { + let history_json = json!([{ + "id": 123, + "sourceTitle": "z episode", + "episodeId": 1007, + "quality": { "quality": { "name": "Bluray-1080p" } }, + "languages": [{ "id": 1, "name": "English" }], + "date": "2024-02-10T07:28:45Z", + "eventType": "grabbed", + "data": { + "droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv", + "importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv" + } + }, + { + "id": 456, + "sourceTitle": "A Episode", + "episodeId": 2001, + "quality": { "quality": { "name": "Bluray-1080p" } }, + "languages": [{ "id": 1, "name": "English" }], + "date": "2024-02-10T07:28:45Z", + "eventType": "grabbed", + "data": { + "droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv", + "importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv" + } + }]); + let response: Vec = serde_json::from_value(history_json.clone()).unwrap(); + let (mock, app, _server) = MockServarrApi::get() + .returns(history_json) + .query("seriesId=1&seasonNumber=1") + .build_for(SonarrEvent::GetSeasonHistory((1, 1))) + .await; + app.lock().await.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); + app + .lock() + .await + .data + .sonarr_data + .season_details_modal + .as_mut() + .unwrap() + .season_history + .sort_asc = true; + app.lock().await.server_tabs.next(); + app + .lock() + .await + .push_navigation_stack(ActiveSonarrBlock::SeasonHistorySortPrompt.into()); + let mut network = test_network(&app); + + let SonarrSerdeable::SonarrHistoryItems(history) = network + .handle_sonarr_event(SonarrEvent::GetSeasonHistory((1, 1))) + .await + .unwrap() + else { + panic!("Expected SonarrHistoryItems") + }; + mock.assert_async().await; + assert_is_empty!( + app + .lock() + .await + .data + .sonarr_data + .season_details_modal + .as_ref() + .unwrap() + .season_history + .items + ); + assert!( + app + .lock() + .await + .data + .sonarr_data + .season_details_modal + .as_ref() + .unwrap() + .season_history + .sort_asc + ); + assert_eq!(history, response); + } + #[tokio::test] async fn test_handle_trigger_automatic_season_search_event() { let (mock, app, _server) = MockServarrApi::post()