feat: SPA pour les pages existantes
This commit is contained in:
parent
668702b1fb
commit
140cc14316
@@ -15,23 +15,31 @@ import {
|
||||
|
||||
export function HomePage() {
|
||||
const navigate = useNavigate();
|
||||
const { collection, loading, error, loadCollection } = useManga();
|
||||
const {
|
||||
collection,
|
||||
loading,
|
||||
error,
|
||||
isBackgroundLoading,
|
||||
loadCollection,
|
||||
refreshCollectionInBackground
|
||||
} = useManga();
|
||||
|
||||
useEffect(() => {
|
||||
loadCollection();
|
||||
}, [loadCollection]);
|
||||
|
||||
const handleMangaClick = (slug) => {
|
||||
navigate(`/manga/${slug}`);
|
||||
};
|
||||
|
||||
const handleAddMangaClick = (query = '') => {
|
||||
navigate(`/add${query ? `?q=${encodeURIComponent(query)}` : ''}`);
|
||||
};
|
||||
|
||||
const toolbarConfig = {
|
||||
leftSection: [
|
||||
{ icon: faRefresh, label: 'Refresh', onClick: loadCollection },
|
||||
{
|
||||
icon: faRefresh,
|
||||
label: 'Refresh',
|
||||
onClick: refreshCollectionInBackground,
|
||||
active: isBackgroundLoading
|
||||
},
|
||||
{ icon: faSearch, label: 'Search', onClick: () => {} }
|
||||
],
|
||||
rightSection: [
|
||||
@@ -42,7 +50,7 @@ export function HomePage() {
|
||||
]
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
if (loading && !collection) {
|
||||
return <div className="flex justify-center items-center h-screen">Loading...</div>;
|
||||
}
|
||||
|
||||
@@ -51,10 +59,15 @@ export function HomePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout onMangaClick={handleMangaClick} onAddMangaClick={handleAddMangaClick}>
|
||||
<Layout onAddMangaClick={handleAddMangaClick}>
|
||||
<Toolbar {...toolbarConfig} className="sticky top-16 z-10" />
|
||||
<div className="container mx-auto px-4">
|
||||
<MangaGrid mangas={collection?.items || []} onMangaClick={handleMangaClick} />
|
||||
<MangaGrid mangas={collection?.items || []} />
|
||||
{isBackgroundLoading && (
|
||||
<div className="fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-2 rounded-lg shadow-lg">
|
||||
Mise à jour en cours...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Layout } from '../components/Layout/Layout.jsx';
|
||||
import { Toolbar } from '../components/Toolbar/Toolbar.jsx';
|
||||
@@ -25,19 +25,26 @@ export function MangaDetailPage() {
|
||||
getMangaFromCollection
|
||||
} = useManga();
|
||||
|
||||
const [isLoadingDetails, setIsLoadingDetails] = useState(false);
|
||||
|
||||
// Obtenir les données du manga depuis le cache ou la collection
|
||||
const manga = detailedMangas[slug];
|
||||
const collectionManga = getMangaFromCollection(slug);
|
||||
const displayManga = manga || collectionManga;
|
||||
|
||||
useEffect(() => {
|
||||
// Si on n'a pas les détails du manga, on les charge
|
||||
if (!manga) {
|
||||
loadMangaDetail(slug);
|
||||
}
|
||||
}, [slug, manga, loadMangaDetail]);
|
||||
const loadDetails = async () => {
|
||||
if (!manga && displayManga) {
|
||||
setIsLoadingDetails(true);
|
||||
await loadMangaDetail(slug);
|
||||
setIsLoadingDetails(false);
|
||||
} else if (!manga && !displayManga) {
|
||||
await loadMangaDetail(slug);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMangaClick = (mangaSlug) => {
|
||||
navigate(`/manga/${mangaSlug}`);
|
||||
};
|
||||
loadDetails();
|
||||
}, [slug, manga, displayManga, loadMangaDetail]);
|
||||
|
||||
const handleAddMangaClick = (query = '') => {
|
||||
navigate(`/add${query ? `?q=${encodeURIComponent(query)}` : ''}`);
|
||||
@@ -45,8 +52,8 @@ export function MangaDetailPage() {
|
||||
|
||||
const toolbarConfig = {
|
||||
leftSection: [
|
||||
{ icon: faArrowLeft, onClick: () => navigate(-1) },
|
||||
{ icon: faRefresh, onClick: () => loadMangaDetail(slug) }
|
||||
{ icon: faArrowLeft, navigateBack: true },
|
||||
{ icon: faRefresh, onClick: () => {} }
|
||||
],
|
||||
rightSection: [
|
||||
{ icon: faBookmark, onClick: () => {} },
|
||||
@@ -55,7 +62,7 @@ export function MangaDetailPage() {
|
||||
]
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
if (loading && !displayManga) {
|
||||
return <div className="flex justify-center items-center h-screen">Loading...</div>;
|
||||
}
|
||||
|
||||
@@ -63,14 +70,12 @@ export function MangaDetailPage() {
|
||||
return <div className="text-red-500 text-center p-4">{error}</div>;
|
||||
}
|
||||
|
||||
// Utiliser les données de base de la collection pendant le chargement des détails
|
||||
const displayManga = manga || collectionManga;
|
||||
if (!displayManga) {
|
||||
return <div className="text-center p-4">Manga not found</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout onMangaClick={handleMangaClick} onAddMangaClick={handleAddMangaClick}>
|
||||
<Layout onAddMangaClick={handleAddMangaClick}>
|
||||
<Toolbar {...toolbarConfig} className="sticky top-16 z-10" />
|
||||
|
||||
{/* Hero section with manga info */}
|
||||
@@ -91,6 +96,11 @@ export function MangaDetailPage() {
|
||||
<span className="px-2 py-1 bg-green-500 rounded-full text-sm">
|
||||
{displayManga.status}
|
||||
</span>
|
||||
{isLoadingDetails && (
|
||||
<span className="text-sm text-gray-300">
|
||||
Chargement des détails...
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-6 mb-4">
|
||||
@@ -182,6 +192,14 @@ export function MangaDetailPage() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!manga && (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="bg-gray-100 rounded-lg p-4 text-gray-600 text-center">
|
||||
Chargement des chapitres...
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useReader } from '../context/ReaderContext';
|
||||
import { Toolbar } from '../components/Toolbar/Toolbar.jsx';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
faArrowLeft,
|
||||
@@ -104,6 +105,25 @@ export function ReaderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const toolbarConfig = {
|
||||
leftSection: [
|
||||
{ icon: faArrowLeft, navigateBack: true }
|
||||
],
|
||||
rightSection: [
|
||||
{
|
||||
icon: mode === 'classic' ? faScroll : faBookOpen,
|
||||
onClick: () => setMode(mode === 'classic' ? 'scrolling' : 'classic'),
|
||||
label: `Mode ${mode === 'classic' ? 'défilement' : 'page par page'}`
|
||||
},
|
||||
{
|
||||
icon: isFullscreen ? faCompress : faExpand,
|
||||
onClick: toggleFullscreen,
|
||||
label: isFullscreen ? 'Quitter le plein écran' : 'Plein écran'
|
||||
},
|
||||
{ icon: faList, onClick: () => {}, label: 'Chapitres' }
|
||||
]
|
||||
};
|
||||
|
||||
if (loading || (!currentPageData && mode === 'classic' && pages.length === 0)) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-screen bg-gray-900 text-white">
|
||||
@@ -135,39 +155,12 @@ export function ReaderPage() {
|
||||
<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>
|
||||
<Toolbar {...toolbarConfig} />
|
||||
{context && (
|
||||
<div className="text-center flex-1">
|
||||
<span className="font-medium">Chapitre {context.number}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -243,4 +236,4 @@ export function ReaderPage() {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user