import React, { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { Typography, Box, CircularProgress, IconButton } from '@mui/material'; import { Home as HomeIcon, ExitToApp as ExitIcon } from '@mui/icons-material'; // Utils import { fetchAllGroups } from '../../Utils/batchUpload'; import { getImageSrc } from '../../Utils/imageUtils'; // Custom Hooks import useImagePreloader from '../../hooks/useImagePreloader'; // Styles moved inline to sx props below function SlideshowPage() { const navigate = useNavigate(); const [allGroups, setAllGroups] = useState([]); const [currentGroupIndex, setCurrentGroupIndex] = useState(0); const [currentImageIndex, setCurrentImageIndex] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [fadeOut, setFadeOut] = useState(false); // Slideshow-Timing Konstanten const IMAGE_DISPLAY_TIME = 4000; // 4 Sekunden pro Bild const TRANSITION_TIME = 500; // 0.5 Sekunden für Fade-Effekt // Image Preloader Hook - lädt nächste 2 Bilder im Hintergrund const { isPreloaded, preloadProgress } = useImagePreloader( allGroups, currentGroupIndex, currentImageIndex, getImageSrc, 2 // Preload next 2 images ); // Gruppen laden useEffect(() => { const loadAllGroups = async () => { try { setLoading(true); const groupsData = await fetchAllGroups(); if (groupsData.groups && groupsData.groups.length > 0) { // Sortiere chronologisch: Jahr (aufsteigend) → Upload-Datum (aufsteigend) const sortedGroups = [...groupsData.groups].sort((a, b) => { // Primär: Nach Jahr sortieren (älteste zuerst) if (a.year !== b.year) { return a.year - b.year; } // Sekundär: Bei gleichem Jahr nach Upload-Datum sortieren return new Date(a.uploadDate) - new Date(b.uploadDate); }); setAllGroups(sortedGroups); setCurrentGroupIndex(0); setCurrentImageIndex(0); } else { setError('Keine Slideshows gefunden'); } } catch (err) { console.error('Fehler beim Laden der Gruppen:', err); setError('Fehler beim Laden der Slideshows'); } finally { setLoading(false); } }; loadAllGroups(); }, []); // Automatischer Slideshow-Wechsel const nextImage = useCallback(() => { if (allGroups.length === 0) return; const currentGroup = allGroups[currentGroupIndex]; if (!currentGroup || !currentGroup.images) return; setFadeOut(true); setTimeout(() => { if (currentImageIndex + 1 < currentGroup.images.length) { // Nächstes Bild in der aktuellen Gruppe setCurrentImageIndex(prev => prev + 1); } else { // Zur nächsten Gruppe wechseln (sequenziell, chronologisch sortiert) const nextGroupIndex = (currentGroupIndex + 1) % allGroups.length; setCurrentGroupIndex(nextGroupIndex); setCurrentImageIndex(0); } setFadeOut(false); }, TRANSITION_TIME); }, [allGroups, currentGroupIndex, currentImageIndex, TRANSITION_TIME]); // Timer für automatischen Wechsel useEffect(() => { if (loading || error || allGroups.length === 0) return; const timer = setInterval(nextImage, IMAGE_DISPLAY_TIME); return () => clearInterval(timer); }, [loading, error, allGroups, nextImage, IMAGE_DISPLAY_TIME]); // Keyboard-Navigation useEffect(() => { const handleKeyPress = (event) => { switch (event.key) { case 'Escape': navigate('/'); break; case ' ': case 'ArrowRight': nextImage(); break; default: break; } }; document.addEventListener('keydown', handleKeyPress); return () => document.removeEventListener('keydown', handleKeyPress); }, [nextImage, navigate]); // Aktuelle Gruppe und Bild const currentGroup = allGroups[currentGroupIndex]; const currentImage = currentGroup?.images?.[currentImageIndex]; // Debug: Log Preload-Status (nur in Development) useEffect(() => { if (process.env.NODE_ENV === 'development' && currentImage) { const currentUrl = getImageSrc(currentImage, false); console.log('[Slideshow Debug]', { group: `${currentGroupIndex + 1}/${allGroups.length}`, image: `${currentImageIndex + 1}/${currentGroup?.images?.length || 0}`, preloaded: isPreloaded(currentUrl), preloadProgress, year: currentGroup?.year, title: currentGroup?.title }); } }, [currentGroupIndex, currentImageIndex, currentImage, currentGroup, allGroups.length, isPreloaded, preloadProgress]); const fullscreenSx = { position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', backgroundColor: '#000', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', zIndex: 9999, overflow: 'hidden' }; const loadingContainerSx = { display: 'flex', flexDirection: 'column', alignItems: 'center', color: 'white' }; if (loading) { return ( Slideshow wird geladen... ); } const homeButtonSx = { position: 'absolute', top: '20px', left: '20px', color: 'white', backgroundColor: 'rgba(0,0,0,0.5)', '&:hover': { backgroundColor: 'rgba(0,0,0,0.8)' } }; if (error) { return ( {error} navigate('/')} title="Zur Startseite"> ); } if (!currentGroup || !currentImage) { return ( Keine Bilder verfügbar navigate('/')} title="Zur Startseite"> ); } const exitButtonSx = { position: 'absolute', top: '20px', right: '20px', color: 'white', backgroundColor: 'rgba(0,0,0,0.5)', '&:hover': { backgroundColor: 'rgba(0,0,0,0.8)' } }; const slideshowImageSx = { maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', transition: `opacity ${TRANSITION_TIME}ms ease-in-out` }; const imageDescriptionSx = { position: 'fixed', bottom: '140px', left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0,0,0,0.7)', p: '15px 30px', borderRadius: '8px', maxWidth: '80%', textAlign: 'center', backdropFilter: 'blur(5px)', zIndex: 10002 }; const imageDescriptionTextSx = { color: 'white', fontSize: '18px', margin: 0, lineHeight: 1.4, fontFamily: 'roboto' }; const descriptionContainerSx = { position: 'fixed', left: 40, bottom: 40, backgroundColor: 'rgba(0,0,0,0.8)', p: '25px 35px', borderRadius: '12px', maxWidth: '35vw', minWidth: '260px', textAlign: 'left', backdropFilter: 'blur(5px)', zIndex: 10001, boxShadow: '0 4px 24px rgba(0,0,0,0.4)' }; const titleTextSx = { color: 'white', fontSize: '28px', fontWeight: 500, mb: 1, fontFamily: 'roboto' }; const yearAuthorTextSx = { color: '#FFD700', fontSize: '18px', fontWeight: 400, mb: 1, fontFamily: 'roboto' }; const descriptionTextSx = { color: '#E0E0E0', fontSize: '16px', fontWeight: 300, mb: 1, fontFamily: 'roboto', lineHeight: 1.4 }; const metaTextSx = { color: '#999', fontSize: '12px', mt: 1, fontFamily: 'roboto' }; return ( {/* Navigation Buttons */} navigate('/')} title="Zur Startseite"> navigate('/')} title="Slideshow beenden"> {/* Hauptbild */} {/* Bildbeschreibung (wenn vorhanden) */} {currentImage.imageDescription && ( {currentImage.imageDescription} )} {/* Beschreibung */} {/* Titel */} {currentGroup.title || 'Unbenanntes Projekt'} {/* Jahr und Name */} {currentGroup.year}{currentGroup.name && ` • ${currentGroup.name}`} {/* Beschreibung (wenn vorhanden) */} {currentGroup.description && {currentGroup.description}} {/* Meta-Informationen */} Bild {currentImageIndex + 1} von {currentGroup.images.length} • Slideshow {currentGroupIndex + 1} von {allGroups.length} ); } export default SlideshowPage;