feat: collapse mentions (#1034)
This commit is contained in:
parent
d39ea9a6de
commit
36ae8be40a
6 changed files with 145 additions and 15 deletions
|
@ -13,6 +13,7 @@ export interface ContentParseOptions {
|
|||
replaceUnicodeEmoji?: boolean
|
||||
astTransforms?: Transform[]
|
||||
convertMentionLink?: boolean
|
||||
collapseMentionLink?: boolean
|
||||
}
|
||||
|
||||
const sanitizerBasicClasses = filterClasses(/^(h-\S*|p-\S*|u-\S*|dt-\S*|e-\S*|mention|hashtag|ellipsis|invisible)$/u)
|
||||
|
@ -48,6 +49,7 @@ export function parseMastodonHTML(
|
|||
markdown = true,
|
||||
replaceUnicodeEmoji = true,
|
||||
convertMentionLink = false,
|
||||
collapseMentionLink = false,
|
||||
mentions,
|
||||
} = options
|
||||
|
||||
|
@ -89,6 +91,9 @@ export function parseMastodonHTML(
|
|||
|
||||
transforms.push(transformParagraphs)
|
||||
|
||||
if (collapseMentionLink)
|
||||
transforms.push(transformCollapseMentions())
|
||||
|
||||
return transformSync(parse(html), transforms)
|
||||
}
|
||||
|
||||
|
@ -174,16 +179,16 @@ export function treeToText(input: Node): string {
|
|||
// Strings get converted to text nodes.
|
||||
// The input node's children have been transformed before the node itself
|
||||
// gets transformed.
|
||||
type Transform = (node: Node) => (Node | string)[] | Node | string | null
|
||||
type Transform = (node: Node, root: Node) => (Node | string)[] | Node | string | null
|
||||
|
||||
// Helpers for transforming (filtering, modifying, ...) a parsed HTML tree
|
||||
// by running the given chain of transform functions one-by-one.
|
||||
function transformSync(doc: Node, transforms: Transform[]) {
|
||||
function visit(node: Node, transform: Transform, isRoot = false) {
|
||||
function visit(node: Node, transform: Transform, root: Node) {
|
||||
if (Array.isArray(node.children)) {
|
||||
const children = [] as (Node | string)[]
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
const result = visit(node.children[i], transform)
|
||||
const result = visit(node.children[i], transform, root)
|
||||
if (Array.isArray(result))
|
||||
children.push(...result)
|
||||
|
||||
|
@ -198,11 +203,11 @@ function transformSync(doc: Node, transforms: Transform[]) {
|
|||
return value
|
||||
})
|
||||
}
|
||||
return isRoot ? node : transform(node)
|
||||
return transform(node, root)
|
||||
}
|
||||
|
||||
for (const transform of transforms)
|
||||
doc = visit(doc, transform, true) as Node
|
||||
doc = visit(doc, transform, doc) as Node
|
||||
|
||||
return doc
|
||||
}
|
||||
|
@ -376,6 +381,48 @@ function transformParagraphs(node: Node): Node | Node[] {
|
|||
return node
|
||||
}
|
||||
|
||||
function transformCollapseMentions() {
|
||||
let processed = false
|
||||
function isMention(node: Node) {
|
||||
const child = node.children?.length === 1 ? node.children[0] : null
|
||||
return Boolean(child?.name === 'a' && child.attributes.class?.includes('mention'))
|
||||
}
|
||||
|
||||
return (node: Node, root: Node): Node | Node[] => {
|
||||
if (processed || node.parent !== root)
|
||||
return node
|
||||
const metions: (Node | undefined)[] = []
|
||||
const children = node.children as Node[]
|
||||
for (const child of children) {
|
||||
// metion
|
||||
if (isMention(child)) {
|
||||
metions.push(child)
|
||||
}
|
||||
// spaces in between
|
||||
else if (child.type === TEXT_NODE && !child.value.trim()) {
|
||||
metions.push(child)
|
||||
}
|
||||
// other content, stop collapsing
|
||||
else {
|
||||
if (child.type === TEXT_NODE)
|
||||
child.value = child.value.trimStart()
|
||||
// remove <br> after mention
|
||||
if (child.name === 'br')
|
||||
metions.push(undefined)
|
||||
break
|
||||
}
|
||||
}
|
||||
processed = true
|
||||
if (metions.length === 0)
|
||||
return node
|
||||
|
||||
return {
|
||||
...node,
|
||||
children: [h('mention-group', null, ...metions.filter(Boolean)), ...children.slice(metions.length)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transformMentionLink(node: Node): string | Node | (string | Node)[] | null {
|
||||
if (node.name === 'a' && node.attributes.class?.includes('mention')) {
|
||||
const href = node.attributes.href
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue