Fix epub use theme and saved location
This commit is contained in:
parent
737cf5c9f5
commit
9ae6dd9e82
4 changed files with 149 additions and 148 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import nlp from 'compromise';
|
||||
|
||||
const MAX_BLOCK_LENGTH = 450;
|
||||
const MAX_BLOCK_LENGTH = 350;
|
||||
|
||||
const preprocessSentenceForAudio = (text: string): string => {
|
||||
return text
|
||||
|
|
|
|||
|
|
@ -5,84 +5,18 @@ import { useParams } from 'next/navigation';
|
|||
import dynamic from 'next/dynamic';
|
||||
import { useEPUB } from '@/contexts/EPUBContext';
|
||||
import { useTTS } from '@/contexts/TTSContext';
|
||||
import { useConfig } from '@/contexts/ConfigContext';
|
||||
import { DocumentSkeleton } from '@/components/DocumentSkeleton';
|
||||
import TTSPlayer from '@/components/player/TTSPlayer';
|
||||
import { setLastDocumentLocation } from '@/utils/indexedDB';
|
||||
import type { Rendition, Book, NavItem } from 'epubjs';
|
||||
import { ReactReaderStyle, type IReactReaderStyle } from 'react-reader';
|
||||
import { useConfig } from '@/contexts/ConfigContext';
|
||||
import { useEPUBTheme, getThemeStyles } from '@/hooks/useEPUBTheme';
|
||||
|
||||
const ReactReader = dynamic(() => import('react-reader').then(mod => mod.ReactReader), {
|
||||
ssr: false,
|
||||
loading: () => <DocumentSkeleton />
|
||||
});
|
||||
|
||||
const getThemeStyles = (): IReactReaderStyle => {
|
||||
const baseStyle = {
|
||||
...ReactReaderStyle,
|
||||
readerArea: {
|
||||
...ReactReaderStyle.readerArea,
|
||||
transition: undefined,
|
||||
}
|
||||
};
|
||||
|
||||
const colors = {
|
||||
background: getComputedStyle(document.documentElement).getPropertyValue('--background'),
|
||||
foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'),
|
||||
base: getComputedStyle(document.documentElement).getPropertyValue('--base'),
|
||||
offbase: getComputedStyle(document.documentElement).getPropertyValue('--offbase'),
|
||||
muted: getComputedStyle(document.documentElement).getPropertyValue('--muted'),
|
||||
};
|
||||
|
||||
return {
|
||||
...baseStyle,
|
||||
arrow: {
|
||||
...baseStyle.arrow,
|
||||
color: colors.foreground,
|
||||
},
|
||||
arrowHover: {
|
||||
...baseStyle.arrowHover,
|
||||
color: colors.muted,
|
||||
},
|
||||
readerArea: {
|
||||
...baseStyle.readerArea,
|
||||
backgroundColor: colors.base,
|
||||
},
|
||||
titleArea: {
|
||||
...baseStyle.titleArea,
|
||||
color: colors.foreground,
|
||||
display: 'none',
|
||||
},
|
||||
tocArea: {
|
||||
...baseStyle.tocArea,
|
||||
background: colors.base,
|
||||
},
|
||||
tocButtonExpanded: {
|
||||
...baseStyle.tocButtonExpanded,
|
||||
background: colors.offbase,
|
||||
},
|
||||
tocButtonBar: {
|
||||
...baseStyle.tocButtonBar,
|
||||
background: colors.muted,
|
||||
},
|
||||
tocButton: {
|
||||
...baseStyle.tocButton,
|
||||
color: colors.muted,
|
||||
},
|
||||
tocAreaButton: {
|
||||
...baseStyle.tocAreaButton,
|
||||
color: colors.muted,
|
||||
backgroundColor: colors.offbase,
|
||||
padding: '0.25rem',
|
||||
paddingLeft: '0.5rem',
|
||||
paddingRight: '0.5rem',
|
||||
marginBottom: '0.25rem',
|
||||
borderRadius: '0.25rem',
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
interface EPUBViewerProps {
|
||||
className?: string;
|
||||
}
|
||||
|
|
@ -96,11 +30,11 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
|
|||
const rendition = useRef<Rendition | undefined>(undefined);
|
||||
const toc = useRef<NavItem[]>([]);
|
||||
const locationRef = useRef<string | number>(currDocPage);
|
||||
const [reloadKey, setReloadKey] = useState(0);
|
||||
const [initialPrevLocLoad, setInitialPrevLocLoad] = useState(false);
|
||||
const { updateTheme } = useEPUBTheme(epubTheme, rendition.current);
|
||||
|
||||
const handleLocationChanged = useCallback((location: string | number, initial = false) => {
|
||||
if (!bookRef.current?.isOpen) return;
|
||||
if (!bookRef.current?.isOpen || !rendition.current) return;
|
||||
// Handle special 'next' and 'prev' cases, which
|
||||
if (location === 'next' && rendition.current) {
|
||||
rendition.current.next();
|
||||
|
|
@ -111,32 +45,32 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (bookRef.current && rendition.current) {
|
||||
const { displayed, href } = rendition.current.location.start;
|
||||
const chapter = toc.current.find((item) => item.href === href);
|
||||
|
||||
console.log('Displayed:', displayed, 'Chapter:', chapter);
|
||||
|
||||
if (locationRef.current !== 1) {
|
||||
// Save the location to IndexedDB
|
||||
if (id) {
|
||||
console.log('Saving location:', location);
|
||||
setLastDocumentLocation(id as string, location.toString());
|
||||
}
|
||||
}
|
||||
|
||||
setEPUBPageInChapter(displayed.page, displayed.total, chapter?.label || '');
|
||||
|
||||
// Add a small delay for initial load to ensure rendition is ready
|
||||
if (initial) {
|
||||
rendition.current.display(location.toString()).then(() => {
|
||||
setInitialPrevLocLoad(true);
|
||||
});
|
||||
} else {
|
||||
locationRef.current = location;
|
||||
extractPageText(bookRef.current, rendition.current);
|
||||
const { displayed, href } = rendition.current.location.start;
|
||||
const chapter = toc.current.find((item) => item.href === href);
|
||||
|
||||
console.log('Displayed:', displayed, 'Chapter:', chapter);
|
||||
|
||||
if (locationRef.current !== 1) {
|
||||
// Save the location to IndexedDB
|
||||
if (id) {
|
||||
console.log('Saving location:', location);
|
||||
setLastDocumentLocation(id as string, location.toString());
|
||||
}
|
||||
}
|
||||
|
||||
setEPUBPageInChapter(displayed.page, displayed.total, chapter?.label || '');
|
||||
|
||||
// Add a small delay for initial load to ensure rendition is ready
|
||||
if (initial) {
|
||||
rendition.current.display(location.toString()).then(() => {
|
||||
setInitialPrevLocLoad(true);
|
||||
});
|
||||
} else {
|
||||
locationRef.current = location;
|
||||
extractPageText(bookRef.current, rendition.current);
|
||||
}
|
||||
|
||||
}, [id, setEPUBPageInChapter, extractPageText]);
|
||||
|
||||
// Replace the debounced text extraction with a proper implementation using useMemo
|
||||
|
|
@ -174,46 +108,6 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
|
|||
}
|
||||
}, [extractPageText, debouncedExtractText, initialPrevLocLoad]);
|
||||
|
||||
const updateTheme = useCallback((rendition: Rendition) => {
|
||||
if (!epubTheme) return; // Only apply theme if enabled
|
||||
|
||||
const colors = {
|
||||
foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'),
|
||||
base: getComputedStyle(document.documentElement).getPropertyValue('--base'),
|
||||
};
|
||||
|
||||
rendition.themes.override('color', colors.foreground);
|
||||
rendition.themes.override('background', colors.base);
|
||||
}, [epubTheme]);
|
||||
|
||||
// Watch for theme changes
|
||||
useEffect(() => {
|
||||
if (!epubTheme || !bookRef.current?.isOpen || !rendition.current) return;
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.attributeName === 'class') {
|
||||
if (epubTheme) {
|
||||
setReloadKey(prev => prev + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class']
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [epubTheme]);
|
||||
|
||||
// Watch for epubTheme changes
|
||||
useEffect(() => {
|
||||
if (!epubTheme || !bookRef.current?.isOpen || !rendition.current) return;
|
||||
setReloadKey(prev => prev + 1);
|
||||
}, [epubTheme]);
|
||||
|
||||
// Register the location change handler
|
||||
useEffect(() => {
|
||||
registerLocationChangeHandler(handleLocationChanged);
|
||||
|
|
@ -230,7 +124,7 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
|
|||
</div>
|
||||
<div className="flex-1 -mt-16 pt-16">
|
||||
<ReactReader
|
||||
key={reloadKey} // Add this line to force remount
|
||||
key={'epub-reader'}
|
||||
location={locationRef.current}
|
||||
locationChanged={handleLocationChanged}
|
||||
url={currDocData}
|
||||
|
|
@ -239,10 +133,9 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
|
|||
showToc={true}
|
||||
readerStyles={epubTheme && getThemeStyles() || undefined}
|
||||
getRendition={(_rendition: Rendition) => {
|
||||
updateTheme(_rendition);
|
||||
|
||||
bookRef.current = _rendition.book;
|
||||
rendition.current = _rendition;
|
||||
updateTheme();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ interface EPUBContextType {
|
|||
currDocText: string | undefined;
|
||||
setCurrentDocument: (id: string) => Promise<void>;
|
||||
clearCurrDoc: () => void;
|
||||
onDocumentLoadSuccess: ({ numPages }: { numPages: number }) => void;
|
||||
extractPageText: (book: Book, rendition: Rendition) => Promise<string>;
|
||||
}
|
||||
|
||||
|
|
@ -35,14 +34,6 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
const [currDocName, setCurrDocName] = useState<string>();
|
||||
const [currDocText, setCurrDocText] = useState<string>();
|
||||
|
||||
/**
|
||||
* Handles successful document load
|
||||
*/
|
||||
const onDocumentLoadSuccess = useCallback(({ numPages }: { numPages: number }) => {
|
||||
console.log('EPUB loaded:', numPages);
|
||||
setCurrDocPages(numPages);
|
||||
}, [setCurrDocPages]);
|
||||
|
||||
/**
|
||||
* Clears the current document state
|
||||
*/
|
||||
|
|
@ -106,7 +97,6 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
// Context value memoization
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
onDocumentLoadSuccess,
|
||||
setCurrentDocument,
|
||||
currDocData,
|
||||
currDocName,
|
||||
|
|
@ -117,7 +107,6 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
extractPageText,
|
||||
}),
|
||||
[
|
||||
onDocumentLoadSuccess,
|
||||
setCurrentDocument,
|
||||
currDocData,
|
||||
currDocName,
|
||||
|
|
|
|||
119
src/hooks/useEPUBTheme.ts
Normal file
119
src/hooks/useEPUBTheme.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import { useCallback, useEffect } from 'react';
|
||||
import { Rendition } from 'epubjs';
|
||||
import { ReactReaderStyle, IReactReaderStyle } from 'react-reader';
|
||||
|
||||
export const getThemeStyles = (): IReactReaderStyle => {
|
||||
const baseStyle = {
|
||||
...ReactReaderStyle,
|
||||
readerArea: {
|
||||
...ReactReaderStyle.readerArea,
|
||||
transition: undefined,
|
||||
}
|
||||
};
|
||||
|
||||
const colors = {
|
||||
background: getComputedStyle(document.documentElement).getPropertyValue('--background'),
|
||||
foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'),
|
||||
base: getComputedStyle(document.documentElement).getPropertyValue('--base'),
|
||||
offbase: getComputedStyle(document.documentElement).getPropertyValue('--offbase'),
|
||||
muted: getComputedStyle(document.documentElement).getPropertyValue('--muted'),
|
||||
};
|
||||
|
||||
return {
|
||||
...baseStyle,
|
||||
arrow: {
|
||||
...baseStyle.arrow,
|
||||
color: colors.foreground,
|
||||
},
|
||||
arrowHover: {
|
||||
...baseStyle.arrowHover,
|
||||
color: colors.muted,
|
||||
},
|
||||
readerArea: {
|
||||
...baseStyle.readerArea,
|
||||
backgroundColor: colors.base,
|
||||
},
|
||||
titleArea: {
|
||||
...baseStyle.titleArea,
|
||||
color: colors.foreground,
|
||||
display: 'none',
|
||||
},
|
||||
tocArea: {
|
||||
...baseStyle.tocArea,
|
||||
background: colors.base,
|
||||
},
|
||||
tocButtonExpanded: {
|
||||
...baseStyle.tocButtonExpanded,
|
||||
background: colors.offbase,
|
||||
},
|
||||
tocButtonBar: {
|
||||
...baseStyle.tocButtonBar,
|
||||
background: colors.muted,
|
||||
},
|
||||
tocButton: {
|
||||
...baseStyle.tocButton,
|
||||
color: colors.muted,
|
||||
},
|
||||
tocAreaButton: {
|
||||
...baseStyle.tocAreaButton,
|
||||
color: colors.muted,
|
||||
backgroundColor: colors.offbase,
|
||||
padding: '0.25rem',
|
||||
paddingLeft: '0.5rem',
|
||||
paddingRight: '0.5rem',
|
||||
marginBottom: '0.25rem',
|
||||
borderRadius: '0.25rem',
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const useEPUBTheme = (epubTheme: boolean, rendition: Rendition | undefined) => {
|
||||
const updateTheme = useCallback(() => {
|
||||
if (!epubTheme || !rendition) return;
|
||||
|
||||
const colors = {
|
||||
foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'),
|
||||
base: getComputedStyle(document.documentElement).getPropertyValue('--base'),
|
||||
};
|
||||
|
||||
// Register theme rules instead of using override
|
||||
rendition.themes.registerRules('theme-light', {
|
||||
'body': {
|
||||
'color': colors.foreground,
|
||||
'background-color': colors.base
|
||||
}
|
||||
});
|
||||
|
||||
// Select the theme to apply it
|
||||
rendition.themes.select('theme-light');
|
||||
}, [epubTheme, rendition]);
|
||||
|
||||
// Watch for theme changes
|
||||
useEffect(() => {
|
||||
if (!epubTheme || !rendition) return;
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.attributeName === 'class') {
|
||||
updateTheme();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class']
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [epubTheme, rendition, updateTheme]);
|
||||
|
||||
// Watch for epubTheme changes
|
||||
useEffect(() => {
|
||||
if (!epubTheme || !rendition) return;
|
||||
updateTheme();
|
||||
}, [epubTheme, rendition, updateTheme]);
|
||||
|
||||
return { updateTheme };
|
||||
};
|
||||
Loading…
Reference in a new issue