feat(ui): Full Sonarr support for the indexer tab
This commit is contained in:
@@ -56,7 +56,10 @@ impl DrawUi for BlocklistUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_blocklist_table(f, app, area);
|
draw_blocklist_table(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::BlocklistClearAllItemsPrompt => {
|
ActiveRadarrBlock::BlocklistClearAllItemsPrompt => {
|
||||||
let confirmation_prompt = ConfirmationPrompt::new()
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
|
|||||||
@@ -81,7 +81,10 @@ impl DrawUi for CollectionsUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_collections(f, app, area);
|
draw_collections(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ impl DrawUi for DownloadsUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_downloads(f, app, area);
|
draw_downloads(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::UpdateDownloadsPrompt => {
|
ActiveRadarrBlock::UpdateDownloadsPrompt => {
|
||||||
let confirmation_prompt = ConfirmationPrompt::new()
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
@@ -53,7 +56,10 @@ impl DrawUi for DownloadsUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_downloads(f, app, area);
|
draw_downloads(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl DrawUi for EditIndexerUi {
|
|||||||
area,
|
area,
|
||||||
draw_indexers,
|
draw_indexers,
|
||||||
draw_edit_indexer_prompt,
|
draw_edit_indexer_prompt,
|
||||||
Size::LargePrompt,
|
Size::WideLargePrompt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ impl DrawUi for IndexerSettingsUi {
|
|||||||
area,
|
area,
|
||||||
draw_indexers,
|
draw_indexers,
|
||||||
draw_edit_indexer_settings_prompt,
|
draw_edit_indexer_settings_prompt,
|
||||||
Size::LargePrompt,
|
Size::WideLargePrompt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,8 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
|||||||
if indexer_settings_option.is_some() {
|
if indexer_settings_option.is_some() {
|
||||||
let indexer_settings = indexer_settings_option.as_ref().unwrap();
|
let indexer_settings = indexer_settings_option.as_ref().unwrap();
|
||||||
|
|
||||||
let [settings_area, _, buttons_area, help_area] = Layout::vertical([
|
let [_, settings_area, _, buttons_area, help_area] = Layout::vertical([
|
||||||
|
Constraint::Fill(1),
|
||||||
Constraint::Length(15),
|
Constraint::Length(15),
|
||||||
Constraint::Fill(1),
|
Constraint::Fill(1),
|
||||||
Constraint::Length(3),
|
Constraint::Length(3),
|
||||||
|
|||||||
@@ -86,7 +86,10 @@ impl DrawUi for IndexersUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_indexers(f, app, area);
|
draw_indexers(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ impl DrawUi for DeleteMovieUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_library(f, app, area);
|
draw_library(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,10 @@ impl DrawUi for LibraryUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_library(f, app, area);
|
draw_library(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,7 +61,10 @@ impl DrawUi for MovieDetailsUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_movie_info(f, app, content_area);
|
draw_movie_info(f, app, content_area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::UpdateAndScanPrompt => {
|
ActiveRadarrBlock::UpdateAndScanPrompt => {
|
||||||
let prompt = format!(
|
let prompt = format!(
|
||||||
@@ -73,7 +76,10 @@ impl DrawUi for MovieDetailsUi {
|
|||||||
.prompt(&prompt)
|
.prompt(&prompt)
|
||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::ManualSearchConfirmPrompt => {
|
ActiveRadarrBlock::ManualSearchConfirmPrompt => {
|
||||||
draw_manual_search_confirm_prompt(f, app);
|
draw_manual_search_confirm_prompt(f, app);
|
||||||
@@ -532,7 +538,10 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
.prompt(&prompt)
|
.prompt(&prompt)
|
||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,10 @@ impl DrawUi for RootFoldersUi {
|
|||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_root_folders(f, app, area);
|
draw_root_folders(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,10 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.prompt(&prompt)
|
.prompt(&prompt)
|
||||||
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
.yes_no_value(app.data.radarr_data.prompt_confirm);
|
||||||
|
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,10 @@ impl DrawUi for BlocklistUi {
|
|||||||
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_blocklist_table(f, app, area);
|
draw_blocklist_table(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
|
ActiveSonarrBlock::BlocklistClearAllItemsPrompt => {
|
||||||
let confirmation_prompt = ConfirmationPrompt::new()
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ impl DrawUi for DownloadsUi {
|
|||||||
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_downloads(f, app, area);
|
draw_downloads(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ActiveSonarrBlock::UpdateDownloadsPrompt => {
|
ActiveSonarrBlock::UpdateDownloadsPrompt => {
|
||||||
let confirmation_prompt = ConfirmationPrompt::new()
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
@@ -53,7 +56,10 @@ impl DrawUi for DownloadsUi {
|
|||||||
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_downloads(f, app, area);
|
draw_downloads(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::render_selectable_input_box;
|
||||||
|
use crate::ui::sonarr_ui::indexers::draw_indexers;
|
||||||
|
use crate::ui::styles::ManagarrStyle;
|
||||||
|
use crate::ui::utils::title_block_centered;
|
||||||
|
use crate::ui::widgets::button::Button;
|
||||||
|
use crate::ui::widgets::checkbox::Checkbox;
|
||||||
|
use crate::ui::widgets::input_box::InputBox;
|
||||||
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
|
use crate::ui::widgets::popup::Size;
|
||||||
|
use crate::ui::{draw_popup_over, DrawUi};
|
||||||
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
|
use ratatui::text::Text;
|
||||||
|
use ratatui::widgets::Paragraph;
|
||||||
|
use ratatui::Frame;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "edit_indexer_ui_tests.rs"]
|
||||||
|
mod edit_indexer_ui_tests;
|
||||||
|
|
||||||
|
pub(super) struct EditIndexerUi;
|
||||||
|
|
||||||
|
impl DrawUi for EditIndexerUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
if let Route::Sonarr(active_sonarr_block, _) = route {
|
||||||
|
return EDIT_INDEXER_BLOCKS.contains(&active_sonarr_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
draw_popup_over(
|
||||||
|
f,
|
||||||
|
app,
|
||||||
|
area,
|
||||||
|
draw_indexers,
|
||||||
|
draw_edit_indexer_prompt,
|
||||||
|
Size::WideLargePrompt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let block = title_block_centered("Edit Indexer");
|
||||||
|
let yes_no_value = app.data.sonarr_data.prompt_confirm;
|
||||||
|
let selected_block = app.data.sonarr_data.selected_block.get_active_block();
|
||||||
|
let highlight_yes_no = selected_block == ActiveSonarrBlock::EditIndexerConfirmPrompt;
|
||||||
|
let edit_indexer_modal_option = &app.data.sonarr_data.edit_indexer_modal;
|
||||||
|
let protocol = &app.data.sonarr_data.indexers.current_selection().protocol;
|
||||||
|
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
||||||
|
let help_paragraph = Paragraph::new(help_text).centered();
|
||||||
|
|
||||||
|
if edit_indexer_modal_option.is_some() {
|
||||||
|
let edit_indexer_modal = edit_indexer_modal_option.as_ref().unwrap();
|
||||||
|
|
||||||
|
let [_, settings_area, _, buttons_area, help_area] = Layout::vertical([
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(18),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(1),
|
||||||
|
])
|
||||||
|
.margin(1)
|
||||||
|
.areas(area);
|
||||||
|
let [left_side_area, right_side_area] =
|
||||||
|
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
||||||
|
.margin(1)
|
||||||
|
.areas(settings_area);
|
||||||
|
let [name_area, rss_area, auto_search_area, interactive_search_area, priority_area] =
|
||||||
|
Layout::vertical([
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
])
|
||||||
|
.areas(left_side_area);
|
||||||
|
let [url_area, api_key_area, seed_ratio_area, tags_area] = Layout::vertical([
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
])
|
||||||
|
.areas(right_side_area);
|
||||||
|
|
||||||
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
|
let priority = edit_indexer_modal.priority.to_string();
|
||||||
|
let name_input_box = InputBox::new(&edit_indexer_modal.name.text)
|
||||||
|
.offset(edit_indexer_modal.name.offset.load(Ordering::SeqCst))
|
||||||
|
.label("Name")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerNameInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::EditIndexerNameInput);
|
||||||
|
let url_input_box = InputBox::new(&edit_indexer_modal.url.text)
|
||||||
|
.offset(edit_indexer_modal.url.offset.load(Ordering::SeqCst))
|
||||||
|
.label("URL")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerUrlInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::EditIndexerUrlInput);
|
||||||
|
let api_key_input_box = InputBox::new(&edit_indexer_modal.api_key.text)
|
||||||
|
.offset(edit_indexer_modal.api_key.offset.load(Ordering::SeqCst))
|
||||||
|
.label("API Key")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerApiKeyInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::EditIndexerApiKeyInput);
|
||||||
|
let tags_input_box = InputBox::new(&edit_indexer_modal.tags.text)
|
||||||
|
.offset(edit_indexer_modal.tags.offset.load(Ordering::SeqCst))
|
||||||
|
.label("Tags")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerTagsInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::EditIndexerTagsInput);
|
||||||
|
let priority_input_box = InputBox::new(&priority)
|
||||||
|
.cursor_after_string(false)
|
||||||
|
.label("Indexer Priority ▴▾")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerPriorityInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::EditIndexerPriorityInput);
|
||||||
|
|
||||||
|
render_selectable_input_box!(name_input_box, f, name_area);
|
||||||
|
render_selectable_input_box!(url_input_box, f, url_area);
|
||||||
|
render_selectable_input_box!(api_key_input_box, f, api_key_area);
|
||||||
|
|
||||||
|
if protocol == "torrent" {
|
||||||
|
let seed_ratio_input_box = InputBox::new(&edit_indexer_modal.seed_ratio.text)
|
||||||
|
.offset(edit_indexer_modal.seed_ratio.offset.load(Ordering::SeqCst))
|
||||||
|
.label("Seed Ratio")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerSeedRatioInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::EditIndexerSeedRatioInput);
|
||||||
|
let tags_input_box = InputBox::new(&edit_indexer_modal.tags.text)
|
||||||
|
.offset(edit_indexer_modal.tags.offset.load(Ordering::SeqCst))
|
||||||
|
.label("Tags")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerTagsInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::EditIndexerTagsInput);
|
||||||
|
|
||||||
|
render_selectable_input_box!(seed_ratio_input_box, f, seed_ratio_area);
|
||||||
|
render_selectable_input_box!(tags_input_box, f, tags_area);
|
||||||
|
render_selectable_input_box!(priority_input_box, f, priority_area);
|
||||||
|
} else {
|
||||||
|
render_selectable_input_box!(tags_input_box, f, seed_ratio_area);
|
||||||
|
render_selectable_input_box!(priority_input_box, f, tags_area);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rss_checkbox = Checkbox::new("Enable RSS")
|
||||||
|
.checked(edit_indexer_modal.enable_rss.unwrap_or_default())
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerToggleEnableRss);
|
||||||
|
let auto_search_checkbox = Checkbox::new("Enable Automatic Search")
|
||||||
|
.checked(
|
||||||
|
edit_indexer_modal
|
||||||
|
.enable_automatic_search
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerToggleEnableAutomaticSearch);
|
||||||
|
let interactive_search_checkbox = Checkbox::new("Enable Interactive Search")
|
||||||
|
.checked(
|
||||||
|
edit_indexer_modal
|
||||||
|
.enable_interactive_search
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::EditIndexerToggleEnableInteractiveSearch);
|
||||||
|
|
||||||
|
let [save_area, cancel_area] =
|
||||||
|
Layout::horizontal([Constraint::Percentage(25), Constraint::Percentage(25)])
|
||||||
|
.flex(Flex::Center)
|
||||||
|
.areas(buttons_area);
|
||||||
|
|
||||||
|
let save_button = Button::new()
|
||||||
|
.title("Save")
|
||||||
|
.selected(yes_no_value && highlight_yes_no);
|
||||||
|
let cancel_button = Button::new()
|
||||||
|
.title("Cancel")
|
||||||
|
.selected(!yes_no_value && highlight_yes_no);
|
||||||
|
|
||||||
|
f.render_widget(block, area);
|
||||||
|
f.render_widget(rss_checkbox, rss_area);
|
||||||
|
f.render_widget(auto_search_checkbox, auto_search_area);
|
||||||
|
f.render_widget(interactive_search_checkbox, interactive_search_area);
|
||||||
|
f.render_widget(save_button, save_area);
|
||||||
|
f.render_widget(cancel_button, cancel_area);
|
||||||
|
f.render_widget(help_paragraph, help_area);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
|
use crate::ui::sonarr_ui::indexers::edit_indexer_ui::EditIndexerUi;
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edit_indexer_ui_accepts() {
|
||||||
|
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
|
||||||
|
if EDIT_INDEXER_BLOCKS.contains(&active_sonarr_block) {
|
||||||
|
assert!(EditIndexerUi::accepts(active_sonarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!EditIndexerUi::accepts(active_sonarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
|
use ratatui::text::Text;
|
||||||
|
use ratatui::widgets::Paragraph;
|
||||||
|
use ratatui::Frame;
|
||||||
|
|
||||||
|
use crate::app::context_clues::{build_context_clue_string, CONFIRMATION_PROMPT_CONTEXT_CLUES};
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{
|
||||||
|
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::render_selectable_input_box;
|
||||||
|
use crate::ui::sonarr_ui::indexers::draw_indexers;
|
||||||
|
use crate::ui::styles::ManagarrStyle;
|
||||||
|
use crate::ui::utils::title_block_centered;
|
||||||
|
use crate::ui::widgets::button::Button;
|
||||||
|
use crate::ui::widgets::input_box::InputBox;
|
||||||
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
|
use crate::ui::widgets::popup::Size;
|
||||||
|
use crate::ui::{draw_popup_over, DrawUi};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "indexer_settings_ui_tests.rs"]
|
||||||
|
mod indexer_settings_ui_tests;
|
||||||
|
|
||||||
|
pub(super) struct IndexerSettingsUi;
|
||||||
|
|
||||||
|
impl DrawUi for IndexerSettingsUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
if let Route::Sonarr(active_sonarr_block, _) = route {
|
||||||
|
return INDEXER_SETTINGS_BLOCKS.contains(&active_sonarr_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
draw_popup_over(
|
||||||
|
f,
|
||||||
|
app,
|
||||||
|
area,
|
||||||
|
draw_indexers,
|
||||||
|
draw_edit_indexer_settings_prompt,
|
||||||
|
Size::LargePrompt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let block = title_block_centered("Configure All Indexer Settings");
|
||||||
|
let yes_no_value = app.data.sonarr_data.prompt_confirm;
|
||||||
|
let selected_block = app.data.sonarr_data.selected_block.get_active_block();
|
||||||
|
let highlight_yes_no = selected_block == ActiveSonarrBlock::IndexerSettingsConfirmPrompt;
|
||||||
|
let indexer_settings_option = &app.data.sonarr_data.indexer_settings;
|
||||||
|
let help_text = Text::from(build_context_clue_string(&CONFIRMATION_PROMPT_CONTEXT_CLUES).help());
|
||||||
|
let help_paragraph = Paragraph::new(help_text).centered();
|
||||||
|
|
||||||
|
if indexer_settings_option.is_some() {
|
||||||
|
let indexer_settings = indexer_settings_option.as_ref().unwrap();
|
||||||
|
|
||||||
|
let [_, min_age_area, retention_area, max_size_area, rss_sync_area, _, buttons_area, help_area] =
|
||||||
|
Layout::vertical([
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(1),
|
||||||
|
])
|
||||||
|
.margin(1)
|
||||||
|
.areas(area);
|
||||||
|
|
||||||
|
if let Route::Sonarr(active_sonarr_block, _) = app.get_current_route() {
|
||||||
|
let min_age = indexer_settings.minimum_age.to_string();
|
||||||
|
let retention = indexer_settings.retention.to_string();
|
||||||
|
let max_size = indexer_settings.maximum_size.to_string();
|
||||||
|
let rss_sync_interval = indexer_settings.rss_sync_interval.to_string();
|
||||||
|
|
||||||
|
let min_age_text_box = InputBox::new(&min_age)
|
||||||
|
.cursor_after_string(false)
|
||||||
|
.label("Minimum Age (minutes) ▴▾")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::IndexerSettingsMinimumAgeInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::IndexerSettingsMinimumAgeInput);
|
||||||
|
let retention_input_box = InputBox::new(&retention)
|
||||||
|
.cursor_after_string(false)
|
||||||
|
.label("Retention (days) ▴▾")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::IndexerSettingsRetentionInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::IndexerSettingsRetentionInput);
|
||||||
|
let max_size_input_box = InputBox::new(&max_size)
|
||||||
|
.cursor_after_string(false)
|
||||||
|
.label("Maximum Size (MB) ▴▾")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::IndexerSettingsMaximumSizeInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::IndexerSettingsMaximumSizeInput);
|
||||||
|
let rss_sync_interval_input_box = InputBox::new(&rss_sync_interval)
|
||||||
|
.cursor_after_string(false)
|
||||||
|
.label("RSS Sync Interval (minutes) ▴▾")
|
||||||
|
.highlighted(selected_block == ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput)
|
||||||
|
.selected(active_sonarr_block == ActiveSonarrBlock::IndexerSettingsRssSyncIntervalInput);
|
||||||
|
|
||||||
|
render_selectable_input_box!(min_age_text_box, f, min_age_area);
|
||||||
|
render_selectable_input_box!(retention_input_box, f, retention_area);
|
||||||
|
render_selectable_input_box!(max_size_input_box, f, max_size_area);
|
||||||
|
render_selectable_input_box!(rss_sync_interval_input_box, f, rss_sync_area);
|
||||||
|
}
|
||||||
|
|
||||||
|
let [save_area, cancel_area] =
|
||||||
|
Layout::horizontal([Constraint::Percentage(25), Constraint::Percentage(25)])
|
||||||
|
.flex(Flex::Center)
|
||||||
|
.areas(buttons_area);
|
||||||
|
|
||||||
|
let save_button = Button::new()
|
||||||
|
.title("Save")
|
||||||
|
.selected(yes_no_value && highlight_yes_no);
|
||||||
|
let cancel_button = Button::new()
|
||||||
|
.title("Cancel")
|
||||||
|
.selected(!yes_no_value && highlight_yes_no);
|
||||||
|
|
||||||
|
f.render_widget(block, area);
|
||||||
|
f.render_widget(save_button, save_area);
|
||||||
|
f.render_widget(cancel_button, cancel_area);
|
||||||
|
f.render_widget(help_paragraph, help_area);
|
||||||
|
} else {
|
||||||
|
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{
|
||||||
|
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::ui::sonarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi;
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_indexer_settings_ui_accepts() {
|
||||||
|
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
|
||||||
|
if INDEXER_SETTINGS_BLOCKS.contains(&active_sonarr_block) {
|
||||||
|
assert!(IndexerSettingsUi::accepts(active_sonarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!IndexerSettingsUi::accepts(active_sonarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{
|
||||||
|
ActiveSonarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::ui::sonarr_ui::indexers::IndexersUi;
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_indexers_ui_accepts() {
|
||||||
|
let mut indexers_blocks = Vec::new();
|
||||||
|
indexers_blocks.extend(INDEXERS_BLOCKS);
|
||||||
|
indexers_blocks.extend(INDEXER_SETTINGS_BLOCKS);
|
||||||
|
indexers_blocks.extend(EDIT_INDEXER_BLOCKS);
|
||||||
|
indexers_blocks.push(ActiveSonarrBlock::TestAllIndexers);
|
||||||
|
|
||||||
|
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
|
||||||
|
if indexers_blocks.contains(&active_sonarr_block) {
|
||||||
|
assert!(IndexersUi::accepts(active_sonarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!IndexersUi::accepts(active_sonarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
use ratatui::layout::{Constraint, Rect};
|
||||||
|
use ratatui::style::{Style, Stylize};
|
||||||
|
use ratatui::text::Text;
|
||||||
|
use ratatui::widgets::{Cell, Row};
|
||||||
|
use ratatui::Frame;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, INDEXERS_BLOCKS};
|
||||||
|
use crate::models::servarr_models::Indexer;
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::ui::sonarr_ui::indexers::edit_indexer_ui::EditIndexerUi;
|
||||||
|
use crate::ui::sonarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi;
|
||||||
|
use crate::ui::sonarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi;
|
||||||
|
use crate::ui::styles::ManagarrStyle;
|
||||||
|
use crate::ui::utils::{layout_block_top_border, title_block};
|
||||||
|
use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt;
|
||||||
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
|
use crate::ui::widgets::message::Message;
|
||||||
|
use crate::ui::widgets::popup::{Popup, Size};
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
|
||||||
|
mod edit_indexer_ui;
|
||||||
|
mod indexer_settings_ui;
|
||||||
|
mod test_all_indexers_ui;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "indexers_ui_tests.rs"]
|
||||||
|
mod indexers_ui_tests;
|
||||||
|
|
||||||
|
pub(super) struct IndexersUi;
|
||||||
|
|
||||||
|
impl DrawUi for IndexersUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
if let Route::Sonarr(active_sonarr_block, _) = route {
|
||||||
|
return EditIndexerUi::accepts(route)
|
||||||
|
|| IndexerSettingsUi::accepts(route)
|
||||||
|
|| TestAllIndexersUi::accepts(route)
|
||||||
|
|| INDEXERS_BLOCKS.contains(&active_sonarr_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let route = app.get_current_route();
|
||||||
|
let mut indexers_matchers = |active_sonarr_block| match active_sonarr_block {
|
||||||
|
ActiveSonarrBlock::Indexers => draw_indexers(f, app, area),
|
||||||
|
ActiveSonarrBlock::TestIndexer => {
|
||||||
|
draw_indexers(f, app, area);
|
||||||
|
if app.is_loading || app.is_routing {
|
||||||
|
let loading_popup = Popup::new(LoadingBlock::new(
|
||||||
|
app.is_loading,
|
||||||
|
title_block("Testing Indexer"),
|
||||||
|
))
|
||||||
|
.size(Size::LargeMessage);
|
||||||
|
f.render_widget(loading_popup, f.area());
|
||||||
|
} else {
|
||||||
|
let popup = if let Some(result) = app.data.sonarr_data.indexer_test_error.as_ref() {
|
||||||
|
Popup::new(Message::new(result.clone())).size(Size::LargeMessage)
|
||||||
|
} else {
|
||||||
|
let message = Message::new("Indexer test succeeded!")
|
||||||
|
.title("Success")
|
||||||
|
.style(Style::new().success().bold());
|
||||||
|
Popup::new(message).size(Size::Message)
|
||||||
|
};
|
||||||
|
|
||||||
|
f.render_widget(popup, f.area());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveSonarrBlock::DeleteIndexerPrompt => {
|
||||||
|
let prompt = format!(
|
||||||
|
"Do you really want to delete this indexer: \n{}?",
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.indexers
|
||||||
|
.current_selection()
|
||||||
|
.name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_default()
|
||||||
|
);
|
||||||
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
|
.title("Delete Indexer")
|
||||||
|
.prompt(&prompt)
|
||||||
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
|
draw_indexers(f, app, area);
|
||||||
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
match route {
|
||||||
|
_ if EditIndexerUi::accepts(route) => EditIndexerUi::draw(f, app, area),
|
||||||
|
_ if IndexerSettingsUi::accepts(route) => IndexerSettingsUi::draw(f, app, area),
|
||||||
|
_ if TestAllIndexersUi::accepts(route) => TestAllIndexersUi::draw(f, app, area),
|
||||||
|
Route::Sonarr(active_sonarr_block, _) if INDEXERS_BLOCKS.contains(&active_sonarr_block) => {
|
||||||
|
indexers_matchers(active_sonarr_block)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let indexers_row_mapping = |indexer: &'_ Indexer| {
|
||||||
|
let Indexer {
|
||||||
|
name,
|
||||||
|
enable_rss,
|
||||||
|
enable_automatic_search,
|
||||||
|
enable_interactive_search,
|
||||||
|
priority,
|
||||||
|
tags,
|
||||||
|
..
|
||||||
|
} = indexer;
|
||||||
|
let bool_to_text = |flag: bool| {
|
||||||
|
if flag {
|
||||||
|
return Text::from("Enabled").success();
|
||||||
|
}
|
||||||
|
|
||||||
|
Text::from("Disabled").failure()
|
||||||
|
};
|
||||||
|
|
||||||
|
let rss = bool_to_text(*enable_rss);
|
||||||
|
let automatic_search = bool_to_text(*enable_automatic_search);
|
||||||
|
let interactive_search = bool_to_text(*enable_interactive_search);
|
||||||
|
let tags: String = tags
|
||||||
|
.iter()
|
||||||
|
.map(|tag_id| {
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.tags_map
|
||||||
|
.get_by_left(&tag_id.as_i64().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
Row::new(vec![
|
||||||
|
Cell::from(name.clone().unwrap_or_default()),
|
||||||
|
Cell::from(rss),
|
||||||
|
Cell::from(automatic_search),
|
||||||
|
Cell::from(interactive_search),
|
||||||
|
Cell::from(priority.to_string()),
|
||||||
|
Cell::from(tags),
|
||||||
|
])
|
||||||
|
.primary()
|
||||||
|
};
|
||||||
|
let indexers_table_footer = app
|
||||||
|
.data
|
||||||
|
.sonarr_data
|
||||||
|
.main_tabs
|
||||||
|
.get_active_tab_contextual_help();
|
||||||
|
let indexers_table = ManagarrTable::new(
|
||||||
|
Some(&mut app.data.sonarr_data.indexers),
|
||||||
|
indexers_row_mapping,
|
||||||
|
)
|
||||||
|
.block(layout_block_top_border())
|
||||||
|
.footer(indexers_table_footer)
|
||||||
|
.loading(app.is_loading)
|
||||||
|
.headers([
|
||||||
|
"Indexer",
|
||||||
|
"RSS",
|
||||||
|
"Automatic Search",
|
||||||
|
"Interactive Search",
|
||||||
|
"Priority",
|
||||||
|
"Tags",
|
||||||
|
])
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
Constraint::Percentage(13),
|
||||||
|
Constraint::Percentage(13),
|
||||||
|
Constraint::Percentage(13),
|
||||||
|
Constraint::Percentage(13),
|
||||||
|
Constraint::Percentage(23),
|
||||||
|
]);
|
||||||
|
|
||||||
|
f.render_widget(indexers_table, area);
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES};
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::ui::sonarr_ui::indexers::draw_indexers;
|
||||||
|
use crate::ui::styles::ManagarrStyle;
|
||||||
|
use crate::ui::utils::{borderless_block, get_width_from_percentage, title_block};
|
||||||
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
|
use crate::ui::widgets::popup::Size;
|
||||||
|
use crate::ui::{draw_popup_over, DrawUi};
|
||||||
|
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||||
|
use ratatui::widgets::{Cell, Row};
|
||||||
|
use ratatui::Frame;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "test_all_indexers_ui_tests.rs"]
|
||||||
|
mod test_all_indexers_ui_tests;
|
||||||
|
|
||||||
|
pub(super) struct TestAllIndexersUi;
|
||||||
|
|
||||||
|
impl DrawUi for TestAllIndexersUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
if let Route::Sonarr(active_sonarr_block, _) = route {
|
||||||
|
return active_sonarr_block == ActiveSonarrBlock::TestAllIndexers;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
draw_popup_over(
|
||||||
|
f,
|
||||||
|
app,
|
||||||
|
area,
|
||||||
|
draw_indexers,
|
||||||
|
draw_test_all_indexers_test_results,
|
||||||
|
Size::Large,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let current_selection =
|
||||||
|
if let Some(test_all_results) = app.data.sonarr_data.indexer_test_all_results.as_ref() {
|
||||||
|
test_all_results.current_selection().clone()
|
||||||
|
} else {
|
||||||
|
IndexerTestResultModalItem::default()
|
||||||
|
};
|
||||||
|
f.render_widget(title_block("Test All Indexers"), area);
|
||||||
|
let help_footer = format!(
|
||||||
|
"<↑↓> scroll | {}",
|
||||||
|
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
||||||
|
);
|
||||||
|
let test_results_row_mapping = |result: &IndexerTestResultModalItem| {
|
||||||
|
result.validation_failures.scroll_left_or_reset(
|
||||||
|
get_width_from_percentage(area, 86),
|
||||||
|
*result == current_selection,
|
||||||
|
app.tick_count % app.ticks_until_scroll == 0,
|
||||||
|
);
|
||||||
|
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
||||||
|
let row = Row::new(vec![
|
||||||
|
Cell::from(result.name.to_owned()),
|
||||||
|
Cell::from(pass_fail.to_owned()),
|
||||||
|
Cell::from(result.validation_failures.to_string()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if result.is_valid {
|
||||||
|
row.success()
|
||||||
|
} else {
|
||||||
|
row.failure()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let indexers_test_results_table = ManagarrTable::new(
|
||||||
|
app.data.sonarr_data.indexer_test_all_results.as_mut(),
|
||||||
|
test_results_row_mapping,
|
||||||
|
)
|
||||||
|
.block(borderless_block())
|
||||||
|
.loading(app.is_loading)
|
||||||
|
.footer(Some(help_footer))
|
||||||
|
.footer_alignment(Alignment::Center)
|
||||||
|
.margin(1)
|
||||||
|
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
Constraint::Percentage(10),
|
||||||
|
Constraint::Percentage(70),
|
||||||
|
]);
|
||||||
|
|
||||||
|
f.render_widget(indexers_test_results_table, area);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
|
use crate::ui::sonarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi;
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_test_all_indexers_ui_accepts() {
|
||||||
|
ActiveSonarrBlock::iter().for_each(|active_sonarr_block| {
|
||||||
|
if active_sonarr_block == ActiveSonarrBlock::TestAllIndexers {
|
||||||
|
assert!(TestAllIndexersUi::accepts(active_sonarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!TestAllIndexersUi::accepts(active_sonarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,10 @@ impl DrawUi for DeleteSeriesUi {
|
|||||||
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_library(f, app, area);
|
draw_library(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,10 @@ impl DrawUi for LibraryUi {
|
|||||||
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_library(f, app, area);
|
draw_library(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use blocklist::BlocklistUi;
|
|||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
use downloads::DownloadsUi;
|
use downloads::DownloadsUi;
|
||||||
use history::HistoryUi;
|
use history::HistoryUi;
|
||||||
|
use indexers::IndexersUi;
|
||||||
use library::LibraryUi;
|
use library::LibraryUi;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Constraint, Layout, Rect},
|
layout::{Constraint, Layout, Rect},
|
||||||
@@ -39,6 +40,7 @@ use super::{
|
|||||||
mod blocklist;
|
mod blocklist;
|
||||||
mod downloads;
|
mod downloads;
|
||||||
mod history;
|
mod history;
|
||||||
|
mod indexers;
|
||||||
mod library;
|
mod library;
|
||||||
mod root_folders;
|
mod root_folders;
|
||||||
|
|
||||||
@@ -63,6 +65,7 @@ impl DrawUi for SonarrUi {
|
|||||||
_ if BlocklistUi::accepts(route) => BlocklistUi::draw(f, app, content_area),
|
_ if BlocklistUi::accepts(route) => BlocklistUi::draw(f, app, content_area),
|
||||||
_ if HistoryUi::accepts(route) => HistoryUi::draw(f, app, content_area),
|
_ if HistoryUi::accepts(route) => HistoryUi::draw(f, app, content_area),
|
||||||
_ if RootFoldersUi::accepts(route) => RootFoldersUi::draw(f, app, content_area),
|
_ if RootFoldersUi::accepts(route) => RootFoldersUi::draw(f, app, content_area),
|
||||||
|
_ if IndexersUi::accepts(route) => IndexersUi::draw(f, app, content_area),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,10 @@ impl DrawUi for RootFoldersUi {
|
|||||||
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
.yes_no_value(app.data.sonarr_data.prompt_confirm);
|
||||||
|
|
||||||
draw_root_folders(f, app, area);
|
draw_root_folders(f, app, area);
|
||||||
f.render_widget(Popup::new(confirmation_prompt).size(Size::Prompt), f.area());
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ mod popup_tests;
|
|||||||
|
|
||||||
pub enum Size {
|
pub enum Size {
|
||||||
SmallPrompt,
|
SmallPrompt,
|
||||||
Prompt,
|
MediumPrompt,
|
||||||
LargePrompt,
|
LargePrompt,
|
||||||
|
WideLargePrompt,
|
||||||
Message,
|
Message,
|
||||||
NarrowMessage,
|
NarrowMessage,
|
||||||
LargeMessage,
|
LargeMessage,
|
||||||
@@ -28,8 +29,9 @@ impl Size {
|
|||||||
pub fn to_percent(&self) -> (u16, u16) {
|
pub fn to_percent(&self) -> (u16, u16) {
|
||||||
match self {
|
match self {
|
||||||
Size::SmallPrompt => (20, 20),
|
Size::SmallPrompt => (20, 20),
|
||||||
Size::Prompt => (37, 37),
|
Size::MediumPrompt => (37, 37),
|
||||||
Size::LargePrompt => (70, 50),
|
Size::LargePrompt => (45, 45),
|
||||||
|
Size::WideLargePrompt => (70, 50),
|
||||||
Size::Message => (25, 8),
|
Size::Message => (25, 8),
|
||||||
Size::NarrowMessage => (50, 20),
|
Size::NarrowMessage => (50, 20),
|
||||||
Size::LargeMessage => (25, 25),
|
Size::LargeMessage => (25, 25),
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_dimensions_to_percent() {
|
fn test_dimensions_to_percent() {
|
||||||
assert_eq!(Size::SmallPrompt.to_percent(), (20, 20));
|
assert_eq!(Size::SmallPrompt.to_percent(), (20, 20));
|
||||||
assert_eq!(Size::Prompt.to_percent(), (37, 37));
|
assert_eq!(Size::MediumPrompt.to_percent(), (37, 37));
|
||||||
assert_eq!(Size::LargePrompt.to_percent(), (70, 50));
|
assert_eq!(Size::LargePrompt.to_percent(), (45, 45));
|
||||||
|
assert_eq!(Size::WideLargePrompt.to_percent(), (70, 50));
|
||||||
assert_eq!(Size::Message.to_percent(), (25, 8));
|
assert_eq!(Size::Message.to_percent(), (25, 8));
|
||||||
assert_eq!(Size::NarrowMessage.to_percent(), (50, 20));
|
assert_eq!(Size::NarrowMessage.to_percent(), (50, 20));
|
||||||
assert_eq!(Size::LargeMessage.to_percent(), (25, 25));
|
assert_eq!(Size::LargeMessage.to_percent(), (25, 25));
|
||||||
|
|||||||
Reference in New Issue
Block a user