forked from FoundKeyGang/FoundKey
server: better matching for MFM mentions
When rendering the HTML for outgoing activities, the mentions are now matched case insensitive and should also work properly for IDNs. The username is also compared case insensitive. Mentions of local users are also handled properly independed of whether the hostname was given or omitted. The query to get mentions is now also only executed once instead of for each mention individually. Changelog: Fixed
This commit is contained in:
parent
1d99657a45
commit
34d55e2dda
1 changed files with 35 additions and 23 deletions
|
@ -4,6 +4,7 @@ import config from '@/config/index.js';
|
||||||
import { UserProfiles } from '@/models/index.js';
|
import { UserProfiles } from '@/models/index.js';
|
||||||
import { extractMentions } from '@/misc/extract-mentions.js';
|
import { extractMentions } from '@/misc/extract-mentions.js';
|
||||||
import { intersperse } from '@/prelude/array.js';
|
import { intersperse } from '@/prelude/array.js';
|
||||||
|
import { toPunyNullable } from '@/misc/convert-host.js';
|
||||||
|
|
||||||
// Transforms MFM to HTML, given the MFM text and a list of user IDs that are
|
// Transforms MFM to HTML, given the MFM text and a list of user IDs that are
|
||||||
// mentioned in the text. If the list of mentions is not given, all mentions
|
// mentioned in the text. If the list of mentions is not given, all mentions
|
||||||
|
@ -14,6 +15,19 @@ export async function toHtml(mfmText: string, mentions?: string[]): Promise<stri
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mentionedUsers = [];
|
||||||
|
const ids = mentions ?? extractMentions(nodes);
|
||||||
|
if (ids.length > 0) {
|
||||||
|
mentionedUsers = await UserProfiles.createQueryBuilder('user_profile')
|
||||||
|
.leftJoin('user_profile.user', 'user')
|
||||||
|
.select('user.usernameLower', 'username')
|
||||||
|
.addSelect('user.host', 'host')
|
||||||
|
// links should preferably use user friendly urls, only fall back to AP ids
|
||||||
|
.addSelect('COALESCE(user_profile.url, user.uri)', 'url')
|
||||||
|
.where('"userId" IN (:...ids)', { ids })
|
||||||
|
.getRawMany();
|
||||||
|
}
|
||||||
|
|
||||||
const doc = new JSDOM('').window.document;
|
const doc = new JSDOM('').window.document;
|
||||||
|
|
||||||
const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType<K>) => Promise<Node> } = {
|
const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType<K>) => Promise<Node> } = {
|
||||||
|
@ -103,30 +117,28 @@ export async function toHtml(mfmText: string, mentions?: string[]): Promise<stri
|
||||||
},
|
},
|
||||||
|
|
||||||
async mention(node): Promise<HTMLElement | Text> {
|
async mention(node): Promise<HTMLElement | Text> {
|
||||||
const { username, host, acct } = node.props;
|
let { username, host, acct } = node.props;
|
||||||
const ids = mentions ?? extractMentions(nodes);
|
// normalize username and host for searching the user
|
||||||
if (ids.length > 0) {
|
username = username.toLowerCase();
|
||||||
const mentionedUsers = await UserProfiles.createQueryBuilder('user_profile')
|
host = toPunyNullable(host);
|
||||||
.leftJoin('user_profile.user', 'user')
|
// Discard host if it is the local host. Otherwise mentions of local users where the
|
||||||
.select('user.username', 'username')
|
// hostname is not omitted are not handled correctly.
|
||||||
.addSelect('user.host', 'host')
|
if (host == config.hostname) {
|
||||||
// links should preferably use user friendly urls, only fall back to AP ids
|
host = null;
|
||||||
.addSelect('COALESCE(user_profile.url, user.uri)', 'url')
|
}
|
||||||
.where('"userId" IN (:...ids)', { ids })
|
const userInfo = mentionedUsers.find(user => user.username === username && user.host === host);
|
||||||
.getRawMany();
|
if (userInfo != null) {
|
||||||
const userInfo = mentionedUsers.find(user => user.username === username && user.host === host);
|
// Mastodon microformat: span.h-card > a.u-url.mention
|
||||||
if (userInfo != null) {
|
const a = doc.createElement('a');
|
||||||
// Mastodon microformat: span.h-card > a.u-url.mention
|
// The fallback will only be used for local users, so the host part can be discarded.
|
||||||
const a = doc.createElement('a');
|
a.href = userInfo.url ?? `${config.url}/@${username}`;
|
||||||
a.href = userInfo.url ?? `${config.url}/${acct}`;
|
a.className = 'u-url mention';
|
||||||
a.className = 'u-url mention';
|
a.textContent = acct;
|
||||||
a.textContent = acct;
|
|
||||||
|
|
||||||
const card = doc.createElement('span');
|
const card = doc.createElement('span');
|
||||||
card.className = 'h-card';
|
card.className = 'h-card';
|
||||||
card.appendChild(a);
|
card.appendChild(a);
|
||||||
return card;
|
return card;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// this user does not actually exist
|
// this user does not actually exist
|
||||||
return doc.createTextNode(acct);
|
return doc.createTextNode(acct);
|
||||||
|
|
Loading…
Reference in a new issue