index.tsx raw

   1  import { useFetchEvent } from '@/hooks'
   2  import { generateBech32IdFromATag } from '@/lib/tag'
   3  import { useNostr } from '@/providers/NostrProvider'
   4  import { useEffect, useMemo, useRef, useState } from 'react'
   5  import { useTranslation } from 'react-i18next'
   6  import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard'
   7  
   8  const SHOW_COUNT = 10
   9  
  10  export default function EmojiPackList() {
  11    const { t } = useTranslation()
  12    const { userEmojiListEvent } = useNostr()
  13    const eventIds = useMemo(() => {
  14      if (!userEmojiListEvent) return []
  15  
  16      return (
  17        userEmojiListEvent.tags
  18          .map((tag) => (tag[0] === 'a' ? generateBech32IdFromATag(tag) : null))
  19          .filter(Boolean) as `naddr1${string}`[]
  20      ).reverse()
  21    }, [userEmojiListEvent])
  22    const [showCount, setShowCount] = useState(SHOW_COUNT)
  23    const bottomRef = useRef<HTMLDivElement | null>(null)
  24  
  25    useEffect(() => {
  26      const options = {
  27        root: null,
  28        rootMargin: '10px',
  29        threshold: 0.1
  30      }
  31  
  32      const loadMore = () => {
  33        if (showCount < eventIds.length) {
  34          setShowCount((prev) => prev + SHOW_COUNT)
  35        }
  36      }
  37  
  38      const observerInstance = new IntersectionObserver((entries) => {
  39        if (entries[0].isIntersecting) {
  40          loadMore()
  41        }
  42      }, options)
  43  
  44      const currentBottomRef = bottomRef.current
  45  
  46      if (currentBottomRef) {
  47        observerInstance.observe(currentBottomRef)
  48      }
  49  
  50      return () => {
  51        if (observerInstance && currentBottomRef) {
  52          observerInstance.unobserve(currentBottomRef)
  53        }
  54      }
  55    }, [showCount, eventIds])
  56  
  57    if (eventIds.length === 0) {
  58      return (
  59        <div className="mt-2 text-sm text-center text-muted-foreground">
  60          {t('no emoji packs found')}
  61        </div>
  62      )
  63    }
  64  
  65    return (
  66      <div>
  67        {eventIds.slice(0, showCount).map((eventId) => (
  68          <EmojiPackNote key={eventId} eventId={eventId} />
  69        ))}
  70      </div>
  71    )
  72  }
  73  
  74  function EmojiPackNote({ eventId }: { eventId: string }) {
  75    const { event, isFetching } = useFetchEvent(eventId)
  76  
  77    if (isFetching) {
  78      return <NoteCardLoadingSkeleton className="border-b" />
  79    }
  80  
  81    if (!event) {
  82      return null
  83    }
  84  
  85    return <NoteCard event={event} className="w-full" />
  86  }
  87