You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
2.4 KiB
82 lines
2.4 KiB
import { logContentSpacing, reprString } from '@/lib/content-spacing-debug' |
|
import customEmojiService from '@/services/custom-emoji.service' |
|
import { emojis, shortcodeToEmoji } from '@tiptap/extension-emoji' |
|
import { JSONContent } from '@tiptap/react' |
|
import { nip19 } from 'nostr-tools' |
|
|
|
export function parseEditorJsonToText(node?: JSONContent) { |
|
const rawJoined = _parseEditorJsonToText(node) |
|
let text = rawJoined.trim() |
|
const trace = rawJoined.includes('nostr:') || /npub1|nprofile1/.test(rawJoined) |
|
if (trace) { |
|
logContentSpacing('parseEditorJsonToText:joined', { |
|
beforeTrimRepr: reprString(rawJoined), |
|
afterTrimRepr: reprString(text) |
|
}) |
|
} |
|
const regex = /(?:^|\s)(nevent|naddr|nprofile|npub)1[a-zA-Z0-9]+/g |
|
|
|
text = text.replace(regex, (match) => { |
|
const trimmed = match.trim() |
|
const leadingSpace = match.startsWith(' ') ? ' ' : '' |
|
|
|
try { |
|
nip19.decode(trimmed) |
|
return `${leadingSpace}nostr:${trimmed}` |
|
} catch { |
|
return match |
|
} |
|
}) |
|
|
|
// Ensure space before nostr: when not already preceded by space (fixes "Like:nostr:npub" and "Like:\nnostr:npub") |
|
const beforeNostrSpacePass = text |
|
text = text.replace(/(.)(?=nostr:)/g, (_, prev) => (prev === ' ' ? prev : prev + ' ')) |
|
if (trace) { |
|
logContentSpacing('parseEditorJsonToText:after-nostr-prefix-pass', { |
|
beforeRepr: reprString(beforeNostrSpacePass), |
|
afterRepr: reprString(text) |
|
}) |
|
} |
|
return text |
|
} |
|
|
|
function _parseEditorJsonToText(node?: JSONContent): string { |
|
if (!node) return '' |
|
|
|
if (typeof node === 'string') return node |
|
|
|
if (node.type === 'text') { |
|
return node.text || '' |
|
} |
|
|
|
if (node.type === 'hardBreak') { |
|
return '\n' |
|
} |
|
|
|
if (Array.isArray(node.content)) { |
|
return ( |
|
node.content.map(_parseEditorJsonToText).join('') + (node.type === 'paragraph' ? '\n' : '') |
|
) |
|
} |
|
|
|
switch (node.type) { |
|
case 'paragraph': |
|
return '\n' |
|
case 'mention': |
|
return node.attrs ? `nostr:${node.attrs.id}` : '' |
|
case 'emoji': |
|
return parseEmojiNodeName(node.attrs?.name) |
|
default: |
|
return '' |
|
} |
|
} |
|
|
|
function parseEmojiNodeName(name?: string): string { |
|
if (!name) return '' |
|
if (customEmojiService.isCustomEmojiId(name)) { |
|
const custom = customEmojiService.getEmojiById(name) |
|
return custom ? `:${custom.shortcode}:` : `:${name}:` |
|
} |
|
const native = shortcodeToEmoji(name, emojis) ?? shortcodeToEmoji(name.replace(/\s+/g, '_'), emojis) |
|
return native?.emoji ?? `:${name}:` |
|
}
|
|
|