11'use client' ;
22
3- import { useEffect , useRef , useCallback } from 'react' ;
3+ import { useEffect , useRef , useCallback , useState } from 'react' ;
44import { useParams } from 'next/navigation' ;
55import dynamic from 'next/dynamic' ;
66import { useEPUB } from '@/contexts/EPUBContext' ;
@@ -9,12 +9,80 @@ import { DocumentSkeleton } from '@/components/DocumentSkeleton';
99import TTSPlayer from '@/components/player/TTSPlayer' ;
1010import { setLastDocumentLocation } from '@/utils/indexedDB' ;
1111import type { Rendition , Book , NavItem } from 'epubjs' ;
12+ import { ReactReaderStyle , type IReactReaderStyle } from 'react-reader' ;
13+ import { useConfig } from '@/contexts/ConfigContext' ;
1214
1315const ReactReader = dynamic ( ( ) => import ( 'react-reader' ) . then ( mod => mod . ReactReader ) , {
1416 ssr : false ,
1517 loading : ( ) => < DocumentSkeleton />
1618} ) ;
1719
20+ const colors = {
21+ background : getComputedStyle ( document . documentElement ) . getPropertyValue ( '--background' ) ,
22+ foreground : getComputedStyle ( document . documentElement ) . getPropertyValue ( '--foreground' ) ,
23+ base : getComputedStyle ( document . documentElement ) . getPropertyValue ( '--base' ) ,
24+ offbase : getComputedStyle ( document . documentElement ) . getPropertyValue ( '--offbase' ) ,
25+ muted : getComputedStyle ( document . documentElement ) . getPropertyValue ( '--muted' ) ,
26+ } ;
27+
28+ const getThemeStyles = ( ) : IReactReaderStyle => {
29+ const baseStyle = {
30+ ...ReactReaderStyle ,
31+ readerArea : {
32+ ...ReactReaderStyle . readerArea ,
33+ transition : undefined ,
34+ }
35+ } ;
36+
37+ return {
38+ ...baseStyle ,
39+ arrow : {
40+ ...baseStyle . arrow ,
41+ color : colors . foreground ,
42+ } ,
43+ arrowHover : {
44+ ...baseStyle . arrowHover ,
45+ color : colors . muted ,
46+ } ,
47+ readerArea : {
48+ ...baseStyle . readerArea ,
49+ backgroundColor : colors . base ,
50+ } ,
51+ titleArea : {
52+ ...baseStyle . titleArea ,
53+ color : colors . foreground ,
54+ display : 'none' ,
55+ } ,
56+ tocArea : {
57+ ...baseStyle . tocArea ,
58+ background : colors . base ,
59+ } ,
60+ tocButtonExpanded : {
61+ ...baseStyle . tocButtonExpanded ,
62+ background : colors . offbase ,
63+ } ,
64+ tocButtonBar : {
65+ ...baseStyle . tocButtonBar ,
66+ background : colors . muted ,
67+ } ,
68+ tocButton : {
69+ ...baseStyle . tocButton ,
70+ color : colors . muted ,
71+ } ,
72+ tocAreaButton : {
73+ ...baseStyle . tocAreaButton ,
74+ color : colors . muted ,
75+ backgroundColor : colors . offbase ,
76+ padding : '0.25rem' ,
77+ paddingLeft : '0.5rem' ,
78+ paddingRight : '0.5rem' ,
79+ marginBottom : '0.25rem' ,
80+ borderRadius : '0.25rem' ,
81+ borderColor : 'transparent' ,
82+ } ,
83+ } ;
84+ } ;
85+
1886interface EPUBViewerProps {
1987 className ?: string ;
2088}
@@ -23,13 +91,16 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
2391 const { id } = useParams ( ) ;
2492 const { currDocData, currDocName, currDocPage, extractPageText } = useEPUB ( ) ;
2593 const { setEPUBPageInChapter, registerLocationChangeHandler } = useTTS ( ) ;
94+ const { epubTheme } = useConfig ( ) ;
2695 const bookRef = useRef < Book | null > ( null ) ;
2796 const rendition = useRef < Rendition | undefined > ( undefined ) ;
2897 const toc = useRef < NavItem [ ] > ( [ ] ) ;
2998 const locationRef = useRef < string | number > ( currDocPage ) ;
30-
99+ const [ reloadKey , setReloadKey ] = useState ( 0 ) ;
100+ const [ initialPrevLocLoad , setInitialPrevLocLoad ] = useState ( false ) ;
31101
32102 const handleLocationChanged = useCallback ( ( location : string | number , initial = false ) => {
103+ if ( ! bookRef . current ?. isOpen ) return ;
33104 // Handle special 'next' and 'prev' cases, which
34105 if ( location === 'next' && rendition . current ) {
35106 rendition . current . next ( ) ;
@@ -60,17 +131,55 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
60131
61132 // Add a small delay for initial load to ensure rendition is ready
62133 if ( initial ) {
63- setTimeout ( ( ) => {
64- if ( bookRef . current && rendition . current ) {
65- extractPageText ( bookRef . current , rendition . current ) ;
66- }
67- } , 100 ) ;
134+ setInitialPrevLocLoad ( true ) ;
68135 } else {
69136 extractPageText ( bookRef . current , rendition . current ) ;
70137 }
71138 }
72139 } , [ id , setEPUBPageInChapter , extractPageText ] ) ;
73140
141+ // Load the initial location
142+ useEffect ( ( ) => {
143+ if ( bookRef . current && rendition . current ) {
144+ extractPageText ( bookRef . current , rendition . current ) ;
145+ }
146+ } , [ extractPageText , initialPrevLocLoad ] ) ;
147+
148+ const updateTheme = useCallback ( ( rendition : Rendition ) => {
149+ if ( ! epubTheme ) return ; // Only apply theme if enabled
150+
151+ rendition . themes . override ( 'color' , colors . foreground ) ;
152+ rendition . themes . override ( 'background' , colors . base ) ;
153+ } , [ epubTheme ] ) ;
154+
155+ // Watch for theme changes
156+ useEffect ( ( ) => {
157+ if ( ! epubTheme || ! bookRef . current ?. isOpen || ! rendition . current ) return ;
158+
159+ const observer = new MutationObserver ( ( mutations ) => {
160+ mutations . forEach ( ( mutation ) => {
161+ if ( mutation . attributeName === 'class' ) {
162+ if ( epubTheme ) {
163+ setReloadKey ( prev => prev + 1 ) ;
164+ }
165+ }
166+ } ) ;
167+ } ) ;
168+
169+ observer . observe ( document . documentElement , {
170+ attributes : true ,
171+ attributeFilter : [ 'class' ]
172+ } ) ;
173+
174+ return ( ) => observer . disconnect ( ) ;
175+ } , [ epubTheme ] ) ;
176+
177+ // Watch for epubTheme changes
178+ useEffect ( ( ) => {
179+ if ( ! epubTheme || ! bookRef . current ?. isOpen || ! rendition . current ) return ;
180+ setReloadKey ( prev => prev + 1 ) ;
181+ } , [ epubTheme ] ) ;
182+
74183 // Register the location change handler
75184 useEffect ( ( ) => {
76185 registerLocationChangeHandler ( handleLocationChanged ) ;
@@ -87,13 +196,17 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
87196 </ div >
88197 < div className = "flex-1 -mt-16 pt-16" >
89198 < ReactReader
199+ key = { reloadKey } // Add this line to force remount
90200 location = { locationRef . current }
91201 locationChanged = { handleLocationChanged }
92202 url = { currDocData }
93203 title = { currDocName }
94204 tocChanged = { ( _toc ) => ( toc . current = _toc ) }
95205 showToc = { true }
206+ readerStyles = { epubTheme && getThemeStyles ( ) || undefined }
96207 getRendition = { ( _rendition : Rendition ) => {
208+ updateTheme ( _rendition ) ;
209+
97210 bookRef . current = _rendition . book ;
98211 rendition . current = _rendition ;
99212 } }
0 commit comments