diff --git a/packages/backend/src/server/api/mastodon/converters/note.ts b/packages/backend/src/server/api/mastodon/converters/note.ts index 01b590599..dad172792 100644 --- a/packages/backend/src/server/api/mastodon/converters/note.ts +++ b/packages/backend/src/server/api/mastodon/converters/note.ts @@ -15,10 +15,11 @@ import { MentionConverter } from "@/server/api/mastodon/converters/mention.js"; import { PollConverter } from "@/server/api/mastodon/converters/poll.js"; import { populatePoll } from "@/models/repositories/note.js"; import { FileConverter } from "@/server/api/mastodon/converters/file.js"; +import { awaitAll } from "@/prelude/await-all.js"; export class NoteConverter { public static async encode(note: Note, user: ILocalUser | null): Promise { - const noteUser = note.user ?? await getUser(note.userId); + const noteUser = note.user ?? getUser(note.userId); if (!await Notes.isVisibleForMe(note, user?.id ?? null)) throw new Error('Cannot encode note not visible for user'); @@ -30,28 +31,28 @@ export class NoteConverter { .map((x) => decodeReaction(x).reaction) .map((x) => x.replace(/:/g, "")); - const noteEmoji = await populateEmojis( + const noteEmoji = populateEmojis( note.emojis.concat(reactionEmojiNames), host, ); - const reactionCount = await NoteReactions.countBy({id: note.id}); + const reactionCount = NoteReactions.countBy({id: note.id}); - const reaction = user ? await NoteReactions.findOneBy({ + const reaction = user ? NoteReactions.findOneBy({ userId: user.id, noteId: note.id, }) : null; - const isReblogged = user ? await Notes.exist({ + const isReblogged = user ? Notes.exist({ where: { userId: user.id, renoteId: note.id } }) : null; - const reply = note.reply ?? (note.replyId ? await getNote(note.replyId, user) : null); + const reply = note.reply ?? (note.replyId ? getNote(note.replyId, user) : null); - const isBookmarked = user ? await NoteFavorites.exist({ + const isBookmarked = user ? NoteFavorites.exist({ where: { userId: user.id, noteId: note.id, @@ -59,59 +60,62 @@ export class NoteConverter { take: 1, }) : false; - const isMuted = user ? await NoteThreadMutings.exist({ + const isMuted = user ? NoteThreadMutings.exist({ where: { userId: user.id, threadId: note.threadId || note.id, } }) : false; - const files = await DriveFiles.packMany(note.fileIds); + const files = DriveFiles.packMany(note.fileIds); - const mentions = note.mentions.map(async p => - await getUser(p) + const mentions = Promise.all(note.mentions.map(p => + getUser(p) .then(u => MentionConverter.encode(u)) - .catch(() => null)); + .catch(() => null))) + .then(p => p.filter(m => m)) as Promise; // FIXME use await-all - return { + // noinspection ES6MissingAwait + return await awaitAll({ id: note.id, uri: note.uri ? note.uri : `https://${config.host}/notes/${note.id}`, url: note.uri ? note.uri : `https://${config.host}/notes/${note.id}`, - account: await UserConverter.encode(noteUser), + account: Promise.resolve(noteUser).then(p => UserConverter.encode(p)), in_reply_to_id: note.replyId, - in_reply_to_account_id: reply?.userId ?? null, - reblog: note.renote ? await this.encode(note.renote, user) : null, + in_reply_to_account_id: Promise.resolve(reply).then(reply => reply?.userId ?? null), + reblog: note.renote ? this.encode(note.renote, user) : null, content: note.text ? toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)) ?? escapeMFM(note.text) : "", text: note.text ? note.text : null, created_at: note.createdAt.toISOString(), // Remove reaction emojis with names containing @ from the emojis list. emojis: noteEmoji + .then(noteEmoji => noteEmoji .filter((e) => e.name.indexOf("@") === -1) - .map((e) => EmojiConverter.encode(e)), + .map((e) => EmojiConverter.encode(e))), replies_count: note.repliesCount, reblogs_count: note.renoteCount, favourites_count: reactionCount, reblogged: isReblogged, favourited: !!reaction, muted: isMuted, - sensitive: files.length > 0 ? files.some((f) => f.isSensitive) : false, + sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false), spoiler_text: note.cw ? note.cw : "", visibility: VisibilityConverter.encode(note.visibility), - media_attachments: files.length > 0 ? files.map((f) => FileConverter.encode(f)) : [], - mentions: (await Promise.all(mentions)).filter(p => p) as MastodonEntity.Mention[], + media_attachments: files.then(files => files.length > 0 ? files.map((f) => FileConverter.encode(f)) : []), + mentions: mentions, tags: [], //FIXME card: null, //FIXME - poll: note.hasPoll ? PollConverter.encode(await populatePoll(note, user?.id ?? null), note.id) : null, + poll: note.hasPoll ? populatePoll(note, user?.id ?? null).then(p => PollConverter.encode(p, note.id)) : null, application: null, //FIXME language: null, //FIXME pinned: null, //FIXME // Use emojis list to provide URLs for emoji reactions. reactions: [], //FIXME: this.mapReactions(n.emojis, n.reactions, n.myReaction), bookmarked: isBookmarked, - quote: note.renote && note.text ? await this.encode(note.renote, user) : null, - }; + quote: note.renote && note.text ? this.encode(note.renote, user) : null, + }); } public static async encodeMany(notes: Note[], user: ILocalUser | null): Promise { diff --git a/packages/backend/src/server/api/mastodon/converters/user.ts b/packages/backend/src/server/api/mastodon/converters/user.ts index b90ccef25..c286ac318 100644 --- a/packages/backend/src/server/api/mastodon/converters/user.ts +++ b/packages/backend/src/server/api/mastodon/converters/user.ts @@ -6,6 +6,7 @@ import { populateEmojis } from "@/misc/populate-emojis.js"; import { toHtml } from "@/mfm/to-html.js"; import { escapeMFM } from "@/server/api/mastodon/converters/mfm.js"; import mfm from "mfm-js"; +import { awaitAll } from "@/prelude/await-all.js"; type Field = { name: string; @@ -21,10 +22,10 @@ export class UserConverter { acct = `${u.username}@${u.host}`; acctUrl = `https://${u.host}/@${u.username}`; } - const profile = await UserProfiles.findOneBy({userId: u.id}); - const bio = toHtml(mfm.parse(profile?.description ?? "")) ?? escapeMFM(profile?.description ?? ""); + const profile = UserProfiles.findOneBy({userId: u.id}); + const bio = profile.then(profile => toHtml(mfm.parse(profile?.description ?? "")) ?? escapeMFM(profile?.description ?? "")); - return { + return awaitAll({ id: u.id, username: u.username, acct: acct, @@ -40,11 +41,11 @@ export class UserConverter { avatar_static: u.avatar?.url ?? Users.getIdenticonUrl(u.id), header: u.banner?.url ?? `${config.url}/static-assets/transparent.png`, header_static: u.banner?.url ?? `${config.url}/static-assets/transparent.png`, - emojis: (await populateEmojis(u.emojis, u.host)).map((e) => EmojiConverter.encode(e)), + emojis: populateEmojis(u.emojis, u.host).then(emoji => emoji.map((e) => EmojiConverter.encode(e))), moved: null, //FIXME - fields: profile?.fields.map(p => this.encodeField(p)) ?? [], + fields: profile.then(profile => profile?.fields.map(p => this.encodeField(p)) ?? []), bot: u.isBot - }; + }); } private static encodeField(f: Field): MastodonEntity.Field {