index.tsx raw

   1  import { EmbeddedEmojiParser, parseContent } from '@/lib/content-parser'
   2  import { TEmoji } from '@/types'
   3  import { useMemo } from 'react'
   4  import Emoji from '../Emoji'
   5  
   6  /**
   7   * Component that renders text with custom emojis replaced by emoji images
   8   * According to NIP-30, custom emojis are defined in emoji tags and referenced as :shortcode: in text
   9   */
  10  export default function TextWithEmojis({
  11    text,
  12    emojis,
  13    className,
  14    emojiClassName
  15  }: {
  16    text: string
  17    emojis?: TEmoji[]
  18    className?: string
  19    emojiClassName?: string
  20  }) {
  21    const nodes = useMemo(() => {
  22      if (!emojis || emojis.length === 0) {
  23        return [{ type: 'text' as const, data: text }]
  24      }
  25  
  26      // Use the existing content parser infrastructure
  27      return parseContent(text, [EmbeddedEmojiParser])
  28    }, [text, emojis])
  29  
  30    // Create emoji map for quick lookup
  31    const emojiMap = useMemo(() => {
  32      const map = new Map<string, TEmoji>()
  33      emojis?.forEach((emoji) => {
  34        map.set(emoji.shortcode, emoji)
  35      })
  36      return map
  37    }, [emojis])
  38  
  39    return (
  40      <span className={className}>
  41        {nodes.map((node, index) => {
  42          if (node.type === 'text') {
  43            return node.data
  44          }
  45          if (node.type === 'emoji') {
  46            const shortcode = node.data.split(':')[1]
  47            const emoji = emojiMap.get(shortcode)
  48            if (!emoji) return node.data
  49            return <Emoji key={index} emoji={emoji} classNames={{ img: emojiClassName }} />
  50          }
  51          return null
  52        })}
  53      </span>
  54    )
  55  }
  56