index.tsx raw

   1  import ImageWithLightbox from '@/components/ImageWithLightbox'
   2  import NormalFeed from '@/components/NormalFeed'
   3  import ProfileList from '@/components/ProfileList'
   4  import { Skeleton } from '@/components/ui/skeleton'
   5  import { useFetchEvent } from '@/hooks/useFetchEvent'
   6  import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
   7  import { getFollowPackInfoFromEvent } from '@/lib/event-metadata'
   8  import { cn } from '@/lib/utils'
   9  import { useNostr } from '@/providers/NostrProvider'
  10  import client from '@/services/client.service'
  11  import { TFeedSubRequest } from '@/types'
  12  import { forwardRef, useEffect, useMemo, useState } from 'react'
  13  import { useTranslation } from 'react-i18next'
  14  
  15  const FollowPackPage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => {
  16    const { t } = useTranslation()
  17    const [tab, setTab] = useState<'users' | 'feed'>('users')
  18  
  19    const { event, isFetching } = useFetchEvent(id)
  20  
  21    const { title, description, image, pubkeys } = useMemo(() => {
  22      if (!event) return { title: '', description: '', image: '', pubkeys: [] }
  23      return getFollowPackInfoFromEvent(event)
  24    }, [event])
  25  
  26    if (isFetching) {
  27      return (
  28        <SecondaryPageLayout ref={ref} index={index} title={t('Follow Pack')}>
  29          <div className="px-4 py-3 space-y-2">
  30            <Skeleton className="h-48 w-full" />
  31            <Skeleton className="h-7 py-1 w-full" />
  32          </div>
  33        </SecondaryPageLayout>
  34      )
  35    }
  36  
  37    if (!event) {
  38      return (
  39        <SecondaryPageLayout ref={ref} index={index} title={t('Follow Pack')}>
  40          <div className="p-4 text-center text-muted-foreground">{t('Follow pack not found')}</div>
  41        </SecondaryPageLayout>
  42      )
  43    }
  44  
  45    return (
  46      <SecondaryPageLayout ref={ref} index={index} title={t('Follow Pack')} displayScrollToTopButton>
  47        <div>
  48          {/* Header */}
  49          <div className="px-4 pt-3 space-y-2">
  50            {image && (
  51              <ImageWithLightbox
  52                image={{ url: image, pubkey: event.pubkey }}
  53                className="w-full h-48 object-cover rounded-lg"
  54                classNames={{
  55                  wrapper: 'w-full h-48 border-none'
  56                }}
  57              />
  58            )}
  59  
  60            <div className="flex items-center gap-2">
  61              <h3 className="text-2xl font-semibold mb-1 truncate">{title}</h3>
  62              <span className="text-xs text-muted-foreground shrink-0">
  63                {t('n users', { count: pubkeys.length })}
  64              </span>
  65            </div>
  66  
  67            {description && (
  68              <p className="text-sm text-muted-foreground whitespace-pre-wrap">{description}</p>
  69            )}
  70  
  71            <div className="inline-flex items-center rounded-lg border bg-muted/50">
  72              <button
  73                onClick={() => setTab('users')}
  74                className={cn(
  75                  'px-3 py-1.5 text-sm font-medium rounded-l-lg transition-colors',
  76                  tab === 'users'
  77                    ? 'bg-background text-foreground shadow-sm'
  78                    : 'text-muted-foreground hover:text-foreground'
  79                )}
  80              >
  81                {t('Users')}
  82              </button>
  83              <button
  84                onClick={() => setTab('feed')}
  85                className={cn(
  86                  'px-3 py-1.5 text-sm font-medium rounded-r-lg transition-colors',
  87                  tab === 'feed'
  88                    ? 'bg-background text-foreground shadow-sm'
  89                    : 'text-muted-foreground hover:text-foreground'
  90                )}
  91              >
  92                {t('Feed')}
  93              </button>
  94            </div>
  95          </div>
  96  
  97          {/* Content */}
  98          {tab === 'users' && <ProfileList pubkeys={pubkeys} />}
  99          {tab === 'feed' && pubkeys.length > 0 && <Feed pubkeys={pubkeys} />}
 100        </div>
 101      </SecondaryPageLayout>
 102    )
 103  })
 104  FollowPackPage.displayName = 'FollowPackPage'
 105  export default FollowPackPage
 106  
 107  function Feed({ pubkeys }: { pubkeys: string[] }) {
 108    const { pubkey: myPubkey } = useNostr()
 109    const [subRequests, setSubRequests] = useState<TFeedSubRequest[]>([])
 110  
 111    useEffect(() => {
 112      client.generateSubRequestsForPubkeys(pubkeys, myPubkey).then(setSubRequests)
 113    }, [pubkeys, myPubkey])
 114  
 115    return <NormalFeed subRequests={subRequests} />
 116  }
 117