fix(web,api): lecteur — worker pdf.js dédié, shell commun et préférences par livre
Régression worker PDF : le worker pdf.js est désormais instancié une seule fois via un port dédié (?worker&url) et reconfiguré à chaque montage, au lieu d'un workerSrc recalculé qui cassait le rendu. - ReaderShell : chrome commun aux lecteurs (toolbar, zones de tap, statut) et contrat ReaderControls pour EPUB/PDF/CBZ - préférences de lecture par livre (mode horizontal/vertical, fit) : table reader_preferences + migrations idempotentes, module API, DTO partagés, client web avec fallback localStorage hors-ligne Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ArrowLeft, ArrowRight } from "lucide-react";
|
||||
import { ReaderError, readerErrorMessage } from "./ReaderError";
|
||||
import type { ReaderControls } from "./ReaderShell";
|
||||
import type { ReaderMode } from "../api/types";
|
||||
|
||||
type FoliateLocation = {
|
||||
cfi?: string;
|
||||
@ -42,12 +43,16 @@ export function EpubReader({
|
||||
url,
|
||||
locator,
|
||||
backHref,
|
||||
onLocatorChange
|
||||
mode,
|
||||
onLocatorChange,
|
||||
onControlsChange
|
||||
}: {
|
||||
url: string;
|
||||
locator?: string;
|
||||
backHref: string;
|
||||
mode: ReaderMode;
|
||||
onLocatorChange: (locator: string, percent: number) => void;
|
||||
onControlsChange: (controls: ReaderControls) => void;
|
||||
}) {
|
||||
const hostRef = useRef<HTMLDivElement>(null);
|
||||
const viewRef = useRef<FoliateView | null>(null);
|
||||
@ -56,6 +61,16 @@ export function EpubReader({
|
||||
const [error, setError] = useState<string>();
|
||||
const [attempt, setAttempt] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
onControlsChange({
|
||||
canPrevious: !loading && !error,
|
||||
canNext: !loading && !error,
|
||||
positionLabel: loading ? "Ouverture EPUB" : "Lecture integree",
|
||||
onPrevious: () => void viewRef.current?.goLeft(),
|
||||
onNext: () => void viewRef.current?.goRight()
|
||||
});
|
||||
}, [error, loading, onControlsChange]);
|
||||
|
||||
useEffect(() => {
|
||||
locatorRef.current = locator;
|
||||
}, [locator]);
|
||||
@ -108,6 +123,11 @@ export function EpubReader({
|
||||
};
|
||||
}, [attempt, onLocatorChange, url]);
|
||||
|
||||
useEffect(() => {
|
||||
viewRef.current?.classList.toggle("epub-view-vertical", mode === "vertical");
|
||||
viewRef.current?.classList.toggle("epub-view-horizontal", mode === "horizontal");
|
||||
}, [mode]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="epub-reader">
|
||||
@ -124,24 +144,13 @@ export function EpubReader({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="epub-reader">
|
||||
<div className={`epub-reader epub-reader-${mode}`}>
|
||||
{loading && (
|
||||
<div className="reader-fallback">
|
||||
<span>Ouverture EPUB</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="epub-host" ref={hostRef} />
|
||||
<div className="reader-stepper">
|
||||
<button className="ghost-button" onClick={() => void viewRef.current?.goLeft()} disabled={loading}>
|
||||
<ArrowLeft size={16} />
|
||||
Précédent
|
||||
</button>
|
||||
<span>{loading ? "Chargement" : "Lecture intégrée"}</span>
|
||||
<button className="ghost-button" onClick={() => void viewRef.current?.goRight()} disabled={loading}>
|
||||
Suivant
|
||||
<ArrowRight size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user