use strum::IntoEnumIterator; use super::sonarr_data::{ActiveSonarrBlock, SonarrData}; use crate::app::sonarr::sonarr_context_clues::SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES; use crate::models::sonarr_models::EpisodeFile; use crate::{ app::sonarr::sonarr_context_clues::{ EPISODE_DETAILS_CONTEXT_CLUES, MANUAL_EPISODE_SEARCH_CONTEXT_CLUES, MANUAL_SEASON_SEARCH_CONTEXT_CLUES, SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES, }, models::{ HorizontallyScrollableText, ScrollableText, TabRoute, TabState, servarr_data::modals::EditIndexerModal, servarr_models::{Indexer, RootFolder}, sonarr_models::{Episode, Series, SeriesMonitor, SeriesType, SonarrHistoryItem, SonarrRelease}, stateful_list::StatefulList, stateful_table::StatefulTable, }, }; #[cfg(test)] #[path = "modals_tests.rs"] mod modals_tests; #[derive(Default)] pub struct AddSeriesModal { pub root_folder_list: StatefulList, pub monitor_list: StatefulList, pub quality_profile_list: StatefulList, pub language_profile_list: StatefulList, pub series_type_list: StatefulList, pub use_season_folder: bool, pub tags: HorizontallyScrollableText, } impl From<&SonarrData<'_>> for AddSeriesModal { fn from(sonarr_data: &SonarrData<'_>) -> AddSeriesModal { let mut add_series_modal = AddSeriesModal { use_season_folder: true, ..AddSeriesModal::default() }; add_series_modal .monitor_list .set_items(Vec::from_iter(SeriesMonitor::iter())); add_series_modal .series_type_list .set_items(Vec::from_iter(SeriesType::iter())); add_series_modal .quality_profile_list .set_items(sonarr_data.sorted_quality_profile_names()); add_series_modal .language_profile_list .set_items(sonarr_data.sorted_language_profile_names()); add_series_modal .root_folder_list .set_items(sonarr_data.root_folders.items.to_vec()); add_series_modal } } impl From<&SonarrData<'_>> for EditIndexerModal { fn from(sonarr_data: &SonarrData<'_>) -> EditIndexerModal { let mut edit_indexer_modal = EditIndexerModal::default(); let Indexer { name, enable_rss, enable_automatic_search, enable_interactive_search, tags, fields, priority, .. } = sonarr_data.indexers.current_selection(); let seed_ratio_field_option = fields .as_ref() .expect("indexer fields must exist") .iter() .find(|field| { field.name.as_ref().expect("indexer field name must exist") == "seedCriteria.seedRatio" }); let seed_ratio_value_option = if let Some(seed_ratio_field) = seed_ratio_field_option { seed_ratio_field.value.clone() } else { None }; edit_indexer_modal.name = name.clone().expect("indexer name must exist").into(); edit_indexer_modal.enable_rss = Some(*enable_rss); edit_indexer_modal.enable_automatic_search = Some(*enable_automatic_search); edit_indexer_modal.enable_interactive_search = Some(*enable_interactive_search); edit_indexer_modal.priority = *priority; edit_indexer_modal.url = fields .as_ref() .expect("indexer fields must exist") .iter() .find(|field| field.name.as_ref().expect("indexer field name must exist") == "baseUrl") .expect("baseUrl field must exist") .value .clone() .expect("baseUrl field value must exist") .as_str() .expect("baseUrl field value must be a string") .into(); edit_indexer_modal.api_key = fields .as_ref() .expect("indexer fields must exist") .iter() .find(|field| field.name.as_ref().expect("indexer field name must exist") == "apiKey") .expect("apiKey field must exist") .value .clone() .expect("apiKey field value must exist") .as_str() .expect("apiKey field value must be a string") .into(); if let Some(seed_ratio_value) = seed_ratio_value_option { edit_indexer_modal.seed_ratio = seed_ratio_value .as_f64() .expect("Seed ratio value must be a valid f64") .to_string() .into(); } edit_indexer_modal.tags = sonarr_data.tag_ids_to_display(tags).into(); edit_indexer_modal } } #[derive(Default)] pub struct EditSeriesModal { pub series_type_list: StatefulList, pub quality_profile_list: StatefulList, pub language_profile_list: StatefulList, pub monitored: Option, pub use_season_folders: Option, pub path: HorizontallyScrollableText, pub tags: HorizontallyScrollableText, } impl From<&SonarrData<'_>> for EditSeriesModal { fn from(sonarr_data: &SonarrData<'_>) -> EditSeriesModal { let mut edit_series_modal = EditSeriesModal::default(); let Series { path, tags, monitored, season_folder, series_type, quality_profile_id, language_profile_id, .. } = sonarr_data.series.current_selection(); edit_series_modal .series_type_list .set_items(Vec::from_iter(SeriesType::iter())); edit_series_modal.path = path.clone().into(); edit_series_modal.tags = sonarr_data.tag_ids_to_display(tags).into(); edit_series_modal.monitored = Some(*monitored); edit_series_modal.use_season_folders = Some(*season_folder); let series_type_index = edit_series_modal .series_type_list .items .iter() .position(|st| st == series_type); edit_series_modal .series_type_list .state .select(series_type_index); edit_series_modal .quality_profile_list .set_items(sonarr_data.sorted_quality_profile_names()); let quality_profile_name = sonarr_data .quality_profile_map .get_by_left(quality_profile_id) .unwrap(); let quality_profile_index = edit_series_modal .quality_profile_list .items .iter() .position(|profile| profile == quality_profile_name); edit_series_modal .quality_profile_list .state .select(quality_profile_index); edit_series_modal .language_profile_list .set_items(sonarr_data.sorted_language_profile_names()); let language_profile_name = sonarr_data .language_profiles_map .get_by_left(language_profile_id) .unwrap(); let language_profile_index = edit_series_modal .language_profile_list .items .iter() .position(|profile| profile == language_profile_name); edit_series_modal .language_profile_list .state .select(language_profile_index); edit_series_modal } } pub struct EpisodeDetailsModal { pub episode_details: ScrollableText, pub file_details: String, pub audio_details: String, pub video_details: String, pub episode_history: StatefulTable, pub episode_releases: StatefulTable, pub episode_details_tabs: TabState, } impl Default for EpisodeDetailsModal { fn default() -> EpisodeDetailsModal { EpisodeDetailsModal { episode_details: ScrollableText::default(), file_details: String::new(), audio_details: String::new(), video_details: String::new(), episode_history: StatefulTable::default(), episode_releases: StatefulTable::default(), episode_details_tabs: TabState::new(vec![ TabRoute { title: "Details".to_string(), route: ActiveSonarrBlock::EpisodeDetails.into(), contextual_help: Some(&EPISODE_DETAILS_CONTEXT_CLUES), config: None, }, TabRoute { title: "History".to_string(), route: ActiveSonarrBlock::EpisodeHistory.into(), contextual_help: Some(&SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES), config: None, }, TabRoute { title: "File".to_string(), route: ActiveSonarrBlock::EpisodeFile.into(), contextual_help: Some(&EPISODE_DETAILS_CONTEXT_CLUES), config: None, }, TabRoute { title: "Manual Search".to_string(), route: ActiveSonarrBlock::ManualEpisodeSearch.into(), contextual_help: Some(&MANUAL_EPISODE_SEARCH_CONTEXT_CLUES), config: None, }, ]), } } } pub struct SeasonDetailsModal { pub episodes: StatefulTable, pub episode_files: StatefulTable, pub episode_details_modal: Option, pub season_history: StatefulTable, pub season_releases: StatefulTable, pub season_details_tabs: TabState, } impl Default for SeasonDetailsModal { fn default() -> SeasonDetailsModal { SeasonDetailsModal { episodes: StatefulTable::default(), episode_details_modal: None, episode_files: StatefulTable::default(), season_releases: StatefulTable::default(), season_history: StatefulTable::default(), season_details_tabs: TabState::new(vec![ TabRoute { title: "Episodes".to_string(), route: ActiveSonarrBlock::SeasonDetails.into(), contextual_help: Some(&SEASON_DETAILS_CONTEXT_CLUES), config: None, }, TabRoute { title: "History".to_string(), route: ActiveSonarrBlock::SeasonHistory.into(), contextual_help: Some(&SEASON_HISTORY_CONTEXT_CLUES), config: None, }, TabRoute { title: "Manual Search".to_string(), route: ActiveSonarrBlock::ManualSeasonSearch.into(), contextual_help: Some(&MANUAL_SEASON_SEARCH_CONTEXT_CLUES), config: None, }, ]), } } }