index.tsx raw
1 import client from '@/services/client.service'
2 import storage from '@/services/local-storage.service'
3 import dayjs from 'dayjs'
4 import { useEffect, useRef, useState } from 'react'
5 import UserItem, { UserItemSkeleton } from '../UserItem'
6
7 const LIMIT = 50
8
9 export function ProfileListBySearch({ search }: { search: string }) {
10 const [until, setUntil] = useState<number>(() => dayjs().unix())
11 const [hasMore, setHasMore] = useState<boolean>(true)
12 const [pubkeySet, setPubkeySet] = useState(new Set<string>())
13 const bottomRef = useRef<HTMLDivElement>(null)
14
15 useEffect(() => {
16 setUntil(dayjs().unix())
17 setHasMore(true)
18 setPubkeySet(new Set<string>())
19 loadMore()
20 }, [search])
21
22 useEffect(() => {
23 if (!hasMore) return
24 const options = {
25 root: null,
26 rootMargin: '10px',
27 threshold: 1
28 }
29
30 const observerInstance = new IntersectionObserver((entries) => {
31 if (entries[0].isIntersecting && hasMore) {
32 loadMore()
33 }
34 }, options)
35
36 const currentBottomRef = bottomRef.current
37
38 if (currentBottomRef) {
39 observerInstance.observe(currentBottomRef)
40 }
41
42 return () => {
43 if (observerInstance && currentBottomRef) {
44 observerInstance.unobserve(currentBottomRef)
45 }
46 }
47 }, [hasMore, search, until])
48
49 const loadMore = async () => {
50 const profiles = await client.searchProfiles(storage.getSearchRelays(), {
51 search,
52 until,
53 limit: LIMIT
54 })
55 const newPubkeySet = new Set<string>()
56 profiles.forEach((profile) => {
57 if (!pubkeySet.has(profile.pubkey)) {
58 newPubkeySet.add(profile.pubkey)
59 }
60 })
61 setPubkeySet((prev) => new Set([...prev, ...newPubkeySet]))
62 setHasMore(profiles.length >= LIMIT)
63 const lastProfileCreatedAt = profiles[profiles.length - 1].created_at
64 setUntil(lastProfileCreatedAt ? lastProfileCreatedAt - 1 : 0)
65 }
66
67 return (
68 <div className="px-4">
69 {Array.from(pubkeySet).map((pubkey, index) => (
70 <UserItem key={`${index}-${pubkey}`} userId={pubkey} />
71 ))}
72 {hasMore && <UserItemSkeleton />}
73 {hasMore && <div ref={bottomRef} />}
74 </div>
75 )
76 }
77