Save reading progress in book config

This commit is contained in:
chrox
2024-10-14 15:58:37 +02:00
parent 3e78f867e5
commit a493fee4ed
12 changed files with 272 additions and 164 deletions
@@ -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;