From 027d4e04aea6d091a77757e6cbc2a857888a3b14 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:21:19 +0900 Subject: [PATCH 01/12] style : update button height --- src/components/common/Button/Button.variants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/common/Button/Button.variants.ts b/src/components/common/Button/Button.variants.ts index cd638b5..6dd6b25 100644 --- a/src/components/common/Button/Button.variants.ts +++ b/src/components/common/Button/Button.variants.ts @@ -10,8 +10,8 @@ export const ButtonVariants = cva( gray: 'bg-gray-150 text-gray-950 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed', }, size: { - large: 'w-full h-16 text-xl font-medium', - medium: 'w-[19rem] h-14 text-lg font-medium', + large: 'w-full h-14 text-xl font-medium', + medium: 'w-[19rem] h-12 text-lg font-medium', }, }, defaultVariants: { From 4d6017bbbb4f3524e82547089f40b59c9d138a22 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:21:45 +0900 Subject: [PATCH 02/12] style : update Modal padding --- src/components/common/Modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/Modal/index.tsx b/src/components/common/Modal/index.tsx index d562598..06bd409 100644 --- a/src/components/common/Modal/index.tsx +++ b/src/components/common/Modal/index.tsx @@ -13,7 +13,7 @@ const Content = ({ children }: PropsWithChildren) => { initial={FADE_IN_ANIMATION.initial} animate={FADE_IN_ANIMATION.animate} exit={FADE_IN_ANIMATION.exit} - className="relative z-modal bg-white px-6 py-7 rounded-[1.25rem] shadow-lg" + className="relative z-modal bg-white px-4 py-4 rounded-[1.25rem] shadow-lg" > {children} From 81dc94b733462e94f3fc259e40393b3a8e3e104c Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:23:51 +0900 Subject: [PATCH 03/12] refactor: update useBookMarkList to use useInfiniteQuery for pagination --- src/hooks/api/bookmarks/useBookMarkList.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hooks/api/bookmarks/useBookMarkList.ts b/src/hooks/api/bookmarks/useBookMarkList.ts index 1adbfb7..a6ee19b 100644 --- a/src/hooks/api/bookmarks/useBookMarkList.ts +++ b/src/hooks/api/bookmarks/useBookMarkList.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; import { get } from '@/lib/axios'; @@ -20,13 +20,15 @@ export type BookmarkDetail = { }; export const useBookMarkList = (token: string) => { - return useQuery({ + return useInfiniteQuery({ queryKey: ['bookmarklist', token], - queryFn: () => - get('api/bookmarks?cursor=0&size=10', { + queryFn: ({ pageParam = 0 }) => + get(`api/bookmarks?cursor=${pageParam}&size=7`, { headers: { Authorization: `Bearer ${token}`, }, }), + initialPageParam: 0, + getNextPageParam: (lastPage) => (lastPage.hasNext ? lastPage.nextCursor : undefined), }); }; From 8f2561e8bacbcd0c0dc5bc183339f09d8402c823 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:25:39 +0900 Subject: [PATCH 04/12] feat: add IntersectionObserver and infinite scroll logic to BookMarkDetail --- .../BookmarkDetail/BookmarkDetail.tsx | 64 +++++++++++++++---- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/components/features/BookmarkDetail/BookmarkDetail.tsx b/src/components/features/BookmarkDetail/BookmarkDetail.tsx index 5c7d565..f18d943 100644 --- a/src/components/features/BookmarkDetail/BookmarkDetail.tsx +++ b/src/components/features/BookmarkDetail/BookmarkDetail.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useAtom } from 'jotai'; import { Button } from '@/components/common/Button'; @@ -19,11 +19,12 @@ export const BookmarkDetail = ({ bookmarkId, onPrev }: Props) => { const [isOpen, setIsOpen] = useState(false); const [isEditing, setIsEditing] = useState(false); const [selectedId, setSelectedId] = useState(0); + const [, setMarkers] = useAtom(markersAtom); + const observerTarget = useRef(null); const { token } = useAuth(); - const { data } = useMarkerList(bookmarkId, token); + const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useMarkerList(bookmarkId, token); const deleteMarkersMutation = useDeleteMarkers(); - const [, setMarkers] = useAtom(markersAtom); const handleToggleSelect = (markerId: number) => { setSelectedId((prev) => (prev === markerId ? 0 : markerId)); @@ -40,14 +41,51 @@ export const BookmarkDetail = ({ bookmarkId, onPrev }: Props) => { setSelectedId(0); }; + const allMarkers = useMemo( + () => (data?.pages ? data.pages.flatMap((page) => page.markers.data) : []), + [data?.pages] + ); + useEffect(() => { - if (data?.markers?.data) { - setMarkers(data.markers.data); + if (allMarkers.length) { + setMarkers((prevMarkers) => { + const newMarkersStr = JSON.stringify(allMarkers); + const prevMarkersStr = JSON.stringify(prevMarkers); + return newMarkersStr !== prevMarkersStr ? allMarkers : prevMarkers; + }); } - }, [data?.markers?.data, setMarkers]); + }, [allMarkers, setMarkers]); + + const selectedMarkerName = useMemo( + () => + data?.pages.flatMap((page) => page.markers.data).find((item) => item.markerId === selectedId) + ?.title || '마커', + [data?.pages, selectedId] + ); - const selectedMarkerName = - data?.markers.data.find((item) => item.markerId === selectedId)?.title || '마커'; + const handleObserver = useCallback( + (entries: IntersectionObserverEntry[]) => { + entries.forEach((entry) => { + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }); + }, + [fetchNextPage, hasNextPage, isFetchingNextPage] + ); + + useEffect(() => { + const observer = new IntersectionObserver(handleObserver, { + root: null, + rootMargin: '20px', + threshold: 0.1, + }); + + if (observerTarget.current) { + observer.observe(observerTarget.current); + } + return () => observer.disconnect(); + }, [handleObserver]); return (
@@ -62,15 +100,15 @@ export const BookmarkDetail = ({ bookmarkId, onPrev }: Props) => { className="cursor-pointer" /> - {data?.bookmarkName} + {data?.pages[0]?.bookmarkName} 리스트
- {data?.markers.data.map((item, index) => ( + {allMarkers.map((item, index) => (
@@ -96,8 +134,8 @@ export const BookmarkDetail = ({ bookmarkId, onPrev }: Props) => { /> )}
- - {index < data.markers.data.length - 1 &&
} + {index < allMarkers.length - 1 &&
} +
))} From 580db4a5577346b2aa0c952981a9f9f772e35d07 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:26:00 +0900 Subject: [PATCH 05/12] refactor: update useMarkList to use useInfiniteQuery for pagination --- src/hooks/api/marker/useMarkerList.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/hooks/api/marker/useMarkerList.ts b/src/hooks/api/marker/useMarkerList.ts index 975b206..0c14dcc 100644 --- a/src/hooks/api/marker/useMarkerList.ts +++ b/src/hooks/api/marker/useMarkerList.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; import { get } from '@/lib/axios'; @@ -28,13 +28,17 @@ export type MarkerDetail = { }; export const useMarkerList = (markerId: number, token: string) => { - return useQuery({ + return useInfiniteQuery({ queryKey: ['marker', markerId, token], - queryFn: () => - get(`api/markers/${markerId}?cursor=0&size=10`, { + queryFn: ({ pageParam = 0 }) => + get(`api/markers/${markerId}?cursor=${pageParam}&size=7`, { headers: { Authorization: `Bearer ${token}`, }, }), + initialPageParam: 0, + getNextPageParam: (page) => { + return page.markers.nextCursor || undefined; + }, }); }; From aa989adf6e080d59d9548a97ce7acb649cb2f30f Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:30:22 +0900 Subject: [PATCH 06/12] feat: implement infinite scroll in BookmarkSelectionList --- .../BookmarkSelectionList.tsx | 96 +++++++++++++------ 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/src/components/features/SearchResultsList/BookmarkSelectionList.tsx b/src/components/features/SearchResultsList/BookmarkSelectionList.tsx index db6ba73..0596e1e 100644 --- a/src/components/features/SearchResultsList/BookmarkSelectionList.tsx +++ b/src/components/features/SearchResultsList/BookmarkSelectionList.tsx @@ -1,9 +1,10 @@ -import { useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { Button } from '@/components/common/Button'; import { Icon } from '@/components/common/Icon'; import { ListCard } from '@/components/common/ListCard'; import { Body1, Body2, Body3 } from '@/components/common/Typography'; +import { findyIconNames } from '@/constants/findyIcons'; import { useBookMarkList } from '@/hooks/api/bookmarks/useBookMarkList'; import { NewMarker, useNewMarker } from '@/hooks/api/marker/useNewMarker'; import { useAuth } from '@/hooks/auth/useAuth'; @@ -16,10 +17,11 @@ type Props = { export const BookmarkSelectionList = ({ selectedPlace, onNext }: Props) => { const { token } = useAuth(); - const { data } = useBookMarkList(token); + const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useBookMarkList(token); const { clearMarkers } = useMarkers(); const [bookmarkId, setBookmarkId] = useState(0); const mutation = useNewMarker(bookmarkId, token); + const observerTarget = useRef(null); const handleToggleSelect = (id: number) => { setBookmarkId((prev) => (prev === id ? 0 : id)); @@ -37,43 +39,77 @@ export const BookmarkSelectionList = ({ selectedPlace, onNext }: Props) => { } }; + const handleObserver = useCallback( + (entries: IntersectionObserverEntry[]) => { + entries.forEach((entry) => { + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }); + }, + [fetchNextPage, hasNextPage, isFetchingNextPage] + ); + + useEffect(() => { + const observer = new IntersectionObserver(handleObserver, { + root: null, + rootMargin: '100px', + threshold: 0.1, + }); + + if (observerTarget.current) { + observer.observe(observerTarget.current); + } + return () => observer.disconnect(); + }, [handleObserver]); + return (
북마크 리스트 - {data?.data.map((item, index) => ( -
-
-
- {item.youtuberProfile ? ( - {`${item.name}의 - ) : ( - - )} -
- {item.name} -
- - {item.markersCount} + {data?.pages + .flatMap((page) => page.data) + .map((item, index) => ( +
+
handleToggleSelect(item.bookmarkId)} + > +
+ {item.youtuberProfile ? ( + {`${item.name}의 + ) : ( + + )} +
+ {item.name} +
+ + {item.markersCount} +
+ {item.bookmarkType !== 'YOUTUBE' && ( + + )}
- {item.bookmarkType !== 'YOUTUBE' && ( - handleToggleSelect(item.bookmarkId)} - /> + {index < data.pages.flatMap((page) => page.data).length - 1 && ( +
)} +
- {index < data.data.length - 1 &&
} -
- ))} + ))}

- {data?.data.map((item, index) => ( -
-
handleBookmarkClick(item.bookmarkId)} - className="flex flex-row justify-between items-center cursor-pointer" - > -
- {item.youtuberProfile ? ( - {`${item.name}의 - ) : ( + {data?.pages + .flatMap((page) => page.data) + .map((item, index) => ( +
+
handleBookmarkClick(item.bookmarkId)} + className="flex flex-row justify-between items-center cursor-pointer" + > +
+ {item.youtuberProfile ? ( + {`${item.name}의 + ) : ( + + )} +
+ {item.name} +
+ + {item.markersCount} +
+
+
+ {isEditing && ( )} -
- {item.name} -
- - {item.markersCount} -
-
- {isEditing && ( - + {index < data.pages.flatMap((page) => page.data).length - 1 && ( +
)}
- {index < data.data.length - 1 &&
} -
- ))} + ))}
{isEditing ? ( From 10e3e115b339774eb4fce1ce1b2b754845087257 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:38:19 +0900 Subject: [PATCH 08/12] fix: update getNextPageParam to handle nextCursor for markers pagination --- src/hooks/api/marker/useMarkerList.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hooks/api/marker/useMarkerList.ts b/src/hooks/api/marker/useMarkerList.ts index 0c14dcc..d9397d6 100644 --- a/src/hooks/api/marker/useMarkerList.ts +++ b/src/hooks/api/marker/useMarkerList.ts @@ -37,8 +37,6 @@ export const useMarkerList = (markerId: number, token: string) => { }, }), initialPageParam: 0, - getNextPageParam: (page) => { - return page.markers.nextCursor || undefined; - }, + getNextPageParam: (page) => (page.markers.hasNext ? page.markers.nextCursor : undefined), }); }; From 4a95a6ff37b8e1329296fadaf766646ea292be82 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:40:23 +0900 Subject: [PATCH 09/12] style : update IntersectionObserver rootMargin --- .../features/SearchResultsList/BookmarkSelectionList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/features/SearchResultsList/BookmarkSelectionList.tsx b/src/components/features/SearchResultsList/BookmarkSelectionList.tsx index 0596e1e..83c5c2d 100644 --- a/src/components/features/SearchResultsList/BookmarkSelectionList.tsx +++ b/src/components/features/SearchResultsList/BookmarkSelectionList.tsx @@ -53,7 +53,7 @@ export const BookmarkSelectionList = ({ selectedPlace, onNext }: Props) => { useEffect(() => { const observer = new IntersectionObserver(handleObserver, { root: null, - rootMargin: '100px', + rootMargin: '20px', threshold: 0.1, }); From 89d8eb81213a9a0787886449fb7f8026d45e2307 Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Mon, 25 Nov 2024 23:52:46 +0900 Subject: [PATCH 10/12] style : edit marker font-size, font-weight --- src/utils/CustomMarker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/CustomMarker.ts b/src/utils/CustomMarker.ts index 59678ce..dbf7271 100644 --- a/src/utils/CustomMarker.ts +++ b/src/utils/CustomMarker.ts @@ -42,7 +42,7 @@ export const CustomMarker = ({ - + ${shouldShowTitle ? title : ''}
From 07d9ea0a3c4db4e3a6e22384ed69fd9a8a08dd9b Mon Sep 17 00:00:00 2001 From: keemsebeen Date: Tue, 26 Nov 2024 00:32:03 +0900 Subject: [PATCH 11/12] refactor: relocate observerTarget position --- src/components/features/BookmarkDetail/BookmarkDetail.tsx | 2 +- src/components/features/BookmarkList/BookmarkList.tsx | 2 +- .../features/SearchResultsList/BookmarkSelectionList.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/features/BookmarkDetail/BookmarkDetail.tsx b/src/components/features/BookmarkDetail/BookmarkDetail.tsx index f18d943..b31cf1e 100644 --- a/src/components/features/BookmarkDetail/BookmarkDetail.tsx +++ b/src/components/features/BookmarkDetail/BookmarkDetail.tsx @@ -135,9 +135,9 @@ export const BookmarkDetail = ({ bookmarkId, onPrev }: Props) => { )}
{index < allMarkers.length - 1 &&
} -
))} +
{isEditing ? ( diff --git a/src/components/features/BookmarkList/BookmarkList.tsx b/src/components/features/BookmarkList/BookmarkList.tsx index 0047ed0..0587101 100644 --- a/src/components/features/BookmarkList/BookmarkList.tsx +++ b/src/components/features/BookmarkList/BookmarkList.tsx @@ -113,7 +113,6 @@ export const BookmarkList = ({ onNext }: Props) => { .map((item, index) => (
handleBookmarkClick(item.bookmarkId)} className="flex flex-row justify-between items-center cursor-pointer" > @@ -151,6 +150,7 @@ export const BookmarkList = ({ onNext }: Props) => { )}
))} +
{isEditing ? ( diff --git a/src/components/features/SearchResultsList/BookmarkSelectionList.tsx b/src/components/features/SearchResultsList/BookmarkSelectionList.tsx index 83c5c2d..cc5fbdf 100644 --- a/src/components/features/SearchResultsList/BookmarkSelectionList.tsx +++ b/src/components/features/SearchResultsList/BookmarkSelectionList.tsx @@ -107,9 +107,9 @@ export const BookmarkSelectionList = ({ selectedPlace, onNext }: Props) => { {index < data.pages.flatMap((page) => page.data).length - 1 && (
)} -
))} +