Files
smesh/src/components/EmojiPackList/index.tsx
2025-11-07 22:36:07 +08:00

87 lines
2.1 KiB
TypeScript

import { useFetchEvent } from '@/hooks'
import { generateBech32IdFromATag } from '@/lib/tag'
import { useNostr } from '@/providers/NostrProvider'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard'
const SHOW_COUNT = 10
export default function EmojiPackList() {
const { t } = useTranslation()
const { userEmojiListEvent } = useNostr()
const eventIds = useMemo(() => {
if (!userEmojiListEvent) return []
return (
userEmojiListEvent.tags
.map((tag) => (tag[0] === 'a' ? generateBech32IdFromATag(tag) : null))
.filter(Boolean) as `naddr1${string}`[]
).reverse()
}, [userEmojiListEvent])
const [showCount, setShowCount] = useState(SHOW_COUNT)
const bottomRef = useRef<HTMLDivElement | null>(null)
useEffect(() => {
const options = {
root: null,
rootMargin: '10px',
threshold: 0.1
}
const loadMore = () => {
if (showCount < eventIds.length) {
setShowCount((prev) => prev + SHOW_COUNT)
}
}
const observerInstance = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadMore()
}
}, options)
const currentBottomRef = bottomRef.current
if (currentBottomRef) {
observerInstance.observe(currentBottomRef)
}
return () => {
if (observerInstance && currentBottomRef) {
observerInstance.unobserve(currentBottomRef)
}
}
}, [showCount, eventIds])
if (eventIds.length === 0) {
return (
<div className="mt-2 text-sm text-center text-muted-foreground">
{t('no emoji packs found')}
</div>
)
}
return (
<div>
{eventIds.slice(0, showCount).map((eventId) => (
<EmojiPackNote key={eventId} eventId={eventId} />
))}
</div>
)
}
function EmojiPackNote({ eventId }: { eventId: string }) {
const { event, isFetching } = useFetchEvent(eventId)
if (isFetching) {
return <NoteCardLoadingSkeleton className="border-b" />
}
if (!event) {
return null
}
return <NoteCard event={event} className="w-full" />
}