gerhardsletten / react-reader

An ePub-reader for React, powered by Epub.js
https://react-reader.metabits.no
Apache License 2.0
676 stars 130 forks source link

EPUB glitching when trying to implement highlighting (Epilepsy warning!) #179

Closed codepeach closed 1 month ago

codepeach commented 1 month ago

I'm currently working with NextJS 14 and everything seemed to be working completely fine until I added the following line in the component, to start working with highlighting and text selection.

getRendition={(_rendition: Rendition) => { setRendition(_rendition); }}

I'm unsure as to how to proceed, as the above line just makes my epub glitch continuously as shown below.

ScreenRecording2024-07-19at3 21 14PM

Here is my full code:

  'use client';

  import React, { useState, useCallback, useEffect, useRef } from 'react';
  import { Button } from '@/components/ui/button';
  import { Menu } from 'lucide-react';
  import dynamic from 'next/dynamic';
  import book from '/public/100m-offers.epub';
  import { ScrollArea } from './ui/scroll-area';
  import { Separator } from './ui/separator';
  import type { Contents, Rendition } from 'epubjs';

  type Props = {};

  type ITextSelection = {
    text: string | undefined;
    cfiRange: string;
  };

const EPUBDisplay = (props: Props) => {
  const ReactReader = dynamic(
    () => import('react-reader').then((res) => res.ReactReader),
    { ssr: false }
  );

const [currentLocation, setCurrentLocation] = useState(0);
const [selections, setSelections] = useState<ITextSelection[]>([]);
const [rendition, setRendition] = useState<Rendition | undefined>(undefined);

const renditionRef = useRef<any>(null);

const [counter, setCounter] = useState(0);
const [sheetOpen, setSheetOpen] = useState(false);
const [isTOC, setIsTOC] = useState<any[]>([]);

const onLocationChanged = useCallback(
  (location: any) => setCurrentLocation(location),
  []
);

useEffect(() => {
  console.log('cfiRange, contents');
  if (rendition) {
    function setRenderSelection(cfiRange: string, contents: Contents) {
      if (rendition) {
        setSelections((list) =>
          list.concat({
            text: rendition.getRange(cfiRange).toString(),
            cfiRange,
          })
        );
        rendition.annotations.add(
          'highlight',
          cfiRange,
          {},
          undefined,
          'hl',
          {
            fill: 'red',
            'fill-opacity': '0.5',
            'mix-blend-mode': 'multiply',
          }
        );
        const selection = contents.window.getSelection();
        selection?.removeAllRanges();
      }
    }
    rendition.on('selected', setRenderSelection);
    return () => {
      rendition?.off('selected', setRenderSelection);
    };
  }
}, [setSelections, rendition]);

  const setTOC = (toc: any) => {
    if (counter == 0) {
      console.log(toc);
      setCounter(counter + 1);
      setIsTOC(toc);
    }
  };
  return (
    <>
      ...
      <ReactReader
        url={book}
        location={currentLocation}
        locationChanged={onLocationChanged}
        showToc={false}
        tocChanged={(toc) => setTOC(toc)}
        epubInitOptions={{
          openAs: 'epub',
        }}
        getRendition={(_rendition: Rendition) => {
          setRendition(_rendition);
        }}

      />
    </>
  );
};

export default EPUBDisplay;
codepeach commented 1 month ago

I was able to solve this issue temporarily by copying this https://github.com/gerhardsletten/react-reader/blob/main/lib/ReactReader/ReactReader.tsx component into my project.