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