[backend] More mentions fixes

This commit is contained in:
Laura Hausmann 2023-10-14 17:11:21 +02:00
parent 588a39f17a
commit 8719a6922e
Signed by: zotan
GPG key ID: D044E84C5BE01605
10 changed files with 20 additions and 14 deletions

View file

@ -8,6 +8,7 @@ import { resolveMentionWithFallback } from "@/remote/resolve-user.js";
export async function toHtml( export async function toHtml(
nodes: mfm.MfmNode[] | null, nodes: mfm.MfmNode[] | null,
mentionedRemoteUsers: IMentionedRemoteUsers = [], mentionedRemoteUsers: IMentionedRemoteUsers = [],
objectHost: string | null
) { ) {
if (nodes == null) { if (nodes == null) {
return null; return null;
@ -118,7 +119,7 @@ export async function toHtml(
el.setAttribute("translate", "no"); el.setAttribute("translate", "no");
const a = doc.createElement("a"); const a = doc.createElement("a");
const { username, host, acct } = node.props; const { username, host, acct } = node.props;
a.href = await resolveMentionWithFallback(username, host, acct, mentionedRemoteUsers); a.href = await resolveMentionWithFallback(username, host, objectHost, mentionedRemoteUsers);
a.className = "u-url mention"; a.className = "u-url mention";
const span = doc.createElement("span"); const span = doc.createElement("span");
span.textContent = username; span.textContent = username;

View file

@ -4,5 +4,5 @@ import { toHtml } from "../../../mfm/to-html.js";
export default async function (note: Note) { export default async function (note: Note) {
if (!note.text) return ""; if (!note.text) return "";
return toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); return toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers), note.userHost);
} }

View file

@ -73,7 +73,7 @@ export async function renderPerson(user: ILocalUser) {
preferredUsername: user.username, preferredUsername: user.username,
name: user.name, name: user.name,
summary: profile.description summary: profile.description
? await toHtml(mfm.parse(profile.description)) ? await toHtml(mfm.parse(profile.description), [], profile.userHost)
: null, : null,
icon: avatar ? renderImage(avatar) : null, icon: avatar ? renderImage(avatar) : null,
image: banner ? renderImage(banner) : null, image: banner ? renderImage(banner) : null,

View file

@ -177,11 +177,15 @@ export async function resolveUser(
return user; return user;
} }
export async function resolveMentionWithFallback(username: string, host: string | null, acct: string, cache: IMentionedRemoteUsers): Promise<string> { export async function resolveMentionWithFallback(username: string, host: string | null, objectHost: string | null, cache: IMentionedRemoteUsers): Promise<string> {
const fallback = `${config.url}/${acct}`; let fallback = `${config.url}/@${username}`;
if (host !== null) fallback += `@${host}`;
else if (objectHost !== null) fallback += `@${objectHost}`;
const cached = cache.find(r => r.username.toLowerCase() === username.toLowerCase() && r.host === host); const cached = cache.find(r => r.username.toLowerCase() === username.toLowerCase() && r.host === host);
if (cached) return cached.url ?? cached.uri; if (cached) return cached.url ?? cached.uri;
if (host === null || host === config.domain) return fallback; if (host === null || host === config.domain) return fallback;
try { try {
const user = await resolveUser(username, host, false); const user = await resolveUser(username, host, false);
const profile = await UserProfiles.findOneBy({ userId: user.id }); const profile = await UserProfiles.findOneBy({ userId: user.id });

View file

@ -6,7 +6,7 @@ export class AnnouncementConverter {
public static async encode(announcement: Announcement, isRead: boolean): Promise<MastodonEntity.Announcement> { public static async encode(announcement: Announcement, isRead: boolean): Promise<MastodonEntity.Announcement> {
return { return {
id: announcement.id, id: announcement.id,
content: `<h1>${await MfmHelpers.toHtml(mfm.parse(announcement.title), []) ?? 'Announcement'}</h1>${await MfmHelpers.toHtml(mfm.parse(announcement.text), []) ?? ''}`, content: `<h1>${await MfmHelpers.toHtml(mfm.parse(announcement.title), [], null) ?? 'Announcement'}</h1>${await MfmHelpers.toHtml(mfm.parse(announcement.text), [], null) ?? ''}`,
starts_at: null, starts_at: null,
ends_at: null, ends_at: null,
published: true, published: true,

View file

@ -117,7 +117,7 @@ export class NoteConverter {
in_reply_to_id: note.replyId, in_reply_to_id: note.replyId,
in_reply_to_account_id: note.replyUserId, in_reply_to_account_id: note.replyUserId,
reblog: reblog.then(reblog => note.text === null ? reblog : null), reblog: reblog.then(reblog => note.text === null ? reblog : null),
content: text.then(async text => text !== null ? MfmHelpers.toHtml(mfm.parse(text), JSON.parse(note.mentionedRemoteUsers)).then(p => p ?? escapeMFM(text)) : ""), content: text.then(async text => text !== null ? MfmHelpers.toHtml(mfm.parse(text), JSON.parse(note.mentionedRemoteUsers), note.userHost).then(p => p ?? escapeMFM(text)) : ""),
text: text, text: text,
created_at: note.createdAt.toISOString(), created_at: note.createdAt.toISOString(),
emojis: noteEmoji, emojis: noteEmoji,

View file

@ -31,7 +31,7 @@ export class UserConverter {
acctUrl = `https://${u.host}/@${u.username}`; acctUrl = `https://${u.host}/@${u.username}`;
} }
const profile = UserProfiles.findOneBy({ userId: u.id }); const profile = UserProfiles.findOneBy({ userId: u.id });
const bio = profile.then(profile => MfmHelpers.toHtml(mfm.parse(profile?.description ?? ""), []).then(p => p ?? escapeMFM(profile?.description ?? ""))); const bio = profile.then(profile => MfmHelpers.toHtml(mfm.parse(profile?.description ?? ""), [], u.host).then(p => p ?? escapeMFM(profile?.description ?? "")));
const avatar = u.avatarId const avatar = u.avatarId
? (DriveFiles.findOneBy({ id: u.avatarId })) ? (DriveFiles.findOneBy({ id: u.avatarId }))
.then(p => p?.url ?? Users.getIdenticonUrl(u.id)) .then(p => p?.url ?? Users.getIdenticonUrl(u.id))
@ -110,7 +110,7 @@ export class UserConverter {
private static async encodeField(f: Field, host: string | null): Promise<MastodonEntity.Field> { private static async encodeField(f: Field, host: string | null): Promise<MastodonEntity.Field> {
return { return {
name: f.name, name: f.name,
value: await MfmHelpers.toHtml(mfm.parse(f.value), [], true) ?? escapeMFM(f.value), value: await MfmHelpers.toHtml(mfm.parse(f.value), [], host, true) ?? escapeMFM(f.value),
verified_at: f.verified ? (new Date()).toISOString() : null, verified_at: f.verified ? (new Date()).toISOString() : null,
} }
} }

View file

@ -9,7 +9,8 @@ export class MfmHelpers {
public static async toHtml( public static async toHtml(
nodes: mfm.MfmNode[] | null, nodes: mfm.MfmNode[] | null,
mentionedRemoteUsers: IMentionedRemoteUsers = [], mentionedRemoteUsers: IMentionedRemoteUsers = [],
inline: boolean = false objectHost: string | null,
inline: boolean = false,
) { ) {
if (nodes == null) { if (nodes == null) {
return null; return null;
@ -138,7 +139,7 @@ export class MfmHelpers {
el.setAttribute("translate", "no"); el.setAttribute("translate", "no");
const a = doc.createElement("a"); const a = doc.createElement("a");
const { username, host, acct } = node.props; const { username, host, acct } = node.props;
a.href = await resolveMentionWithFallback(username, host, acct, mentionedRemoteUsers); a.href = await resolveMentionWithFallback(username, host, objectHost, mentionedRemoteUsers);
a.className = "u-url mention"; a.className = "u-url mention";
const span = doc.createElement("span"); const span = doc.createElement("span");
span.textContent = username; span.textContent = username;

View file

@ -201,7 +201,7 @@ export class NoteHelpers {
const files = DriveFiles.packMany(edit.fileIds); const files = DriveFiles.packMany(edit.fileIds);
const item = { const item = {
account: account, account: account,
content: MfmHelpers.toHtml(mfm.parse(edit.text ?? ''), JSON.parse(note.mentionedRemoteUsers)).then(p => p ?? ''), content: MfmHelpers.toHtml(mfm.parse(edit.text ?? ''), JSON.parse(note.mentionedRemoteUsers), note.userHost).then(p => p ?? ''),
created_at: lastDate.toISOString(), created_at: lastDate.toISOString(),
emojis: [], emojis: [],
sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false), sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false),

View file

@ -8,13 +8,13 @@ describe("toHtml", () => {
it("br", async () => { it("br", async () => {
const input = "foo\nbar\nbaz"; const input = "foo\nbar\nbaz";
const output = "<p><span>foo<br>bar<br>baz</span></p>"; const output = "<p><span>foo<br>bar<br>baz</span></p>";
assert.equal(await toHtml(mfm.parse(input)), output); assert.equal(await toHtml(mfm.parse(input), [], null), output);
}); });
it("br alt", async () => { it("br alt", async () => {
const input = "foo\r\nbar\rbaz"; const input = "foo\r\nbar\rbaz";
const output = "<p><span>foo<br>bar<br>baz</span></p>"; const output = "<p><span>foo<br>bar<br>baz</span></p>";
assert.equal(await toHtml(mfm.parse(input)), output); assert.equal(await toHtml(mfm.parse(input), [], null), output);
}); });
}); });