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
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;
|
|
|