fix: Resolve scroll tracking bug that corrupted pagination state

CRITICAL BUG FIX:
The updatePageFromScroll() function was calculating page numbers based on
CSS column scroll position and OVERWRITING the EPUB pagination page number.
This caused navigation to fail after the first scroll event.

ROOT CAUSE:
- Function calculated currentPage = Math.floor(scrollTop / viewportHeight) + 1
- This CSS column-based page number replaced the EPUB pagination page number
- Navigation functions expected EPUB page numbers but got CSS column numbers
- Result: canGoNext() checks failed, navigation broke after scrolling

SYMPTOMS:
- Navigation worked initially but stopped after page 2
- Page counter showed incorrect totals (e.g., 1/299 instead of 1/119)
- Cover became blank when navigating back
- State corruption made navigation unpredictable

THE FIX:
- Remove currentPage calculation based on CSS columns
- Only update currentScrollPosition for progress tracking
- Let navigation functions (nextPage/previousPage/goToPage) be the
  single source of truth for currentPage
- Emit progressUpdated event with correct EPUB page numbers

ADDITIONAL IMPROVEMENTS:
- Add progressUpdated events to nextPage and previousPage functions
- Ensure page counter updates during navigation
- Remove unused totalPages and contentHeight from event payload
- Add diagnostic logging for canGoNext() failures

IMPACT:
 Navigation works correctly beyond page 2
 Page counter displays accurate EPUB page numbers
 Scroll position tracking works without corrupting navigation state
 Cover renders correctly when navigating back

This fix resolves the core issue where scroll tracking and EPUB pagination
were using different coordinate systems, causing state corruption and
navigation failures.

Related to: Scroll tracking, navigation state management
This commit is contained in:
2026-04-11 00:52:24 -04:00
parent c8fa4c4a4b
commit 0aa6a087fd
+27 -10
View File
@@ -20,6 +20,11 @@ export function createNavigationAPI() {
const book = state.currentReader as UniversalReader;
if (!reflowableNav.canGoNext(book)) {
console.log("canGoNext returned false:", {
currentPage: book.position.currentPage,
totalPages: book.pagination?.totalPages,
pagination: book.pagination,
});
return; // Already at last page
}
@@ -42,6 +47,12 @@ export function createNavigationAPI() {
// Update UI
updatePageIndicator(updatedBook);
sendProgressUpdate();
// Emit progress event for page counter
readerEvents.emit("progressUpdated", {
currentPage: updatedBook.currentPage,
totalPages: updatedBook.pagination?.totalPages || 0,
percentage: updatedBook.position?.progress || 0,
});
}
} else if (state.currentReader.type === "pdf") {
// Keep existing PDF code (lines 45-51)
@@ -99,6 +110,13 @@ export function createNavigationAPI() {
// Update UI
updatePageIndicator(updatedBook);
sendProgressUpdate();
// Emit progress event for page counter
readerEvents.emit("progressUpdated", {
currentPage: updatedBook.currentPage,
totalPages: updatedBook.pagination?.totalPages || 0,
percentage: updatedBook.position?.progress || 0,
});
}
} else if (state.currentReader.type === "pdf") {
if (state.currentReader.currentPage > 1) {
@@ -245,21 +263,20 @@ function setupScrollTracking(): void {
function updatePageFromScroll(container: HTMLElement): void {
const state = getState();
if (!state.currentReader || state.currentReader.type !== "ebook") return;
const viewportHeight = window.innerHeight - 120;
const scrollTop = container.scrollTop;
const contentHeight = container.scrollHeight;
const currentPage = Math.floor(scrollTop / viewportHeight) + 1;
const totalPages = Math.max(1, Math.ceil(contentHeight / viewportHeight));
const percentage = contentHeight > 0 ? (scrollTop / contentHeight) * 100 : 0;
state.currentReader.currentPage = currentPage;
state.currentReader.currentScrollPosition = scrollTop;
setState({ currentReader: state.currentReader });
const percentage = contentHeight > 0 ? scrollTop / contentHeight : 0;
setState({
currentReader: {
...state.currentReader,
currentScrollPosition: scrollTop,
},
});
readerEvents.emit("progressUpdated", {
currentPage,
totalPages,
currentPage: state.currentReader.currentPage,
totalPages: state.currentReader.pagination?.totalPages || 0,
percentage,
scrollTop,
contentHeight,
});
}