Upgraded to Ratatui 0.26 and did a partial refactor to clean up the UI module. Created the ManagarrStyle trait to make it cleaner to use consistent styles across the project. Still need to update the layouts to be consistent with the newer and nicer format. That's a tomorrow problem
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::text::Text;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::{Line, Text};
|
||||
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||
use ratatui::Frame;
|
||||
|
||||
@@ -12,10 +13,10 @@ use crate::models::servarr_data::radarr::radarr_data::{
|
||||
};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::collections::draw_collections;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, layout_block_top_border_with_title,
|
||||
line_info_primary, style_default, style_help, style_primary, title_block, title_style,
|
||||
vertical_chunks_with_margin,
|
||||
borderless_block, get_width_from_percentage, layout_block_top_border_with_title, title_block,
|
||||
title_style, vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::{draw_large_popup_over, draw_small_popup_over, draw_table, DrawUi, TableProps};
|
||||
use crate::utils::convert_runtime;
|
||||
@@ -98,11 +99,13 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, content_are
|
||||
.current_selection()
|
||||
.clone()
|
||||
};
|
||||
let mut help_text = Text::from(format!(
|
||||
"<↑↓> scroll table | {}",
|
||||
build_context_clue_string(&COLLECTION_DETAILS_CONTEXT_CLUES)
|
||||
));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text = Text::from(
|
||||
format!(
|
||||
"<↑↓> scroll table | {}",
|
||||
build_context_clue_string(&COLLECTION_DETAILS_CONTEXT_CLUES)
|
||||
)
|
||||
.help(),
|
||||
);
|
||||
let monitored = if collection_selection.monitored {
|
||||
"Yes"
|
||||
} else {
|
||||
@@ -116,24 +119,35 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, content_are
|
||||
let minimum_availability = collection_selection.minimum_availability.to_display_str();
|
||||
|
||||
let collection_description = Text::from(vec![
|
||||
line_info_primary(
|
||||
"Overview: ".to_owned(),
|
||||
collection_selection.overview.clone().unwrap_or_default(),
|
||||
),
|
||||
line_info_primary(
|
||||
"Root Folder Path: ".to_owned(),
|
||||
Line::from(vec![
|
||||
"Overview ".primary().bold(),
|
||||
collection_selection
|
||||
.overview
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.default(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"Root Folder Path: ".primary().bold(),
|
||||
collection_selection
|
||||
.root_folder_path
|
||||
.clone()
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
line_info_primary("Quality Profile: ".to_owned(), quality_profile),
|
||||
line_info_primary(
|
||||
"Minimum Availability: ".to_owned(),
|
||||
minimum_availability.to_owned(),
|
||||
),
|
||||
line_info_primary("Monitored: ".to_owned(), monitored.to_owned()),
|
||||
line_info_primary("Search on Add: ".to_owned(), search_on_add.to_owned()),
|
||||
.unwrap_or_default()
|
||||
.default(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"Quality Profile: ".primary().bold(),
|
||||
quality_profile.default(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"Minimum Availability: ".primary().bold(),
|
||||
minimum_availability.default(),
|
||||
]),
|
||||
Line::from(vec!["Monitored: ".primary().bold(), monitored.default()]),
|
||||
Line::from(vec![
|
||||
"Search on Add: ".primary().bold(),
|
||||
search_on_add.default(),
|
||||
]),
|
||||
]);
|
||||
|
||||
let description_paragraph = Paragraph::new(collection_description)
|
||||
@@ -230,7 +244,7 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, content_are
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -246,7 +260,7 @@ fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
content_area,
|
||||
1,
|
||||
);
|
||||
let mut overview = Text::from(
|
||||
let overview = Text::from(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
@@ -254,10 +268,9 @@ fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
.current_selection()
|
||||
.clone()
|
||||
.overview,
|
||||
);
|
||||
overview.patch_style(style_default());
|
||||
let mut help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES));
|
||||
help_text.patch_style(style_help());
|
||||
)
|
||||
.default();
|
||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
||||
|
||||
let paragraph = Paragraph::new(overview)
|
||||
.block(borderless_block())
|
||||
|
||||
@@ -10,11 +10,12 @@ use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, COLLEC
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::collections::collection_details_ui::CollectionDetailsUi;
|
||||
use crate::ui::radarr_ui::collections::edit_collection_ui::EditCollectionUi;
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border, style_primary};
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::{
|
||||
draw_error_message_popup, draw_input_box_popup, draw_popup_over, draw_prompt_box,
|
||||
draw_prompt_popup_over, draw_table, DrawUi, TableProps,
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
|
||||
mod collection_details_ui;
|
||||
#[cfg(test)]
|
||||
@@ -167,8 +168,7 @@ pub(super) fn draw_collections(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
||||
),
|
||||
Cell::from(search_on_add),
|
||||
Cell::from(monitored),
|
||||
])
|
||||
.style(style_primary())
|
||||
]).primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -6,8 +6,9 @@ use crate::app::App;
|
||||
use crate::models::radarr_models::DownloadRecord;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS};
|
||||
use crate::models::{HorizontallyScrollableText, Route};
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border, style_primary};
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -119,8 +120,7 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
),
|
||||
Cell::from(indexer.to_owned()),
|
||||
Cell::from(download_client.to_owned()),
|
||||
])
|
||||
.style(style_primary())
|
||||
]).primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::ui::{
|
||||
draw_button, draw_checkbox_with_label, draw_popup_over, draw_text_box_with_label, loading,
|
||||
DrawUi, LabeledTextBoxProps,
|
||||
};
|
||||
use ratatui::layout::{Constraint, Rect};
|
||||
use ratatui::layout::{Constraint, Layout, Rect};
|
||||
use ratatui::Frame;
|
||||
use std::iter;
|
||||
|
||||
@@ -66,16 +66,14 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
1,
|
||||
);
|
||||
|
||||
let left_chunks = vertical_chunks(
|
||||
vec![
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(0),
|
||||
],
|
||||
split_chunks[0],
|
||||
);
|
||||
let [name, rss, auto_search, interactive_search, _] = Layout::vertical([
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(0),
|
||||
])
|
||||
.areas(split_chunks[0]);
|
||||
let right_chunks = vertical_chunks(
|
||||
vec![
|
||||
Constraint::Length(3),
|
||||
@@ -91,7 +89,7 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
draw_text_box_with_label(
|
||||
f,
|
||||
LabeledTextBoxProps {
|
||||
area: left_chunks[0],
|
||||
area: name,
|
||||
label: "Name",
|
||||
text: &edit_indexer_modal.name.text,
|
||||
offset: *edit_indexer_modal.name.offset.borrow(),
|
||||
@@ -166,14 +164,14 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
|
||||
draw_checkbox_with_label(
|
||||
f,
|
||||
left_chunks[1],
|
||||
rss,
|
||||
"Enable RSS",
|
||||
edit_indexer_modal.enable_rss.unwrap_or_default(),
|
||||
selected_block == &ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||
);
|
||||
draw_checkbox_with_label(
|
||||
f,
|
||||
left_chunks[2],
|
||||
auto_search,
|
||||
"Enable Automatic Search",
|
||||
edit_indexer_modal
|
||||
.enable_automatic_search
|
||||
@@ -182,7 +180,7 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
);
|
||||
draw_checkbox_with_label(
|
||||
f,
|
||||
left_chunks[3],
|
||||
interactive_search,
|
||||
"Enable Interactive Search",
|
||||
edit_indexer_modal
|
||||
.enable_interactive_search
|
||||
|
||||
@@ -10,7 +10,8 @@ use crate::models::Route;
|
||||
use crate::ui::radarr_ui::indexers::edit_indexer_ui::EditIndexerUi;
|
||||
use crate::ui::radarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi;
|
||||
use crate::ui::radarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi;
|
||||
use crate::ui::utils::{layout_block_top_border, style_failure, style_primary, style_success};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
||||
|
||||
mod edit_indexer_ui;
|
||||
@@ -103,24 +104,15 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
} = indexer;
|
||||
let bool_to_text = |flag: bool| {
|
||||
if flag {
|
||||
return ("Enabled", style_success());
|
||||
return Text::from("Enabled").success();
|
||||
}
|
||||
|
||||
("Disabled", style_failure())
|
||||
Text::from("Disabled").failure()
|
||||
};
|
||||
|
||||
let (rss_text, rss_style) = bool_to_text(*enable_rss);
|
||||
let mut rss = Text::from(rss_text);
|
||||
rss.patch_style(rss_style);
|
||||
|
||||
let (auto_search_text, auto_search_style) = bool_to_text(*enable_automatic_search);
|
||||
let mut automatic_search = Text::from(auto_search_text);
|
||||
automatic_search.patch_style(auto_search_style);
|
||||
|
||||
let (interactive_search_text, interactive_search_style) =
|
||||
bool_to_text(*enable_interactive_search);
|
||||
let mut interactive_search = Text::from(interactive_search_text);
|
||||
interactive_search.patch_style(interactive_search_style);
|
||||
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| {
|
||||
@@ -143,7 +135,7 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(priority.to_string()),
|
||||
Cell::from(tags),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -4,9 +4,8 @@ use crate::models::servarr_data::radarr::modals::IndexerTestResultModalItem;
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::indexers::draw_indexers;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, style_failure, style_success, title_block,
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{borderless_block, get_width_from_percentage, title_block};
|
||||
use crate::ui::{
|
||||
draw_help_and_get_content_rect, draw_large_popup_over, draw_table, DrawUi, TableProps,
|
||||
};
|
||||
@@ -76,18 +75,17 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
||||
let row_style = if result.is_valid {
|
||||
style_success()
|
||||
} else {
|
||||
style_failure()
|
||||
};
|
||||
|
||||
Row::new(vec![
|
||||
let row = Row::new(vec![
|
||||
Cell::from(result.name.to_owned()),
|
||||
Cell::from(pass_fail.to_owned()),
|
||||
Cell::from(result.validation_failures.to_string()),
|
||||
])
|
||||
.style(row_style)
|
||||
]);
|
||||
|
||||
if result.is_valid {
|
||||
row.success()
|
||||
} else {
|
||||
row.failure()
|
||||
}
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -11,10 +11,10 @@ use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ADD_MO
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::collections::{draw_collection_details, draw_collections};
|
||||
use crate::ui::radarr_ui::library::draw_library;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, horizontal_chunks, layout_block,
|
||||
layout_paragraph_borderless, style_help, style_primary, title_block_centered,
|
||||
vertical_chunks_with_margin,
|
||||
layout_paragraph_borderless, title_block_centered, vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::{
|
||||
draw_button, draw_drop_down_menu_button, draw_drop_down_popup, draw_error_popup,
|
||||
@@ -148,8 +148,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
);
|
||||
f.render_widget(layout_block(), chunks[1]);
|
||||
|
||||
let mut help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
@@ -167,10 +166,8 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
| ActiveRadarrBlock::AddMovieSelectRootFolder
|
||||
| ActiveRadarrBlock::AddMovieAlreadyInLibrary
|
||||
| ActiveRadarrBlock::AddMovieTagsInput => {
|
||||
let mut help_text = Text::from(build_context_clue_string(
|
||||
&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES,
|
||||
));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text =
|
||||
Text::from(build_context_clue_string(&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES).help());
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
@@ -259,7 +256,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
is_loading,
|
||||
true,
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::app::App;
|
||||
use crate::models::radarr_models::Movie;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, LIBRARY_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::determine_row_style;
|
||||
use crate::ui::radarr_ui::decorate_with_row_style;
|
||||
use crate::ui::radarr_ui::library::add_movie_ui::AddMovieUi;
|
||||
use crate::ui::radarr_ui::library::delete_movie_ui::DeleteMovieUi;
|
||||
use crate::ui::radarr_ui::library::edit_movie_ui::EditMovieUi;
|
||||
@@ -186,19 +186,22 @@ pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(movie.studio.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(certification),
|
||||
Cell::from(movie.original_language.name.to_owned()),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(quality_profile),
|
||||
Cell::from(monitored.to_owned()),
|
||||
Cell::from(tags),
|
||||
])
|
||||
.style(determine_row_style(downloads_vec, movie))
|
||||
decorate_with_row_style(
|
||||
downloads_vec,
|
||||
movie,
|
||||
Row::new(vec![
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(movie.studio.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(certification),
|
||||
Cell::from(movie.original_language.name.to_owned()),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(quality_profile),
|
||||
Cell::from(monitored.to_owned()),
|
||||
Cell::from(tags),
|
||||
]),
|
||||
)
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::iter;
|
||||
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::style::{Modifier, Style};
|
||||
use ratatui::style::{Style, Stylize};
|
||||
use ratatui::text::{Line, Span, Text};
|
||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row, Wrap};
|
||||
use ratatui::Frame;
|
||||
@@ -11,10 +11,10 @@ use crate::models::radarr_models::{Credit, MovieHistoryItem, Release, ReleaseFie
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::library::draw_library;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, layout_block_bottom_border, layout_block_top_border,
|
||||
line_info_default, style_awaiting_import, style_bold, style_failure, style_primary,
|
||||
style_success, style_warning, vertical_chunks,
|
||||
vertical_chunks,
|
||||
};
|
||||
use crate::ui::{
|
||||
draw_drop_down_popup, draw_large_popup_over, draw_prompt_box, draw_prompt_box_with_content,
|
||||
@@ -162,19 +162,13 @@ fn draw_file_info(f: &mut Frame<'_>, app: &App<'_>, content_area: Rect) {
|
||||
],
|
||||
content_area,
|
||||
);
|
||||
let mut file_details_title = Text::from("File Details");
|
||||
let mut audio_details_title = Text::from("Audio Details");
|
||||
let mut video_details_title = Text::from("Video Details");
|
||||
file_details_title.patch_style(style_bold());
|
||||
audio_details_title.patch_style(style_bold());
|
||||
video_details_title.patch_style(style_bold());
|
||||
|
||||
let file_details_title_paragraph =
|
||||
Paragraph::new(file_details_title).block(layout_block_top_border());
|
||||
Paragraph::new("File Details".bold()).block(layout_block_top_border());
|
||||
let audio_details_title_paragraph =
|
||||
Paragraph::new(audio_details_title).block(borderless_block());
|
||||
Paragraph::new("Audio Details".bold()).block(borderless_block());
|
||||
let video_details_title_paragraph =
|
||||
Paragraph::new(video_details_title).block(borderless_block());
|
||||
Paragraph::new("Video Details".bold()).block(borderless_block());
|
||||
|
||||
let file_details = Text::from(file_info);
|
||||
let audio_details = Text::from(audio_details);
|
||||
@@ -214,19 +208,22 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, content_area: Rect) {
|
||||
.unwrap()
|
||||
.split(": ")
|
||||
.collect::<Vec<&str>>()[1];
|
||||
let mut text = Text::from(
|
||||
let text = Text::from(
|
||||
movie_details
|
||||
.items
|
||||
.iter()
|
||||
.map(|line| {
|
||||
let split = line.split(':').collect::<Vec<&str>>();
|
||||
let title = format!("{}:", split[0]);
|
||||
let style = style_from_download_status(download_status);
|
||||
|
||||
line_info_default(title, split[1..].join(":"))
|
||||
Line::from(vec![
|
||||
title.bold().style(style),
|
||||
Span::styled(split[1..].join(":"), style),
|
||||
])
|
||||
})
|
||||
.collect::<Vec<Line<'_>>>(),
|
||||
);
|
||||
text.patch_style(determine_style_from_download_status(download_status));
|
||||
|
||||
let paragraph = Paragraph::new(text)
|
||||
.block(block)
|
||||
@@ -304,7 +301,7 @@ fn draw_movie_history(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
Cell::from(quality.quality.name.to_owned()),
|
||||
Cell::from(date.to_string()),
|
||||
])
|
||||
.style(style_success())
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -347,7 +344,7 @@ fn draw_movie_cast(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect) {
|
||||
Cell::from(person_name.to_owned()),
|
||||
Cell::from(character.clone().unwrap_or_default()),
|
||||
])
|
||||
.style(style_success())
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -391,7 +388,7 @@ fn draw_movie_crew(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect) {
|
||||
Cell::from(job.clone().unwrap_or_default()),
|
||||
Cell::from(department.clone().unwrap_or_default()),
|
||||
])
|
||||
.style(style_success())
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -505,14 +502,16 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
let size = convert_to_gb(*size);
|
||||
let rejected_str = if *rejected { "⛔" } else { "" };
|
||||
let peers = if seeders.is_none() || leechers.is_none() {
|
||||
Text::default()
|
||||
Text::from("")
|
||||
} else {
|
||||
let seeders = seeders.clone().unwrap().as_u64().unwrap();
|
||||
let leechers = leechers.clone().unwrap().as_u64().unwrap();
|
||||
let mut text = Text::from(format!("{seeders} / {leechers}"));
|
||||
text.patch_style(determine_peer_style(seeders, leechers));
|
||||
|
||||
text
|
||||
decorate_peer_style(
|
||||
seeders,
|
||||
leechers,
|
||||
Text::from(format!("{seeders} / {leechers}")),
|
||||
)
|
||||
};
|
||||
|
||||
let language = if languages.is_some() {
|
||||
@@ -533,7 +532,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
Cell::from(language),
|
||||
Cell::from(quality),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading || is_empty,
|
||||
true,
|
||||
@@ -567,16 +566,13 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>, promp
|
||||
};
|
||||
|
||||
if current_selection.rejected {
|
||||
let mut lines_vec = vec![Line::from(vec![Span::styled(
|
||||
"Rejection reasons: ",
|
||||
style_primary().add_modifier(Modifier::BOLD),
|
||||
)])];
|
||||
let mut lines_vec = vec![Line::from("Rejection reasons: ".primary().bold())];
|
||||
let mut rejections_spans = current_selection
|
||||
.rejections
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|item| Line::from(vec![Span::styled(format!("• {item}"), style_primary())]))
|
||||
.map(|item| Line::from(format!("• {item}").primary().bold()))
|
||||
.collect::<Vec<Line<'_>>>();
|
||||
lines_vec.append(&mut rejections_spans);
|
||||
|
||||
@@ -604,22 +600,22 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>, promp
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_style_from_download_status(download_status: &str) -> Style {
|
||||
fn style_from_download_status(download_status: &str) -> Style {
|
||||
match download_status {
|
||||
"Downloaded" => style_success(),
|
||||
"Awaiting Import" => style_awaiting_import(),
|
||||
"Downloading" => style_warning(),
|
||||
"Missing" => style_failure(),
|
||||
_ => style_success(),
|
||||
"Downloaded" => Style::new().success(),
|
||||
"Awaiting Import" => Style::new().awaiting_import(),
|
||||
"Downloading" => Style::new().warning(),
|
||||
"Missing" => Style::new().failure(),
|
||||
_ => Style::new().success(),
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_peer_style(seeders: u64, leechers: u64) -> Style {
|
||||
fn decorate_peer_style(seeders: u64, leechers: u64, text: Text<'_>) -> Text<'_> {
|
||||
if seeders == 0 {
|
||||
style_failure()
|
||||
text.failure()
|
||||
} else if seeders < leechers {
|
||||
style_warning()
|
||||
text.warning()
|
||||
} else {
|
||||
style_success()
|
||||
text.success()
|
||||
}
|
||||
}
|
||||
|
||||
+30
-30
@@ -1,10 +1,10 @@
|
||||
use std::iter;
|
||||
|
||||
use chrono::{Duration, Utc};
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::style::{Color, Style};
|
||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||
use ratatui::prelude::Stylize;
|
||||
use ratatui::text::Text;
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::{Paragraph, Row};
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::App;
|
||||
@@ -20,10 +20,10 @@ use crate::ui::radarr_ui::indexers::IndexersUi;
|
||||
use crate::ui::radarr_ui::library::LibraryUi;
|
||||
use crate::ui::radarr_ui::root_folders::RootFoldersUi;
|
||||
use crate::ui::radarr_ui::system::SystemUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, horizontal_chunks, layout_block, line_gauge_with_label, line_gauge_with_title,
|
||||
style_awaiting_import, style_bold, style_default, style_failure, style_success,
|
||||
style_unmonitored, style_warning, title_block, vertical_chunks_with_margin,
|
||||
borderless_block, layout_block, line_gauge_with_label, line_gauge_with_title, title_block,
|
||||
vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::utils::convert_to_gb;
|
||||
@@ -63,16 +63,14 @@ impl DrawUi for RadarrUi {
|
||||
}
|
||||
|
||||
fn draw_context_row(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
let chunks = horizontal_chunks(vec![Constraint::Min(0), Constraint::Length(20)], area);
|
||||
let [main, logo] = Layout::horizontal([Constraint::Min(0), Constraint::Length(20)]).areas(area);
|
||||
|
||||
let context_chunks = horizontal_chunks(
|
||||
vec![Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)],
|
||||
chunks[0],
|
||||
);
|
||||
let [stats, downloads] =
|
||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(main);
|
||||
|
||||
draw_stats_context(f, app, context_chunks[0]);
|
||||
draw_downloads_context(f, app, context_chunks[1]);
|
||||
draw_radarr_logo(f, chunks[1]);
|
||||
draw_stats_context(f, app, stats);
|
||||
draw_downloads_context(f, app, downloads);
|
||||
draw_radarr_logo(f, logo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +105,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
app.data.radarr_data.version
|
||||
)))
|
||||
.block(borderless_block())
|
||||
.style(style_bold());
|
||||
.bold();
|
||||
|
||||
let uptime = Utc::now() - start_time.to_owned();
|
||||
let days = uptime.num_days();
|
||||
@@ -122,12 +120,10 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
width = 2
|
||||
)))
|
||||
.block(borderless_block())
|
||||
.style(style_bold());
|
||||
.bold();
|
||||
|
||||
let storage =
|
||||
Paragraph::new(Text::from("Storage:")).block(borderless_block().style(style_bold()));
|
||||
let folders =
|
||||
Paragraph::new(Text::from("Root Folders:")).block(borderless_block().style(style_bold()));
|
||||
let storage = Paragraph::new(Text::from("Storage:")).block(borderless_block().bold());
|
||||
let folders = Paragraph::new(Text::from("Root Folders:")).block(borderless_block().bold());
|
||||
|
||||
f.render_widget(version_paragraph, chunks[0]);
|
||||
f.render_widget(uptime_paragraph, chunks[1]);
|
||||
@@ -159,7 +155,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
let space: f64 = convert_to_gb(*free_space);
|
||||
let root_folder_space = Paragraph::new(format!("{path}: {space:.2} GB free"))
|
||||
.block(borderless_block())
|
||||
.style(style_default());
|
||||
.default();
|
||||
|
||||
f.render_widget(root_folder_space, chunks[i + disk_space_vec.len() + 4])
|
||||
}
|
||||
@@ -198,36 +194,40 @@ fn draw_downloads_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_row_style(downloads_vec: &[DownloadRecord], movie: &Movie) -> Style {
|
||||
fn decorate_with_row_style<'a>(
|
||||
downloads_vec: &[DownloadRecord],
|
||||
movie: &Movie,
|
||||
row: Row<'a>,
|
||||
) -> Row<'a> {
|
||||
if !movie.has_file {
|
||||
if let Some(download) = downloads_vec
|
||||
.iter()
|
||||
.find(|&download| download.movie_id == movie.id)
|
||||
{
|
||||
if download.status == "downloading" {
|
||||
return style_warning();
|
||||
return row.warning();
|
||||
}
|
||||
|
||||
if download.status == "completed" {
|
||||
return style_awaiting_import();
|
||||
return row.awaiting_import();
|
||||
}
|
||||
}
|
||||
|
||||
return style_failure();
|
||||
return row.failure();
|
||||
}
|
||||
|
||||
if !movie.monitored {
|
||||
style_unmonitored()
|
||||
row.unmonitored()
|
||||
} else {
|
||||
style_success()
|
||||
row.success()
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_radarr_logo(f: &mut Frame<'_>, area: Rect) {
|
||||
let mut logo_text = Text::from(RADARR_LOGO);
|
||||
logo_text.patch_style(Style::default().fg(Color::LightYellow));
|
||||
let logo_text = Text::from(RADARR_LOGO);
|
||||
let logo = Paragraph::new(logo_text)
|
||||
.block(layout_block())
|
||||
.light_yellow()
|
||||
.block(layout_block().default())
|
||||
.alignment(Alignment::Center);
|
||||
f.render_widget(logo, area);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
use crate::ui::utils::{style_default, style_failure, style_secondary};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::style::{Style, Stylize};
|
||||
use ratatui::widgets::ListItem;
|
||||
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "radarr_ui_utils_tests.rs"]
|
||||
mod radarr_ui_utils_tests;
|
||||
|
||||
pub(super) fn determine_log_style_by_level(level: &str) -> Style {
|
||||
pub(super) fn style_log_list_item(list_item: ListItem<'_>, level: String) -> ListItem<'_> {
|
||||
match level.to_lowercase().as_str() {
|
||||
"trace" => Style::default().fg(Color::Gray),
|
||||
"debug" => Style::default().fg(Color::Blue),
|
||||
"info" => style_default(),
|
||||
"warn" => style_secondary(),
|
||||
"error" => style_failure(),
|
||||
"fatal" => style_failure().add_modifier(Modifier::BOLD),
|
||||
_ => style_default(),
|
||||
"trace" => list_item.gray(),
|
||||
"debug" => list_item.blue(),
|
||||
"info" => list_item.style(Style::new().default()),
|
||||
"warn" => list_item.style(Style::new().secondary()),
|
||||
"error" => list_item.style(Style::new().failure()),
|
||||
"fatal" => list_item.style(Style::new().failure().bold()),
|
||||
_ => list_item.style(Style::new().default()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,32 +2,50 @@
|
||||
mod tests {
|
||||
use super::super::*;
|
||||
use pretty_assertions::assert_str_eq;
|
||||
use ratatui::prelude::Text;
|
||||
use ratatui::text::Span;
|
||||
|
||||
#[test]
|
||||
fn test_determine_log_style_by_level() {
|
||||
let list_item = ListItem::new(Text::from(Span::raw("test")));
|
||||
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("trace"),
|
||||
Style::default().fg(Color::Gray)
|
||||
style_log_list_item(list_item.clone(), "trace".to_string()),
|
||||
list_item.clone().gray()
|
||||
);
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("debug"),
|
||||
Style::default().fg(Color::Blue)
|
||||
style_log_list_item(list_item.clone(), "debug".to_string()),
|
||||
list_item.clone().blue()
|
||||
);
|
||||
assert_eq!(determine_log_style_by_level("info"), style_default());
|
||||
assert_eq!(determine_log_style_by_level("warn"), style_secondary());
|
||||
assert_eq!(determine_log_style_by_level("error"), style_failure());
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("fatal"),
|
||||
style_failure().add_modifier(Modifier::BOLD)
|
||||
style_log_list_item(list_item.clone(), "info".to_string()),
|
||||
list_item.clone().style(Style::new().default())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "warn".to_string()),
|
||||
list_item.clone().style(Style::new().secondary())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "error".to_string()),
|
||||
list_item.clone().style(Style::new().failure())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "fatal".to_string()),
|
||||
list_item.clone().style(Style::new().failure().bold())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "".to_string()),
|
||||
list_item.style(Style::new().default())
|
||||
);
|
||||
assert_eq!(determine_log_style_by_level(""), style_default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_determine_log_style_by_level_case_insensitive() {
|
||||
let list_item = ListItem::new(Text::from(Span::raw("test")));
|
||||
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("TrAcE"),
|
||||
Style::default().fg(Color::Gray)
|
||||
style_log_list_item(list_item.clone(), "TrAcE".to_string()),
|
||||
list_item.gray()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@ use crate::app::App;
|
||||
use crate::models::radarr_models::RootFolder;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::utils::{layout_block_top_border, style_primary};
|
||||
use crate::ui::utils::{layout_block_top_border};
|
||||
use crate::ui::{
|
||||
draw_input_box_popup, draw_popup_over, draw_prompt_box, draw_prompt_popup_over, draw_table,
|
||||
DrawUi, TableProps,
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -94,8 +95,7 @@ fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
.len()
|
||||
.to_string(),
|
||||
),
|
||||
])
|
||||
.style(style_primary())
|
||||
]).primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -13,11 +13,10 @@ use ratatui::{
|
||||
use crate::app::App;
|
||||
use crate::models::radarr_models::Task;
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::{
|
||||
convert_to_minutes_hours_days, determine_log_style_by_level,
|
||||
};
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::{convert_to_minutes_hours_days, style_log_list_item};
|
||||
use crate::ui::radarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||
use crate::ui::utils::{layout_block_top_border, style_help, style_primary};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::{draw_table, ListProps, TableProps};
|
||||
use crate::{
|
||||
models::Route,
|
||||
@@ -117,7 +116,7 @@ fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
false,
|
||||
@@ -177,7 +176,7 @@ pub(super) fn draw_queued_events(f: &mut Frame<'_>, app: &mut App<'_>, area: Rec
|
||||
Cell::from(started_string),
|
||||
Cell::from(duration.to_owned()),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
false,
|
||||
@@ -190,10 +189,9 @@ fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
area,
|
||||
|log| {
|
||||
let log_line = log.to_string();
|
||||
let level = log_line.split('|').collect::<Vec<&str>>()[1];
|
||||
let style = determine_log_style_by_level(level);
|
||||
let level = log_line.split('|').collect::<Vec<&str>>()[1].to_string();
|
||||
|
||||
ListItem::new(Text::from(Span::raw(log_line))).style(style)
|
||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||
},
|
||||
ListProps {
|
||||
content: &mut app.data.radarr_data.logs,
|
||||
@@ -206,16 +204,18 @@ fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
}
|
||||
|
||||
fn draw_help(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let mut help_text = Text::from(format!(
|
||||
" {}",
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help()
|
||||
.unwrap()
|
||||
));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text = Text::from(
|
||||
format!(
|
||||
" {}",
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help()
|
||||
.unwrap()
|
||||
)
|
||||
.help(),
|
||||
);
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(layout_block_top_border())
|
||||
.alignment(Alignment::Left);
|
||||
|
||||
@@ -8,12 +8,13 @@ use crate::app::radarr::radarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES;
|
||||
use crate::app::App;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::determine_log_style_by_level;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::style_log_list_item;
|
||||
use crate::ui::radarr_ui::system::{
|
||||
draw_queued_events, draw_system_ui_layout, extract_task_props, TASK_TABLE_CONSTRAINTS,
|
||||
TASK_TABLE_HEADERS,
|
||||
};
|
||||
use crate::ui::utils::{borderless_block, style_primary, title_block};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{borderless_block, title_block};
|
||||
use crate::ui::{
|
||||
draw_help_and_get_content_rect, draw_large_popup_over, draw_list_box, draw_medium_popup_over,
|
||||
draw_prompt_box, draw_prompt_popup_over, draw_table, loading, DrawUi, ListProps, TableProps,
|
||||
@@ -75,10 +76,9 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
area,
|
||||
|log| {
|
||||
let log_line = log.to_string();
|
||||
let level = log.text.split('|').collect::<Vec<&str>>()[1];
|
||||
let style = determine_log_style_by_level(level);
|
||||
let level = log.text.split('|').collect::<Vec<&str>>()[1].to_string();
|
||||
|
||||
ListItem::new(Text::from(Span::raw(log_line))).style(style)
|
||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||
},
|
||||
ListProps {
|
||||
content: &mut app.data.radarr_data.log_details,
|
||||
@@ -124,7 +124,7 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
Reference in New Issue
Block a user