You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							424 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							424 lines
						
					
					
						
							12 KiB
						
					
					
				import { useState, useRef } from "react"; | 
						|
import { useSelector, useDispatch } from "react-redux"; | 
						|
import { Provider } from "react-redux"; | 
						|
import { ReactEpubViewer } from "react-epub-viewer"; | 
						|
// import styled from "styled-components"; | 
						|
// containers | 
						|
import Header from "containers/Header"; | 
						|
import Footer from "containers/Footer"; | 
						|
import Nav from "containers/menu/Nav"; | 
						|
import Option from "containers/menu/Option"; | 
						|
import Learning from "containers/menu/Note"; | 
						|
import ContextMenu from "containers/commons/ContextMenu"; | 
						|
import Snackbar from "containers/commons/Snackbar"; | 
						|
// components | 
						|
import ViewerWrapper from "components/commons/ViewerWrapper"; | 
						|
import LoadingView from "LoadingView"; | 
						|
// import BookInfo from "components/nav/BookInfo"; | 
						|
// slices | 
						|
import store from "slices"; | 
						|
import { updateBook, updateCurrentPage, updateToc } from "slices/book"; | 
						|
// hooks | 
						|
import useMenu from "lib/hooks/useMenu"; | 
						|
import useHighlight from "lib/hooks/useHighlight"; | 
						|
// styles | 
						|
import "lib/styles/readerStyle.css"; | 
						|
import viewerLayout from "lib/styles/viewerLayout"; | 
						|
// types | 
						|
import { RootState } from "slices"; | 
						|
import { ViewerRef } from "types"; | 
						|
import Book, { BookStyle, BookOption } from "types/book"; | 
						|
import Page from "types/page"; | 
						|
import Toc from "types/toc"; | 
						|
// import { title } from "node:process"; | 
						|
import ArrowBackIosRoundedIcon from "@material-ui/icons/ArrowBackIosRounded"; | 
						|
import ArrowForwardIosRoundedIcon from "@material-ui/icons/ArrowForwardIosRounded"; | 
						|
import Setting from "components/Setting/setting"; | 
						|
// import MoveBtn from "components/footer/MoveBtn"; | 
						|
 | 
						|
