1- import { Badge , PopupContainer } from '@pinback/design-system/ui' ;
2- import { useState , useRef } from 'react' ;
3- import {
4- useGetBookmarkArticles ,
5- useGetBookmarkUnreadArticles ,
6- useGetCategoryBookmarkArticles ,
7- } from '@pages/myBookmark/apis/queries' ;
1+ import { PopupContainer } from '@pinback/design-system/ui' ;
2+ import { useState , useRef , Suspense } from 'react' ;
83import { useSearchParams } from 'react-router-dom' ;
94import { REMIND_MOCK_DATA } from '@pages/remind/constants' ;
105import CardEditModal from '@shared/components/cardEditModal/CardEditModal' ;
116import OptionsMenuPortal from '@shared/components/sidebar/OptionsMenuPortal' ;
127import { useAnchoredMenu } from '@shared/hooks/useAnchoredMenu' ;
138import { belowOf } from '@shared/utils/anchorPosition' ;
14- import NoArticles from '@pages/myBookmark/components/NoArticles/NoArticles' ;
159import { Icon } from '@pinback/design-system/icons' ;
1610import { useQueryClient } from '@tanstack/react-query' ;
1711import {
1812 useGetArticleDetail ,
1913 useDeleteRemindArticle ,
2014 usePutArticleReadStatus ,
2115} from '@shared/apis/queries' ;
22- import NoUnreadArticles from '@pages/myBookmark/components/noUnreadArticles/NoUnreadArticles' ;
23- import FetchCard from '@pages/myBookmark/components/fetchCard/FetchCard' ;
24- import { useInfiniteScroll } from '@shared/hooks/useInfiniteScroll' ;
2516import Tooltip from '@shared/components/tooltip/Tooltip' ;
17+ import ArticlesLoadingBoundary from '@shared/components/articlesLoadingBoundary/ArticlesLoadingBoundary' ;
18+ import ArticlesErrorBoundary from '@shared/components/articlesErrorBoundary/ArticlesErrorBoundary' ;
19+ import { ErrorBoundary } from 'react-error-boundary' ;
20+ import MyBookmarkContent from '@pages/myBookmark/components/myBookmarkContent/MyBookmarkContent' ;
2621
2722const MyBookmark = ( ) => {
2823 const [ activeBadge , setActiveBadge ] = useState < 'all' | 'notRead' > ( 'all' ) ;
@@ -32,35 +27,14 @@ const MyBookmark = () => {
3227
3328 const [ searchParams ] = useSearchParams ( ) ;
3429 const queryClient = useQueryClient ( ) ;
30+
3531 const category = searchParams . get ( 'category' ) ;
3632 const categoryId = searchParams . get ( 'id' ) ;
3733
3834 const scrollContainerRef = useRef < HTMLDivElement > ( null ) ;
3935
4036 const { mutate : updateToReadStatus } = usePutArticleReadStatus ( ) ;
4137 const { mutate : deleteArticle } = useDeleteRemindArticle ( ) ;
42-
43- const {
44- data : articlesData ,
45- fetchNextPage : fetchNextArticles ,
46- hasNextPage : hasNextArticles ,
47- } = useGetBookmarkArticles ( ) ;
48-
49- const {
50- data : unreadArticlesData ,
51- fetchNextPage : fetchNextUnreadArticles ,
52- hasNextPage : hasNextUnreadArticles ,
53- } = useGetBookmarkUnreadArticles ( ) ;
54-
55- const {
56- data : categoryArticlesData ,
57- fetchNextPage : fetchNextCategoryArticles ,
58- hasNextPage : hasNextCategoryArticles ,
59- } = useGetCategoryBookmarkArticles (
60- categoryId ,
61- activeBadge === 'notRead' ? false : null
62- ) ;
63-
6438 const { mutate : getArticleDetail , data : articleDetail } =
6539 useGetArticleDetail ( ) ;
6640
@@ -72,34 +46,9 @@ const MyBookmark = () => {
7246 containerRef,
7347 } = useAnchoredMenu ( ( anchor ) => belowOf ( anchor , 8 ) ) ;
7448
75- const articlesToDisplay = category
76- ? ( categoryArticlesData ?. pages . flatMap ( ( page ) => page . articles ) ?? [ ] )
77- : activeBadge === 'all'
78- ? ( articlesData ?. pages . flatMap ( ( page ) => page . articles ) ?? [ ] )
79- : ( unreadArticlesData ?. pages . flatMap ( ( page ) => page . articles ) ?? [ ] ) ;
80-
81- const hasNextPage = category
82- ? hasNextCategoryArticles
83- : activeBadge === 'all'
84- ? hasNextArticles
85- : hasNextUnreadArticles ;
86-
87- const fetchNextPage = category
88- ? fetchNextCategoryArticles
89- : activeBadge === 'all'
90- ? fetchNextArticles
91- : fetchNextUnreadArticles ;
92-
93- const observerRef = useInfiniteScroll ( {
94- fetchNextPage,
95- hasNextPage,
96- root : scrollContainerRef ,
97- } ) ;
98-
9949 const handleDeleteArticle = ( id : number ) => {
10050 deleteArticle ( id , {
10151 onSuccess : ( ) => {
102- // TODO: 쿼리키 팩토리 패턴 적용
10352 queryClient . invalidateQueries ( { queryKey : [ 'bookmarkReadArticles' ] } ) ;
10453 queryClient . invalidateQueries ( { queryKey : [ 'bookmarkUnreadArticles' ] } ) ;
10554 queryClient . invalidateQueries ( {
@@ -119,31 +68,6 @@ const MyBookmark = () => {
11968 const getBookmarkTitle = ( id : number | null ) =>
12069 id == null ? '' : ( REMIND_MOCK_DATA . find ( ( d ) => d . id === id ) ?. title ?? '' ) ;
12170
122- const handleBadgeClick = ( badgeType : 'all' | 'notRead' ) => {
123- setActiveBadge ( badgeType ) ;
124- } ;
125-
126- const EmptyStateComponent = ( ) => {
127- if ( articlesToDisplay . length === 0 ) {
128- const totalArticlesInAllView = articlesData ?. pages [ 0 ] ?. totalArticle ;
129- if ( totalArticlesInAllView === 0 ) {
130- return < NoArticles /> ;
131- }
132- return < NoUnreadArticles /> ;
133- }
134- return null ;
135- } ;
136-
137- const totalArticleCount =
138- ( category
139- ? categoryArticlesData ?. pages [ 0 ] ?. totalArticle
140- : articlesData ?. pages [ 0 ] ?. totalArticle ) ?? 0 ;
141-
142- const totalUnreadArticleCount =
143- ( category
144- ? categoryArticlesData ?. pages [ 0 ] ?. totalUnreadArticle
145- : articlesData ?. pages [ 0 ] ?. totalUnreadArticle ) ?? 0 ;
146-
14771 return (
14872 < div className = "flex h-screen flex-col py-[5.2rem] pl-[8rem] pr-[5rem]" >
14973 < div className = "flex items-center gap-[0.4rem]" >
@@ -162,63 +86,22 @@ const MyBookmark = () => {
16286 < p className = "head3 text-main500" > { category || '' } </ p >
16387 </ div >
16488
165- < div className = "mt-[3rem] flex gap-[2.4rem]" >
166- < Badge
167- text = "전체보기"
168- countNum = { totalArticleCount }
169- onClick = { ( ) => handleBadgeClick ( 'all' ) }
170- isActive = { activeBadge === 'all' }
171- />
172- < Badge
173- text = "안 읽음"
174- countNum = { totalUnreadArticleCount }
175- onClick = { ( ) => handleBadgeClick ( 'notRead' ) }
176- isActive = { activeBadge === 'notRead' }
177- />
178- </ div >
17989 < Tooltip />
18090
181- { articlesToDisplay . length > 0 ? (
182- < div
183- ref = { scrollContainerRef }
184- className = "scrollbar-hide mt-[2.6rem] flex h-screen flex-wrap content-start gap-[1.6rem] overflow-y-auto scroll-smooth"
185- >
186- { articlesToDisplay . map ( ( article ) => (
187- < FetchCard
188- key = { article . articleId }
189- article = { article }
190- onClick = { ( ) => {
191- window . open ( article . url , '_blank' ) ;
192- updateToReadStatus ( article . articleId , {
193- onSuccess : ( ) => {
194- // TODO: 쿼리키 팩토리 패턴 적용
195- queryClient . invalidateQueries ( {
196- queryKey : [ 'bookmarkReadArticles' ] ,
197- } ) ;
198- queryClient . invalidateQueries ( {
199- queryKey : [ 'bookmarkUnreadArticles' ] ,
200- } ) ;
201- queryClient . invalidateQueries ( {
202- queryKey : [ 'categoryBookmarkArticles' ] ,
203- } ) ;
204- queryClient . invalidateQueries ( { queryKey : [ 'arcons' ] } ) ;
205- } ,
206- onError : ( error ) => {
207- console . error ( error ) ;
208- } ,
209- } ) ;
210- } }
211- onOptionsClick = { ( e ) => {
212- e . stopPropagation ( ) ;
213- openMenu ( article . articleId , e . currentTarget ) ;
214- } }
215- />
216- ) ) }
217- < div ref = { observerRef } style = { { height : '1px' , width : '100%' } } />
218- </ div >
219- ) : (
220- < EmptyStateComponent />
221- ) }
91+ < Suspense fallback = { < ArticlesLoadingBoundary /> } >
92+ < ErrorBoundary FallbackComponent = { ArticlesErrorBoundary } >
93+ < MyBookmarkContent
94+ category = { category }
95+ categoryId = { categoryId }
96+ activeBadge = { activeBadge }
97+ onBadgeChange = { setActiveBadge }
98+ updateToReadStatus = { updateToReadStatus }
99+ openMenu = { openMenu }
100+ queryClient = { queryClient }
101+ scrollContainerRef = { scrollContainerRef }
102+ />
103+ </ ErrorBoundary >
104+ </ Suspense >
222105
223106 < OptionsMenuPortal
224107 open = { menu . open }
0 commit comments