웹: AlbumDetail, AlbumGallery 페이지 useQuery로 리팩토링
This commit is contained in:
parent
443bd203ca
commit
dc65858d6b
4 changed files with 65 additions and 89 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
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 { Play, Calendar, Music2, Clock, X, Download, ChevronDown, ChevronUp, FileText, ChevronRight } from 'lucide-react';
|
||||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||||
import { Virtual } from 'swiper/modules';
|
import { Virtual } from 'swiper/modules';
|
||||||
|
|
@ -9,17 +10,21 @@ import { getAlbumByName } from '../../../api/public/albums';
|
||||||
import { formatDate } from '../../../utils/date';
|
import { formatDate } from '../../../utils/date';
|
||||||
import LightboxIndicator from '../../../components/common/LightboxIndicator';
|
import LightboxIndicator from '../../../components/common/LightboxIndicator';
|
||||||
|
|
||||||
// 모바일 앨범 상세 페이지
|
|
||||||
function MobileAlbumDetail() {
|
function MobileAlbumDetail() {
|
||||||
const { name } = useParams();
|
const { name } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [album, setAlbum] = useState(null);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, showNav: true });
|
const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0, showNav: true });
|
||||||
const [showAllTracks, setShowAllTracks] = useState(false);
|
const [showAllTracks, setShowAllTracks] = useState(false);
|
||||||
const [showDescriptionModal, setShowDescriptionModal] = useState(false);
|
const [showDescriptionModal, setShowDescriptionModal] = useState(false);
|
||||||
const swiperRef = useRef(null);
|
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 = {}) => {
|
const openLightbox = useCallback((images, index, options = {}) => {
|
||||||
setLightbox({ open: true, images, index, showNav: options.showNav !== false, teasers: options.teasers });
|
setLightbox({ open: true, images, index, showNav: options.showNav !== false, teasers: options.teasers });
|
||||||
|
|
@ -85,18 +90,6 @@ function MobileAlbumDetail() {
|
||||||
return () => { document.body.style.overflow = ''; };
|
return () => { document.body.style.overflow = ''; };
|
||||||
}, [lightbox.open, showDescriptionModal]);
|
}, [lightbox.open, showDescriptionModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getAlbumByName(name)
|
|
||||||
.then(data => {
|
|
||||||
setAlbum(data);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('앨범 로드 오류:', error);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, [name]);
|
|
||||||
|
|
||||||
// 총 재생 시간 계산
|
// 총 재생 시간 계산
|
||||||
const getTotalDuration = () => {
|
const getTotalDuration = () => {
|
||||||
if (!album?.tracks) return '';
|
if (!album?.tracks) return '';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { X, Download, ChevronRight, Info, Users, Tag } from 'lucide-react';
|
import { X, Download, ChevronRight, Info, Users, Tag } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||||
|
|
@ -12,34 +13,29 @@ import LightboxIndicator from '../../../components/common/LightboxIndicator';
|
||||||
function MobileAlbumGallery() {
|
function MobileAlbumGallery() {
|
||||||
const { name } = useParams();
|
const { name } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [album, setAlbum] = useState(null);
|
|
||||||
const [photos, setPhotos] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [selectedIndex, setSelectedIndex] = useState(null);
|
const [selectedIndex, setSelectedIndex] = useState(null);
|
||||||
const [showInfo, setShowInfo] = useState(false);
|
const [showInfo, setShowInfo] = useState(false);
|
||||||
const swiperRef = useRef(null);
|
const swiperRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
// useQuery로 앨범 데이터 로드
|
||||||
getAlbumByName(name)
|
const { data: album, isLoading: loading } = useQuery({
|
||||||
.then(data => {
|
queryKey: ['album', name],
|
||||||
setAlbum(data);
|
queryFn: () => getAlbumByName(name),
|
||||||
const allPhotos = [];
|
enabled: !!name,
|
||||||
if (data.conceptPhotos && typeof data.conceptPhotos === 'object') {
|
});
|
||||||
Object.entries(data.conceptPhotos).forEach(([concept, photos]) => {
|
|
||||||
photos.forEach(p => allPhotos.push({
|
// 앨범 데이터에서 사진 목록 추출
|
||||||
...p,
|
const photos = useMemo(() => {
|
||||||
concept: concept !== 'Default' ? concept : null
|
if (!album?.conceptPhotos) return [];
|
||||||
}));
|
const allPhotos = [];
|
||||||
});
|
Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => {
|
||||||
}
|
conceptPhotos.forEach(p => allPhotos.push({
|
||||||
setPhotos(allPhotos);
|
...p,
|
||||||
setLoading(false);
|
concept: concept !== 'Default' ? concept : null
|
||||||
})
|
}));
|
||||||
.catch(error => {
|
});
|
||||||
console.error('앨범 데이터 로드 오류:', error);
|
return allPhotos;
|
||||||
setLoading(false);
|
}, [album]);
|
||||||
});
|
|
||||||
}, [name]);
|
|
||||||
|
|
||||||
// 라이트박스 열기 - 히스토리 추가
|
// 라이트박스 열기 - 히스토리 추가
|
||||||
const openLightbox = useCallback((index) => {
|
const openLightbox = useCallback((index) => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useState, useEffect, useCallback, memo } from 'react';
|
import { useState, useEffect, useCallback, memo } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { Calendar, Music2, Clock, X, ChevronLeft, ChevronRight, Download, MoreVertical, FileText } from 'lucide-react';
|
import { Calendar, Music2, Clock, X, ChevronLeft, ChevronRight, Download, MoreVertical, FileText } from 'lucide-react';
|
||||||
import { getAlbumByName } from '../../../api/public/albums';
|
import { getAlbumByName } from '../../../api/public/albums';
|
||||||
|
|
@ -9,15 +10,20 @@ import LightboxIndicator from '../../../components/common/LightboxIndicator';
|
||||||
function AlbumDetail() {
|
function AlbumDetail() {
|
||||||
const { name } = useParams();
|
const { name } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [album, setAlbum] = useState(null);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0 });
|
const [lightbox, setLightbox] = useState({ open: false, images: [], index: 0 });
|
||||||
const [slideDirection, setSlideDirection] = useState(0);
|
const [slideDirection, setSlideDirection] = useState(0);
|
||||||
const [imageLoaded, setImageLoaded] = useState(false);
|
const [imageLoaded, setImageLoaded] = useState(false);
|
||||||
const [preloadedImages] = useState(() => new Set()); // 프리로드된 이미지 URL 추적
|
const [preloadedImages] = useState(() => new Set());
|
||||||
const [showDescriptionModal, setShowDescriptionModal] = useState(false);
|
const [showDescriptionModal, setShowDescriptionModal] = useState(false);
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
|
|
||||||
|
// useQuery로 앨범 데이터 로드
|
||||||
|
const { data: album, isLoading: loading } = useQuery({
|
||||||
|
queryKey: ['album', name],
|
||||||
|
queryFn: () => getAlbumByName(name),
|
||||||
|
enabled: !!name,
|
||||||
|
});
|
||||||
|
|
||||||
// 라이트박스 네비게이션 함수
|
// 라이트박스 네비게이션 함수
|
||||||
const goToPrev = useCallback(() => {
|
const goToPrev = useCallback(() => {
|
||||||
if (lightbox.images.length <= 1) return;
|
if (lightbox.images.length <= 1) return;
|
||||||
|
|
@ -125,18 +131,6 @@ function AlbumDetail() {
|
||||||
});
|
});
|
||||||
}, [lightbox.open, lightbox.index, lightbox.images, preloadedImages]);
|
}, [lightbox.open, lightbox.index, lightbox.images, preloadedImages]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getAlbumByName(name)
|
|
||||||
.then(data => {
|
|
||||||
setAlbum(data);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('앨범 데이터 로드 오류:', error);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, [name]);
|
|
||||||
|
|
||||||
// URL 헬퍼 함수는 더 이상 필요 없음 - API에서 직접 제공
|
// URL 헬퍼 함수는 더 이상 필요 없음 - API에서 직접 제공
|
||||||
|
|
||||||
// 총 재생 시간 계산
|
// 총 재생 시간 계산
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useState, useEffect, useCallback, memo } from 'react';
|
import { useState, useEffect, useCallback, memo, useMemo } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { X, ChevronLeft, ChevronRight, Download } from 'lucide-react';
|
import { X, ChevronLeft, ChevronRight, Download } from 'lucide-react';
|
||||||
import { RowsPhotoAlbum } from 'react-photo-album';
|
import { RowsPhotoAlbum } from 'react-photo-album';
|
||||||
|
|
@ -40,42 +41,34 @@ const galleryStyles = `
|
||||||
function AlbumGallery() {
|
function AlbumGallery() {
|
||||||
const { name } = useParams();
|
const { name } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [album, setAlbum] = useState(null);
|
|
||||||
const [photos, setPhotos] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [lightbox, setLightbox] = useState({ open: false, index: 0 });
|
const [lightbox, setLightbox] = useState({ open: false, index: 0 });
|
||||||
const [imageLoaded, setImageLoaded] = useState(false);
|
const [imageLoaded, setImageLoaded] = useState(false);
|
||||||
const [slideDirection, setSlideDirection] = useState(0);
|
const [slideDirection, setSlideDirection] = useState(0);
|
||||||
const [preloadedImages] = useState(() => new Set()); // 프리로드된 이미지 URL 추적
|
const [preloadedImages] = useState(() => new Set());
|
||||||
|
|
||||||
useEffect(() => {
|
// useQuery로 앨범 데이터 로드
|
||||||
getAlbumByName(name)
|
const { data: album, isLoading: loading } = useQuery({
|
||||||
.then(data => {
|
queryKey: ['album', name],
|
||||||
setAlbum(data);
|
queryFn: () => getAlbumByName(name),
|
||||||
const allPhotos = [];
|
enabled: !!name,
|
||||||
|
});
|
||||||
if (data.conceptPhotos && typeof data.conceptPhotos === 'object') {
|
|
||||||
Object.entries(data.conceptPhotos).forEach(([concept, photos]) => {
|
// 앨범 데이터에서 사진 목록 추출
|
||||||
photos.forEach(p => allPhotos.push({
|
const photos = useMemo(() => {
|
||||||
// API에서 직접 제공하는 URL 및 크기 정보 사용
|
if (!album?.conceptPhotos) return [];
|
||||||
mediumUrl: p.medium_url,
|
const allPhotos = [];
|
||||||
originalUrl: p.original_url,
|
Object.entries(album.conceptPhotos).forEach(([concept, conceptPhotos]) => {
|
||||||
width: p.width || 800,
|
conceptPhotos.forEach(p => allPhotos.push({
|
||||||
height: p.height || 1200,
|
mediumUrl: p.medium_url,
|
||||||
title: concept,
|
originalUrl: p.original_url,
|
||||||
members: p.members ? p.members.split(', ') : []
|
width: p.width || 800,
|
||||||
}));
|
height: p.height || 1200,
|
||||||
});
|
title: concept,
|
||||||
}
|
members: p.members ? p.members.split(', ') : []
|
||||||
|
}));
|
||||||
setPhotos(allPhotos);
|
});
|
||||||
setLoading(false);
|
return allPhotos;
|
||||||
})
|
}, [album]);
|
||||||
.catch(error => {
|
|
||||||
console.error('앨범 데이터 로드 오류:', error);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, [name]);
|
|
||||||
|
|
||||||
// 라이트박스 열기
|
// 라이트박스 열기
|
||||||
const openLightbox = (index) => {
|
const openLightbox = (index) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue