1
1
'use client' ;
2
- import { useQuery } from '@tanstack/react-query' ;
2
+ import { useInfiniteQuery , useQuery } from '@tanstack/react-query' ;
3
3
import { AxiosError } from 'axios' ;
4
4
import React from 'react' ;
5
5
import ReactTimeAgo from 'react-time-ago' ;
6
6
7
7
import { searchUser , userKey } from '@/lib/api/user' ;
8
- import { buildPaginatedTableURL } from '@/lib/table' ;
8
+ import axios from '@/lib/axios' ;
9
+ import { buildInfiniteTableURL , buildPaginatedTableURL } from '@/lib/table' ;
9
10
import useQueryToast from '@/hooks/toast/useQueryToast' ;
10
11
import useServerTable from '@/hooks/useServerTable' ;
11
12
12
13
import ButtonLink from '@/components/links/ButtonLink' ;
13
14
import NextImage from '@/components/NextImage' ;
14
15
import ServerCard from '@/components/table/ServerCard' ;
16
+ import ServerInfiniteCard from '@/components/table/ServerInfiniteCard' ;
15
17
import Typography from '@/components/typography/Typography' ;
16
18
17
19
import useAuthStore from '@/store/useAuthStore' ;
18
20
import useChatStore from '@/store/useChatStore' ;
19
21
20
- import { ApiError , PaginatedApiResponse } from '@/types/api' ;
22
+ import {
23
+ ApiError ,
24
+ ApiResponse ,
25
+ BasePaginationResponseField ,
26
+ PaginatedApiResponse ,
27
+ } from '@/types/api' ;
21
28
import { User } from '@/types/user' ;
22
29
23
30
export default function SocialPage ( ) {
24
31
const user = useAuthStore . useUser ( ) ;
25
32
const chatStore = useChatStore ( ) ;
26
33
27
- const { tableState, setTableState } =
28
- useServerTable < Omit < User , 'email' | 'roles' > > ( ) ;
34
+ const { tableState, setTableState } = useServerTable <
35
+ Omit < User , 'email' | 'roles' >
36
+ > ( { pageSize : 6 } ) ;
29
37
30
38
const url = buildPaginatedTableURL ( {
31
39
baseUrl : '/user/search' ,
@@ -45,9 +53,42 @@ export default function SocialPage() {
45
53
{ hideSuccess : true }
46
54
) ;
47
55
56
+ const result = useInfiniteQuery ( {
57
+ queryKey : [ userKey , 'users' , tableState . globalFilter ] ,
58
+ queryFn : async ( { pageParam } ) => {
59
+ return await axios
60
+ . get <
61
+ ApiResponse <
62
+ {
63
+ users : Array < Omit < User , 'email' | 'roles' > > ;
64
+ } & BasePaginationResponseField
65
+ >
66
+ > (
67
+ buildInfiniteTableURL ( {
68
+ baseUrl : '/user/search' ,
69
+ page : pageParam as number ,
70
+ tableState,
71
+ } )
72
+ )
73
+ . then ( ( res ) => {
74
+ const { users, ...rest } = res . data . data ;
75
+ return { ...rest , statusCode : 200 , data : users } ;
76
+ } ) ;
77
+ } ,
78
+ initialPageParam : 0 ,
79
+ getNextPageParam : ( lastPage , allPages , lastPageParam ) => {
80
+ const maxPage = Math . ceil (
81
+ lastPage . total / tableState . pagination . pageSize
82
+ ) ;
83
+ return lastPageParam + 1 < maxPage ? lastPage . page : null ;
84
+ } ,
85
+ getPreviousPageParam : ( firstPage ) => firstPage . page - 1 ,
86
+ } ) ;
87
+
48
88
return (
49
89
< >
50
90
< div className = 'w-full' >
91
+ < h2 className = 'text-lg md:text-xl' > Paginated Card</ h2 >
51
92
< ServerCard
52
93
card = {
53
94
< >
@@ -153,6 +194,119 @@ export default function SocialPage() {
153
194
withFilter
154
195
/>
155
196
</ div >
197
+ < div className = 'w-full' >
198
+ < h2 className = 'text-lg md:text-xl' > Infinite Scroll</ h2 >
199
+ < ServerInfiniteCard
200
+ card = {
201
+ < >
202
+ < div className = 'mt-5' >
203
+ { result . data ?. pages
204
+ . flatMap ( ( page ) => page . data )
205
+ . map ( ( item ) => (
206
+ < div
207
+ key = { item . id }
208
+ className = 'flex h-32 p-5 shadow-lg dark:text-white dark:shadow-gray-700'
209
+ >
210
+ < div className = 'w-[80px] rounded-full' >
211
+ < NextImage
212
+ width = { 80 }
213
+ height = { 80 }
214
+ src = { item . picture ?? '/images/profile_picture.jpg' }
215
+ classNames = { {
216
+ image :
217
+ 'rounded-full w-[80px] h-[80px] object-cover' ,
218
+ } }
219
+ alt = 'User'
220
+ className = 'w-[80px]'
221
+ unoptimized = { true }
222
+ />
223
+ </ div >
224
+ < div className = 'ml-5' >
225
+ < Typography variant = 's2' className = 'dark:text-white' >
226
+ { item . username }
227
+ </ Typography >
228
+ < Typography variant = 's4' className = 'dark:text-white' >
229
+ Joined{ ' ' }
230
+ < ReactTimeAgo
231
+ date = { new Date ( item . createdAt ) }
232
+ locale = 'en-US'
233
+ />
234
+ </ Typography >
235
+ { item . id !== user ?. id && (
236
+ < div className = 'mt-1' >
237
+ < ButtonLink
238
+ href = '/chat'
239
+ variant = 'outline'
240
+ className = 'border-graydark text-graydark hover:bg-gray-100 dark:border-white dark:text-white dark:hover:bg-gray-600'
241
+ onClick = { ( ) => {
242
+ const chat = chatStore . chatList ?. find (
243
+ ( chat ) =>
244
+ chat . user1 . id == item . id ||
245
+ chat . user2 . id == item . id
246
+ ) ;
247
+ if ( chat ) {
248
+ chatStore . setActiveChat ( chat ) ;
249
+ } else {
250
+ const newChat = {
251
+ id : `${ ( user as User ) . id } -${ item . id } ` ,
252
+ user1 : user as User ,
253
+ user2 : item ,
254
+ lastMessage : '' ,
255
+ lastMessageAt : null ,
256
+ isNewChat : true ,
257
+ } ;
258
+ chatStore . setNewChat ( newChat ) ;
259
+ chatStore . setActiveChat ( newChat ) ;
260
+ }
261
+ } }
262
+ >
263
+ < svg
264
+ className = 'mr-1 fill-current duration-300 ease-in-out'
265
+ width = '18'
266
+ height = '18'
267
+ viewBox = '0 0 18 18'
268
+ fill = 'none'
269
+ xmlns = 'http://www.w3.org/2000/svg'
270
+ >
271
+ < path
272
+ d = 'M10.9688 1.57495H7.03135C3.43135 1.57495 0.506348 4.41558 0.506348 7.90308C0.506348 11.3906 2.75635 13.8375 8.26885 16.3125C8.40947 16.3687 8.52197 16.3968 8.6626 16.3968C8.85947 16.3968 9.02822 16.3406 9.19697 16.2281C9.47822 16.0593 9.64697 15.75 9.64697 15.4125V14.2031H10.9688C14.5688 14.2031 17.522 11.3625 17.522 7.87495C17.522 4.38745 14.5688 1.57495 10.9688 1.57495ZM10.9688 12.9937H9.3376C8.80322 12.9937 8.35322 13.4437 8.35322 13.9781V15.0187C3.6001 12.825 1.74385 10.8 1.74385 7.9312C1.74385 5.14683 4.10635 2.8687 7.03135 2.8687H10.9688C13.8657 2.8687 16.2563 5.14683 16.2563 7.9312C16.2563 10.7156 13.8657 12.9937 10.9688 12.9937Z'
273
+ fill = ''
274
+ />
275
+ < path
276
+ d = 'M5.42812 7.28442C5.0625 7.28442 4.78125 7.56567 4.78125 7.9313C4.78125 8.29692 5.0625 8.57817 5.42812 8.57817C5.79375 8.57817 6.075 8.29692 6.075 7.9313C6.075 7.56567 5.79375 7.28442 5.42812 7.28442Z'
277
+ fill = ''
278
+ />
279
+ < path
280
+ d = 'M9.00015 7.28442C8.63452 7.28442 8.35327 7.56567 8.35327 7.9313C8.35327 8.29692 8.63452 8.57817 9.00015 8.57817C9.33765 8.57817 9.64702 8.29692 9.64702 7.9313C9.64702 7.56567 9.33765 7.28442 9.00015 7.28442Z'
281
+ fill = ''
282
+ />
283
+ < path
284
+ d = 'M12.5719 7.28442C12.2063 7.28442 11.925 7.56567 11.925 7.9313C11.925 8.29692 12.2063 8.57817 12.5719 8.57817C12.9375 8.57817 13.2188 8.29692 13.2188 7.9313C13.2188 7.56567 12.9094 7.28442 12.5719 7.28442Z'
285
+ fill = ''
286
+ />
287
+ </ svg >
288
+ Chat
289
+ </ ButtonLink >
290
+ </ div >
291
+ ) }
292
+ </ div >
293
+ </ div >
294
+ ) ) }
295
+ </ div >
296
+ </ >
297
+ }
298
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
299
+ //@ts -ignore
300
+ response = { result . data ?. pages . flatMap ( ( page ) => page . data ) }
301
+ data = { result . data ?. pages . flatMap ( ( page ) => page . data ) ?? [ ] }
302
+ isLoading = { isPending }
303
+ tableState = { tableState }
304
+ setTableState = { setTableState }
305
+ className = 'mt-8'
306
+ withFilter
307
+ infiniteResult = { result }
308
+ />
309
+ </ div >
156
310
</ >
157
311
) ;
158
312
}
0 commit comments