From a40df84380693f3fc07ab94594193cd81b8f1228 Mon Sep 17 00:00:00 2001 From: noobcpuls Date: Thu, 22 Aug 2024 22:01:26 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Fix:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=20zip=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EC=9D=BC=EB=B0=98=20=EB=8B=A4=EC=9A=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/instance.ts | 5 +- .../ShareGroupBottomBar.tsx | 2 +- .../ShareGroupCloudButton.tsx | 4 +- .../ShareGroupImageList.tsx | 77 ++++++++++++--- .../ShareGroupDetailPage.tsx | 10 +- src/utils/ImageZipDownloader.ts | 94 +++---------------- 6 files changed, 87 insertions(+), 105 deletions(-) diff --git a/src/apis/instance.ts b/src/apis/instance.ts index b4a3d9b..022a8df 100644 --- a/src/apis/instance.ts +++ b/src/apis/instance.ts @@ -17,8 +17,9 @@ export const baseInstance = ( export const authInstance = ( options: AxiosRequestConfig = {}, ): AxiosInstance => { - const TOKEN = getCookie('access-token'); - console.log(`token : ${TOKEN}`); + // const TOKEN = getCookie('access-token'); + const TOKEN = process.env.REACT_APP_REFRESH_TOKEN; + // console.log(`token : ${TOKEN}`); return axios.create({ baseURL: BASE_URL, headers: { diff --git a/src/components/ShareGroup/ShareGroupBottomBar/ShareGroupBottomBar.tsx b/src/components/ShareGroup/ShareGroupBottomBar/ShareGroupBottomBar.tsx index 4f2eea4..a1f214b 100644 --- a/src/components/ShareGroup/ShareGroupBottomBar/ShareGroupBottomBar.tsx +++ b/src/components/ShareGroup/ShareGroupBottomBar/ShareGroupBottomBar.tsx @@ -21,7 +21,7 @@ const ShareGroupBottomBar: React.FC = ({ // 선택한 이미지들의 URL을 다운로드함 const imageUrls: string[] = srcs; const handleDownload = async (): Promise => { - await imageZipDownloader({ imageUrls }); + await imageZipDownloader(imageUrls); }; return ( diff --git a/src/components/ShareGroup/ShareGroupCloudButton/ShareGroupCloudButton.tsx b/src/components/ShareGroup/ShareGroupCloudButton/ShareGroupCloudButton.tsx index 47f9a3d..fe6b7e5 100644 --- a/src/components/ShareGroup/ShareGroupCloudButton/ShareGroupCloudButton.tsx +++ b/src/components/ShareGroup/ShareGroupCloudButton/ShareGroupCloudButton.tsx @@ -94,9 +94,7 @@ const ShareGroupCloudButton: React.FC = () => { const response = await getDownloadPhotosAll(shareGroupId, profileId); console.log(response.data.data); if (response.status === 200) { - await imageZipDownloader({ - imageUrls: response.data.data.photoDownloadUrlList, - }); + await imageZipDownloader(response.data.data.photoDownloadUrlList); } } catch (error) { console.log(error); diff --git a/src/components/ShareGroup/ShareGroupImageList/ShareGroupImageList.tsx b/src/components/ShareGroup/ShareGroupImageList/ShareGroupImageList.tsx index 18042bf..be90271 100644 --- a/src/components/ShareGroup/ShareGroupImageList/ShareGroupImageList.tsx +++ b/src/components/ShareGroup/ShareGroupImageList/ShareGroupImageList.tsx @@ -11,6 +11,8 @@ import { useRecoilState } from 'recoil'; import ShareGroupBottomBar from '../ShareGroupBottomBar/ShareGroupBottomBar'; import { deletePhoto } from 'apis/deletePhoto'; import { useLocation, useNavigate } from 'react-router-dom'; +import { getPhotosAll, getPhotosEtc } from 'apis/getPhotosAll'; +import { getPhotos } from 'apis/getPhotos'; export interface itemProp { createdAt: string; @@ -24,14 +26,14 @@ export interface itemProp { const ShareGroupImageList = ({ items, maxPage, - getApi, + profileId, shareGroupId, loading, setLoading, }: { items: itemProp[]; maxPage: number; - getApi: (page: number) => Promise; + profileId: number; shareGroupId: number; // Add shareGroupId as a prop // infinite scroll loading을 위한 state loading: boolean; @@ -76,14 +78,12 @@ const ShareGroupImageList = ({ setIsModal(false); }; - // infinite scroll의 다음 아이템을 가져올지 결정하는 함수 - // 사진 삭제 const handleDelete = async () => { let photoToDelete: number[] = []; if (selectedImage) { const id = localItems.find( - (item) => item.w400PhotoUrl === selectedImage, + (item) => item.rawPhotoUrl === selectedImage, )?.photoId; if (id) photoToDelete.push(id); } else if (checkedImg) photoToDelete = checkedImg; @@ -109,8 +109,56 @@ const ShareGroupImageList = ({ if (!isChecked) setCheckedImg([]); }, [isChecked]); + // 사진 불러오기 API 호출 함수 + const handleApi = async ( + page: number, + profileId: number, + shareGroupId: number, + ): Promise => { + // page가 있으면 page를 넣어줌 + const reqDataWithPage = { + shareGroupId: shareGroupId, + profileId: profileId, + size: 20, + page: page, + }; + setLoading(true); + + try { + if (profileId === 0) { + const { status, data } = await getPhotosAll(reqDataWithPage); + if (status === 200) { + setLoading(false); + return data.photoInfoList; + } + } else if (profileId === -1) { + const { status, data } = await getPhotosEtc(reqDataWithPage); + if (status === 200) { + setLoading(false); + return data.photoInfoList; + } + } else { + const { status, data } = await getPhotos(reqDataWithPage); + if (status === 200) { + setLoading(false); + return data.photoInfoList; + } + } + return []; + } catch (e) { + alert('앨범 조회 중 오류가 발생하였습니다'); + console.error('Failed to fetch more items:', e); + return []; + } + }; + // 스크롤 이벤트 핸들러 const handleScroll = useCallback(() => { + console.log('scroll'); + if (page >= maxPage) { + setHasMore(false); // 페이지가 최대치에 도달하면 더 이상 로드하지 않도록 설정 + return; + } if (containerRef.current && !loading && hasMore) { const { scrollTop, scrollHeight, clientHeight } = containerRef.current; // 스크롤이 하단에 도달했는지 확인 @@ -123,10 +171,16 @@ const ShareGroupImageList = ({ // 페이지가 변경될 때마다 API 호출 useEffect(() => { - if (page > 0) { + if (page > 0 && hasMore) { + // hasMore가 true일 때만 API 호출 const fetchMoreItems = async () => { try { - await getApi(page); + const newItems: itemProp[] = await handleApi( + page, + profileId, + shareGroupId, + ); + setLocalItems((prevItems) => [...prevItems, ...newItems]); // 새로운 아이템을 기존 아이템에 추가 } catch (error) { console.error('Failed to fetch more items:', error); } finally { @@ -135,7 +189,7 @@ const ShareGroupImageList = ({ }; fetchMoreItems(); } - }, [page, getApi, setLoading]); + }, [page, handleApi, setLoading, hasMore]); // 스크롤 이벤트 리스너 등록 useEffect(() => { @@ -150,11 +204,6 @@ const ShareGroupImageList = ({ }; }, [handleScroll]); - useEffect(() => { - if (choiceMode) setIsChecked(true); - if (!isChecked) setCheckedImg([]); - }, [isChecked]); - return ( <> @@ -166,7 +215,7 @@ const ShareGroupImageList = ({ selected={false} isDownload={item.isDownload} onClick={() => - handleImageClick(i, item.photoId, item.w200PhotoUrl) + handleImageClick(i, item.photoId, item.rawPhotoUrl) } checked={checkedImg.includes(item.photoId)} /> diff --git a/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx b/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx index 925eba8..72ef4fc 100644 --- a/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx +++ b/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx @@ -45,11 +45,9 @@ const ShareGroupDetailPage: React.FC = () => { ...requestData, page: page, }; - console.log(reqDataWithPage); try { if (isAllPhoto || requestData.profileId === 0 || profileId === -0) { const { status, data } = await getPhotosAll(reqDataWithPage); - console.log(status, data); if (status === 200) { setItems(data.photoInfoList); setMaxPage(data.totalPages); @@ -60,7 +58,6 @@ const ShareGroupDetailPage: React.FC = () => { profileId === -1 ) { const { status, data } = await getPhotosEtc(reqDataWithPage); - console.log(status, data); if (status === 200) { setItems(data.photoInfoList); setMaxPage(data.totalPages); @@ -69,7 +66,6 @@ const ShareGroupDetailPage: React.FC = () => { const { status, data } = await getPhotos( page > 0 ? reqDataWithPage : requestData, ); - console.log(status, data); if (status === 200) { setItems(data.photoInfoList); setMaxPage(data.totalPages); @@ -84,6 +80,10 @@ const ShareGroupDetailPage: React.FC = () => { } }; + useEffect(() => { + console.log(maxPage); + }, [maxPage]); + const getApi = async (page?: number, profileId?: number): Promise => { if (typeof page === 'undefined') { setIsLoading(true); @@ -119,7 +119,7 @@ const ShareGroupDetailPage: React.FC = () => { { - if (window.matchMedia('(max-width: 767px)').matches) { - return true; - } - // 두 번째 방법 - const userAgent = navigator.userAgent; - - // iOS, Android, Windows Phone 등의 모바일 장치를 감지 - if (/android/i.test(userAgent)) { - return true; - } - - if (/iPhone|iPad|iPod/i.test(userAgent)) { - return true; - } - - if (/windows phone/i.test(userAgent)) { - return true; - } - - // 기타 모바일 장치 감지 - if (/mobile/i.test(userAgent)) { - return true; - } - - return false; -}; - -// 이미지들을 jpeg로 변환하여 zip 파일로 다운로드 -const imageZipDownloader = async ({ - imageUrls, -}: { - imageUrls: string[]; -}): Promise => { - if (isMobile()) { - // 모바일일 경우 개별 다운로드 - for (const url of imageUrls) { - try { - const response = await fetch(url); - const blob = await response.blob(); - const fileName = url.split('/').pop() || 'image'; - const downloadUrl = window.URL.createObjectURL(blob); +// 이미지들을 jpeg로 변환하여 다운로드 +const imageZipDownloader = async (imageUrls: string[]) => { + imageUrls.forEach((url, index) => { + fetch(url) + .then((response) => response.blob()) + .then((blob) => { + const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); - a.href = downloadUrl; - a.download = fileName; + a.href = url; + a.download = `image-${index + 1}.jpeg`; // 파일 이름 지정 document.body.appendChild(a); a.click(); - document.body.removeChild(a); - window.URL.revokeObjectURL(downloadUrl); - } catch (error) { - console.error('Error processing image:', error); - throw error; - } - } - } else { - // 모바일이 아니면 ZIP 파일로 압축하여 다운로드 - const zip = new JSZip(); - - for (const url of imageUrls) { - try { - const response = await fetch(url); - const blob = await response.blob(); - const fileName = url.split('/').pop() || 'image'; - zip.file(fileName, blob); - } catch (error) { - console.error('Error processing image:', error); - throw error; - } - } - - const zipBlob = await zip.generateAsync({ type: 'blob' }); - const zipUrl = window.URL.createObjectURL(zipBlob); - const a = document.createElement('a'); - a.href = zipUrl; - a.download = 'images.zip'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - window.URL.revokeObjectURL(zipUrl); - } + a.remove(); + window.URL.revokeObjectURL(url); + }) + .catch((error) => console.error('Image download failed:', error)); + }); }; export default imageZipDownloader; From 21e522bf108c51d34ac97651ab69aae524e8b31e Mon Sep 17 00:00:00 2001 From: noobcpuls Date: Thu, 22 Aug 2024 22:12:03 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Fix:=20Dropdown=20=ED=83=80=EC=9D=B4?= =?UTF-8?q?=ED=8B=80=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20=EC=82=AC=EC=A7=84?= =?UTF-8?q?=20=EC=95=88=20=EB=B0=94=EB=80=8C=EB=8A=94=20=EB=B2=84=EA=B7=B8?= =?UTF-8?q?=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ShareGroup/ShareGroupDetailPage/DropDown.tsx | 2 ++ .../ShareGroupDetailPage/ShareGroupDetailPage.tsx | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pages/ShareGroup/ShareGroupDetailPage/DropDown.tsx b/src/pages/ShareGroup/ShareGroupDetailPage/DropDown.tsx index c3e0dbd..53d536e 100644 --- a/src/pages/ShareGroup/ShareGroupDetailPage/DropDown.tsx +++ b/src/pages/ShareGroup/ShareGroupDetailPage/DropDown.tsx @@ -31,7 +31,9 @@ const DropDown: React.FC = ({ groupId, setter }) => { const handleClick = () => { setIsClicked(!isClicked); }; + const handleItemClick = (idx: number, profileId: number) => { + console.log('idx', idx); setIsClicked(false); setTitle(names[idx].name); const newData = { shareGroupId: groupId, profileId: profileId, size: 20 }; diff --git a/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx b/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx index 72ef4fc..ee880e2 100644 --- a/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx +++ b/src/pages/ShareGroup/ShareGroupDetailPage/ShareGroupDetailPage.tsx @@ -80,10 +80,6 @@ const ShareGroupDetailPage: React.FC = () => { } }; - useEffect(() => { - console.log(maxPage); - }, [maxPage]); - const getApi = async (page?: number, profileId?: number): Promise => { if (typeof page === 'undefined') { setIsLoading(true); @@ -95,7 +91,7 @@ const ShareGroupDetailPage: React.FC = () => { useEffect(() => { getApi(); - }, []); + }, [requestData]); if (isLoading) { return (