|
|
|
@ -3,13 +3,12 @@ import { |
|
|
|
EMBEDDED_MENTION_REGEX, |
|
|
|
EMBEDDED_MENTION_REGEX, |
|
|
|
EMOJI_SHORT_CODE_REGEX, |
|
|
|
EMOJI_SHORT_CODE_REGEX, |
|
|
|
HASHTAG_REGEX, |
|
|
|
HASHTAG_REGEX, |
|
|
|
IMAGE_REGEX, |
|
|
|
|
|
|
|
LN_INVOICE_REGEX, |
|
|
|
LN_INVOICE_REGEX, |
|
|
|
URL_REGEX, |
|
|
|
URL_REGEX, |
|
|
|
MEDIA_REGEX, |
|
|
|
|
|
|
|
WS_URL_REGEX, |
|
|
|
WS_URL_REGEX, |
|
|
|
YOUTUBE_URL_REGEX |
|
|
|
YOUTUBE_URL_REGEX |
|
|
|
} from '@/constants' |
|
|
|
} from '@/constants' |
|
|
|
|
|
|
|
import { isImage, isMedia } from './url' |
|
|
|
|
|
|
|
|
|
|
|
export type TEmbeddedNodeType = |
|
|
|
export type TEmbeddedNodeType = |
|
|
|
| 'text' |
|
|
|
| 'text' |
|
|
|
@ -36,7 +35,9 @@ export type TEmbeddedNode = |
|
|
|
data: string[] |
|
|
|
data: string[] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type TContentParser = { type: Exclude<TEmbeddedNodeType, 'images'>; regex: RegExp } |
|
|
|
type TContentParser = |
|
|
|
|
|
|
|
| { type: Exclude<TEmbeddedNodeType, 'images'>; regex: RegExp } |
|
|
|
|
|
|
|
| ((content: string) => TEmbeddedNode[]) |
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedHashtagParser: TContentParser = { |
|
|
|
export const EmbeddedHashtagParser: TContentParser = { |
|
|
|
type: 'hashtag', |
|
|
|
type: 'hashtag', |
|
|
|
@ -58,31 +59,11 @@ export const EmbeddedEventParser: TContentParser = { |
|
|
|
regex: EMBEDDED_EVENT_REGEX |
|
|
|
regex: EMBEDDED_EVENT_REGEX |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedImageParser: TContentParser = { |
|
|
|
|
|
|
|
type: 'image', |
|
|
|
|
|
|
|
regex: IMAGE_REGEX |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedMediaParser: TContentParser = { |
|
|
|
|
|
|
|
type: 'media', |
|
|
|
|
|
|
|
regex: MEDIA_REGEX |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedWebsocketUrlParser: TContentParser = { |
|
|
|
export const EmbeddedWebsocketUrlParser: TContentParser = { |
|
|
|
type: 'websocket-url', |
|
|
|
type: 'websocket-url', |
|
|
|
regex: WS_URL_REGEX |
|
|
|
regex: WS_URL_REGEX |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedNormalUrlParser: TContentParser = { |
|
|
|
|
|
|
|
type: 'url', |
|
|
|
|
|
|
|
regex: URL_REGEX |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedYoutubeParser: TContentParser = { |
|
|
|
|
|
|
|
type: 'youtube', |
|
|
|
|
|
|
|
regex: YOUTUBE_URL_REGEX |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedEmojiParser: TContentParser = { |
|
|
|
export const EmbeddedEmojiParser: TContentParser = { |
|
|
|
type: 'emoji', |
|
|
|
type: 'emoji', |
|
|
|
regex: EMOJI_SHORT_CODE_REGEX |
|
|
|
regex: EMOJI_SHORT_CODE_REGEX |
|
|
|
@ -93,6 +74,48 @@ export const EmbeddedLNInvoiceParser: TContentParser = { |
|
|
|
regex: LN_INVOICE_REGEX |
|
|
|
regex: LN_INVOICE_REGEX |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const EmbeddedUrlParser: TContentParser = (content: string) => { |
|
|
|
|
|
|
|
const matches = content.matchAll(URL_REGEX) |
|
|
|
|
|
|
|
const result: TEmbeddedNode[] = [] |
|
|
|
|
|
|
|
let lastIndex = 0 |
|
|
|
|
|
|
|
for (const match of matches) { |
|
|
|
|
|
|
|
const matchStart = match.index! |
|
|
|
|
|
|
|
// Add text before the match
|
|
|
|
|
|
|
|
if (matchStart > lastIndex) { |
|
|
|
|
|
|
|
result.push({ |
|
|
|
|
|
|
|
type: 'text', |
|
|
|
|
|
|
|
data: content.slice(lastIndex, matchStart) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const url = match[0] |
|
|
|
|
|
|
|
let type: TEmbeddedNodeType = 'url' |
|
|
|
|
|
|
|
if (isImage(url)) { |
|
|
|
|
|
|
|
type = 'image' |
|
|
|
|
|
|
|
} else if (isMedia(url)) { |
|
|
|
|
|
|
|
type = 'media' |
|
|
|
|
|
|
|
} else if (YOUTUBE_URL_REGEX.test(url)) { |
|
|
|
|
|
|
|
type = 'youtube' |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add the match as specific type
|
|
|
|
|
|
|
|
result.push({ |
|
|
|
|
|
|
|
type, |
|
|
|
|
|
|
|
data: url |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lastIndex = matchStart + url.length |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Add text after the last match
|
|
|
|
|
|
|
|
if (lastIndex < content.length) { |
|
|
|
|
|
|
|
result.push({ |
|
|
|
|
|
|
|
type: 'text', |
|
|
|
|
|
|
|
data: content.slice(lastIndex) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return result |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export function parseContent(content: string, parsers: TContentParser[]) { |
|
|
|
export function parseContent(content: string, parsers: TContentParser[]) { |
|
|
|
let nodes: TEmbeddedNode[] = [{ type: 'text', data: content.trim() }] |
|
|
|
let nodes: TEmbeddedNode[] = [{ type: 'text', data: content.trim() }] |
|
|
|
|
|
|
|
|
|
|
|
@ -100,6 +123,11 @@ export function parseContent(content: string, parsers: TContentParser[]) { |
|
|
|
nodes = nodes |
|
|
|
nodes = nodes |
|
|
|
.flatMap((node) => { |
|
|
|
.flatMap((node) => { |
|
|
|
if (node.type !== 'text') return [node] |
|
|
|
if (node.type !== 'text') return [node] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof parser === 'function') { |
|
|
|
|
|
|
|
return parser(node.data) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const matches = node.data.matchAll(parser.regex) |
|
|
|
const matches = node.data.matchAll(parser.regex) |
|
|
|
const result: TEmbeddedNode[] = [] |
|
|
|
const result: TEmbeddedNode[] = [] |
|
|
|
let lastIndex = 0 |
|
|
|
let lastIndex = 0 |
|
|
|
|