Skip to content

Commit

Permalink
Refactors to fix blank sections
Browse files Browse the repository at this point in the history
  • Loading branch information
richardr1126 committed Feb 19, 2025
1 parent 8acf3f6 commit d447132
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 99 deletions.
52 changes: 17 additions & 35 deletions src/components/EPUBViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useEffect, useRef, useCallback, useMemo } from 'react';
import { useEffect, useRef, useCallback } from 'react';
import { useParams } from 'next/navigation';
import dynamic from 'next/dynamic';
import { useEPUB } from '@/contexts/EPUBContext';
Expand All @@ -10,7 +10,8 @@ import { DocumentSkeleton } from '@/components/DocumentSkeleton';
import TTSPlayer from '@/components/player/TTSPlayer';
import { setLastDocumentLocation } from '@/utils/indexedDB';
import type { Rendition, Book, NavItem } from 'epubjs';
import { useEPUBTheme, getThemeStyles } from '@/hooks/useEPUBTheme';
import { useEPUBTheme, getThemeStyles } from '@/hooks/epub/useEPUBTheme';
import { useEPUBResize } from '@/hooks/epub/useEPUBResize';

const ReactReader = dynamic(() => import('react-reader').then(mod => mod.ReactReader), {
ssr: false,
Expand All @@ -31,8 +32,13 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
const toc = useRef<NavItem[]>([]);
const locationRef = useRef<string | number>(currDocPage);
const { updateTheme } = useEPUBTheme(epubTheme, rendition.current);
const containerRef = useRef<HTMLDivElement>(null);

const isEPUBSetOnce = useRef(false);
const isResizing = useRef(false);

useEPUBResize(containerRef, isResizing);

const handleLocationChanged = useCallback((location: string | number) => {
// Set the EPUB flag once the location changes
if (!isEPUBSetOnce.current) {
Expand Down Expand Up @@ -62,47 +68,23 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
setLastDocumentLocation(id as string, location.toString());
}

skipToLocation(location);
if (isResizing.current) {
skipToLocation(location, false);
isResizing.current = false;
} else {
skipToLocation(location, true);
}

locationRef.current = location;
extractPageText(bookRef.current, rendition.current);

}, [id, skipToLocation, extractPageText, setIsEPUB]);

// Replace the debounced text extraction with a proper implementation using useMemo
const debouncedExtractText = useMemo(() => {
let timeout: NodeJS.Timeout;
return (book: Book, rendition: Rendition) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
extractPageText(book, rendition);
}, 150);
};
}, [extractPageText]);

// Load the initial location and setup resize handler
// Load the initial location
useEffect(() => {
if (!bookRef.current || !rendition.current || isEPUBSetOnce.current) return;

extractPageText(bookRef.current, rendition.current);

// Add resize observer
const resizeObserver = new ResizeObserver(() => {
if (bookRef.current && rendition.current) {
debouncedExtractText(bookRef.current, rendition.current);
}
});

// Observe the container element
const container = document.querySelector('.epub-container');
if (container) {
resizeObserver.observe(container);
}

return () => {
resizeObserver.disconnect();
};
}, [extractPageText, debouncedExtractText]);
}, [extractPageText]);