const Reader = ({ url, loadingView }: Props) => { | 
						|
  const dispatch = useDispatch(); | 
						|
  const currentLocation = useSelector<RootState, Page>((state) => { | 
						|
    return state.book.currentLocation; | 
						|
  }); | 
						|
 | 
						|
  const viewerRef = useRef<ViewerRef | any>(null); | 
						|
  const navRef = useRef<HTMLDivElement | null>(null); | 
						|
  const optionRef = useRef<HTMLDivElement | null>(null); | 
						|
  const learningRef = useRef<HTMLDivElement | null>(null); | 
						|
 | 
						|
  const [isContextMenu, setIsContextMenu] = useState<boolean>(false); | 
						|
 | 
						|
  const [bookStyle, setBookStyle] = useState<BookStyle>({ | 
						|
    fontFamily: "Sans", | 
						|
    color: "red", | 
						|
    fontSize: 14, | 
						|
    lineHeight: 1.4, | 
						|
    marginHorizontal: 15, | 
						|
    marginVertical: 5, | 
						|
  }); | 
						|
 | 
						|
  const [bookOption, setBookOption] = useState<BookOption>({ | 
						|
    flow: "paginated", | 
						|
    resizeOnOrientationChange: true, | 
						|
    spread: "auto", | 
						|
  }); | 
						|
 | 
						|
  const [navControl, onNavToggle] = useMenu(navRef, 300); | 
						|
  const [optionControl, onOptionToggle, emitEvent] = useMenu(optionRef, 300); | 
						|
  const [learningControl, onLearningToggle] = useMenu(learningRef, 300); | 
						|
  const { | 
						|
    selection, | 
						|
    onSelection, | 
						|
    onClickHighlight, | 
						|
    onAddHighlight, | 
						|
    onRemoveHighlight, | 
						|
    onUpdateHighlight, | 
						|
  } = useHighlight(viewerRef, setIsContextMenu, bookStyle, bookOption.flow); | 
						|
 | 
						|
  /** | 
						|
   * Change Epub book information | 
						|
   * @param book Epub Book Info | 
						|
   */ | 
						|
  const loadFont = () => { | 
						|
    const iframe = document.querySelector("iframe"); | 
						|
    const new_style_element = document.createElement("style"); | 
						|
    new_style_element.textContent = `@font-face { | 
						|
      font-family: "Nazanin"; | 
						|
      src: url(/react-epub-viewer/css/Nazanin.woff); | 
						|
    } | 
						|
    @font-face { | 
						|
      font-family: "Sans"; | 
						|
      src: url(/react-epub-viewer/css/IRANSansWebFaNum.woff); | 
						|
    }`; | 
						|
    const node = | 
						|
      iframe && iframe.contentWindow && iframe.contentWindow.document; | 
						|
    //   node && node.head && node.head.appendChild(new_style_element); | 
						|
  }; | 
						|
  const onBookInfoChange = (book: Book) => { | 
						|
    loadFont(); | 
						|
    dispatch(updateBook(book)); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Change Epub location | 
						|
   * @param loc epubCFI or href | 
						|
   */ | 
						|
  const onLocationChange = (loc: string) => { | 
						|
    if (!viewerRef.current) return; | 
						|
    viewerRef.current.setLocation(loc); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Move page | 
						|
   * @param type Direction | 
						|
   */ | 
						|
  const onPageMove = (type: "NEXT" | "PREV") => { | 
						|
    //loadFont(); | 
						|
    const node = viewerRef.current; | 
						|
    if (!node || !node.prevPage || !node.nextPage) return; | 
						|
 | 
						|
    type === "NEXT" && node.nextPage(); | 
						|
    type === "PREV" && node.prevPage(); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Set toc | 
						|
   * @param toc Table of Epub contents | 
						|
   */ | 
						|
  const onTocChange = (toc: Toc[]) => { | 
						|
    //  loadFont(); | 
						|
    dispatch(updateToc(toc)); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Set Epub viewer styles | 
						|
   * @param bokkStyle_ viewer style | 
						|
   */ | 
						|
  const onBookStyleChange = (bookStyle_: BookStyle) => setBookStyle(bookStyle_); | 
						|
 | 
						|
  /** | 
						|
   * Set Epub viewer options | 
						|
   * @param bookOption_ viewer option | 
						|
   */ | 
						|
  const onBookOptionChange = (bookOption_: BookOption) => | 
						|
    setBookOption(bookOption_); | 
						|
 | 
						|
  /** | 
						|
   * Change current page | 
						|
   * @param page Epub page | 
						|
   */ | 
						|
  const onPageChange = (page: Page) => dispatch(updateCurrentPage(page)); | 
						|
 | 
						|
  /** | 
						|
   * ContextMenu on | 
						|
   * @param cfiRange CfiRange | 
						|
   */ | 
						|
  const onContextMenu = (cfiRange: string) => { | 
						|
    const result = onSelection(cfiRange); | 
						|
    setIsContextMenu(result); | 
						|
  }; | 
						|
 | 
						|
  /** ContextMenu off */ | 
						|
  const onContextmMenuRemove = () => setIsContextMenu(false); | 
						|
 | 
						|
  // const [settingicon, setsettingicon] = useState(false); | 
						|
 | 
						|
  // const changeiconsettinghandler = () => { | 
						|
  //   setsettingicon(!settingicon); | 
						|
  // }; | 
						|
 | 
						|
  const [isScrollHorizontal, setIsScrollHorizontal] = useState(true); | 
						|
  const onClickDirection = (type: "Horizontal" | "Vertical") => { | 
						|
    if (type === "Horizontal") { | 
						|
      onBookOptionChange({ | 
						|
        ...bookOption, | 
						|
        flow: "paginated", | 
						|
      }); | 
						|
    } else if (type === "Vertical") { | 
						|
      onBookOptionChange({ | 
						|
        ...bookOption, | 
						|
        flow: "scrolled-doc", | 
						|
      }); | 
						|
    } | 
						|
  }; | 
						|
 | 
						|
  const [viewType, setViewType] = useState({ | 
						|
    spread: true, | 
						|
  }); | 
						|
  const onClickViewType = (isSpread: boolean) => { | 
						|
    if (isSpread) { | 
						|
      setViewType({ ...viewType, spread: true }); | 
						|
      onBookOptionChange({ | 
						|
        ...bookOption, | 
						|
        spread: "auto", | 
						|
      }); | 
						|
    } else { | 
						|
      setViewType({ ...viewType, spread: false }); | 
						|
      onBookOptionChange({ | 
						|
        ...bookOption, | 
						|
        spread: "none", | 
						|
      }); | 
						|
    } | 
						|
  }; | 
						|
 | 
						|
  const book = useSelector<RootState, Book>((state) => state.book.book); | 
						|
 | 
						|
  const [CountPage, setCountPage] = useState(0); | 
						|
  const [isOpen, setIsOpen] = useState(false); | 
						|
 | 
						|
  const [color, setcolor] = useState("light-white"); | 
						|
 | 
						|
  // const Colors = [ | 
						|
  //   { name: "dark-white", value: "#fff" }, | 
						|
  //   { name: "dark-blue", value: "#fff" }, | 
						|
  //   { name: "dark-brown", value: "#fff4e1" }, | 
						|
  //   { name: "light-white", value: "#121212" }, | 
						|
  //   { name: "light-brown", value: "#af622e" }, | 
						|
  //   { name: "light-blue", value: "#5858df" }, | 
						|
  // ]; | 
						|
 | 
						|
  const readyColors = [ | 
						|
    { name: "dark-white", bgColor: "#4e4e4e", color: "#fff" }, | 
						|
    { name: "light-white", bgColor: "#fff", color: "#292929" }, | 
						|
    { name: "dark-blue", bgColor: "#5858d3", color: "#fff" }, | 
						|
    { name: "dark-brown", bgColor: "#af622e", color: "#fff4e1" }, | 
						|
    { name: "light-brown", bgColor: "#fff4e1", color: "#af622e" }, | 
						|
    { name: "light-blue", bgColor: "#C3DDFF", color: "#5858df" }, | 
						|
  ]; | 
						|
 | 
						|
  const colorHandler = (color: any) => { | 
						|
    let myColor = readyColors.filter((item) => item.name === color); | 
						|
    return myColor[0].bgColor; | 
						|
  }; | 
						|
 | 
						|
  return ( | 
						|
    <> | 
						|
      <ViewerWrapper> | 
						|
        <Header | 
						|
          nameBook={book.title} | 
						|
          title={currentLocation.chapterName} | 
						|
          onNavToggle={onNavToggle} | 
						|
          onOptionToggle={onOptionToggle} | 
						|
          onLearningToggle={onLearningToggle} | 
						|
          isOpen={isOpen} | 
						|
          setIsOpen={setIsOpen} | 
						|
        /> | 
						|
        <Setting | 
						|
          setcolor={setcolor} | 
						|
          setBookStyle={setBookStyle} | 
						|
          bookStyle={bookStyle} | 
						|
          bookOption={bookOption} | 
						|
          setBookOption={setBookOption} | 
						|
          isOpen={isOpen} | 
						|
        /> | 
						|
 | 
						|
        <nav | 
						|
          style={{ | 
						|
            backgroundColor: colorHandler(color), | 
						|
            color: "green", | 
						|
            display: "flex", | 
						|
            justifyContent: "center", | 
						|
            paddingTop: "1rem", | 
						|
          }} | 
						|
        > | 
						|
          {window.innerWidth < 768 ? ( | 
						|
            <> | 
						|
              <nav onClick={() => setCountPage(CountPage + 1)}> | 
						|
                <button | 
						|
                  style={{ | 
						|
                    opacity: "0", | 
						|
                    position: "absolute", | 
						|
                    left: "0", | 
						|
                    width: "20%", | 
						|
                    height: "85%", | 
						|
                    top: "6rem", | 
						|
                    zIndex: 10, | 
						|
                  }} | 
						|
                  onClick={() => onPageMove("NEXT")} | 
						|
                > | 
						|
                  <ArrowBackIosRoundedIcon /> | 
						|
                </button> | 
						|
              </nav> | 
						|
            </> | 
						|
          ) : ( | 
						|
            <> | 
						|
              <nav | 
						|
                style={{ display: "flex" }} | 
						|
                onClick={() => setCountPage(CountPage + 1)} | 
						|
              > | 
						|
                <button onClick={() => onPageMove("NEXT")}> | 
						|
                  <ArrowBackIosRoundedIcon /> | 
						|
                </button> | 
						|
              </nav> | 
						|
            </> | 
						|
          )} | 
						|
 | 
						|
          <nav className="nav-book"> | 
						|
            <ReactEpubViewer | 
						|
              url={url} | 
						|
              viewerLayout={viewerLayout} | 
						|
              viewerStyle={bookStyle} | 
						|
              viewerOption={bookOption} | 
						|
              onBookInfoChange={onBookInfoChange} | 
						|
              onPageChange={onPageChange} | 
						|
              onTocChange={onTocChange} | 
						|
              onSelection={onContextMenu} | 
						|
              loadingView={loadingView || <LoadingView />} | 
						|
              ref={viewerRef} | 
						|
            /> | 
						|
          </nav> | 
						|
          {window.innerWidth < 768 ? ( | 
						|
            <> | 
						|
              <nav onClick={() => setCountPage(CountPage - 1)}> | 
						|
                <button | 
						|
                  style={{ | 
						|
                    opacity: "0", | 
						|
                    position: "absolute", | 
						|
                    right: "0", | 
						|
                    width: "20%", | 
						|
                    height: "85%", | 
						|
                    top: "6rem", | 
						|
                  }} | 
						|
                  onClick={() => onPageMove("PREV")} | 
						|
                > | 
						|
                  <ArrowForwardIosRoundedIcon /> | 
						|
                </button> | 
						|
              </nav> | 
						|
            </> | 
						|
          ) : ( | 
						|
            <> | 
						|
              <nav | 
						|
                style={{ display: "flex" }} | 
						|
                onClick={() => setCountPage(CountPage - 1)} | 
						|
              > | 
						|
                <button onClick={() => onPageMove("PREV")}> | 
						|
                  <ArrowForwardIosRoundedIcon /> | 
						|
                </button> | 
						|
              </nav> | 
						|
            </> | 
						|
          )} | 
						|
        </nav> | 
						|
 | 
						|
        <Footer | 
						|
          viewType={viewType} | 
						|
          bookOption={bookOption} | 
						|
          onBookOptionChange={onBookOptionChange} | 
						|
          onClickViewType={onClickViewType} | 
						|
          onClickDirection={onClickDirection} | 
						|
          CountPage={CountPage} | 
						|
          title={currentLocation.chapterName} | 
						|
          nowPage={currentLocation.currentPage} | 
						|
          totalPage={currentLocation.totalPage} | 
						|
          onPageMove={onPageMove} | 
						|
        /> | 
						|
      </ViewerWrapper> | 
						|
 | 
						|
      <Nav | 
						|
        control={navControl} | 
						|
        onToggle={onNavToggle} | 
						|
        onLocation={onLocationChange} | 
						|
        ref={navRef} | 
						|
      /> | 
						|
 | 
						|
      <Option | 
						|
        control={optionControl} | 
						|
        bookStyle={bookStyle} | 
						|
        bookOption={bookOption} | 
						|
        bookFlow={bookOption.flow} | 
						|
        onToggle={onOptionToggle} | 
						|
        emitEvent={emitEvent} | 
						|
        onBookStyleChange={onBookStyleChange} | 
						|
        onBookOptionChange={onBookOptionChange} | 
						|
        ref={optionRef} | 
						|
      /> | 
						|
 | 
						|
      <Learning | 
						|
        control={learningControl} | 
						|
        onToggle={onLearningToggle} | 
						|
        onClickHighlight={onClickHighlight} | 
						|
        emitEvent={emitEvent} | 
						|
        viewerRef={viewerRef} | 
						|
        ref={learningRef} | 
						|
      /> | 
						|
 | 
						|
      <ContextMenu | 
						|
        active={isContextMenu} | 
						|
        viewerRef={viewerRef} | 
						|
        selection={selection} | 
						|
        onAddHighlight={onAddHighlight} | 
						|
        onRemoveHighlight={onRemoveHighlight} | 
						|
        onUpdateHighlight={onUpdateHighlight} | 
						|
        onContextmMenuRemove={onContextmMenuRemove} | 
						|
      /> | 
						|
      <Snackbar /> | 
						|
    </> | 
						|
  ); | 
						|
}; | 
						|
 | 
						|
const ReaderWrapper = ({ url, loadingView, colorHandler }: Props) => { | 
						|
  return ( | 
						|
    <> | 
						|
      <Provider store={store}> | 
						|
        <Reader | 
						|
          colorHandler={colorHandler} | 
						|
          url={url} | 
						|
          loadingView={loadingView} | 
						|
        /> | 
						|
      </Provider> | 
						|
    </> | 
						|
  ); | 
						|
}; | 
						|
 | 
						|
interface Props { | 
						|
  colorHandler?: () => void; | 
						|
  viewerStyle?: BookStyle; | 
						|
  url: string; | 
						|
  loadingView?: React.ReactNode; | 
						|
  isOpen?: any; | 
						|
  setIsOpen?: any; | 
						|
  setIsScrollHorizontal?: any; | 
						|
  color?: any; | 
						|
} | 
						|
 | 
						|
export default ReaderWrapper;
 | 
						|
 |