diff --git a/src/components/EPUBViewer.tsx b/src/components/EPUBViewer.tsx index fe78fc8..e4d295c 100644 --- a/src/components/EPUBViewer.tsx +++ b/src/components/EPUBViewer.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect, useRef, useCallback } from 'react'; +import { useEffect, useRef, useCallback } from 'react'; import dynamic from 'next/dynamic'; import { useEPUB } from '@/contexts/EPUBContext'; import { useTTS } from '@/contexts/TTSContext'; @@ -18,8 +18,8 @@ interface EPUBViewerProps { } export function EPUBViewer({ className = '' }: EPUBViewerProps) { - const { currDocData, currDocName, currDocPage, currDocPages, extractPageText } = useEPUB(); - const { skipToLocation, registerLocationChangeHandler } = useTTS(); + const { currDocData, currDocName, currDocPage, extractPageText } = useEPUB(); + const { setEPUBPageInChapter, registerLocationChangeHandler } = useTTS(); const bookRef = useRef(null); const rendition = useRef(undefined); const toc = useRef([]); @@ -43,11 +43,11 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) { console.log('Displayed:', displayed, 'Chapter:', chapter); locationRef.current = location; - skipToLocation(displayed.page, displayed.total); + setEPUBPageInChapter(displayed.page, displayed.total, chapter?.label || ''); extractPageText(bookRef.current, rendition.current); } - }, [skipToLocation, extractPageText]); + }, [setEPUBPageInChapter, extractPageText]); // Register the location change handler useEffect(() => { diff --git a/src/contexts/EPUBContext.tsx b/src/contexts/EPUBContext.tsx index df96d4b..0fe8637 100644 --- a/src/contexts/EPUBContext.tsx +++ b/src/contexts/EPUBContext.tsx @@ -10,7 +10,7 @@ import { } from 'react'; import { indexedDBService } from '@/utils/indexedDB'; import { useTTS } from '@/contexts/TTSContext'; -import { Book, Contents, Rendition } from 'epubjs'; +import { Book, Rendition } from 'epubjs'; import { createRangeCfi } from '@/utils/epub'; interface EPUBContextType { @@ -28,7 +28,7 @@ interface EPUBContextType { const EPUBContext = createContext(undefined); export function EPUBProvider({ children }: { children: ReactNode }) { - const { setText: setTTSText, currDocPage, currDocPages, setCurrDocPages } = useTTS(); + const { setText: setTTSText, stop, currDocPage, currDocPages, setCurrDocPages } = useTTS(); // Current document state const [currDocData, setCurrDocData] = useState(); @@ -51,8 +51,8 @@ export function EPUBProvider({ children }: { children: ReactNode }) { setCurrDocName(undefined); setCurrDocText(undefined); setCurrDocPages(undefined); - setTTSText(''); - }, [setCurrDocPages, setTTSText]); + stop(); + }, [setCurrDocPages, stop]); /** * Sets the current document based on its ID @@ -90,7 +90,6 @@ export function EPUBProvider({ children }: { children: ReactNode }) { const { start, end } = rendition.location; if (!start?.cfi || !end?.cfi) return ''; - const rangeCfi = createRangeCfi(start.cfi, end.cfi); const range = await book.getRange(rangeCfi); diff --git a/src/contexts/TTSContext.tsx b/src/contexts/TTSContext.tsx index 189fab8..2b1b3e6 100644 --- a/src/contexts/TTSContext.tsx +++ b/src/contexts/TTSContext.tsx @@ -69,7 +69,7 @@ interface TTSContextType { setSpeedAndRestart: (speed: number) => void; setVoiceAndRestart: (voice: string) => void; skipToPage: (page: number) => void; - skipToLocation: (page: string | number, total: number) => void; + setEPUBPageInChapter: (page: string | number, total: number, chapter: string | number) => void; // Add this line registerLocationChangeHandler: (handler: (location: string | number) => void) => void; } @@ -120,7 +120,7 @@ export function TTSProvider({ children }: { children: React.ReactNode }) { const [isProcessing, setIsProcessing] = useState(false); const [speed, setSpeed] = useState(voiceSpeed); const [voice, setVoice] = useState(configVoice); - const [currDocPage, setCurrDocPage] = useState(0); + const [currDocPage, setCurrDocPage] = useState(1); const [currDocPages, setCurrDocPages] = useState(); const [nextPageLoading, setNextPageLoading] = useState(false); @@ -217,19 +217,25 @@ export function TTSProvider({ children }: { children: React.ReactNode }) { * Navigates to a specific location in the EPUB document * Similar to skipToPage but for EPUB locations */ - const skipToLocation = useCallback((page: string | number, total: number) => { + const [currChapter, setCurrChapter] = useState(''); + const setEPUBPageInChapter = useCallback((page: string | number, total: number, chapter: string | number) => { + const alreadyPlaying = isPlaying; + if (chapter !== currChapter) { + setCurrDocPages(total); + setCurrChapter(chapter); + } + abortAudio(); setIsPlaying(false); setNextPageLoading(true); setCurrentIndex(0); + setSentences([]); setCurrDocPage(Number(page)); - setCurrDocPages(total); - // if (locationChangeHandlerRef.current) { - // setIsPlaying(true); - // } - }, [abortAudio]); + setIsPlaying(alreadyPlaying); + + }, [abortAudio, currChapter]); /** * Moves to the next or previous sentence @@ -238,41 +244,49 @@ export function TTSProvider({ children }: { children: React.ReactNode }) { * @returns {Promise} */ const advance = useCallback(async (backwards = false) => { - setCurrentIndex((prev) => { - const nextIndex = prev + (backwards ? -1 : 1); - if (nextIndex < sentences.length && nextIndex >= 0) { - console.log('Advancing to next sentence:', sentences[nextIndex]); - return nextIndex; - } else if (nextIndex >= sentences.length && currDocPage < currDocPages!) { - console.log('Advancing to next page:', currDocPage + 1); - - // Trigger page turn in React Reader - if (locationChangeHandlerRef.current) { - locationChangeHandlerRef.current('next'); - } else { - incrementPage(); - } - - return 0; - } else if (nextIndex < 0 && currDocPage > 1) { - console.log('Advancing to previous page:', currDocPage - 1); - - // Trigger page turn in React Reader - if (locationChangeHandlerRef.current) { - locationChangeHandlerRef.current('prev'); - } else { - incrementPage(-1); - } - - return 0; - } else if (nextIndex >= sentences.length && currDocPage >= currDocPages!) { - console.log('Reached end of document'); - setIsPlaying(false); - return prev; + const nextIndex = currentIndex + (backwards ? -1 : 1); + + // Handle within current page bounds + if (nextIndex < sentences.length && nextIndex >= 0) { + console.log('Advancing to next sentence:', sentences[nextIndex]); + setCurrentIndex(nextIndex); + return; + } + + // Handle next page + if (nextIndex >= sentences.length && currDocPage < currDocPages!) { + console.log('Advancing to next page:', currDocPage + 1); + setCurrentIndex(0); + + // Trigger page turn in React Reader + if (locationChangeHandlerRef.current) { + locationChangeHandlerRef.current('next'); + } else { + incrementPage(); } - return prev; - }); - }, [sentences, currDocPage, currDocPages]); + return; + } + + // Handle previous page + if (nextIndex < 0 && currDocPage > 1) { + console.log('Advancing to previous page:', currDocPage - 1); + setCurrentIndex(0); + + // Trigger page turn in React Reader + if (locationChangeHandlerRef.current) { + locationChangeHandlerRef.current('prev'); + } else { + incrementPage(-1); + } + return; + } + + // Handle end of document + if (nextIndex >= sentences.length && currDocPage >= currDocPages!) { + console.log('Reached end of document'); + setIsPlaying(false); + } + }, [currentIndex, incrementPage, sentences, currDocPage, currDocPages]); /** * Moves forward one sentence in the text @@ -502,9 +516,14 @@ export function TTSProvider({ children }: { children: React.ReactNode }) { const stop = useCallback(() => { // Cancel any ongoing request abortAudio(); - + locationChangeHandlerRef.current = null; setIsPlaying(false); setCurrentIndex(0); + setSentences([]); + setCurrChapter(''); + setCurrDocPage(1); + setCurrDocPages(undefined); + setNextPageLoading(false); setIsProcessing(false); }, [abortAudio]); @@ -579,7 +598,7 @@ export function TTSProvider({ children }: { children: React.ReactNode }) { setSpeedAndRestart, setVoiceAndRestart, skipToPage, - skipToLocation, // Add this line + setEPUBPageInChapter, // Add this line registerLocationChangeHandler, }), [ isPlaying, @@ -599,7 +618,7 @@ export function TTSProvider({ children }: { children: React.ReactNode }) { setSpeedAndRestart, setVoiceAndRestart, skipToPage, - skipToLocation, // Add this line + setEPUBPageInChapter, // Add this line registerLocationChangeHandler, ]); diff --git a/src/utils/epub.ts b/src/utils/epub.ts index 0644276..3590278 100644 --- a/src/utils/epub.ts +++ b/src/utils/epub.ts @@ -1,5 +1,3 @@ -import { EpubCFI } from 'epubjs'; - export function findCommonBase(startCfi: string, endCfi: string): string { // Remove 'epubcfi(' prefix and ')' suffix const start = startCfi.replace(/^epubcfi\(|\)$/g, '');