Files
Mangarr/assets/react/app/presentation/pages/ReaderPage.jsx
2025-02-17 12:02:56 +01:00

247 lines
8.1 KiB
JavaScript

import React, { useEffect, useState, useCallback } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useReader } from '../context/ReaderContext';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faArrowLeft,
faList,
faExpand,
faCompress,
faBookOpen,
faScroll
} from '@fortawesome/free-solid-svg-icons';
export function ReaderPage() {
const { chapterId } = useParams();
const navigate = useNavigate();
const {
context,
currentPage,
pages,
loading,
error,
mode,
loadChapterContext,
loadPage,
loadPages,
setCurrentPage,
setMode
} = useReader();
const [currentPageData, setCurrentPageData] = useState(null);
const [isFullscreen, setIsFullscreen] = useState(false);
useEffect(() => {
const initializeChapter = async () => {
const contextLoaded = await loadChapterContext(chapterId);
if (contextLoaded) {
await loadPages(chapterId);
}
};
initializeChapter();
}, [chapterId, loadChapterContext, loadPages]);
useEffect(() => {
if (mode === 'classic' && currentPage > 0 && pages.length > 0) {
loadPage(chapterId, currentPage).then(setCurrentPageData);
}
}, [chapterId, currentPage, loadPage, mode, pages.length]);
const handleKeyDown = useCallback((e) => {
if (mode === 'classic') {
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
if (currentPage > 1) {
setCurrentPage(currentPage - 1);
} else if (context?.navigation.previous) {
navigate(`/reader/${context.navigation.previous.id}`);
}
} else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
if (currentPage < pages.length) {
setCurrentPage(currentPage + 1);
} else if (context?.navigation.next) {
navigate(`/reader/${context.navigation.next.id}`);
}
}
}
}, [currentPage, context, mode, navigate, pages.length, setCurrentPage]);
useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [handleKeyDown]);
const toggleFullscreen = () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
setIsFullscreen(true);
} else {
document.exitFullscreen();
setIsFullscreen(false);
}
};
const handleImageClick = (e) => {
if (mode !== 'classic') return;
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left;
const width = rect.width;
if (x < width / 2) {
// Clic sur la partie gauche
if (currentPage > 1) {
setCurrentPage(currentPage - 1);
} else if (context?.navigation.previous) {
navigate(`/reader/${context.navigation.previous.id}`);
}
} else {
// Clic sur la partie droite
if (currentPage < pages.length) {
setCurrentPage(currentPage + 1);
} else if (context?.navigation.next) {
navigate(`/reader/${context.navigation.next.id}`);
}
}
};
if (loading || (!currentPageData && mode === 'classic' && pages.length === 0)) {
return (
<div className="flex justify-center items-center h-screen bg-gray-900 text-white">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-white mx-auto mb-4"></div>
<div>Chargement du chapitre...</div>
</div>
</div>
);
}
if (error) {
return (
<div className="flex flex-col justify-center items-center h-screen bg-gray-900 text-white">
<div className="text-red-500 text-xl mb-4">{error}</div>
<button
onClick={() => navigate(-1)}
className="px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors"
>
Retour
</button>
</div>
);
}
return (
<div className="min-h-screen bg-gray-900 text-white">
{/* Toolbar */}
<div className="fixed top-0 left-0 right-0 bg-gray-800 z-50">
<div className="container mx-auto px-4">
<div className="h-16 flex items-center justify-between">
<div className="flex items-center space-x-4">
<button
onClick={() => navigate(-1)}
className="text-gray-300 hover:text-white"
>
<FontAwesomeIcon icon={faArrowLeft} />
</button>
{context && (
<div>
<span className="font-medium">Manga title</span>
<span className="mx-2">-</span>
<span>Chapter {context.number}</span>
</div>
)}
</div>
<div className="flex items-center space-x-4">
<button
onClick={() => setMode(mode === 'classic' ? 'scrolling' : 'classic')}
className="text-gray-300 hover:text-white"
title={`Switch to ${mode === 'classic' ? 'scrolling' : 'classic'} mode`}
>
<FontAwesomeIcon icon={mode === 'classic' ? faScroll : faBookOpen} />
</button>
<button
onClick={toggleFullscreen}
className="text-gray-300 hover:text-white"
>
<FontAwesomeIcon icon={isFullscreen ? faCompress : faExpand} />
</button>
<button className="text-gray-300 hover:text-white">
<FontAwesomeIcon icon={faList} />
</button>
</div>
</div>
</div>
</div>
{/* Reader content */}
<div className="pt-16">
{mode === 'classic' ? (
// Mode classique
<div className="relative max-w-5xl mx-auto">
{currentPageData && (
<img
src={`data:${currentPageData.mimeType};base64,${currentPageData.base64Content}`}
alt={`Page ${currentPageData.pageNumber}`}
className="w-full h-auto cursor-pointer"
onClick={handleImageClick}
/>
)}
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-gray-800 px-4 py-2 rounded-lg">
Page {currentPage} of {pages.length}
</div>
</div>
) : (
// Mode scrolling
<div className="max-w-5xl mx-auto space-y-4 p-4">
{pages.map((page, index) => (
<div key={index} className="relative">
{page.base64Content ? (
<img
src={`data:${page.mimeType};base64,${page.base64Content}`}
alt={`Page ${page.pageNumber}`}
className="w-full h-auto"
loading="lazy"
/>
) : (
<div className="w-full aspect-[2/3] bg-gray-800 animate-pulse flex items-center justify-center">
<span className="text-gray-400">Chargement...</span>
</div>
)}
</div>
))}
</div>
)}
</div>
{/* Navigation buttons for classic mode */}
{mode === 'classic' && (
<>
<button
onClick={() => {
if (currentPage > 1) {
setCurrentPage(currentPage - 1);
} else if (context?.navigation.previous) {
navigate(`/reader/${context.navigation.previous.id}`);
}
}}
className="fixed left-4 top-1/2 transform -translate-y-1/2 bg-gray-800 p-4 rounded-full opacity-50 hover:opacity-100 transition-opacity"
>
</button>
<button
onClick={() => {
if (currentPage < pages.length) {
setCurrentPage(currentPage + 1);
} else if (context?.navigation.next) {
navigate(`/reader/${context.navigation.next.id}`);
}
}}
className="fixed right-4 top-1/2 transform -translate-y-1/2 bg-gray-800 p-4 rounded-full opacity-50 hover:opacity-100 transition-opacity"
>
</button>
</>
)}
</div>
);
}