Add universal reader features: gestures, keyboard, navigator, offline

- gestures.ts: Touch gesture controls (swipe, pinch, tap, double-tap)
- keyboard-shortcuts.ts: Keyboard navigation (arrows, space, page up/down, etc)
- navigator-panel.ts: Affinity-style minimap for page navigation
- offline-manager.ts: Service worker registration and online status
- reading-speed-tracker.ts: Track and sync reading speed to database
- Add web manifest.json for PWA support
This commit is contained in:
2026-04-04 01:01:08 -04:00
parent 2046960563
commit 0c199963b9
6 changed files with 545 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
// Track reading speed and update database
// Reading speed tracker
// Procedural implementation (no OOP)
interface ReadingSpeedTrackerState {
startTime: number | null;
pagesRead: number;
wordsRead: number;
lastSync: number;
mediaItemId: string;
}
function createReadingSpeedTracker(
mediaItemId: string,
): ReadingSpeedTrackerState {
return {
startTime: null,
pagesRead: 0,
wordsRead: 0,
lastSync: Date.now(),
mediaItemId,
};
}
function startReadingSession(
state: ReadingSpeedTrackerState,
): ReadingSpeedTrackerState {
return {
...state,
startTime: Date.now(),
pagesRead: 0,
wordsRead: 0,
};
}
function recordPageTurn(
state: ReadingSpeedTrackerState,
): ReadingSpeedTrackerState {
if (!state.startTime) return state;
const newPagesRead = state.pagesRead + 1;
const now = Date.now();
if (newPagesRead % 5 === 0 || now - state.lastSync > 5 * 60 * 1000) {
syncReadingSpeed({ ...state, pagesRead: newPagesRead });
return { ...state, pagesRead: newPagesRead, lastSync: now };
}
return { ...state, pagesRead: newPagesRead };
}
function recordWordsRead(
state: ReadingSpeedTrackerState,
wordCount: number,
): ReadingSpeedTrackerState {
return {
...state,
wordsRead: state.wordsRead + wordCount,
};
}
async function syncReadingSpeed(
state: ReadingSpeedTrackerState,
): Promise<void> {
if (!state.startTime) return;
const minutesElapsed = (Date.now() - state.startTime) / (1000 * 60);
const pagesPerMinute = state.pagesRead / minutesElapsed;
const wordsPerMinute = state.wordsRead / minutesElapsed;
await apiPut(`/readers/${state.mediaItemId}/reading-speed`, {
pages_per_minute: pagesPerMinute,
words_per_minute: wordsPerMinute,
pages_read: state.pagesRead,
total_reading_minutes: minutesElapsed,
});
}