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.
 
 
 
 

333 lines
9.3 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 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",
fontSize: 18,
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 [CountPage, setCountPage] = useState(0);
return (
<>
<ViewerWrapper>
<Header
title={currentLocation.chapterName}
onNavToggle={onNavToggle}
onOptionToggle={onOptionToggle}
onLearningToggle={onLearningToggle}
/>
<nav
style={{
display: "flex",
justifyContent: "center",
marginTop: "1rem",
}}
>
{window.innerWidth < 768 ? (
<>
<nav onClick={() => setCountPage(CountPage + 1)}>
<button
style={{
opacity: "0",
position: "absolute",
left: "0",
width: "20%",
height: "85%",
top: "4rem",
zIndex: 10,
}}
onClick={() => onPageMove("NEXT")}
>
<ArrowBackIosRoundedIcon />
</button>
</nav>
</>
) : (
<>
<nav
style={{ display: "flex" }}
onClick={() => setCountPage(CountPage + 1)}
>
<button onClick={() => onPageMove("NEXT")}>
<ArrowBackIosRoundedIcon />
</button>
</nav>
</>
)}
<nav style={{ overflow: "hidden" }}>
<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: "4rem",
}}
onClick={() => onPageMove("PREV")}
>
<ArrowForwardIosRoundedIcon />
</button>
</nav>
</>
) : (
<>
<nav
style={{ display: "flex" }}
onClick={() => setCountPage(CountPage - 1)}
>
<button onClick={() => onPageMove("PREV")}>
<ArrowForwardIosRoundedIcon />
</button>
</nav>
</>
)}
</nav>
<Footer
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 }: Props) => {
return (
<>
<Provider store={store}>
<Reader url={url} loadingView={loadingView} />
</Provider>
</>
);
};
interface Props {
url: string;
loadingView?: React.ReactNode;
}
export default ReaderWrapper;