Some fixes
This commit is contained in:
@@ -53,7 +53,10 @@ void ReadingSessionTracker::markFinished() {
|
||||
if (!active) return;
|
||||
const int64_t walltime = HalClock::isSynced() ? static_cast<int64_t>(HalClock::now()) : 0;
|
||||
READING_STATS.markFinished(docId, title, author, static_cast<time_t>(walltime));
|
||||
READING_STATS.saveToFile();
|
||||
if (!READING_STATS.saveToFile()) {
|
||||
LOG_ERR("RST", "saveToFile failed (markFinished) doc=%s title=%s author=%s wall=%lld", docId.c_str(), title.c_str(),
|
||||
author.c_str(), (long long)walltime);
|
||||
}
|
||||
LOG_DBG("RST", "Marked finished doc=%s wall=%lld", docId.c_str(), (long long)walltime);
|
||||
}
|
||||
|
||||
@@ -77,7 +80,10 @@ void ReadingSessionTracker::end() {
|
||||
if (seconds > 0 && !docId.empty()) {
|
||||
READING_STATS.recordSession(docId, title, author, seconds, pagesTurnedThisSession, lastKnownProgress,
|
||||
static_cast<time_t>(walltime));
|
||||
READING_STATS.saveToFile();
|
||||
if (!READING_STATS.saveToFile()) {
|
||||
LOG_ERR("RST", "saveToFile failed (session end) doc=%s title=%s author=%s secs=%u pages=%u wall=%lld",
|
||||
docId.c_str(), title.c_str(), author.c_str(), seconds, pagesTurnedThisSession, (long long)walltime);
|
||||
}
|
||||
}
|
||||
|
||||
active = false;
|
||||
|
||||
@@ -118,7 +118,6 @@ uint16_t ReadingStatsStore::computeCurrentStreak(uint16_t today) const {
|
||||
// yesterday. After that the chain is broken.
|
||||
uint16_t anchor = today;
|
||||
if (getSecondsForDay(anchor) == 0) {
|
||||
if (anchor == 0) return 0;
|
||||
anchor -= 1;
|
||||
if (getSecondsForDay(anchor) == 0) return 0;
|
||||
}
|
||||
@@ -170,11 +169,8 @@ void ReadingStatsStore::markFinished(const std::string& docId, const std::string
|
||||
}
|
||||
|
||||
size_t ReadingStatsStore::getFinishedBookCount() const {
|
||||
size_t n = 0;
|
||||
for (const auto& b : books) {
|
||||
if (b.finishedCount > 0) ++n;
|
||||
}
|
||||
return n;
|
||||
return static_cast<size_t>(
|
||||
std::count_if(books.begin(), books.end(), [](const BookReadingStats& b) { return b.finishedCount > 0; }));
|
||||
}
|
||||
|
||||
const BookReadingStats* ReadingStatsStore::findBook(const std::string& docId) const {
|
||||
|
||||
@@ -905,23 +905,27 @@ void TxtReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION a
|
||||
case BA::BTN_PAGE_FORWARD:
|
||||
if (currentPage < totalPages - 1) {
|
||||
currentPage++;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
}
|
||||
break;
|
||||
case BA::BTN_PAGE_BACK:
|
||||
if (currentPage > 0) {
|
||||
currentPage--;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
}
|
||||
break;
|
||||
case BA::BTN_PAGE_FORWARD_10:
|
||||
currentPage += 10;
|
||||
clampPage();
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
break;
|
||||
case BA::BTN_PAGE_BACK_10:
|
||||
currentPage -= 10;
|
||||
clampPage();
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
break;
|
||||
case BA::BTN_STAR_PAGE:
|
||||
|
||||
@@ -523,21 +523,25 @@ void XtcReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION a
|
||||
case BA::BTN_PAGE_FORWARD:
|
||||
if (currentPage + 1 < pageCount) {
|
||||
currentPage++;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
}
|
||||
break;
|
||||
case BA::BTN_PAGE_BACK:
|
||||
if (currentPage > 0) {
|
||||
currentPage--;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
}
|
||||
break;
|
||||
case BA::BTN_PAGE_FORWARD_10:
|
||||
currentPage = (currentPage + 10 < pageCount) ? currentPage + 10 : pageCount - 1;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
break;
|
||||
case BA::BTN_PAGE_BACK_10:
|
||||
currentPage = (currentPage >= 10) ? currentPage - 10 : 0;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
break;
|
||||
case BA::BTN_NEXT_SECTION:
|
||||
@@ -547,6 +551,7 @@ void XtcReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION a
|
||||
[this](const auto& ch) { return ch.startPage > currentPage; });
|
||||
if (it != chapters.end()) {
|
||||
currentPage = it->startPage;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
}
|
||||
}
|
||||
@@ -559,6 +564,7 @@ void XtcReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION a
|
||||
|
||||
if (prevChapter != chapters.rend()) {
|
||||
currentPage = prevChapter->startPage;
|
||||
globalReadingSessionTracker().onPageTurn();
|
||||
requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include <Arduino.h> // millis()
|
||||
#include <GfxRenderer.h>
|
||||
#include <I18n.h>
|
||||
#include <Utf8.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -178,7 +180,8 @@ void ReadingStatsActivity::render(RenderLock&&) {
|
||||
if (!store.getBooks().empty()) {
|
||||
std::vector<const BookReadingStats*> sorted;
|
||||
sorted.reserve(store.getBooks().size());
|
||||
for (const auto& b : store.getBooks()) sorted.push_back(&b);
|
||||
std::transform(store.getBooks().begin(), store.getBooks().end(), std::back_inserter(sorted),
|
||||
[](const BookReadingStats& b) { return &b; });
|
||||
std::sort(sorted.begin(), sorted.end(),
|
||||
[](const BookReadingStats* a, const BookReadingStats* b) { return a->totalSeconds > b->totalSeconds; });
|
||||
|
||||
@@ -199,7 +202,7 @@ void ReadingStatsActivity::render(RenderLock&&) {
|
||||
if (maxLabelWidth > 0 && renderer.getTextWidth(UI_10_FONT_ID, label.c_str()) > maxLabelWidth) {
|
||||
while (!label.empty() &&
|
||||
renderer.getTextWidth(UI_10_FONT_ID, label.c_str()) + ellipsisWidth > maxLabelWidth) {
|
||||
label.pop_back();
|
||||
utf8RemoveLastChar(label);
|
||||
}
|
||||
label += "…";
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalClock.h>
|
||||
#include <I18n.h>
|
||||
#include <Utf8.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -107,9 +108,19 @@ void ReadingStatsBookDetailActivity::render(RenderLock&&) {
|
||||
// Header — title (truncated) and author. Title fallback to docId so we
|
||||
// still produce a usable screen if the book's metadata was never recorded.
|
||||
std::string headerTitle = (book && !book->title.empty()) ? book->title : docId;
|
||||
if (headerTitle.size() > 28) {
|
||||
headerTitle.resize(28);
|
||||
headerTitle += "…";
|
||||
{
|
||||
constexpr size_t kMaxChars = 28;
|
||||
const auto* p = reinterpret_cast<const unsigned char*>(headerTitle.c_str());
|
||||
const auto* start = p;
|
||||
size_t chars = 0;
|
||||
while (*p != 0 && chars < kMaxChars) {
|
||||
utf8NextCodepoint(&p);
|
||||
++chars;
|
||||
}
|
||||
if (*p != 0) {
|
||||
headerTitle.resize(static_cast<size_t>(p - start));
|
||||
headerTitle += "…";
|
||||
}
|
||||
}
|
||||
GUI.drawHeader(renderer,
|
||||
Rect{contentRect.x, contentRect.y + metrics.topPadding, contentRect.width, metrics.headerHeight},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <iterator>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "ReadingStatsBookDetailActivity.h"
|
||||
@@ -35,9 +36,9 @@ std::string formatDuration(uint32_t totalSeconds) {
|
||||
|
||||
void ReadingStatsBookListActivity::rebuildSortedBooks() {
|
||||
sortedBooks.clear();
|
||||
for (const auto& b : READING_STATS.getBooks()) {
|
||||
sortedBooks.push_back(&b);
|
||||
}
|
||||
sortedBooks.reserve(READING_STATS.getBooks().size());
|
||||
std::transform(READING_STATS.getBooks().begin(), READING_STATS.getBooks().end(), std::back_inserter(sortedBooks),
|
||||
[](const BookReadingStats& b) { return &b; });
|
||||
std::sort(sortedBooks.begin(), sortedBooks.end(),
|
||||
[](const BookReadingStats* a, const BookReadingStats* b) { return a->totalSeconds > b->totalSeconds; });
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "fontIds.h"
|
||||
|
||||
CardLayout::CardLayout(GfxRenderer& renderer, Rect contentRect, int startY, CardLayoutConfig cfg)
|
||||
CardLayout::CardLayout(GfxRenderer& renderer, Rect contentRect, int startY, const CardLayoutConfig& cfg)
|
||||
: renderer_(renderer), contentRect_(contentRect), cfg_(cfg), y_(startY) {
|
||||
cardLeft_ = contentRect.x + cfg_.outerMarginX;
|
||||
cardWidth_ = contentRect.width - cfg_.outerMarginX * 2;
|
||||
|
||||
@@ -85,7 +85,7 @@ class CardLayout {
|
||||
void centeredMessage(const char* msg);
|
||||
};
|
||||
|
||||
CardLayout(GfxRenderer& renderer, Rect contentRect, int startY, CardLayoutConfig cfg = {});
|
||||
CardLayout(GfxRenderer& renderer, Rect contentRect, int startY, const CardLayoutConfig& cfg = {});
|
||||
|
||||
// Render a single card. `bodyFn` receives a `Body&` and may call its
|
||||
// helpers in any order; the card auto-sizes to whatever the body draws.
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
.nav-links {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
.nav-links {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
.nav-links {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.nav-links a {
|
||||
|
||||
@@ -310,8 +310,10 @@
|
||||
// seconds.
|
||||
function formatEta(seconds) {
|
||||
if (!seconds || seconds <= 0) return "—";
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.round((seconds % 3600) / 60);
|
||||
let h = Math.floor(seconds / 3600);
|
||||
let m = Math.round((seconds % 3600) / 60);
|
||||
// Rounding can push m to 60 (e.g. 3570s → 0h 60m); carry into hours.
|
||||
if (m === 60) { h += 1; m = 0; }
|
||||
if (h > 0) return `${h}h ${String(m).padStart(2, "0")}m`;
|
||||
if (m > 0) return `${m}m`;
|
||||
return "<1m";
|
||||
|
||||
Reference in New Issue
Block a user