import { useState, useEffect, useCallback, useRef } from "react"; import "./Graphics.css"; import { useAdminBanners } from "./../../hooks/useAdminBanners"; import { ClipLoader } from "react-spinners"; import { ChevronLeft, ChevronRight } from "lucide-react"; import GraphicsSkeleton from "./GraphicsSkeleton"; export const Graphics = () => { const [currentImage, setCurrentImage] = useState(0); const [progressBarWidth, setProgressBarWidth] = useState(0); const [images, setImages] = useState([]); const [loadingComplete, setLoadingComplete] = useState(false); const [transitionDirection, setTransitionDirection] = useState(""); const [isAutoPlaying, setIsAutoPlaying] = useState(true); // Touch tracking for swipe const [touchStart, setTouchStart] = useState(null); const [touchEnd, setTouchEnd] = useState(null); const [isSwiping, setIsSwiping] = useState(false); const [swipeOffset, setSwipeOffset] = useState(0); // Detect if device is mobile const [isMobile, setIsMobile] = useState(false); // Loading status tracking const totalImagesLoaded = useRef(0); const imagesRef = useRef({}); const { getHomepageImages } = useAdminBanners(); // Check for mobile device on mount and window resize useEffect(() => { const checkMobile = () => { setIsMobile(window.innerWidth <= 768); }; checkMobile(); window.addEventListener("resize", checkMobile); return () => window.removeEventListener("resize", checkMobile); }, []); // Fetch images on component mount useEffect(() => { const fetchImages = async () => { try { // Check if it's a mobile device const isMobile = window.innerWidth <= 768; // or use a more sophisticated detection method // If mobile, fetch mobile and ALL images const fetchedImages = await getHomepageImages( isMobile ? "MOBILE" : "DESKTOP" ); if (fetchedImages?.getHomepageImages?.length) { // Extract URLs from the image config objects const imageUrls = fetchedImages.getHomepageImages.map( (img) => img.url ); // Reset loading state when new images are set totalImagesLoaded.current = 0; setLoadingComplete(false); setImages(imageUrls); } } catch (error) { console.error("Failed to fetch images:", error); } }; fetchImages(); }, []); // Track image loading status const handleImageLoaded = (index) => { totalImagesLoaded.current += 1; // If all images are loaded, set loading complete if (totalImagesLoaded.current === images.length) { setLoadingComplete(true); } }; // Handle automatic image rotation useEffect(() => { if (!images.length || !isAutoPlaying || !loadingComplete) return; const interval = setInterval(() => { if (progressBarWidth >= 100) { setTransitionDirection("right"); setCurrentImage((prevImage) => (prevImage + 1) % images.length); setProgressBarWidth(0); } else { setProgressBarWidth((prevWidth) => prevWidth + 1); } }, 50); return () => clearInterval(interval); }, [progressBarWidth, images, isAutoPlaying, loadingComplete]); // Navigation handlers const goToNextImage = useCallback(() => { if (!loadingComplete) return; setIsAutoPlaying(false); setTransitionDirection("right"); setCurrentImage((prev) => (prev + 1) % images.length); setProgressBarWidth(0); setTimeout(() => setIsAutoPlaying(true), 5000); }, [images.length, loadingComplete]); const goToPrevImage = useCallback(() => { if (!loadingComplete) return; setIsAutoPlaying(false); setTransitionDirection("left"); setCurrentImage((prev) => (prev - 1 + images.length) % images.length); setProgressBarWidth(0); setTimeout(() => setIsAutoPlaying(true), 5000); }, [images.length, loadingComplete]); // Handle direct navigation to a specific image const goToImage = (index) => { if (!loadingComplete) return; setIsAutoPlaying(false); setTransitionDirection(index > currentImage ? "right" : "left"); setCurrentImage(index); setProgressBarWidth(0); setTimeout(() => setIsAutoPlaying(true), 5000); }; // Touch handlers for swipe functionality const handleTouchStart = (e) => { if (!loadingComplete) return; setIsAutoPlaying(false); setTouchStart(e.touches[0].clientX); setTouchEnd(null); setIsSwiping(true); setSwipeOffset(0); }; const handleTouchMove = (e) => { if (!touchStart || !loadingComplete) return; const currentX = e.touches[0].clientX; setTouchEnd(currentX); const offset = currentX - touchStart; setSwipeOffset(offset); }; const handleTouchEnd = () => { if (!touchStart || !touchEnd || !loadingComplete) { setIsSwiping(false); setSwipeOffset(0); setTimeout(() => setIsAutoPlaying(true), 3000); return; } const distance = touchStart - touchEnd; const minSwipeDistance = 30; if (Math.abs(distance) > minSwipeDistance) { if (distance > 0) { goToNextImage(); } else { goToPrevImage(); } } setTouchStart(null); setTouchEnd(null); setIsSwiping(false); setSwipeOffset(0); setTimeout(() => setIsAutoPlaying(true), 3000); }; return (

Featured Promotions

{images.length > 0 ? ( <>
{/* Pre-render ALL images, but only show the current one */} {images.map((src, index) => ( { imagesRef.current[index] = el; }} className={`graphics-image ${ index === currentImage ? "active" : "" } ${loadingComplete ? "loaded" : ""}`} src={src} alt={`Royal Gift featured promotion ${index + 1}`} width="1200" height="600" onLoad={() => handleImageLoaded(index)} style={{ ...(index === currentImage && isSwiping ? { transform: `translateX(${swipeOffset}px)` } : {}), opacity: loadingComplete && index === currentImage ? 1 : 0, visibility: loadingComplete ? "visible" : "hidden", position: "absolute", zIndex: index === currentImage ? 2 : 0, }} /> ))} {/* Mobile indicator text */} {isMobile && loadingComplete && (
{currentImage + 1} / {images.length}
)} {/* Loading overlay that sits on top of images */} {!loadingComplete && (
)}
{loadingComplete && ( <>
{images.map((_, index) => ( ))}
)} ) : ( )}
); }; export default Graphics;