tiptap.ts raw
1 import customEmojiService from '@/services/custom-emoji.service'
2 import { emojis, shortcodeToEmoji } from '@tiptap/extension-emoji'
3 import { JSONContent } from '@tiptap/react'
4 import { nip19 } from 'nostr-tools'
5
6 export function parseEditorJsonToText(node?: JSONContent) {
7 const text = _parseEditorJsonToText(node)
8 const regex = /(^|\s+|@)(nostr:)?(nevent|naddr|nprofile|npub)1[a-zA-Z0-9]+/g
9
10 return text.replace(regex, (match, leadingWhitespace) => {
11 let bech32 = match.trim()
12 const whitespace = leadingWhitespace || ''
13
14 if (bech32.startsWith('@nostr:')) {
15 bech32 = bech32.slice(7)
16 } else if (bech32.startsWith('@')) {
17 bech32 = bech32.slice(1)
18 } else if (bech32.startsWith('nostr:')) {
19 bech32 = bech32.slice(6)
20 }
21
22 try {
23 nip19.decode(bech32)
24 return `${whitespace}nostr:${bech32}`
25 } catch {
26 return match
27 }
28 }).trim()
29 }
30
31 function _parseEditorJsonToText(node?: JSONContent): string {
32 if (!node) return ''
33
34 if (typeof node === 'string') return node
35
36 if (node.type === 'text') {
37 return node.text || ''
38 }
39
40 if (node.type === 'hardBreak') {
41 return '\n'
42 }
43
44 if (Array.isArray(node.content)) {
45 return (
46 node.content.map(_parseEditorJsonToText).join('') + (node.type === 'paragraph' ? '\n' : '')
47 )
48 }
49
50 switch (node.type) {
51 case 'paragraph':
52 return '\n'
53 case 'mention':
54 return node.attrs ? `nostr:${node.attrs.id}` : ''
55 case 'emoji':
56 return parseEmojiNodeName(node.attrs?.name)
57 default:
58 return ''
59 }
60 }
61
62 function parseEmojiNodeName(name?: string): string {
63 if (!name) return ''
64 if (customEmojiService.isCustomEmojiId(name)) {
65 return `:${name}:`
66 }
67 const emoji = shortcodeToEmoji(name, emojis)
68 return emoji ? (emoji.emoji ?? '') : ''
69 }
70