feat: support NIP-30 custom emojis in bio and display name (#660)

This commit is contained in:
Alex Gleason 2025-11-14 08:01:29 -06:00 committed by GitHub
parent f8cca5522f
commit 82c13006ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 105 additions and 11 deletions

View file

@ -0,0 +1,55 @@
import { EmbeddedEmojiParser, parseContent } from '@/lib/content-parser'
import { TEmoji } from '@/types'
import { useMemo } from 'react'
import Emoji from '../Emoji'
/**
* Component that renders text with custom emojis replaced by emoji images
* According to NIP-30, custom emojis are defined in emoji tags and referenced as :shortcode: in text
*/
export default function TextWithEmojis({
text,
emojis,
className,
emojiClassName
}: {
text: string
emojis?: TEmoji[]
className?: string
emojiClassName?: string
}) {
const nodes = useMemo(() => {
if (!emojis || emojis.length === 0) {
return [{ type: 'text' as const, data: text }]
}
// Use the existing content parser infrastructure
return parseContent(text, [EmbeddedEmojiParser])
}, [text, emojis])
// Create emoji map for quick lookup
const emojiMap = useMemo(() => {
const map = new Map<string, TEmoji>()
emojis?.forEach((emoji) => {
map.set(emoji.shortcode, emoji)
})
return map
}, [emojis])
return (
<span className={className}>
{nodes.map((node, index) => {
if (node.type === 'text') {
return node.data
}
if (node.type === 'emoji') {
const shortcode = node.data.split(':')[1]
const emoji = emojiMap.get(shortcode)
if (!emoji) return node.data
return <Emoji key={index} emoji={emoji} classNames={{ img: emojiClassName }} />
}
return null
})}
</span>
)
}