fix: use mention accts within a status to render links (#955)

This commit is contained in:
Daniel Roe 2023-01-11 17:18:06 +00:00 committed by GitHub
parent f9509f8987
commit c2850a34ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 2 deletions

View file

@ -17,6 +17,7 @@ const vnode = $computed(() => {
return null return null
const vnode = contentToVNode(status.content, { const vnode = contentToVNode(status.content, {
emojis: emojisObject.value, emojis: emojisObject.value,
mentions: 'mentions' in status ? status.mentions : undefined,
markdown: true, markdown: true,
}) })
return vnode return vnode

View file

@ -8,6 +8,7 @@ import { emojiRegEx, getEmojiAttributes } from '../config/emojis'
export interface ContentParseOptions { export interface ContentParseOptions {
emojis?: Record<string, mastodon.v1.CustomEmoji> emojis?: Record<string, mastodon.v1.CustomEmoji>
mentions?: mastodon.v1.StatusMention[]
markdown?: boolean markdown?: boolean
replaceUnicodeEmoji?: boolean replaceUnicodeEmoji?: boolean
astTransforms?: Transform[] astTransforms?: Transform[]
@ -47,6 +48,7 @@ export function parseMastodonHTML(
markdown = true, markdown = true,
replaceUnicodeEmoji = true, replaceUnicodeEmoji = true,
convertMentionLink = false, convertMentionLink = false,
mentions,
} = options } = options
if (markdown) { if (markdown) {
@ -74,6 +76,9 @@ export function parseMastodonHTML(
if (markdown) if (markdown)
transforms.push(transformMarkdown) transforms.push(transformMarkdown)
if (mentions?.length)
transforms.push(createTransformNamedMentions(mentions))
if (convertMentionLink) if (convertMentionLink)
transforms.push(transformMentionLink) transforms.push(transformMentionLink)
@ -377,3 +382,18 @@ function transformMentionLink(node: Node): string | Node | (string | Node)[] | n
} }
return node return node
} }
function createTransformNamedMentions(mentions: mastodon.v1.StatusMention[]) {
return (node: Node): string | Node | (string | Node)[] | null => {
if (node.name === 'a' && node.attributes.class?.includes('mention')) {
const href = node.attributes.href
const mention = href && mentions.find(m => m.url === href)
if (mention) {
node.attributes.href = `/${currentServer.value}/@${mention.acct}`
node.children = [h('span', { 'data-type': 'mention', 'data-id': mention.acct }, `@${mention.username}`)]
return node
}
}
return node
}
}

View file

@ -40,6 +40,19 @@ exports[`content-rich > custom emoji 1`] = `
exports[`content-rich > empty 1`] = `""`; exports[`content-rich > empty 1`] = `""`;
exports[`content-rich > group mention > html 1`] = `
"<p>
<span class=\\"h-card\\"
><a
class=\\"u-url mention\\"
rel=\\"nofollow noopener noreferrer\\"
to=\\"//@pilipinas@lemmy.ml\\"
></a
></span>
</p>
"
`;
exports[`content-rich > handles html within code blocks 1`] = ` exports[`content-rich > handles html within code blocks 1`] = `
"<p> "<p>
HTML block code:<br /> HTML block code:<br />

View file

@ -20,6 +20,11 @@ describe('content-rich', () => {
expect(formatted).toMatchSnapshot() expect(formatted).toMatchSnapshot()
}) })
it('group mention', async () => {
const { formatted } = await render('<p><span class="h-card"><a href="https://lemmy.ml/c/pilipinas" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span>pilipinas</span></a></span></p>', undefined, [{ id: '', username: 'pilipinas', url: 'https://lemmy.ml/c/pilipinas', acct: 'pilipinas@lemmy.ml' }])
expect(formatted).toMatchSnapshot('html')
})
it('inline code with link', async () => { it('inline code with link', async () => {
const { formatted } = await render('<p>Inline code with link: `<a href="https://api.iconify.design/noto.css?icons=1st-place-medal,2nd-place-medal" target="_blank" rel="nofollow noopener noreferrer" class="status-link unhandled-link" title="https://api.iconify.design/noto.css?icons=1st-place-medal,2nd-place-medal"><span class="invisible">https://</span><span class="ellipsis">api.iconify.design/noto.css?ic</span><span class="invisible">ons=1st-place-medal,2nd-place-medal</span></a>`</p>') const { formatted } = await render('<p>Inline code with link: `<a href="https://api.iconify.design/noto.css?icons=1st-place-medal,2nd-place-medal" target="_blank" rel="nofollow noopener noreferrer" class="status-link unhandled-link" title="https://api.iconify.design/noto.css?icons=1st-place-medal,2nd-place-medal"><span class="invisible">https://</span><span class="ellipsis">api.iconify.design/noto.css?ic</span><span class="invisible">ons=1st-place-medal,2nd-place-medal</span></a>`</p>')
expect(formatted).toMatchSnapshot() expect(formatted).toMatchSnapshot()
@ -64,8 +69,8 @@ describe('content-rich', () => {
}) })
}) })
async function render(content: string, emojis?: Record<string, mastodon.v1.CustomEmoji>) { async function render(content: string, emojis?: Record<string, mastodon.v1.CustomEmoji>, mentions?: mastodon.v1.StatusMention[]) {
const vnode = contentToVNode(content, { emojis }) const vnode = contentToVNode(content, { emojis, mentions })
const html = (await renderToString(vnode)) const html = (await renderToString(vnode))
.replace(/<!--[\[\]]-->/g, '') .replace(/<!--[\[\]]-->/g, '')
let formatted = '' let formatted = ''