import { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { motion, AnimatePresence } from 'framer-motion'; import { Play, Calendar, Music2, Clock, X, ChevronDown, ChevronUp, FileText, ChevronRight, } from 'lucide-react'; import { getAlbumByName } from '@/api'; import { formatDate, calculateTotalDuration } from '@/utils'; import { MobileLightbox } from '@/components/common'; /** * Mobile 앨범 상세 페이지 */ function MobileAlbumDetail() { const { name } = useParams(); const navigate = useNavigate(); const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, teasers: null, photos: null }); const [showAllTracks, setShowAllTracks] = useState(false); const [showDescriptionModal, setShowDescriptionModal] = useState(false); const descriptionHistoryRef = useRef(false); // 앨범 데이터 로드 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, teasers: options.teasers || null, photos: options.photos || null, }); window.history.pushState({ lightbox: true }, ''); }, []); // 앨범 소개 열기 const openDescriptionModal = useCallback(() => { setShowDescriptionModal(true); window.history.pushState({ description: true }, ''); descriptionHistoryRef.current = true; }, []); // 앨범 소개 닫기 const closeDescriptionModal = useCallback(() => { setShowDescriptionModal(false); if (descriptionHistoryRef.current) { descriptionHistoryRef.current = false; window.history.back(); } }, []); // 뒤로가기 처리 (앨범 소개만 - 라이트박스는 MobileLightbox에서 처리) useEffect(() => { const handlePopState = () => { if (showDescriptionModal) { descriptionHistoryRef.current = false; setShowDescriptionModal(false); } }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, [showDescriptionModal]); // 앨범 소개 모달 body 스크롤 방지 (라이트박스는 MobileLightbox에서 처리) useEffect(() => { if (showDescriptionModal) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } return () => { document.body.style.overflow = ''; }; }, [showDescriptionModal]); // 총 재생 시간 계산 const totalDuration = calculateTotalDuration(album?.tracks); if (loading) { return (
); } if (!album) { return (

앨범을 찾을 수 없습니다

); } // 컨셉 정보를 포함한 사진 배열 생성 const allPhotosWithInfo = []; if (album.conceptPhotos) { Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => { conceptPhotos.forEach((p) => allPhotosWithInfo.push({ ...p, originalUrl: p.original_url, mediumUrl: p.medium_url, thumbUrl: p.thumb_url, concept: concept !== 'Default' ? concept : null, members: p.members || '', }) ); }); } const previewCount = 6; const previewPhotos = allPhotosWithInfo.slice(0, previewCount); const displayTracks = showAllTracks ? album.tracks : album.tracks?.slice(0, 5); return ( <>
{/* 앨범 히어로 섹션 */}
{/* 배경 블러 이미지 */}
{/* 콘텐츠 */}
{/* 앨범 커버 */} openLightbox([album.cover_original_url || album.cover_medium_url], 0)} > {album.title} {/* 앨범 정보 */} {album.album_type}

{album.title}

{/* 메타 정보 */}
{formatDate(album.release_date, 'YYYY.MM.DD')}
{album.tracks?.length || 0}곡
{totalDuration}
{/* 앨범 소개 버튼 */} {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 } ) } 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 && ( )}
)} {/* 컨셉 포토 */} {previewPhotos.length > 0 && (

컨셉 포토

{previewPhotos.map((photo, idx) => (
openLightbox( previewPhotos.map((p) => p.originalUrl), idx, { photos: previewPhotos } ) } className="aspect-square bg-gray-100 rounded-xl overflow-hidden shadow-sm" > {`컨셉
))}
{/* 전체보기 버튼 */}
)}
{/* 앨범 소개 다이얼로그 */} {showDescriptionModal && album?.description && ( e.stopPropagation()} > {/* 헤더 */}

앨범 소개

{/* 내용 */}

{album.description}

)}
{/* 라이트박스 */} setLightbox((prev) => ({ ...prev, open: false }))} onIndexChange={(index) => setLightbox((prev) => ({ ...prev, index }))} showCounter={lightbox.images.length > 1} downloadPrefix={`fromis9_${album?.title || 'photo'}`} /> ); } export default MobileAlbumDetail;