feat(readarr): add a scrollable overview popup to author details
An author's overview renders as the last line of a description box that does not scroll, so a long overview overflows with no way to read the rest. Pressing v on the author details Books tab now opens a popup holding the full overview. The popup owns its own ScrollableText, built fresh from the author's overview when it opens and torn down on Esc so a reopened popup starts at the top. Scrolling mutates only that owned state. The background paragraph renders the Author record's overview, which carries no scroll offset, so it cannot scroll along with the popup. Add a view keybinding bound to v, cite it from the author details context clues, and give the popup its own clue set so it does not inherit the hints of the view underneath.
This commit is contained in:
59 files changed
+1493
-97
No files matched your search
@@ -276,3 +276,9 @@ impl Default for BookDetailsModal {
|
||||
pub struct EditionDetailsModal {
|
||||
pub edition_details: ScrollableText,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct AuthorOverviewModal {
|
||||
pub overview: ScrollableText,
|
||||
}
|
||||
@@ -7,8 +7,8 @@ mod tests {
|
||||
use crate::models::readarr_models::{Author, MonitorType, NewItemMonitorType};
|
||||
use crate::models::servarr_data::modals::EditIndexerModal;
|
||||
use crate::models::servarr_data::readarr::modals::{
|
||||
AddAuthorModal, AddReadarrRootFolderModal, BookDetailsModal, EditAuthorModal,
|
||||
EditionDetailsModal,
|
||||
AddAuthorModal, AddReadarrRootFolderModal, AuthorOverviewModal, BookDetailsModal,
|
||||
EditAuthorModal, EditionDetailsModal,
|
||||
};
|
||||
use crate::models::servarr_data::readarr::readarr_data::{ActiveReadarrBlock, ReadarrData};
|
||||
use crate::models::servarr_models::{Indexer, IndexerField, RootFolder};
|
||||
@@ -331,4 +331,11 @@ mod tests {
|
||||
|
||||
assert_is_empty!(edition_details_modal.edition_details);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_author_overview_modal_default() {
|
||||
let author_overview_modal = AuthorOverviewModal::default();
|
||||
|
||||
assert_is_empty!(author_overview_modal.overview);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
use serde_json::Number;
|
||||
|
||||
use super::modals::{AddAuthorModal, AddReadarrRootFolderModal, BookDetailsModal, EditAuthorModal};
|
||||
use super::modals::{
|
||||
AddAuthorModal, AddReadarrRootFolderModal, AuthorOverviewModal, BookDetailsModal, EditAuthorModal,
|
||||
};
|
||||
use crate::app::context_clues::{
|
||||
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||
@@ -55,6 +57,7 @@ pub struct ReadarrData<'a> {
|
||||
pub add_searched_authors: Option<StatefulTable<AddAuthorSearchResult>>,
|
||||
pub author_history: StatefulTable<ReadarrHistoryItem>,
|
||||
pub author_info_tabs: TabState,
|
||||
pub author_overview_modal: Option<AuthorOverviewModal>,
|
||||
pub author_releases: StatefulTable<ReadarrRelease>,
|
||||
pub authors: StatefulTable<Author>,
|
||||
pub blocklist: StatefulTable<BlocklistItem>,
|
||||
@@ -141,6 +144,7 @@ impl<'a> Default for ReadarrData<'a> {
|
||||
add_root_folder_modal: None,
|
||||
add_searched_authors: None,
|
||||
author_history: StatefulTable::default(),
|
||||
author_overview_modal: None,
|
||||
author_releases: StatefulTable::default(),
|
||||
authors: StatefulTable::default(),
|
||||
blocklist: StatefulTable::default(),
|
||||
@@ -262,7 +266,14 @@ impl ReadarrData<'_> {
|
||||
author_name: "Test Author".into(),
|
||||
foreign_author_id: "test-foreign-id".to_owned(),
|
||||
status: AuthorStatus::Continuing,
|
||||
overview: Some("some interesting description of the author".to_owned()),
|
||||
overview: Some(
|
||||
"some interesting description of the author\r\n\
|
||||
\r\n\
|
||||
He was born in Madison, Wisconsin: a city he has never really left.\r\n\
|
||||
\r\n\
|
||||
His first novel took him seven years to finish.\r\n"
|
||||
.to_owned(),
|
||||
),
|
||||
path: "/nfs/books/Test Author".to_owned(),
|
||||
quality_profile_id: 1,
|
||||
metadata_profile_id: 1,
|
||||
@@ -551,9 +562,14 @@ impl ReadarrData<'_> {
|
||||
let mut indexer_test_all_results = StatefulTable::default();
|
||||
indexer_test_all_results.set_items(vec![indexer_test_result()]);
|
||||
|
||||
let author_overview_modal = AuthorOverviewModal {
|
||||
overview: ScrollableText::with_string(author.overview.clone().unwrap_or_default()),
|
||||
};
|
||||
|
||||
let mut readarr_data = ReadarrData {
|
||||
add_author_modal: Some(add_author_modal),
|
||||
add_root_folder_modal: Some(add_root_folder_modal),
|
||||
author_overview_modal: Some(author_overview_modal),
|
||||
book_details_modal: Some(book_details_modal),
|
||||
delete_files: true,
|
||||
disk_space_vec: vec![diskspace()],
|
||||
@@ -647,6 +663,7 @@ pub enum ActiveReadarrBlock {
|
||||
AuthorHistory,
|
||||
AuthorHistoryDetails,
|
||||
AuthorHistorySortPrompt,
|
||||
AuthorOverview,
|
||||
AuthorsSortPrompt,
|
||||
AutomaticallySearchAuthorPrompt,
|
||||
AutomaticallySearchBookPrompt,
|
||||
@@ -770,6 +787,8 @@ pub static AUTHOR_DETAILS_BLOCKS: [ActiveReadarrBlock; 15] = [
|
||||
ActiveReadarrBlock::UpdateAndScanAuthorPrompt,
|
||||
];
|
||||
|
||||
pub static AUTHOR_OVERVIEW_BLOCKS: [ActiveReadarrBlock; 1] = [ActiveReadarrBlock::AuthorOverview];
|
||||
|
||||
pub static BOOK_DETAILS_BLOCKS: [ActiveReadarrBlock; 16] = [
|
||||
ActiveReadarrBlock::BookDetails,
|
||||
ActiveReadarrBlock::BookHistory,
|
||||
|
||||
@@ -11,13 +11,14 @@ mod tests {
|
||||
use crate::models::readarr_models::{Book, ReadarrHistoryItem, ReadarrRelease};
|
||||
use crate::models::servarr_data::readarr::readarr_data::{
|
||||
ADD_AUTHOR_BLOCKS, ADD_AUTHOR_SELECTION_BLOCKS, ADD_ROOT_FOLDER_BLOCKS,
|
||||
ADD_ROOT_FOLDER_SELECTION_BLOCKS, AUTHOR_DETAILS_BLOCKS, ActiveReadarrBlock, BLOCKLIST_BLOCKS,
|
||||
BOOK_DETAILS_BLOCKS, DELETE_AUTHOR_BLOCKS, DELETE_AUTHOR_SELECTION_BLOCKS, DELETE_BOOK_BLOCKS,
|
||||
DELETE_BOOK_SELECTION_BLOCKS, DOWNLOADS_BLOCKS, EDIT_AUTHOR_BLOCKS,
|
||||
EDIT_AUTHOR_SELECTION_BLOCKS, EDIT_INDEXER_BLOCKS, EDIT_INDEXER_NZB_SELECTION_BLOCKS,
|
||||
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, EDITION_DETAILS_BLOCKS, HISTORY_BLOCKS,
|
||||
INDEXER_SETTINGS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, LIBRARY_BLOCKS,
|
||||
ROOT_FOLDERS_BLOCKS, ReadarrData, SYSTEM_DETAILS_BLOCKS,
|
||||
ADD_ROOT_FOLDER_SELECTION_BLOCKS, AUTHOR_DETAILS_BLOCKS, AUTHOR_OVERVIEW_BLOCKS,
|
||||
ActiveReadarrBlock, BLOCKLIST_BLOCKS, BOOK_DETAILS_BLOCKS, DELETE_AUTHOR_BLOCKS,
|
||||
DELETE_AUTHOR_SELECTION_BLOCKS, DELETE_BOOK_BLOCKS, DELETE_BOOK_SELECTION_BLOCKS,
|
||||
DOWNLOADS_BLOCKS, EDIT_AUTHOR_BLOCKS, EDIT_AUTHOR_SELECTION_BLOCKS, EDIT_INDEXER_BLOCKS,
|
||||
EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
|
||||
EDITION_DETAILS_BLOCKS, HISTORY_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||
INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, LIBRARY_BLOCKS, ROOT_FOLDERS_BLOCKS,
|
||||
ReadarrData, SYSTEM_DETAILS_BLOCKS,
|
||||
};
|
||||
use crate::models::{BlockSelectionState, Route};
|
||||
use bimap::BiMap;
|
||||
@@ -152,6 +153,7 @@ mod tests {
|
||||
assert_none!(readarr_data.add_root_folder_modal);
|
||||
assert_none!(readarr_data.add_searched_authors);
|
||||
assert_is_empty!(readarr_data.author_history);
|
||||
assert_none!(readarr_data.author_overview_modal);
|
||||
assert_is_empty!(readarr_data.author_releases);
|
||||
assert_is_empty!(readarr_data.authors);
|
||||
assert_is_empty!(readarr_data.blocklist);
|
||||
@@ -361,6 +363,17 @@ mod tests {
|
||||
assert!(EDITION_DETAILS_BLOCKS.contains(&ActiveReadarrBlock::BookEditionDetails));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_author_details_blocks_excludes_author_overview() {
|
||||
assert!(!AUTHOR_DETAILS_BLOCKS.contains(&ActiveReadarrBlock::AuthorOverview));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_author_overview_blocks_contents() {
|
||||
assert_eq!(AUTHOR_OVERVIEW_BLOCKS.len(), 1);
|
||||
assert!(AUTHOR_OVERVIEW_BLOCKS.contains(&ActiveReadarrBlock::AuthorOverview));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blocklist_blocks_contents() {
|
||||
assert_eq!(BLOCKLIST_BLOCKS.len(), 5);
|
||||
|
||||
Reference in new issue
Block a user