import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { X, Download, ChevronRight, Info, Users, Tag } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { Swiper, SwiperSlide } from 'swiper/react'; import { Virtual } from 'swiper/modules'; import 'swiper/css'; import { getAlbumByName } from '@/api/albums'; import { LightboxIndicator } from '@/components/common'; /** * Mobile 앨범 갤러리 페이지 */ function MobileAlbumGallery() { const { name } = useParams(); const navigate = useNavigate(); const [selectedIndex, setSelectedIndex] = useState(null); const [showInfo, setShowInfo] = useState(false); const swiperRef = useRef(null); // 앨범 데이터 로드 const { data: album, isLoading: loading } = useQuery({ queryKey: ['album', name], queryFn: () => getAlbumByName(name), enabled: !!name, }); // 앨범 데이터에서 사진 목록 추출 const photos = useMemo(() => { if (!album?.conceptPhotos) return []; const allPhotos = []; Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => { conceptPhotos.forEach((p) => allPhotos.push({ ...p, concept: concept !== 'Default' ? concept : null, }) ); }); return allPhotos; }, [album]); // 라이트박스 열기 const openLightbox = useCallback((index) => { setSelectedIndex(index); window.history.pushState({ lightbox: true }, ''); }, []); // 정보 시트 열기 const openInfo = useCallback(() => { setShowInfo(true); window.history.pushState({ infoSheet: true }, ''); }, []); // 뒤로가기 처리 useEffect(() => { const handlePopState = () => { if (showInfo) { setShowInfo(false); } else if (selectedIndex !== null) { setSelectedIndex(null); } }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, [showInfo, selectedIndex]); // 이미지 다운로드 const downloadImage = useCallback(async () => { const photo = photos[selectedIndex]; if (!photo) return; try { const response = await fetch(photo.original_url); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `fromis9_${album?.title || 'photo'}_${String(selectedIndex + 1).padStart(2, '0')}.webp`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); } catch (error) { console.error('다운로드 오류:', error); } }, [photos, selectedIndex, album?.title]); // 바디 스크롤 방지 useEffect(() => { if (selectedIndex !== null) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } return () => { document.body.style.overflow = ''; }; }, [selectedIndex]); // 사진을 2열로 균등 분배 (높이 기반) const distributePhotos = () => { const leftColumn = []; const rightColumn = []; let leftHeight = 0; let rightHeight = 0; photos.forEach((photo, index) => { const aspectRatio = photo.height && photo.width ? photo.height / photo.width : 1; if (leftHeight <= rightHeight) { leftColumn.push({ ...photo, originalIndex: index }); leftHeight += aspectRatio; } else { rightColumn.push({ ...photo, originalIndex: index }); rightHeight += aspectRatio; } }); return { leftColumn, rightColumn }; }; const { leftColumn, rightColumn } = distributePhotos(); // 현재 사진 정보 const currentPhoto = selectedIndex !== null ? photos[selectedIndex] : null; const hasInfo = currentPhoto?.concept || currentPhoto?.members; // 정보 시트 드래그 핸들러 const handleInfoDragEnd = (_, info) => { if (info.offset.y > 100 || info.velocity.y > 300) { window.history.back(); } }; if (loading) { return (
컨셉 포토
{album?.title}
{photos.length}장의 사진
멤버
{currentPhoto.members}
컨셉
{currentPhoto.concept}