index.tsx raw
1 import { Favicon } from '@/components/Favicon'
2 import NormalFeed from '@/components/NormalFeed'
3 import { Button } from '@/components/ui/button'
4 import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
5 import { toProfileList } from '@/lib/link'
6 import { fetchPubkeysFromDomain, getWellKnownNip05Url } from '@/lib/nip05'
7 import { useSecondaryPage } from '@/PageManager'
8 import { useNostr } from '@/providers/NostrProvider'
9 import client from '@/services/client.service'
10 import storage from '@/services/local-storage.service'
11 import { TFeedSubRequest } from '@/types'
12 import { UserRound } from 'lucide-react'
13 import React, { forwardRef, useEffect, useState } from 'react'
14 import { useTranslation } from 'react-i18next'
15
16 const NoteListPage = forwardRef(({ index }: { index?: number }, ref) => {
17 const { t } = useTranslation()
18 const { push } = useSecondaryPage()
19 const { pubkey } = useNostr()
20 const [title, setTitle] = useState<React.ReactNode>(null)
21 const [controls, setControls] = useState<React.ReactNode>(null)
22 const [data, setData] = useState<
23 | {
24 type: 'hashtag' | 'search'
25 kinds?: number[]
26 }
27 | {
28 type: 'domain'
29 domain: string
30 kinds?: number[]
31 }
32 | null
33 >(null)
34 const [subRequests, setSubRequests] = useState<TFeedSubRequest[]>([])
35
36 useEffect(() => {
37 const init = async () => {
38 const searchParams = new URLSearchParams(window.location.search)
39 const kinds = searchParams
40 .getAll('k')
41 .map((k) => parseInt(k))
42 .filter((k) => !isNaN(k))
43 const hashtag = searchParams.get('t')
44 if (hashtag) {
45 setData({ type: 'hashtag' })
46 setTitle(`# ${hashtag}`)
47 setSubRequests([
48 {
49 filter: { '#t': [hashtag], ...(kinds.length > 0 ? { kinds } : {}) },
50 urls: client.currentRelays
51 }
52 ])
53 return
54 }
55 const search = searchParams.get('s')
56 if (search) {
57 setData({ type: 'search' })
58 setTitle(`${t('Search')}: ${search}`)
59 setSubRequests([
60 {
61 filter: { search, ...(kinds.length > 0 ? { kinds } : {}) },
62 urls: storage.getSearchRelays()
63 }
64 ])
65 return
66 }
67 const domain = searchParams.get('d')
68 if (domain) {
69 setTitle(
70 <div className="flex items-center gap-1">
71 {domain}
72 <Favicon domain={domain} className="w-5 h-5" />
73 </div>
74 )
75 const pubkeys = await fetchPubkeysFromDomain(domain)
76 setData({
77 type: 'domain',
78 domain
79 })
80 if (pubkeys.length) {
81 setSubRequests(await client.generateSubRequestsForPubkeys(pubkeys, pubkey))
82 setControls(
83 <Button
84 variant="ghost"
85 className="h-10 [&_svg]:size-3"
86 onClick={() => push(toProfileList({ domain }))}
87 >
88 {pubkeys.length.toLocaleString()} <UserRound />
89 </Button>
90 )
91 } else {
92 setSubRequests([])
93 }
94 return
95 }
96 }
97 init()
98 }, [])
99
100 let content: React.ReactNode = null
101 if (data?.type === 'domain' && subRequests.length === 0) {
102 content = (
103 <div className="text-center w-full py-10">
104 <span className="text-muted-foreground">
105 {t('No pubkeys found from {url}', { url: getWellKnownNip05Url(data.domain) })}
106 </span>
107 </div>
108 )
109 } else if (data) {
110 content = <NormalFeed subRequests={subRequests} />
111 }
112
113 return (
114 <SecondaryPageLayout
115 ref={ref}
116 index={index}
117 title={title}
118 controls={controls}
119 displayScrollToTopButton
120 >
121 {content}
122 </SecondaryPageLayout>
123 )
124 })
125 NoteListPage.displayName = 'NoteListPage'
126 export default NoteListPage
127