Added support for adding movies directly from the collection details UI, refactored to support contexts for different routes, and fixed the horizontal scrolling bug with the get_width_with_margin function.

This commit is contained in:
2023-08-08 10:50:05 -06:00
parent 3007f76efe
commit 6866f90329
16 changed files with 646 additions and 208 deletions
+2 -2
View File
@@ -56,7 +56,7 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
draw_header_row(f, app, main_chunks[0]);
draw_context_row(f, app, main_chunks[1]);
if let Route::Radarr(_) = app.get_current_route() {
if let Route::Radarr(_, _) = app.get_current_route() {
radarr_ui::draw_radarr_ui(f, app, main_chunks[2])
}
}
@@ -173,7 +173,7 @@ pub fn draw_drop_down_popup<B: Backend>(
}
fn draw_context_row<B: Backend>(f: &mut Frame<'_, B>, app: &App, area: Rect) {
if let Route::Radarr(_) = app.get_current_route() {
if let Route::Radarr(_, _) = app.get_current_route() {
radarr_ui::draw_radarr_context_row(f, app, area)
}
}
+56 -25
View File
@@ -7,8 +7,9 @@ use tui::Frame;
use crate::app::radarr::ActiveRadarrBlock;
use crate::models::radarr_models::AddMovieSearchResult;
use crate::models::Route;
use crate::ui::radarr_ui::collection_details_ui::draw_collection_details;
use crate::ui::utils::{
borderless_block, get_width_with_margin, horizontal_chunks, layout_block,
borderless_block, get_width_from_percentage, horizontal_chunks, layout_block,
layout_paragraph_borderless, show_cursor, style_default, style_help, style_primary,
title_block_centered, vertical_chunks_with_margin,
};
@@ -24,7 +25,7 @@ pub(super) fn draw_add_movie_search_popup<B: Backend>(
app: &mut App,
area: Rect,
) {
if let Route::Radarr(active_radarr_block) = *app.get_current_route() {
if let Route::Radarr(active_radarr_block, context_option) = *app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::AddMovieSearchInput | ActiveRadarrBlock::AddMovieSearchResults => {
draw_add_movie_search(f, app, area);
@@ -33,7 +34,17 @@ pub(super) fn draw_add_movie_search_popup<B: Backend>(
| ActiveRadarrBlock::AddMovieSelectMonitor
| ActiveRadarrBlock::AddMovieSelectMinimumAvailability
| ActiveRadarrBlock::AddMovieSelectQualityProfile => {
draw_medium_popup_over(f, app, area, draw_add_movie_search, draw_confirmation_popup);
if context_option.is_some() {
draw_medium_popup_over(
f,
app,
area,
draw_collection_details,
draw_confirmation_popup,
);
} else {
draw_medium_popup_over(f, app, area, draw_add_movie_search, draw_confirmation_popup);
}
}
ActiveRadarrBlock::AddMovieAlreadyInLibrary => draw_error_popup_over(
f,
@@ -73,7 +84,7 @@ fn draw_add_movie_search<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area:
.style(style_default())
.block(title_block_centered("Add Movie"));
if let Route::Radarr(active_radarr_block) = *app.get_current_route() {
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::AddMovieSearchInput => {
show_cursor(f, chunks[0], block_content);
@@ -173,9 +184,10 @@ fn draw_add_movie_search<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area:
""
};
movie
.title
.scroll_or_reset(get_width_with_margin(area), *movie == current_selection);
movie.title.scroll_or_reset(
get_width_from_percentage(area, 27),
*movie == current_selection,
);
Row::new(vec![
Cell::from(in_library),
@@ -200,7 +212,7 @@ fn draw_add_movie_search<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area:
}
fn draw_confirmation_popup<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, prompt_area: Rect) {
if let Route::Radarr(active_radarr_block) = *app.get_current_route() {
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::AddMovieSelectMonitor => {
draw_drop_down_popup(
@@ -272,23 +284,42 @@ fn draw_select_quality_profile_popup<B: Backend>(
fn draw_confirmation_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, prompt_area: Rect) {
let title = "Add Movie";
let prompt = format!(
"{}:\n\n{}",
app
.data
.radarr_data
.add_searched_movies
.current_selection()
.title
.to_string()
.trim(),
app
.data
.radarr_data
.add_searched_movies
.current_selection()
.overview
);
let (movie_title, movie_overview) = if let Route::Radarr(_, Some(_)) = app.get_current_route() {
(
app
.data
.radarr_data
.collection_movies
.current_selection()
.title
.to_string(),
app
.data
.radarr_data
.collection_movies
.current_selection()
.overview
.clone(),
)
} else {
(
app
.data
.radarr_data
.add_searched_movies
.current_selection()
.title
.stationary_style(),
app
.data
.radarr_data
.add_searched_movies
.current_selection()
.overview
.clone(),
)
};
let prompt = format!("{}:\n\n{}", movie_title, movie_overview);
let yes_no_value = &app.data.radarr_data.prompt_confirm;
let selected_block = &app.data.radarr_data.selected_block;
let highlight_yes_no = *selected_block == ActiveRadarrBlock::AddMovieConfirmPrompt;
+42 -7
View File
@@ -6,10 +6,12 @@ use tui::Frame;
use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App;
use crate::models::radarr_models::CollectionMovie;
use crate::models::Route;
use crate::ui::utils::{
borderless_block, layout_block_top_border_with_title, spans_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,
spans_info_primary, style_default, style_help, style_primary, title_block, title_style,
vertical_chunks_with_margin,
};
use crate::ui::{draw_small_popup_over, draw_table, TableProps};
use crate::utils::convert_runtime;
@@ -19,7 +21,7 @@ pub(super) fn draw_collection_details_popup<B: Backend>(
app: &mut App,
content_area: Rect,
) {
if let Route::Radarr(active_radarr_block) = app.get_current_route() {
if let Route::Radarr(active_radarr_block, _) = app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::ViewMovieOverview => {
draw_small_popup_over(
@@ -36,7 +38,11 @@ pub(super) fn draw_collection_details_popup<B: Backend>(
}
}
fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, content_area: Rect) {
pub(super) fn draw_collection_details<B: Backend>(
f: &mut Frame<'_, B>,
app: &mut App,
content_area: Rect,
) {
let chunks = vertical_chunks_with_margin(
vec![
Constraint::Percentage(20),
@@ -62,7 +68,17 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
.get(&collection_selection.quality_profile_id.as_u64().unwrap())
.unwrap()
.to_owned();
let mut help_text = Text::from("<↑↓> scroll table | <enter> show overview | <esc> close");
let current_selection = if app.data.radarr_data.collection_movies.items.is_empty() {
CollectionMovie::default()
} else {
app
.data
.radarr_data
.collection_movies
.current_selection_clone()
};
let mut help_text =
Text::from("<↑↓> scroll table | <enter> show overview/add movie | <esc> close");
help_text.patch_style(style_help());
let collection_description = Text::from(vec![
@@ -103,6 +119,7 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
TableProps {
content: &mut app.data.radarr_data.collection_movies,
table_headers: vec![
"",
"Title",
"Year",
"Runtime",
@@ -111,16 +128,33 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
"Genres",
],
constraints: vec![
Constraint::Percentage(2),
Constraint::Percentage(20),
Constraint::Percentage(8),
Constraint::Percentage(10),
Constraint::Percentage(10),
Constraint::Percentage(18),
Constraint::Percentage(30),
Constraint::Percentage(28),
],
help: None,
},
|movie| {
let in_library = if app
.data
.radarr_data
.movies
.items
.iter()
.any(|mov| mov.tmdb_id == movie.tmdb_id)
{
""
} else {
""
};
movie.title.scroll_or_reset(
get_width_from_percentage(chunks[1], 20),
current_selection == *movie,
);
let (hours, minutes) = convert_runtime(movie.runtime.as_u64().unwrap());
let imdb_rating = movie
.ratings
@@ -150,7 +184,8 @@ fn draw_collection_details<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, cont
};
Row::new(vec![
Cell::from(movie.title.to_owned()),
Cell::from(in_library),
Cell::from(movie.title.to_string()),
Cell::from(movie.year.as_u64().unwrap().to_string()),
Cell::from(format!("{}h {}m", hours, minutes)),
Cell::from(imdb_rating),
+26 -14
View File
@@ -21,7 +21,7 @@ use crate::ui::radarr_ui::add_movie_ui::draw_add_movie_search_popup;
use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup;
use crate::ui::radarr_ui::movie_details_ui::draw_movie_info_popup;
use crate::ui::utils::{
borderless_block, get_width_with_margin, horizontal_chunks, layout_block,
borderless_block, get_width_from_percentage, horizontal_chunks, layout_block,
layout_block_top_border, line_gauge_with_label, line_gauge_with_title, show_cursor, style_bold,
style_default, style_failure, style_primary, style_success, style_warning, title_block,
title_block_centered, vertical_chunks_with_margin,
@@ -39,7 +39,7 @@ mod movie_details_ui;
pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let (content_rect, _) = draw_tabs(f, area, "Movies", &app.data.radarr_data.main_tabs);
if let Route::Radarr(active_radarr_block) = *app.get_current_route() {
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::Movies => draw_library(f, app, content_rect),
ActiveRadarrBlock::SearchMovie => {
@@ -71,13 +71,25 @@ pub(super) fn draw_radarr_ui<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, ar
_ if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) => {
draw_large_popup_over(f, app, content_rect, draw_library, draw_movie_info_popup)
}
_ if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over(
f,
app,
content_rect,
draw_library,
draw_add_movie_search_popup,
),
_ if ADD_MOVIE_BLOCKS.contains(&active_radarr_block) => {
if let Route::Radarr(_, Some(_)) = app.get_current_route() {
draw_large_popup_over(
f,
app,
content_rect,
draw_collections,
draw_add_movie_search_popup,
)
} else {
draw_large_popup_over(
f,
app,
content_rect,
draw_library,
draw_add_movie_search_popup,
)
}
}
_ if COLLECTION_DETAILS_BLOCKS.contains(&active_radarr_block) => draw_large_popup_over(
f,
app,
@@ -269,7 +281,7 @@ fn draw_search_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
vertical_chunks_with_margin(vec![Constraint::Length(3), Constraint::Min(0)], area, 1);
if !app.data.radarr_data.is_searching {
let error_msg = match app.get_current_route() {
Route::Radarr(active_radarr_block) => match active_radarr_block {
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
ActiveRadarrBlock::SearchMovie => "Movie not found!",
ActiveRadarrBlock::SearchCollection => "Collection not found!",
_ => "",
@@ -284,7 +296,7 @@ fn draw_search_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
f.render_widget(input, chunks[0]);
} else {
let (block_title, block_content) = match app.get_current_route() {
Route::Radarr(active_radarr_block) => match active_radarr_block {
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
_ if SEARCH_BLOCKS.contains(active_radarr_block) => {
("Search", app.data.radarr_data.search.as_str())
}
@@ -307,7 +319,7 @@ fn draw_filter_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
vertical_chunks_with_margin(vec![Constraint::Length(3), Constraint::Min(0)], area, 1);
if !app.data.radarr_data.is_filtering {
let error_msg = match app.get_current_route() {
Route::Radarr(active_radarr_block) => match active_radarr_block {
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
ActiveRadarrBlock::FilterMovies => "No movies found matching filter!",
ActiveRadarrBlock::FilterCollections => "No collections found matching filter!",
_ => "",
@@ -322,7 +334,7 @@ fn draw_filter_box<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect)
f.render_widget(input, chunks[0]);
} else {
let (block_title, block_content) = match app.get_current_route() {
Route::Radarr(active_radarr_block) => match active_radarr_block {
Route::Radarr(active_radarr_block, _) => match active_radarr_block {
_ if FILTER_BLOCKS.contains(active_radarr_block) => {
("Filter", app.data.radarr_data.filter.as_str())
}
@@ -418,7 +430,7 @@ fn draw_downloads<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let path = output_path.clone().unwrap_or_default();
path.scroll_or_reset(
get_width_with_margin(area),
get_width_from_percentage(area, 18),
current_selection == *download_record,
);
+8 -12
View File
@@ -12,7 +12,7 @@ use crate::app::App;
use crate::models::radarr_models::{Credit, MovieHistoryItem, Release, ReleaseField};
use crate::models::Route;
use crate::ui::utils::{
borderless_block, get_width_with_margin, layout_block_bottom_border, layout_block_top_border,
borderless_block, get_width_from_percentage, layout_block_bottom_border, layout_block_top_border,
spans_info_default, style_bold, style_default, style_failure, style_primary, style_success,
style_warning, vertical_chunks,
};
@@ -25,7 +25,7 @@ use crate::utils::convert_to_gb;
pub(super) fn draw_movie_info_popup<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let (content_area, _) = draw_tabs(f, area, "Movie Info", &app.data.radarr_data.movie_info_tabs);
if let Route::Radarr(active_radarr_block) = app.get_current_route() {
if let Route::Radarr(active_radarr_block, _) = app.get_current_route() {
match active_radarr_block {
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => draw_prompt_popup_over(
f,
@@ -68,7 +68,7 @@ pub(super) fn draw_movie_info_popup<B: Backend>(f: &mut Frame<'_, B>, app: &mut
}
fn draw_movie_info<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
if let Route::Radarr(active_radarr_block) =
if let Route::Radarr(active_radarr_block, _) =
app.data.radarr_data.movie_info_tabs.get_active_route()
{
match active_radarr_block {
@@ -258,14 +258,10 @@ fn draw_movie_history<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, content_a
event_type,
} = movie_history_item;
if current_selection == *movie_history_item
&& movie_history_item.source_title.text.len()
> (content_area.width as f64 * 0.34) as usize
{
source_title.scroll_text();
} else {
source_title.reset_offset();
}
movie_history_item.source_title.scroll_or_reset(
get_width_from_percentage(content_area, 34),
current_selection == *movie_history_item,
);
Row::new(vec![
Cell::from(source_title.to_string()),
@@ -432,7 +428,7 @@ fn draw_movie_releases<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, content_
} = release;
let age = format!("{} days", age.as_u64().unwrap_or(0));
title.scroll_or_reset(
get_width_with_margin(content_area),
get_width_from_percentage(content_area, 30),
current_selection == *release
&& current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(),
);
+13 -10
View File
@@ -241,8 +241,8 @@ pub fn show_cursor<B: Backend>(f: &mut Frame<'_, B>, area: Rect, string: &str) {
f.set_cursor(area.x + string.len() as u16 + 1, area.y + 1);
}
pub fn get_width_with_margin(area: Rect) -> usize {
(area.width as f32 * 0.30) as usize
pub fn get_width_from_percentage(area: Rect, percentage: u16) -> usize {
(area.width as f64 * (percentage as f64 / 100.0)) as usize
}
#[cfg(test)]
@@ -254,7 +254,7 @@ mod test {
use tui::widgets::{Block, BorderType, Borders};
use crate::ui::utils::{
borderless_block, centered_rect, get_width_with_margin, horizontal_chunks,
borderless_block, centered_rect, get_width_from_percentage, horizontal_chunks,
horizontal_chunks_with_margin, layout_block, layout_block_bottom_border,
layout_block_top_border, layout_block_top_border_with_title, layout_block_with_title,
layout_with_constraints, logo_block, spans_info_default, spans_info_primary,
@@ -625,14 +625,17 @@ mod test {
}
#[test]
fn test_get_width_with_margin() {
fn test_get_width_from_percentage() {
assert_eq!(
get_width_with_margin(Rect {
x: 0,
y: 0,
width: 100,
height: 10
}),
get_width_from_percentage(
Rect {
x: 0,
y: 0,
width: 100,
height: 10
},
30
),
30
);
}