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