import { motion, AnimatePresence } from 'framer-motion'; import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { Play, Calendar, Music2, Clock, X, Download, ChevronDown, ChevronUp, FileText, ChevronRight } from 'lucide-react'; import { Swiper, SwiperSlide } from 'swiper/react'; import { Virtual } from 'swiper/modules'; import 'swiper/css'; import { getAlbumByName } from '../../../api/public/albums'; import { formatDate } from '../../../utils/date'; import LightboxIndicator from '../../../components/common/LightboxIndicator'; function MobileAlbumDetail() { const { name } = useParams(); const navigate = useNavigate(); const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, showNav: true }); const [showAllTracks, setShowAllTracks] = useState(false); const [showDescriptionModal, setShowDescriptionModal] = useState(false); const swiperRef = useRef(null); // useQuery로 앨범 데이터 로드 const { data: album, isLoading: loading } = useQuery({ queryKey: ['album', name], queryFn: () => getAlbumByName(name), enabled: !!name, }); // 라이트박스 열기 - 히스토리 추가 const openLightbox = useCallback((images, index, options = {}) => { setLightbox({ open: true, images, index, showNav: options.showNav !== false, teasers: options.teasers }); window.history.pushState({ lightbox: true }, ''); }, []); const closeLightbox = useCallback(() => { setLightbox(prev => ({ ...prev, open: false })); }, []); // 앨범 소개 열기 - 히스토리 추가 const openDescriptionModal = useCallback(() => { setShowDescriptionModal(true); window.history.pushState({ description: true }, ''); }, []); const closeDescriptionModal = useCallback(() => { setShowDescriptionModal(false); }, []); // 뒤로가기 처리 useEffect(() => { const handlePopState = () => { if (showDescriptionModal) { setShowDescriptionModal(false); } else if (lightbox.open) { setLightbox(prev => ({ ...prev, open: false })); } }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, [showDescriptionModal, lightbox.open]); // 이미지 다운로드 const downloadImage = useCallback(async () => { const imageUrl = lightbox.images[lightbox.index]; if (!imageUrl) return; try { const response = await fetch(imageUrl); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `fromis9_photo_${String(lightbox.index + 1).padStart(2, '0')}.webp`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); } catch (error) { console.error('다운로드 오류:', error); } }, [lightbox.images, lightbox.index]); // 라이트박스 body 스크롤 방지 useEffect(() => { if (lightbox.open || showDescriptionModal) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } return () => { document.body.style.overflow = ''; }; }, [lightbox.open, showDescriptionModal]); // 총 재생 시간 계산 const getTotalDuration = () => { if (!album?.tracks) return ''; let totalSeconds = 0; album.tracks.forEach(track => { if (track.duration) { const parts = track.duration.split(':'); totalSeconds += parseInt(parts[0]) * 60 + parseInt(parts[1]); } }); const mins = Math.floor(totalSeconds / 60); const secs = totalSeconds % 60; return `${mins}:${String(secs).padStart(2, '0')}`; }; if (loading) { return (
); } if (!album) { return (

앨범을 찾을 수 없습니다

); } // 모든 컨셉 포토를 하나의 배열로 const allPhotos = album.conceptPhotos ? Object.values(album.conceptPhotos).flat() : []; const displayTracks = showAllTracks ? album.tracks : album.tracks?.slice(0, 5); return ( <>
{/* 앨범 히어로 섹션 - 커버 이미지 배경 */}
{/* 배경 블러 이미지 */}
{/* 콘텐츠 */}
{/* 앨범 커버 */} openLightbox( [album.cover_original_url || album.cover_medium_url], 0, { showNav: false } )} > {album.title} {/* 앨범 정보 */} {album.album_type}

{album.title}

{/* 메타 정보 */}
{formatDate(album.release_date, 'YYYY.MM.DD')}
{album.tracks?.length || 0}곡
{getTotalDuration()}
{/* 앨범 소개 버튼 */} {album.description && ( )}
{/* 티저 이미지 */} {album.teasers && album.teasers.length > 0 && (

티저 이미지

{album.teasers.map((teaser, index) => (
openLightbox( album.teasers.map(t => t.media_type === 'video' ? (t.video_url || t.original_url) : t.original_url ), index, { teasers: album.teasers, showNav: true } )} className="w-24 h-24 flex-shrink-0 bg-gray-100 rounded-2xl overflow-hidden relative shadow-sm" > {`Teaser {teaser.media_type === 'video' && (
)}
))}
)} {/* 수록곡 */} {album.tracks && album.tracks.length > 0 && (

수록곡

{displayTracks?.map((track) => (
navigate(`/album/${encodeURIComponent(album.title)}/track/${encodeURIComponent(track.title)}`)} className="flex items-center gap-3 py-2.5 px-3 rounded-xl hover:bg-gray-50 active:bg-gray-100 transition-colors cursor-pointer" > {String(track.track_number).padStart(2, '0')}

{track.title}

{track.is_title_track === 1 && ( TITLE )}
{track.duration || '-'}
))}
{/* 더보기/접기 버튼 */} {album.tracks.length > 5 && ( )}
)} {/* 컨셉 포토 */} {allPhotos.length > 0 && (

컨셉 포토

{allPhotos.slice(0, 6).map((photo, idx) => (
openLightbox( [photo.original_url], 0, { showNav: false } )} className="aspect-square bg-gray-100 rounded-xl overflow-hidden shadow-sm" > {`컨셉
))}
{/* 전체보기 버튼 - 모바일 스타일 */}
)}
{/* 앨범 소개 다이얼로그 */} {showDescriptionModal && album?.description && ( window.history.back()} > { if (info.offset.y > 100 || info.velocity.y > 300) { window.history.back(); } }} className="bg-white rounded-t-3xl w-full max-h-[80vh] overflow-hidden" onClick={(e) => e.stopPropagation()} > {/* 드래그 핸들 */}
{/* 헤더 */}

앨범 소개

{/* 내용 */}

{album.description}

)} {/* 라이트박스 - Swiper ViewPager 스타일 */} {lightbox.open && ( {/* 상단 헤더 - 3등분 */}
{lightbox.showNav && lightbox.images.length > 1 && ( {lightbox.index + 1} / {lightbox.images.length} )}
{/* Swiper */} { swiperRef.current = swiper; }} onSlideChange={(swiper) => setLightbox(prev => ({ ...prev, index: swiper.activeIndex }))} className="w-full h-full" spaceBetween={0} slidesPerView={1} resistance={true} resistanceRatio={0.5} > {lightbox.images.map((url, index) => (
{lightbox.teasers?.[index]?.media_type === 'video' ? (
))}
{/* 모바일용 인디케이터 */} {lightbox.showNav && lightbox.images.length > 1 && ( swiperRef.current?.slideTo(i)} width={120} /> )}
)}
); } export default MobileAlbumDetail;