forked from akai/readest
Save reading progress in book config
This commit is contained in:
@@ -1,99 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { BookDoc } from '@/libs/document';
|
||||
|
||||
type FoliateViewerProps = {
|
||||
book: BookDoc;
|
||||
};
|
||||
|
||||
const getCSS = (spacing: number, justify: boolean, hyphenate: boolean) => `
|
||||
@namespace epub "http://www.idpf.org/2007/ops";
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
/* https://github.com/whatwg/html/issues/5426 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
a:link {
|
||||
color: lightblue;
|
||||
}
|
||||
}
|
||||
p, li, blockquote, dd {
|
||||
line-height: ${spacing};
|
||||
text-align: ${justify ? 'justify' : 'start'};
|
||||
-webkit-hyphens: ${hyphenate ? 'auto' : 'manual'};
|
||||
hyphens: ${hyphenate ? 'auto' : 'manual'};
|
||||
-webkit-hyphenate-limit-before: 3;
|
||||
-webkit-hyphenate-limit-after: 2;
|
||||
-webkit-hyphenate-limit-lines: 2;
|
||||
hanging-punctuation: allow-end last;
|
||||
widows: 2;
|
||||
}
|
||||
/* prevent the above from overriding the align attribute */
|
||||
[align="left"] { text-align: left; }
|
||||
[align="right"] { text-align: right; }
|
||||
[align="center"] { text-align: center; }
|
||||
[align="justify"] { text-align: justify; }
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
aside[epub|type~="endnote"],
|
||||
aside[epub|type~="footnote"],
|
||||
aside[epub|type~="note"],
|
||||
aside[epub|type~="rearnote"] {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
interface FoliateView extends HTMLElement {
|
||||
open: (book: BookDoc) => Promise<void>;
|
||||
renderer: {
|
||||
setStyles: (css: string) => void;
|
||||
next: () => Promise<void>;
|
||||
prev: () => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
const FoliateViewer: React.FC<FoliateViewerProps> = ({ book }) => {
|
||||
const viewRef = useRef<HTMLDivElement>(null);
|
||||
const isViewCreated = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isViewCreated.current) return;
|
||||
const openBook = async () => {
|
||||
await import('foliate-js/view.js');
|
||||
const view = document.createElement('foliate-view') as FoliateView;
|
||||
document.body.append(view);
|
||||
viewRef.current?.appendChild(view);
|
||||
|
||||
console.log('Open the book with foliate-view:', book);
|
||||
await view.open(book);
|
||||
if ('setStyles' in view.renderer) {
|
||||
view.renderer.setStyles(getCSS(1.4, true, true));
|
||||
}
|
||||
await view.renderer.next();
|
||||
};
|
||||
|
||||
openBook();
|
||||
isViewCreated.current = true;
|
||||
}, [book]);
|
||||
|
||||
const handleTap = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
const { clientX } = event;
|
||||
const width = window.innerWidth;
|
||||
const leftThreshold = width * 0.5;
|
||||
const rightThreshold = width * 0.5;
|
||||
|
||||
const existingView = viewRef.current?.querySelector('foliate-view') as FoliateView;
|
||||
if (clientX < leftThreshold) {
|
||||
existingView?.renderer?.prev();
|
||||
} else if (clientX > rightThreshold) {
|
||||
existingView?.renderer?.next();
|
||||
}
|
||||
};
|
||||
|
||||
return <div ref={viewRef} onClick={handleTap} />;
|
||||
};
|
||||
|
||||
export default FoliateViewer;
|
||||
@@ -1,62 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { BookDoc, DocumentLoader } from '@/libs/document';
|
||||
|
||||
import Spinner from '@/components/Spinner';
|
||||
import FoliateViewer from './FoliateViewer';
|
||||
|
||||
const ReaderContent = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const id = searchParams.get('id');
|
||||
|
||||
const [bookDoc, setBookDoc] = React.useState<BookDoc>();
|
||||
const { envConfig } = useEnv();
|
||||
const { books, fetchBook } = useReaderStore();
|
||||
const bookState = books[id!] || { loading: true, content: null, error: null };
|
||||
|
||||
React.useEffect(() => {
|
||||
if (id && !bookState.content) {
|
||||
envConfig.initAppService().then((appService) => {
|
||||
fetchBook(appService, id).then((book) => {
|
||||
if (book) {
|
||||
book.lastUpdated = Date.now();
|
||||
appService.updateLibraryBook(book);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
const loadDocument = async () => {
|
||||
const content = bookState.content;
|
||||
if (content) {
|
||||
const { book } = await new DocumentLoader(content.file).open();
|
||||
setBookDoc(book);
|
||||
}
|
||||
};
|
||||
if (bookState.content) {
|
||||
loadDocument();
|
||||
}
|
||||
}, [bookState.content, envConfig, fetchBook, id]);
|
||||
|
||||
if (!bookDoc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{bookState.loading && <Spinner loading={bookState.loading} />}
|
||||
{bookState.error && (
|
||||
<div className='text-center'>
|
||||
<h2 className='text-red-500'>{bookState.error}</h2>
|
||||
</div>
|
||||
)}
|
||||
<FoliateViewer book={bookDoc} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReaderContent;
|
||||
Reference in New Issue
Block a user