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