diff --git a/chat-summaries/2026-03-09_02-30-summary.md b/chat-summaries/2026-03-09_02-30-summary.md new file mode 100644 index 00000000..775dc925 --- /dev/null +++ b/chat-summaries/2026-03-09_02-30-summary.md @@ -0,0 +1,51 @@ +# BookInfo Button Mapping, Fallback Load, ManageBook Integration, Cover Regen Fix + +**Date**: 2026-03-09 +**Task**: Implement fixes 1-5 from the BookInfo Buttons and Load plan + +## Changes Made + +### BookInfoActivity.cpp — Fixes 1-3 + +1. **Button mapping** (`loop()`): Removed `Confirm` as exit trigger. Added `Left` and `Right` front buttons as scroll-down/scroll-up handlers alongside existing side-button `Down`/`Up` and `PageForward`/`PageBack`. +2. **Button hints** (`render()`): Updated `mapLabels` to show `STR_DIR_DOWN` on Left (btn3) and `STR_DIR_UP` on Right (btn4), with separate variables for each direction instead of a single combined hint. +3. **Fallback load** (`onEnter()`): Changed `epub.load(false, true)` pattern to try `epub.load(true, true)` on failure, ensuring cache is built for books that were never opened. XTC branch already builds unconditionally via `xtc.load()`. + +### BookManageMenuActivity — Fix 4 + +- Added `BOOK_INFO` to `Action` enum (first entry) +- Added `STR_BOOK_INFO` menu item as first entry in `buildMenuItems()` +- Handled `BOOK_INFO` in all 4 result sites: + - `HomeActivity::openManageMenu()` — launches `BookInfoActivity` via `startActivityForResult` + - `RecentBooksActivity::openManageMenu()` — same pattern + - `FileBrowserActivity::handleManageResult()` — same pattern + - `EpubReaderActivity` inline switch — same pattern +- Added `BOOK_INFO` no-op case to `executeManageAction()` in Home/Recent to prevent compiler warnings + +### HomeActivity cover regeneration — Fix 5 + +- After `generateThumbBmp()` fails or produces invalid BMP, now calls `generateCoverBmp(false)` to extract the full cover from the EPUB before retrying thumbnail generation +- Added `Epub::isValidThumbnailBmp()` validation after each `generateThumbBmp()` call +- Applied same pattern to XTC branch using `xtc.generateCoverBmp()` + validation +- This aligns the HomeActivity pipeline with the EpubReaderActivity's multi-tier fallback approach + +## Files Changed + +- `src/activities/home/BookInfoActivity.cpp` — button mapping, hints, fallback load +- `src/activities/home/BookManageMenuActivity.h` — BOOK_INFO enum entry +- `src/activities/home/BookManageMenuActivity.cpp` — BOOK_INFO menu item +- `src/activities/home/HomeActivity.cpp` — BOOK_INFO handler + cover regen fix +- `src/activities/home/RecentBooksActivity.cpp` — BOOK_INFO handler +- `src/activities/home/FileBrowserActivity.cpp` — BOOK_INFO handler +- `src/activities/reader/EpubReaderActivity.cpp` — BOOK_INFO handler + +## Build Result + +Build succeeded — 0 errors, 0 code warnings. RAM: 30.3%, Flash: 95.7%. + +## Follow-up Items + +- Test on device: verify all 4 BookInfo entry points work (Home manage, Recent manage, FileBrowser manage, Reader manage) +- Test cover regeneration after DELETE_CACHE from reader menu +- Verify button mapping on BookInfo screen (Left=down, Right=up, side buttons=scroll) +- Verify Book Info shows for books that have never been opened diff --git a/src/activities/home/BookInfoActivity.cpp b/src/activities/home/BookInfoActivity.cpp index ef4ca7f9..8b710ed3 100644 --- a/src/activities/home/BookInfoActivity.cpp +++ b/src/activities/home/BookInfoActivity.cpp @@ -63,7 +63,10 @@ void BookInfoActivity::onEnter() { if (FsHelpers::hasEpubExtension(fileName)) { Epub epub(filePath, "/.crosspoint"); - if (epub.load(false, true)) { + if (!epub.load(false, true)) { + epub.load(true, true); + } + { title = epub.getTitle(); author = epub.getAuthor(); series = epub.getSeries(); @@ -177,8 +180,7 @@ void BookInfoActivity::buildLayout(const std::string& title, const std::string& } void BookInfoActivity::loop() { - if (mappedInput.wasReleased(MappedInputManager::Button::Back) || - mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { + if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { ActivityResult r; r.isCancelled = true; setResult(std::move(r)); @@ -189,14 +191,18 @@ void BookInfoActivity::loop() { const int pageH = renderer.getScreenHeight(); const int scrollStep = pageH / 3; - if (mappedInput.wasReleased(MappedInputManager::Button::Down)) { + if (mappedInput.wasReleased(MappedInputManager::Button::Down) || + mappedInput.wasReleased(MappedInputManager::Button::PageForward) || + mappedInput.wasReleased(MappedInputManager::Button::Left)) { if (scrollOffset + pageH < contentHeight) { scrollOffset += scrollStep; requestUpdate(); } } - if (mappedInput.wasReleased(MappedInputManager::Button::Up)) { + if (mappedInput.wasReleased(MappedInputManager::Button::Up) || + mappedInput.wasReleased(MappedInputManager::Button::PageBack) || + mappedInput.wasReleased(MappedInputManager::Button::Right)) { if (scrollOffset > 0) { scrollOffset -= scrollStep; if (scrollOffset < 0) scrollOffset = 0; @@ -252,8 +258,9 @@ void BookInfoActivity::render(RenderLock&&) { const bool canScrollDown = scrollOffset + pageH < contentHeight; const bool canScrollUp = scrollOffset > 0; - const char* scrollHint = canScrollDown ? tr(STR_DIR_DOWN) : (canScrollUp ? tr(STR_DIR_UP) : ""); - const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", scrollHint, ""); + const char* downHint = canScrollDown ? tr(STR_DIR_DOWN) : ""; + const char* upHint = canScrollUp ? tr(STR_DIR_UP) : ""; + const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", downHint, upHint); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); diff --git a/src/activities/home/BookManageMenuActivity.cpp b/src/activities/home/BookManageMenuActivity.cpp index 2abb032e..4e7be3a8 100644 --- a/src/activities/home/BookManageMenuActivity.cpp +++ b/src/activities/home/BookManageMenuActivity.cpp @@ -10,6 +10,7 @@ void BookManageMenuActivity::buildMenuItems() { menuItems.clear(); + menuItems.push_back({Action::BOOK_INFO, StrId::STR_BOOK_INFO}); if (archived) { menuItems.push_back({Action::UNARCHIVE, StrId::STR_UNARCHIVE_BOOK}); } else { diff --git a/src/activities/home/BookManageMenuActivity.h b/src/activities/home/BookManageMenuActivity.h index 3fcaf345..5e46daab 100644 --- a/src/activities/home/BookManageMenuActivity.h +++ b/src/activities/home/BookManageMenuActivity.h @@ -11,6 +11,7 @@ class BookManageMenuActivity final : public Activity { public: enum class Action { + BOOK_INFO, ARCHIVE, UNARCHIVE, DELETE, diff --git a/src/activities/home/FileBrowserActivity.cpp b/src/activities/home/FileBrowserActivity.cpp index 6b69bacb..0607bc38 100644 --- a/src/activities/home/FileBrowserActivity.cpp +++ b/src/activities/home/FileBrowserActivity.cpp @@ -137,9 +137,17 @@ void FileBrowserActivity::handleManageResult(const std::string& fullPath, const const auto& menuResult = std::get(result.data); auto action = static_cast(menuResult.action); + if (action == BookManageMenuActivity::Action::BOOK_INFO) { + startActivityForResult(std::make_unique(renderer, mappedInput, fullPath), + [this](const ActivityResult&) { requestUpdate(); }); + return; + } + auto executeAction = [this, action, fullPath]() { bool success = false; switch (action) { + case BookManageMenuActivity::Action::BOOK_INFO: + return; case BookManageMenuActivity::Action::ARCHIVE: success = BookManager::archiveBook(fullPath); break; diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 9c69a94c..a1f05213 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -15,6 +15,7 @@ #include #include "../util/ConfirmationActivity.h" +#include "BookInfoActivity.h" #include "BookManageMenuActivity.h" #include "CrossPointSettings.h" #include "CrossPointState.h" @@ -81,7 +82,11 @@ void HomeActivity::loadRecentCovers(int coverHeight) { epub.load(true, true); } success = epub.generateThumbBmp(coverHeight); - if (success) { + if (!success || !Epub::isValidThumbnailBmp(epub.getThumbBmpPath(coverHeight))) { + epub.generateCoverBmp(false); + success = epub.generateThumbBmp(coverHeight); + } + if (success && Epub::isValidThumbnailBmp(epub.getThumbBmpPath(coverHeight))) { const std::string thumbPath = epub.getThumbBmpPath(coverHeight); RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, thumbPath); book.coverBmpPath = thumbPath; @@ -93,10 +98,16 @@ void HomeActivity::loadRecentCovers(int coverHeight) { Xtc xtc(book.path, "/.crosspoint"); if (xtc.load()) { success = xtc.generateThumbBmp(coverHeight); - if (success) { + if (!success || !Epub::isValidThumbnailBmp(xtc.getThumbBmpPath(coverHeight))) { + xtc.generateCoverBmp(); + success = xtc.generateThumbBmp(coverHeight); + } + if (success && Epub::isValidThumbnailBmp(xtc.getThumbBmpPath(coverHeight))) { const std::string thumbPath = xtc.getThumbBmpPath(coverHeight); RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, thumbPath); book.coverBmpPath = thumbPath; + } else { + success = false; } } if (!success) { @@ -304,6 +315,8 @@ void HomeActivity::onOpdsBrowserOpen() { activityManager.goToBrowser(); } void HomeActivity::executeManageAction(BookManageMenuActivity::Action action, const std::string& capturedPath) { bool success = false; switch (action) { + case BookManageMenuActivity::Action::BOOK_INFO: + return; case BookManageMenuActivity::Action::ARCHIVE: success = BookManager::archiveBook(capturedPath); break; @@ -352,7 +365,11 @@ void HomeActivity::openManageMenu(const std::string& bookPath) { const auto& menuResult = std::get(result.data); auto action = static_cast(menuResult.action); - if (action == BookManageMenuActivity::Action::DELETE || action == BookManageMenuActivity::Action::ARCHIVE) { + if (action == BookManageMenuActivity::Action::BOOK_INFO) { + startActivityForResult(std::make_unique(renderer, mappedInput, capturedPath), + [this](const ActivityResult&) { requestUpdate(); }); + } else if (action == BookManageMenuActivity::Action::DELETE || + action == BookManageMenuActivity::Action::ARCHIVE) { const char* promptKey = (action == BookManageMenuActivity::Action::DELETE) ? tr(STR_DELETE_BOOK) : tr(STR_ARCHIVE_BOOK); std::string heading = std::string(promptKey) + "?"; diff --git a/src/activities/home/RecentBooksActivity.cpp b/src/activities/home/RecentBooksActivity.cpp index 531b0d1a..d25c4b2c 100644 --- a/src/activities/home/RecentBooksActivity.cpp +++ b/src/activities/home/RecentBooksActivity.cpp @@ -7,6 +7,7 @@ #include #include "../util/ConfirmationActivity.h" +#include "BookInfoActivity.h" #include "BookManageMenuActivity.h" #include "MappedInputManager.h" #include "RecentBooksStore.h" @@ -51,6 +52,8 @@ void RecentBooksActivity::onExit() { void RecentBooksActivity::executeManageAction(BookManageMenuActivity::Action action, const std::string& capturedPath) { bool success = false; switch (action) { + case BookManageMenuActivity::Action::BOOK_INFO: + return; case BookManageMenuActivity::Action::ARCHIVE: success = BookManager::archiveBook(capturedPath); break; @@ -92,7 +95,11 @@ void RecentBooksActivity::openManageMenu(const std::string& bookPath) { const auto& menuResult = std::get(result.data); auto action = static_cast(menuResult.action); - if (action == BookManageMenuActivity::Action::DELETE || action == BookManageMenuActivity::Action::ARCHIVE) { + if (action == BookManageMenuActivity::Action::BOOK_INFO) { + startActivityForResult(std::make_unique(renderer, mappedInput, capturedPath), + [this](const ActivityResult&) { requestUpdate(); }); + } else if (action == BookManageMenuActivity::Action::DELETE || + action == BookManageMenuActivity::Action::ARCHIVE) { const char* promptKey = (action == BookManageMenuActivity::Action::DELETE) ? tr(STR_DELETE_BOOK) : tr(STR_ARCHIVE_BOOK); std::string heading = std::string(promptKey) + "?"; diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 4a223bd0..78e99473 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -26,6 +26,7 @@ #include "ReaderUtils.h" #include "RecentBooksStore.h" #include "activities/ActivityManager.h" +#include "activities/home/BookInfoActivity.h" #include "activities/home/BookManageMenuActivity.h" #include "activities/util/ConfirmationActivity.h" #include "components/UITheme.h" @@ -722,6 +723,12 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction const auto& menu = std::get(result.data); const auto bookAction = static_cast(menu.action); switch (bookAction) { + case BookManageMenuActivity::Action::BOOK_INFO: { + const std::string path = epub ? epub->getPath() : ""; + startActivityForResult(std::make_unique(renderer, mappedInput, path), + [this](const ActivityResult&) { requestUpdate(); }); + return; + } case BookManageMenuActivity::Action::ARCHIVE: { std::string heading = std::string(tr(STR_ARCHIVE_BOOK)) + "?"; std::string bookName = epub ? epub->getTitle() : "";