Files
smesh/src/pages/secondary/FollowPackPage/index.tsx
2025-11-27 00:02:13 +08:00

117 lines
4.1 KiB
TypeScript

import ImageWithLightbox from '@/components/ImageWithLightbox'
import NormalFeed from '@/components/NormalFeed'
import ProfileList from '@/components/ProfileList'
import { Skeleton } from '@/components/ui/skeleton'
import { useFetchEvent } from '@/hooks/useFetchEvent'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { getFollowPackInfoFromEvent } from '@/lib/event-metadata'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service'
import { TFeedSubRequest } from '@/types'
import { forwardRef, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
const FollowPackPage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => {
const { t } = useTranslation()
const [tab, setTab] = useState<'users' | 'feed'>('users')
const { event, isFetching } = useFetchEvent(id)
const { title, description, image, pubkeys } = useMemo(() => {
if (!event) return { title: '', description: '', image: '', pubkeys: [] }
return getFollowPackInfoFromEvent(event)
}, [event])
if (isFetching) {
return (
<SecondaryPageLayout ref={ref} index={index} title={t('Follow Pack')}>
<div className="px-4 py-3 space-y-2">
<Skeleton className="h-48 w-full" />
<Skeleton className="h-7 py-1 w-full" />
</div>
</SecondaryPageLayout>
)
}
if (!event) {
return (
<SecondaryPageLayout ref={ref} index={index} title={t('Follow Pack')}>
<div className="p-4 text-center text-muted-foreground">{t('Follow pack not found')}</div>
</SecondaryPageLayout>
)
}
return (
<SecondaryPageLayout ref={ref} index={index} title={t('Follow Pack')} displayScrollToTopButton>
<div>
{/* Header */}
<div className="px-4 pt-3 space-y-2">
{image && (
<ImageWithLightbox
image={{ url: image, pubkey: event.pubkey }}
className="w-full h-48 object-cover rounded-lg"
classNames={{
wrapper: 'w-full h-48 border-none'
}}
/>
)}
<div className="flex items-center gap-2">
<h3 className="text-2xl font-semibold mb-1 truncate">{title}</h3>
<span className="text-xs text-muted-foreground shrink-0">
{t('n users', { count: pubkeys.length })}
</span>
</div>
{description && (
<p className="text-sm text-muted-foreground whitespace-pre-wrap">{description}</p>
)}
<div className="inline-flex items-center rounded-lg border bg-muted/50">
<button
onClick={() => setTab('users')}
className={cn(
'px-3 py-1.5 text-sm font-medium rounded-l-lg transition-colors',
tab === 'users'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
)}
>
{t('Users')}
</button>
<button
onClick={() => setTab('feed')}
className={cn(
'px-3 py-1.5 text-sm font-medium rounded-r-lg transition-colors',
tab === 'feed'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
)}
>
{t('Feed')}
</button>
</div>
</div>
{/* Content */}
{tab === 'users' && <ProfileList pubkeys={pubkeys} />}
{tab === 'feed' && pubkeys.length > 0 && <Feed pubkeys={pubkeys} />}
</div>
</SecondaryPageLayout>
)
})
FollowPackPage.displayName = 'FollowPackPage'
export default FollowPackPage
function Feed({ pubkeys }: { pubkeys: string[] }) {
const { pubkey: myPubkey } = useNostr()
const [subRequests, setSubRequests] = useState<TFeedSubRequest[]>([])
useEffect(() => {
client.generateSubRequestsForPubkeys(pubkeys, myPubkey).then(setSubRequests)
}, [pubkeys, myPubkey])
return <NormalFeed subRequests={subRequests} />
}