feat: allow removing book from recent list (#2045)

## Summary

Add ability to long press 'confirm' on a book in the recent books list
to be prompted to remove it from the list.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? *Yes, Claude*

---------
This commit is contained in:
KemoNine
2026-05-18 22:16:33 -04:00
committed by GitHub
parent f2adefe729
commit df53faab91
26 changed files with 77 additions and 4 deletions
+1 -2
View File
@@ -9,6 +9,7 @@
#include <Xtc.h>
#include <algorithm>
#include <iterator>
namespace {
constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3;
@@ -64,8 +65,6 @@ bool RecentBooksStore::removeByPath(const std::string& path) {
}
recentBooks.erase(it);
if (!saveToFile()) {
// In-memory removal succeeded; persistence is best-effort here (consistent with
// addBook/updateBook). Log the failure but still report the entry as removed.
LOG_ERR("RBS", "Failed to persist removal of recent book: %s", path.c_str());
}
return true;
+1 -1
View File
@@ -37,7 +37,7 @@ class RecentBooksStore {
void updateBook(const std::string& path, const std::string& title, const std::string& author,
const std::string& coverBmpPath);
// Remove the entry whose path matches (used when a book is finished/read).
// Remove the entry whose path matches (used when a book is removed from recents or finished/read).
// Returns true if an entry was found and removed (no-op + false otherwise).
// Persistence is best-effort: a failed save is logged, not reflected in the return.
bool removeByPath(const std::string& path);
+46 -1
View File
@@ -5,14 +5,17 @@
#include <I18n.h>
#include <algorithm>
#include <memory>
#include "MappedInputManager.h"
#include "RecentBooksStore.h"
#include "activities/util/ConfirmationActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
namespace {
constexpr unsigned long GO_HOME_MS = 1000;
// Hold threshold for the long-press "remove from list" action (firmware convention).
constexpr unsigned long LONG_PRESS_MS = 1000;
} // namespace
void RecentBooksActivity::loadRecentBooks() { recentBooks = RECENT_BOOKS.getBooks(); }
@@ -41,6 +44,25 @@ void RecentBooksActivity::onExit() {
void RecentBooksActivity::loop() {
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, true);
// After a long-press has fired, swallow input until Confirm is physically released
// (so the release doesn't also open the book; re-arm only once the button is up).
if (longPressFired) {
if (!mappedInput.isPressed(MappedInputManager::Button::Confirm)) {
longPressFired = false;
}
return;
}
// Long-press Confirm on the selected book: prompt to remove it from the list.
// Fires when the hold times out while still held (firmware hold-to-act pattern,
// cf. FileBrowserActivity BACK long-press).
if (!recentBooks.empty() && selectorIndex < recentBooks.size() &&
mappedInput.isPressed(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() >= LONG_PRESS_MS) {
longPressFired = true;
promptRemoveBook(recentBooks[selectorIndex].path, recentBooks[selectorIndex].title);
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (!recentBooks.empty() && selectorIndex < static_cast<int>(recentBooks.size())) {
LOG_DBG("RBA", "Selected recent book: %s", recentBooks[selectorIndex].path.c_str());
@@ -76,6 +98,29 @@ void RecentBooksActivity::loop() {
});
}
void RecentBooksActivity::promptRemoveBook(const std::string& path, const std::string& title) {
auto handler = [this, path](const ActivityResult& res) {
if (res.isCancelled) {
LOG_DBG("RBA", "Remove from recents cancelled");
return;
}
if (RECENT_BOOKS.removeByPath(path)) {
LOG_DBG("RBA", "Removed from recents: %s", path.c_str());
loadRecentBooks();
if (recentBooks.empty()) {
selectorIndex = 0;
} else if (selectorIndex >= recentBooks.size()) {
selectorIndex = recentBooks.size() - 1;
}
requestUpdate(true);
}
};
startActivityForResult(
std::make_unique<ConfirmationActivity>(renderer, mappedInput, tr(STR_REMOVE_FROM_RECENTS), title),
std::move(handler));
}
void RecentBooksActivity::render(RenderLock&&) {
renderer.clearScreen();
@@ -15,12 +15,19 @@ class RecentBooksActivity final : public Activity {
size_t selectorIndex = 0;
// Set when a long-press has fired; input is swallowed until Confirm is released
// again so the release doesn't also open the book.
bool longPressFired = false;
// Recent tab state
std::vector<RecentBook> recentBooks;
// Data loading
void loadRecentBooks();
// Show an OK/Cancel prompt to remove the given book from the Recent Books list.
void promptRemoveBook(const std::string& path, const std::string& title);
public:
explicit RecentBooksActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
: Activity("RecentBooks", renderer, mappedInput) {}