feat(bookReader): autoorientation, showcontrols on click, no more toast on load
This commit is contained in:
@@ -29,6 +29,30 @@ interface BookReaderProps {
|
|||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ajout du hook pour détecter l'orientation
|
||||||
|
const useOrientation = () => {
|
||||||
|
const [isLandscape, setIsLandscape] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkOrientation = () => {
|
||||||
|
// Vérifier si la fenêtre est plus large que haute
|
||||||
|
setIsLandscape(window.innerWidth > window.innerHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Vérification initiale
|
||||||
|
checkOrientation();
|
||||||
|
|
||||||
|
// Écouter les changements de taille de fenêtre
|
||||||
|
window.addEventListener("resize", checkOrientation);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", checkOrientation);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return isLandscape;
|
||||||
|
};
|
||||||
|
|
||||||
export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
||||||
const [currentPage, setCurrentPage] = useState(book.readProgress?.page || 1);
|
const [currentPage, setCurrentPage] = useState(book.readProgress?.page || 1);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
@@ -36,7 +60,9 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
const [imageError, setImageError] = useState(false);
|
const [imageError, setImageError] = useState(false);
|
||||||
const [isDoublePage, setIsDoublePage] = useState(false);
|
const [isDoublePage, setIsDoublePage] = useState(false);
|
||||||
const [showNavigation, setShowNavigation] = useState(false);
|
const [showNavigation, setShowNavigation] = useState(false);
|
||||||
|
const [showControls, setShowControls] = useState(false);
|
||||||
const pageCache = useRef<PageCache>({});
|
const pageCache = useRef<PageCache>({});
|
||||||
|
const isLandscape = useOrientation();
|
||||||
|
|
||||||
// Ajout d'un état pour le chargement des miniatures
|
// Ajout d'un état pour le chargement des miniatures
|
||||||
const [loadedThumbnails, setLoadedThumbnails] = useState<{ [key: number]: boolean }>({});
|
const [loadedThumbnails, setLoadedThumbnails] = useState<{ [key: number]: boolean }>({});
|
||||||
@@ -384,6 +410,11 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Effet pour gérer le mode double page automatiquement en paysage
|
||||||
|
useEffect(() => {
|
||||||
|
setIsDoublePage(isLandscape);
|
||||||
|
}, [isLandscape]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-background/95 backdrop-blur-sm z-50">
|
<div className="fixed inset-0 bg-background/95 backdrop-blur-sm z-50">
|
||||||
<div
|
<div
|
||||||
@@ -391,13 +422,22 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
onTouchStart={onTouchStart}
|
onTouchStart={onTouchStart}
|
||||||
onTouchMove={onTouchMove}
|
onTouchMove={onTouchMove}
|
||||||
onTouchEnd={onTouchEnd}
|
onTouchEnd={onTouchEnd}
|
||||||
|
onClick={() => setShowControls(!showControls)}
|
||||||
>
|
>
|
||||||
{/* Contenu principal */}
|
{/* Contenu principal */}
|
||||||
<div className="relative h-full w-full flex items-center justify-center">
|
<div className="relative h-full w-full flex items-center justify-center">
|
||||||
{/* Boutons en haut */}
|
{/* Boutons en haut */}
|
||||||
<div className="absolute top-4 left-4 flex items-center gap-2 z-30">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"absolute top-4 left-4 flex items-center gap-2 z-30 transition-all duration-300",
|
||||||
|
showControls ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||||
|
)}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsDoublePage(!isDoublePage)}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsDoublePage(!isDoublePage);
|
||||||
|
}}
|
||||||
className="p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors"
|
className="p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors"
|
||||||
aria-label={
|
aria-label={
|
||||||
isDoublePage ? "Désactiver le mode double page" : "Activer le mode double page"
|
isDoublePage ? "Désactiver le mode double page" : "Activer le mode double page"
|
||||||
@@ -410,7 +450,10 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowNavigation(!showNavigation)}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setShowNavigation(!showNavigation);
|
||||||
|
}}
|
||||||
className="p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors"
|
className="p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors"
|
||||||
aria-label={showNavigation ? "Masquer la navigation" : "Afficher la navigation"}
|
aria-label={showNavigation ? "Masquer la navigation" : "Afficher la navigation"}
|
||||||
>
|
>
|
||||||
@@ -424,8 +467,14 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
{/* Bouton précédent */}
|
{/* Bouton précédent */}
|
||||||
{currentPage > 1 && (
|
{currentPage > 1 && (
|
||||||
<button
|
<button
|
||||||
onClick={handlePreviousPage}
|
onClick={(e) => {
|
||||||
className="absolute left-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors z-20"
|
e.stopPropagation();
|
||||||
|
handlePreviousPage();
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"absolute left-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-background/50 hover:bg-background/80 transition-all duration-300 z-20",
|
||||||
|
showControls ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||||
|
)}
|
||||||
aria-label="Page précédente"
|
aria-label="Page précédente"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="h-8 w-8" />
|
<ChevronLeft className="h-8 w-8" />
|
||||||
@@ -474,8 +523,14 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
{/* Bouton suivant */}
|
{/* Bouton suivant */}
|
||||||
{currentPage < pages.length && (
|
{currentPage < pages.length && (
|
||||||
<button
|
<button
|
||||||
onClick={handleNextPage}
|
onClick={(e) => {
|
||||||
className="absolute right-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors z-20"
|
e.stopPropagation();
|
||||||
|
handleNextPage();
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"absolute right-4 top-1/2 -translate-y-1/2 p-2 rounded-full bg-background/50 hover:bg-background/80 transition-all duration-300 z-20",
|
||||||
|
showControls ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||||
|
)}
|
||||||
aria-label="Page suivante"
|
aria-label="Page suivante"
|
||||||
>
|
>
|
||||||
<ChevronRight className="h-8 w-8" />
|
<ChevronRight className="h-8 w-8" />
|
||||||
@@ -484,8 +539,14 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
{/* Bouton fermer */}
|
{/* Bouton fermer */}
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={(e) => {
|
||||||
className="absolute top-4 right-4 p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors z-30"
|
e.stopPropagation();
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"absolute top-4 right-4 p-2 rounded-full bg-background/50 hover:bg-background/80 transition-all duration-300 z-30",
|
||||||
|
showControls ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||||
|
)}
|
||||||
aria-label="Fermer"
|
aria-label="Fermer"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -508,9 +569,11 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
|
|
||||||
{/* Barre de navigation des pages */}
|
{/* Barre de navigation des pages */}
|
||||||
<div
|
<div
|
||||||
className={`absolute bottom-0 left-0 right-0 bg-background/50 backdrop-blur-sm border-t border-border/40 transition-all duration-300 ease-in-out z-30 ${
|
className={cn(
|
||||||
showNavigation ? "h-32 opacity-100" : "h-0 opacity-0"
|
"absolute bottom-0 left-0 right-0 bg-background/50 backdrop-blur-sm border-t border-border/40 transition-all duration-300 ease-in-out z-30",
|
||||||
}`}
|
showNavigation ? "h-48 opacity-100" : "h-0 opacity-0",
|
||||||
|
showControls ? "" : "pointer-events-none"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{showNavigation && (
|
{showNavigation && (
|
||||||
<>
|
<>
|
||||||
@@ -552,7 +615,7 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
setImageError(false);
|
setImageError(false);
|
||||||
syncReadProgress(pageNumber);
|
syncReadProgress(pageNumber);
|
||||||
}}
|
}}
|
||||||
className={`relative h-24 w-16 flex-shrink-0 rounded-md overflow-hidden transition-all cursor-pointer ${
|
className={`relative h-40 w-28 flex-shrink-0 rounded-md overflow-hidden transition-all cursor-pointer ${
|
||||||
currentPage === pageNumber
|
currentPage === pageNumber
|
||||||
? "ring-2 ring-primary scale-110 z-10"
|
? "ring-2 ring-primary scale-110 z-10"
|
||||||
: "opacity-60 hover:opacity-100 hover:scale-105"
|
: "opacity-60 hover:opacity-100 hover:scale-105"
|
||||||
@@ -576,8 +639,8 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="absolute bottom-0 inset-x-0 h-6 bg-gradient-to-t from-black/60 to-transparent flex items-center justify-center">
|
<div className="absolute bottom-0 inset-x-0 h-8 bg-gradient-to-t from-black/60 to-transparent flex items-center justify-center">
|
||||||
<span className="text-xs text-white font-medium">{pageNumber}</span>
|
<span className="text-sm text-white font-medium">{pageNumber}</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useRouter } from "next/navigation";
|
|||||||
import { KomgaBook } from "@/types/komga";
|
import { KomgaBook } from "@/types/komga";
|
||||||
import { BookReader } from "./BookReader";
|
import { BookReader } from "./BookReader";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
|
|
||||||
interface ClientBookReaderProps {
|
interface ClientBookReaderProps {
|
||||||
book: KomgaBook;
|
book: KomgaBook;
|
||||||
@@ -14,17 +13,9 @@ interface ClientBookReaderProps {
|
|||||||
|
|
||||||
export function ClientBookReader({ book, pages }: ClientBookReaderProps) {
|
export function ClientBookReader({ book, pages }: ClientBookReaderProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { toast } = useToast();
|
|
||||||
const [isReading, setIsReading] = useState(false);
|
const [isReading, setIsReading] = useState(false);
|
||||||
|
|
||||||
const handleStartReading = () => {
|
const handleStartReading = () => {
|
||||||
// Si le livre a une progression de lecture, on l'affiche dans un toast
|
|
||||||
if (book.readProgress && book.readProgress.page && book.readProgress.page > 0) {
|
|
||||||
toast({
|
|
||||||
title: "Reprise de la lecture",
|
|
||||||
description: `Reprise à la page ${book.readProgress.page}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setIsReading(true);
|
setIsReading(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,6 @@ interface ClientBookWrapperProps {
|
|||||||
|
|
||||||
export function ClientBookWrapper({ book, pages }: ClientBookWrapperProps) {
|
export function ClientBookWrapper({ book, pages }: ClientBookWrapperProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Si le livre a une progression de lecture, on l'affiche dans un toast
|
|
||||||
if (book.readProgress && book.readProgress.page && book.readProgress.page > 0) {
|
|
||||||
toast({
|
|
||||||
title: "Reprise de la lecture",
|
|
||||||
description: `Reprise à la page ${book.readProgress.page}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [book.readProgress, toast]);
|
|
||||||
|
|
||||||
const handleCloseReader = () => {
|
const handleCloseReader = () => {
|
||||||
router.back();
|
router.back();
|
||||||
|
|||||||
Reference in New Issue
Block a user