// Register the location change handler
useEffect(() => {
Expand All @@ -114,7 +96,7 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
}

return (
<div className={`h-screen flex flex-col ${className}`}>
<div className={`h-screen flex flex-col ${className}`} ref={containerRef}>
<div className="z-10">
<TTSPlayer />
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import { GithubIcon } from './icons/Icons'
import { GithubIcon } from '@/components/icons/Icons'

export function Footer() {
return (
Expand Down
25 changes: 2 additions & 23 deletions src/components/PDFViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import { useTTS } from '@/contexts/TTSContext';
import { usePDF } from '@/contexts/PDFContext';
import TTSPlayer from '@/components/player/TTSPlayer';
import { useConfig } from '@/contexts/ConfigContext';
import { debounce } from '@/utils/pdf';
import { usePDFResize } from '@/hooks/pdf/usePDFResize';

interface PDFViewerProps {
zoomLevel: number;
}

export function PDFViewer({ zoomLevel }: PDFViewerProps) {
const [containerWidth, setContainerWidth] = useState<number>(0);
const containerRef = useRef<HTMLDivElement>(null);
const scaleRef = useRef<number>(1);
const { containerWidth } = usePDFResize(containerRef);

// Config context
const { viewType } = useConfig();
Expand Down Expand Up @@ -158,27 +158,6 @@ export function PDFViewer({ zoomLevel }: PDFViewerProps) {
return scaleRef.current;
}, [calculateScale]);

// Modify resize observer effect to use debouncing
useEffect(() => {
if (!containerRef.current) return;

const debouncedResize = debounce((width: unknown) => {
setContainerWidth(Number(width));
}, 150); // 150ms debounce

const observer = new ResizeObserver(entries => {
const width = entries[0]?.contentRect.width;
if (width) {
debouncedResize(width);
}
});

observer.observe(containerRef.current);
return () => {
observer.disconnect();
};
}, []);

return (
<div ref={containerRef} className="flex flex-col items-center overflow-auto max-h-[calc(100vh-100px)] w-full px-6">
<Document
Expand Down
4 changes: 2 additions & 2 deletions src/contexts/DocumentContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client';

import { createContext, useContext, ReactNode } from 'react';
import { usePDFDocuments } from '@/hooks/usePDFDocuments';
import { useEPUBDocuments } from '@/hooks/useEPUBDocuments';
import { usePDFDocuments } from '@/hooks/pdf/usePDFDocuments';
import { useEPUBDocuments } from '@/hooks/epub/useEPUBDocuments';
import { PDFDocument, EPUBDocument } from '@/types/documents';

interface DocumentContextType {
Expand Down
2 changes: 1 addition & 1 deletion src/contexts/EPUBContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
const rangeCfi = createRangeCfi(start.cfi, end.cfi);

const range = await book.getRange(rangeCfi);
const textContent = range.toString();
const textContent = range.toString().trim();

setTTSText(textContent);
setCurrDocText(textContent);
Expand Down
84 changes: 47 additions & 37 deletions src/contexts/TTSContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ interface TTSContextType {
setCurrDocPages: (num: number | undefined) => void;
setSpeedAndRestart: (speed: number) => void;
setVoiceAndRestart: (voice: string) => void;
skipToLocation: (location: string | number) => void;
skipToLocation: (location: string | number, keepPlaying?: boolean) => void;
registerLocationChangeHandler: (handler: (location: string | number) => void) => void; // EPUB-only: Handles chapter navigation
setIsEPUB: (isEPUB: boolean) => void;
}
Expand Down Expand Up @@ -160,6 +160,10 @@ export function TTSProvider({ children }: { children: ReactNode }) {
* @returns {Promise<string[]>} Array of processed sentences
*/
const processTextToSentences = useCallback(async (text: string): Promise<string[]> => {
if (text.length === 0) {
return [];
}

const response = await fetch('/api/nlp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
Expand All @@ -174,14 +178,45 @@ export function TTSProvider({ children }: { children: ReactNode }) {
return sentences;
}, []);

/**
* Stops the current audio playback and clears the active Howl instance
*/
const abortAudio = useCallback(() => {
if (activeHowl) {
activeHowl.stop();
setActiveHowl(null);
}
}, [activeHowl]);

/**
* Navigates to a specific location in the document
* Works for both PDF pages and EPUB locations
*
* @param {string | number} location - The target location to navigate to
*/
const skipToLocation = useCallback((location: string | number, keepPlaying = false) => {
setNextPageLoading(true);

// Reset state for new content
abortAudio();
if (!keepPlaying) {
setIsPlaying(false);
}
setCurrentIndex(0);
setSentences([]);

// Update current page/location
setCurrDocPage(location);
}, [abortAudio]);

/**
* Handles blank text sections based on document type
*
* @param {string[]} sentences - Array of processed sentences
* @returns {boolean} - True if blank section was handled
*/
const handleBlankSection = useCallback((sentences: string[]): boolean => {
if (!isPlaying || !skipBlank || sentences.length > 0) {
const handleBlankSection = useCallback((text: string): boolean => {
if (!isPlaying || !skipBlank || text.length > 0) {
return false;
}

Expand All @@ -205,7 +240,8 @@ export function TTSProvider({ children }: { children: ReactNode }) {
}

if (currDocPageNumber < currDocPages!) {
incrementPage();
// Pass true to keep playing when skipping blank pages
skipToLocation(currDocPageNumber + 1, true);

toast.success(`Skipping blank page ${currDocPageNumber}`, {
id: `page-${currDocPageNumber}`,
Expand All @@ -224,7 +260,7 @@ export function TTSProvider({ children }: { children: ReactNode }) {
}

return false;
}, [isPlaying, skipBlank, isEPUB, currDocPageNumber, currDocPages, incrementPage]);
}, [isPlaying, skipBlank, isEPUB, currDocPageNumber, currDocPages, skipToLocation]);

/**
* Sets the current text and splits it into sentences
Expand All @@ -233,18 +269,21 @@ export function TTSProvider({ children }: { children: ReactNode }) {
*/
const setText = useCallback((text: string) => {
console.log('Setting page text:', text);

if (handleBlankSection(text)) return;

processTextToSentences(text)
.then(newSentences => {
if (handleBlankSection(newSentences)) {
if (newSentences.length === 0) {
console.warn('No sentences found in text');
return;
}

setSentences(newSentences);
setNextPageLoading(false);
})
.catch(error => {
console.error('Error processing text:', error);
console.warn('Error processing text:', error);
toast.error('Failed to process text', {
style: {
background: 'var(--background)',
Expand All @@ -255,16 +294,6 @@ export function TTSProvider({ children }: { children: ReactNode }) {
});
}, [processTextToSentences, handleBlankSection]);

/**
* Stops the current audio playback and clears the active Howl instance
*/
const abortAudio = useCallback(() => {
if (activeHowl) {
activeHowl.stop();
setActiveHowl(null);
}
}, [activeHowl]);

/**
* Toggles the playback state between playing and paused
*/
Expand All @@ -279,25 +308,6 @@ export function TTSProvider({ children }: { children: ReactNode }) {
});
}, [abortAudio]);

/**
* Navigates to a specific location in the document
* Works for both PDF pages and EPUB locations
*
* @param {string | number} location - The target location to navigate to
*/
const skipToLocation = useCallback((location: string | number) => {
setNextPageLoading(true);

// Reset state for new content
abortAudio();
setIsPlaying(false);
setCurrentIndex(0);
setSentences([]);

// Update current page/location
setCurrDocPage(location);
}, [abortAudio]);

/**
* Moves to the next or previous sentence
*
Expand Down
File renamed without changes.
52 changes: 52 additions & 0 deletions src/hooks/epub/useEPUBResize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useEffect, RefObject } from 'react';

export function useEPUBResize(
containerRef: RefObject<HTMLDivElement | null>,
isResizing: RefObject<boolean>
) {
useEffect(() => {
let resizeTimeout: NodeJS.Timeout;

const resizeObserver = new ResizeObserver((entries) => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
console.log('Resizing detected (debounced)', entries[0].contentRect);
isResizing.current = true;
}, 250);
});

const mutationObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length) {
const container = containerRef.current?.querySelector('.epub-container');
if (container) {
console.log('Observer attached to epub-container');
resizeObserver.observe(container);
mutationObserver.disconnect();
break;
}
}
}
});

if (containerRef.current) {
mutationObserver.observe(containerRef.current, {
childList: true,
subtree: true
});

const container = containerRef.current.querySelector('.epub-container');
if (container) {
console.log('Container already exists, attaching observer');
resizeObserver.observe(container);
mutationObserver.disconnect();
}
}

return () => {
clearTimeout(resizeTimeout);
mutationObserver.disconnect();
resizeObserver.disconnect();
};
}, [containerRef, isResizing]);
}
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit d447132

Please # to comment.