feat(bookmarks): shared useBookmarks panel + wired into all four readers

This commit is contained in:
2026-09-08 01:06:39 +08:00
parent b2c5c4a445
commit d989668650
5 changed files with 206 additions and 14 deletions
+16 -3
View File
@@ -1,6 +1,8 @@
import { useEffect, useRef, useState } from "react";
import { apiRaw } from "../api/client";
import { useBookmarks } from "../components/Bookmarks";
import { btn } from "../components/ui";
import { toast } from "../components/Toaster";
import { useProgressSaver, type ReaderProps } from "../lib/useProgress";
// 仅声明用到的最小子集,避免依赖 epubjs 自带类型的完整性
@@ -26,10 +28,19 @@ async function loadEpub(): Promise<(data: Uint8Array) => EpubBookLike> {
return m.default as (data: Uint8Array) => EpubBookLike;
}
export default function EpubReader({ book, initialLocator }: ReaderProps) {
export default function EpubReader({ book, initialLocator, initialPercent }: ReaderProps) {
const hostRef = useRef<HTMLDivElement>(null);
const rendRef = useRef<EpubRendition | null>(null);
const saver = useProgressSaver(book.id);
const saver = useProgressSaver(book.id, { locator: initialLocator, percent: initialPercent });
const bm = useBookmarks({
book,
capture: saver.capture,
seek: (l) => {
const cfi = String(l.cfi ?? "");
if (!cfi) return;
Promise.resolve(rendRef.current?.display(cfi)).catch(() => toast("err", "书签定位失败"));
},
});
const [err, setErr] = useState("");
const [nonce, setNonce] = useState(0);
const [ready, setReady] = useState(false);
@@ -90,7 +101,7 @@ export default function EpubReader({ book, initialLocator }: ReaderProps) {
);
return (
<div className="grid h-full grid-rows-[1fr_auto]">
<div className="relative grid h-full grid-rows-[1fr_auto]">
<div className="relative mx-auto min-h-0 w-full max-w-4xl bg-white shadow-2xl ring-1 ring-stone-800">
<div ref={hostRef} className="absolute inset-0" />
{!ready && !err && <div className="grid h-full place-items-center bg-stone-950 text-stone-500">EPUB 加载中…</div>}
@@ -102,7 +113,9 @@ export default function EpubReader({ book, initialLocator }: ReaderProps) {
<button className={btn} onClick={() => rendRef.current?.next()}>
下一页
</button>
{bm.btn}
</div>
{bm.panel}
</div>
);
}