11"use client" ;
22
3- import { useInfiniteQuery } from "@tanstack/react-query" ;
4- import { myBookmarks } from "@/backend/services/bookmark.action" ;
5- import ArticleCard from "@/components/ArticleCard" ;
3+ import {
4+ myBookmarks ,
5+ toggleResourceBookmark ,
6+ } from "@/backend/services/bookmark.action" ;
7+ import { useAppConfirm } from "@/components/app-confirm" ;
8+ import { Button } from "@/components/ui/button" ;
69import VisibilitySensor from "@/components/VisibilitySensor" ;
10+ import { useTranslation } from "@/i18n/use-translation" ;
11+ import { formattedTime } from "@/lib/utils" ;
12+ import { ChatBubbleIcon } from "@radix-ui/react-icons" ;
13+ import { useInfiniteQuery } from "@tanstack/react-query" ;
14+ import { RemoveFormatting , Trash } from "lucide-react" ;
15+ import Link from "next/link" ;
16+ import { useMemo } from "react" ;
717
818interface BookmarkMeta {
919 totalCount : number ;
@@ -18,117 +28,95 @@ interface BookmarkData {
1828}
1929
2030const BookmarksPage = ( ) => {
21- const {
22- data,
23- fetchNextPage,
24- hasNextPage,
25- isFetchingNextPage,
26- isLoading,
27- error,
28- } = useInfiniteQuery < BookmarkData > ( {
29- queryKey : [ "bookmarks" ] ,
30- queryFn : async ( { pageParam = 1 } ) => {
31- const result = await myBookmarks ( {
32- page : pageParam as number ,
33- limit : 10 ,
34- offset : 0 ,
35- } ) ;
36- return result as BookmarkData ;
37- } ,
31+ const { _t } = useTranslation ( ) ;
32+ const feedInfiniteQuery = useInfiniteQuery ( {
33+ queryKey : [ "dashboard-articles" ] ,
34+ queryFn : ( { pageParam } ) =>
35+ myBookmarks ( { limit : 10 , page : pageParam , offset : 0 } ) ,
36+ initialPageParam : 1 ,
3837 getNextPageParam : ( lastPage ) => {
39- return lastPage ?. meta ?. hasNextPage
40- ? lastPage . meta . currentPage + 1
41- : undefined ;
38+ const _page = lastPage ?. meta ?. currentPage ?? 1 ;
39+ const _totalPages = lastPage ? .meta ?. totalPages ?? 1 ;
40+ return _page + 1 <= _totalPages ? _page + 1 : null ;
4241 } ,
43- initialPageParam : 1 ,
4442 } ) ;
4543
46- const bookmarks = data ?. pages . flatMap ( ( page ) => page ?. nodes || [ ] ) || [ ] ;
47- const totalCount = data ?. pages [ 0 ] ?. meta ?. totalCount || 0 ;
48-
49- if ( isLoading ) {
50- return (
51- < div className = "space-y-6" >
52- < div className = "border-b pb-4" >
53- < h1 className = "text-2xl font-semibold" > My Bookmarks</ h1 >
54- < p className = "text-muted-foreground mt-1" >
55- Articles you've saved for later
56- </ p >
57- </ div >
58- < div className = "space-y-4" >
59- { Array . from ( { length : 3 } ) . map ( ( _ , i ) => (
60- < div key = { i } className = "animate-pulse" >
61- < div className = "bg-muted rounded-lg h-48" />
62- </ div >
63- ) ) }
64- </ div >
65- </ div >
66- ) ;
67- }
44+ const hasItems = useMemo ( ( ) => {
45+ const length = feedInfiniteQuery . data ?. pages . flat ( ) [ 0 ] ?. nodes . length ?? 0 ;
46+ return length > 0 ;
47+ } , [ feedInfiniteQuery ] ) ;
6848
69- if ( error ) {
70- return (
71- < div className = "text-center py-12" >
72- < div className = "text-destructive mb-2" > ⚠️ Error</ div >
73- < p className = "text-muted-foreground" > Failed to load bookmarks</ p >
74- </ div >
75- ) ;
76- }
49+ const appConfirm = useAppConfirm ( ) ;
50+ return (
51+ < div >
52+ < h3 className = "text-xl font-semibold" > { _t ( "Bookmarks" ) } </ h3 >
7753
78- if ( bookmarks . length === 0 ) {
79- return (
80- < div className = "space-y-6" >
81- < div className = "border-b pb-4" >
82- < h1 className = "text-2xl font-semibold" > My Bookmarks</ h1 >
83- < p className = "text-muted-foreground mt-1" >
84- Articles you've saved for later
85- </ p >
86- </ div >
87- < div className = "text-center py-12" >
88- < div className = "text-6xl mb-4" > 📚</ div >
89- < h2 className = "text-xl font-medium mb-2" > No bookmarks yet</ h2 >
90- < p className = "text-muted-foreground mb-6" >
91- Start bookmarking articles to see them here
92- </ p >
93- < a
94- href = "/"
95- className = "inline-flex items-center px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
96- >
97- Explore Articles
98- </ a >
54+ { ! hasItems && (
55+ < div className = " min-h-30 border border-dashed border-muted grid place-content-center mt-4" >
56+ < h3 className = "text-xl" >
57+ { _t ( "You didn't bookmark any article yet" ) }
58+ </ h3 >
9959 </ div >
100- </ div >
101- ) ;
102- }
60+ ) }
10361
104- return (
105- < div className = "space-y-6" >
106- < div className = "border-b pb-4" >
107- < h1 className = "text-2xl font-semibold" > My Bookmarks</ h1 >
108- < p className = "text-muted-foreground mt-1" >
109- { totalCount } article{ totalCount !== 1 ? "s" : "" } saved
110- </ p >
111- </ div >
62+ < div className = "flex flex-col divide-y divide-dashed divide-border-color mt-2" >
63+ { feedInfiniteQuery . isFetching &&
64+ Array . from ( { length : 10 } ) . map ( ( _ , i ) => (
65+ < article key = { i } className = " bg-muted h-20 animate-pulse" />
66+ ) ) }
11267
113- < div className = "space-y-6" >
114- { bookmarks . map ( ( bookmark ) => (
115- < ArticleCard
116- key = { bookmark . id }
117- id = { bookmark . article . id }
118- title = { bookmark . article . title }
119- excerpt = ""
120- coverImage = { bookmark . article . cover_image }
121- publishedAt = { bookmark . created_at }
122- readingTime = { 5 }
123- author = { bookmark . article . author }
124- handle = { "" }
125- likes = { 0 }
126- comments = { 0 }
127- />
128- ) ) }
129- </ div >
68+ { feedInfiniteQuery . data ?. pages . map ( ( page ) => {
69+ return page ?. nodes . map ( ( bookmark ) => (
70+ < article
71+ key = { bookmark . id }
72+ className = "flex justify-between flex-col md:flex-row py-3 space-y-2"
73+ >
74+ < div className = "flex flex-col" >
75+ < Link
76+ className = "text-forground text-lg"
77+ href = { `/@${ bookmark ?. article ?. path } ` }
78+ >
79+ { bookmark ?. article ?. title }
80+ </ Link >
81+ { bookmark ?. created_at && (
82+ < p className = "text-sm text-muted-foreground" >
83+ { _t ( "Bookmarked on" ) } { formattedTime ( bookmark ?. created_at ! ) }
84+ </ p >
85+ ) }
86+ </ div >
13087
131- { /* {hasNextPage && <VisibilitySensor loading={isFetchingNextPage} /> } */ }
88+ < div className = "flex items-center gap-10 justify-between" >
89+ < div className = "flex gap-4 items-center" >
90+ < div className = "text-forground-muted flex items-center gap-1" >
91+ < Button
92+ variant = { "destructive" }
93+ size = { "sm" }
94+ onClick = { ( ) =>
95+ appConfirm . show ( {
96+ title : _t ( "Sure to remove from bookmark?" ) ,
97+ labels : { confirm : _t ( "Remove" ) } ,
98+ onConfirm ( ) {
99+ toggleResourceBookmark ( {
100+ resource_id : bookmark . article . id ,
101+ resource_type : "ARTICLE" ,
102+ } ) . finally ( ( ) => feedInfiniteQuery . refetch ( ) ) ;
103+ } ,
104+ } )
105+ }
106+ >
107+ < Trash className = "h-4 w-4" />
108+ { _t ( "Remove" ) }
109+ </ Button >
110+ </ div >
111+ </ div >
112+ </ div >
113+ </ article >
114+ ) ) ;
115+ } ) }
116+ </ div >
117+ { feedInfiniteQuery . hasNextPage && (
118+ < VisibilitySensor onLoadmore = { feedInfiniteQuery . fetchNextPage } />
119+ ) }
132120 </ div >
133121 ) ;
134122} ;
0 commit comments