refactor(epub): move reader state to route-local hook
This commit is contained in:
parent
2f067c0a90
commit
fd6a06e467
4 changed files with 40 additions and 41 deletions
|
|
@ -4,13 +4,12 @@ import type { ReactNode } from 'react';
|
|||
|
||||
import { ConfigProvider } from '@/contexts/ConfigContext';
|
||||
import { TTSProvider } from '@/contexts/TTSContext';
|
||||
import { EPUBProvider } from '@/contexts/EPUBContext';
|
||||
|
||||
export default function EpubReaderLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<ConfigProvider>
|
||||
<TTSProvider>
|
||||
<EPUBProvider>{children}</EPUBProvider>
|
||||
{children}
|
||||
</TTSProvider>
|
||||
</ConfigProvider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import { useParams, useRouter } from "next/navigation";
|
||||
import Link from 'next/link';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useEPUB } from '@/contexts/EPUBContext';
|
||||
import { DocumentSkeleton } from '@/components/documents/DocumentSkeleton';
|
||||
import { EPUBViewer } from '@/components/views/EPUBViewer';
|
||||
import { DocumentSettings } from '@/components/documents/DocumentSettings';
|
||||
|
|
@ -20,12 +19,21 @@ import { resolveDocumentId } from '@/lib/client/dexie';
|
|||
import { RateLimitBanner } from '@/components/auth/RateLimitBanner';
|
||||
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { useFeatureFlag } from '@/contexts/RuntimeConfigContext';
|
||||
import { useEPUB } from './useEpubDocument';
|
||||
|
||||
export default function EPUBPage() {
|
||||
const canExportAudiobook = useFeatureFlag('enableAudiobookExport');
|
||||
const { id } = useParams();
|
||||
const router = useRouter();
|
||||
const { setCurrentDocument, currDocName, clearCurrDoc, createFullAudioBook: createEPUBAudioBook, regenerateChapter: regenerateEPUBChapter, bookRef } = useEPUB();
|
||||
const epubState = useEPUB();
|
||||
const {
|
||||
setCurrentDocument,
|
||||
currDocName,
|
||||
clearCurrDoc,
|
||||
createFullAudioBook: createEPUBAudioBook,
|
||||
regenerateChapter: regenerateEPUBChapter,
|
||||
bookRef,
|
||||
} = epubState;
|
||||
const { stop } = useTTS();
|
||||
const { isAtLimit } = useAuthRateLimit();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -209,7 +217,7 @@ export default function EPUBPage() {
|
|||
</div>
|
||||
) : (
|
||||
<div className="h-full w-full" style={{ paddingLeft: `${Math.round(maxPadPx * ((100 - padPct) / 100))}px`, paddingRight: `${Math.round(maxPadPx * ((100 - padPct) / 100))}px` }}>
|
||||
<EPUBViewer className="h-full" />
|
||||
<EPUBViewer className="h-full" epubState={epubState} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
|
|
@ -36,7 +33,7 @@ import {
|
|||
type EpubCanonicalWordToken,
|
||||
} from '@/lib/client/epub/epub-word-highlight';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useConfig } from './ConfigContext';
|
||||
import { useConfig } from '@/contexts/ConfigContext';
|
||||
import type {
|
||||
EpubRenderedLocationWalker,
|
||||
TTSSentenceAlignment,
|
||||
|
|
@ -47,7 +44,7 @@ import type { AudiobookGenerationSettings, TTSSegmentLocator } from '@/types/cli
|
|||
import { isStableEpubLocator } from '@/types/client';
|
||||
import type { CanonicalTtsSegment } from '@/lib/shared/tts-segment-plan';
|
||||
|
||||
interface EPUBContextType {
|
||||
export interface EPUBState {
|
||||
currDocData: ArrayBuffer | undefined;
|
||||
currDocName: string | undefined;
|
||||
currDocPages: number | undefined;
|
||||
|
|
@ -90,8 +87,6 @@ interface EPUBContextType {
|
|||
clearWordHighlights: () => void;
|
||||
}
|
||||
|
||||
const EPUBContext = createContext<EPUBContextType | undefined>(undefined);
|
||||
|
||||
const EPUB_CONTINUATION_CHARS = 5000;
|
||||
|
||||
const stepToNextNode = (node: Node | null, root: Node): Node | null => {
|
||||
|
|
@ -526,12 +521,9 @@ const resolveVisibleSegmentRange = (
|
|||
|
||||
|
||||
/**
|
||||
* Provider component for EPUB functionality
|
||||
* Manages the state and operations for EPUB document handling
|
||||
* @param {Object} props - Component props
|
||||
* @param {ReactNode} props.children - Child components to be wrapped by the provider
|
||||
* Route-local EPUB reader hook.
|
||||
*/
|
||||
export function EPUBProvider({ children }: { children: ReactNode }) {
|
||||
export function useEPUB(): EPUBState {
|
||||
const { setText: setTTSText, currDocPage, currDocPages, setCurrDocPages, stop, skipToLocation, setIsEPUB } = useTTS();
|
||||
const { authEnabled } = useAuthConfig();
|
||||
const { id } = useParams();
|
||||
|
|
@ -1141,8 +1133,7 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
|
||||
|
||||
// Context value memoization
|
||||
const contextValue = useMemo(
|
||||
return useMemo(
|
||||
() => ({
|
||||
setCurrentDocument,
|
||||
currDocData,
|
||||
|
|
@ -1190,23 +1181,4 @@ export function EPUBProvider({ children }: { children: ReactNode }) {
|
|||
clearWordHighlights,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<EPUBContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</EPUBContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook to consume the EPUB context
|
||||
* @returns {EPUBContextType} The EPUB context value
|
||||
* @throws {Error} When used outside of EPUBProvider
|
||||
*/
|
||||
export function useEPUB() {
|
||||
const context = useContext(EPUBContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useEPUB must be used within an EPUBProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
import { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useEPUB } from '@/contexts/EPUBContext';
|
||||
import { useTTS } from '@/contexts/TTSContext';
|
||||
import { useConfig } from '@/contexts/ConfigContext';
|
||||
import { DocumentSkeleton } from '@/components/documents/DocumentSkeleton';
|
||||
import { useEPUBTheme, getThemeStyles } from '@/hooks/epub/useEPUBTheme';
|
||||
import { useEPUBResize } from '@/hooks/epub/useEPUBResize';
|
||||
import { DotsVerticalIcon, ChevronLeftIcon, ChevronRightIcon } from '@/components/icons/Icons';
|
||||
import type { EPUBState } from '@/app/(app)/epub/[id]/useEpubDocument';
|
||||
|
||||
const ReactReader = dynamic(() => import('react-reader').then(mod => mod.ReactReader), {
|
||||
ssr: false,
|
||||
|
|
@ -17,9 +17,29 @@ const ReactReader = dynamic(() => import('react-reader').then(mod => mod.ReactRe
|
|||
|
||||
interface EPUBViewerProps {
|
||||
className?: string;
|
||||
epubState: Pick<
|
||||
EPUBState,
|
||||
| 'currDocData'
|
||||
| 'currDocName'
|
||||
| 'currDocPage'
|
||||
| 'currDocPages'
|
||||
| 'locationRef'
|
||||
| 'handleLocationChanged'
|
||||
| 'bookRef'
|
||||
| 'renditionRef'
|
||||
| 'tocRef'
|
||||
| 'setRendition'
|
||||
| 'extractPageText'
|
||||
| 'highlightSegment'
|
||||
| 'clearHighlights'
|
||||
| 'highlightWordIndex'
|
||||
| 'clearWordHighlights'
|
||||
| 'walkUpcomingRenderedLocations'
|
||||
| 'resolveEpubLocator'
|
||||
>;
|
||||
}
|
||||
|
||||
export function EPUBViewer({ className = '' }: EPUBViewerProps) {
|
||||
export function EPUBViewer({ className = '', epubState }: EPUBViewerProps) {
|
||||
const [isTocOpen, setIsTocOpen] = useState(false);
|
||||
const {
|
||||
currDocData,
|
||||
|
|
@ -39,7 +59,7 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
|
|||
clearWordHighlights,
|
||||
walkUpcomingRenderedLocations,
|
||||
resolveEpubLocator,
|
||||
} = useEPUB();
|
||||
} = epubState;
|
||||
const {
|
||||
registerLocationChangeHandler,
|
||||
registerEpubLocationWalker,
|
||||
|
|
|
|||
Loading…
Reference in a new issue