diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 43caaf94b..1fcedbd56 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -85,6 +85,7 @@ export class NoteRepository extends Repository { detail?: boolean; skipHide?: boolean; _hint_?: { + emojis: Emoji[] | null; myReactions: Map; }; } @@ -141,11 +142,43 @@ export class NoteRepository extends Repository { * @param reactionNames Note等にリアクションされたカスタム絵文字名 (:は含めない) */ async function populateEmojis(emojiNames: string[], noteUserHost: string | null, reactionNames: string[]) { + const customReactions = reactionNames?.map(x => decodeReaction(x)).filter(x => x.name); + let all = [] as { name: string, url: string }[]; + // 与えられたhintだけで十分(=新たにクエリする必要がない)かどうかを表すフラグ + let enough = true; + if (options?._hint_?.emojis) { + for (const name of emojiNames) { + const matched = options._hint_.emojis.find(x => x.name === name && x.host === noteUserHost); + if (matched) { + all.push({ + name: matched.name, + url: matched.url, + }); + } else { + enough = false; + } + } + for (const customReaction of customReactions) { + const matched = options._hint_.emojis.find(x => x.name === customReaction.name && x.host === customReaction.host); + if (matched) { + all.push({ + name: `${matched.name}@${matched.host || '.'}`, // @host付きでローカルは. + url: matched.url, + }); + } else { + enough = false; + } + } + } else { + enough = false; + } + if (enough) return all; + // カスタム絵文字 if (emojiNames?.length > 0) { const tmp = await Emojis.find({ @@ -164,8 +197,6 @@ export class NoteRepository extends Repository { all = concat([all, tmp]); } - const customReactions = reactionNames?.map(x => decodeReaction(x)).filter(x => x.name); - if (customReactions?.length > 0) { const where = [] as {}[]; @@ -230,7 +261,12 @@ export class NoteRepository extends Repository { id: note.id, createdAt: note.createdAt.toISOString(), userId: note.userId, - user: Users.pack(note.user || note.userId, meId), + user: Users.pack(note.user || note.userId, meId, { + detail: false, + _hint_: { + emojis: options?._hint_?.emojis || null + } + }), text: text, cw: note.cw, visibility: note.visibility, @@ -258,12 +294,12 @@ export class NoteRepository extends Repository { _prId_: (note as any)._prId_ || undefined, ...(opts.detail ? { - reply: note.replyId ? this.pack(note.replyId, meId, { + reply: note.replyId ? this.pack(note.reply || note.replyId, meId, { detail: false, _hint_: options?._hint_ }) : undefined, - renote: note.renoteId ? this.pack(note.renoteId, meId, { + renote: note.renoteId ? this.pack(note.renote || note.renoteId, meId, { detail: true, _hint_: options?._hint_ }) : undefined, @@ -314,10 +350,48 @@ export class NoteRepository extends Repository { } } + // TODO: ここら辺の処理をaggregateEmojisみたいな関数に切り出したい + let emojisWhere: any[] = []; + for (const note of notes) { + if (typeof note !== 'object') continue; + emojisWhere.push({ + name: In(note.emojis), + host: note.userHost + }); + if (note.renote) { + emojisWhere.push({ + name: In(note.renote.emojis), + host: note.renote.userHost + }); + if (note.renote.user) { + emojisWhere.push({ + name: In(note.renote.user.emojis), + host: note.renote.userHost + }); + } + } + const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name); + emojisWhere = emojisWhere.concat(customReactions.map(x => ({ + name: x.name, + host: x.host + }))); + if (note.user) { + emojisWhere.push({ + name: In(note.user.emojis), + host: note.userHost + }); + } + } + const emojis = emojisWhere.length > 0 ? await Emojis.find({ + where: emojisWhere, + select: ['name', 'host', 'url'] + }) : null; + return await Promise.all(notes.map(n => this.pack(n, me, { ...options, _hint_: { - myReactions: myReactionsMap + myReactions: myReactionsMap, + emojis: emojis } }))); } diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index a9fe5e7b4..102715587 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,11 +1,13 @@ import { EntityRepository, In, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '..'; +import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions, Emojis } from '..'; import { Notification } from '../entities/notification'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; import { Note } from '../entities/note'; import { NoteReaction } from '../entities/note-reaction'; import { User } from '../entities/user'; +import { decodeReaction } from '../../misc/reaction-lib'; +import { Emoji } from '../entities/emoji'; export type PackedNotification = SchemaType; @@ -15,6 +17,7 @@ export class NotificationRepository extends Repository { src: Notification['id'] | Notification, options: { _hintForEachNotes_: { + emojis: Emoji[] | null; myReactions: Map; }; } @@ -98,9 +101,47 @@ export class NotificationRepository extends Repository { myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); } + // TODO: ここら辺の処理をaggregateEmojisみたいな関数に切り出したい + let emojisWhere: any[] = []; + for (const note of notes) { + if (typeof note !== 'object') continue; + emojisWhere.push({ + name: In(note.emojis), + host: note.userHost + }); + if (note.renote) { + emojisWhere.push({ + name: In(note.renote.emojis), + host: note.renote.userHost + }); + if (note.renote.user) { + emojisWhere.push({ + name: In(note.renote.user.emojis), + host: note.renote.userHost + }); + } + } + const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name); + emojisWhere = emojisWhere.concat(customReactions.map(x => ({ + name: x.name, + host: x.host + }))); + if (note.user) { + emojisWhere.push({ + name: In(note.user.emojis), + host: note.userHost + }); + } + } + const emojis = emojisWhere.length > 0 ? await Emojis.find({ + where: emojisWhere, + select: ['name', 'host', 'url'] + }) : null; + return await Promise.all(notifications.map(x => this.pack(x, { _hintForEachNotes_: { - myReactions: myReactionsMap + myReactions: myReactionsMap, + emojis: emojis, } }))); } diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 3a6ab48c5..19b0e5423 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -5,6 +5,7 @@ import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMes import config from '../../config'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; +import { Emoji } from '../entities/emoji'; export type PackedUser = SchemaType; @@ -149,6 +150,9 @@ export class UserRepository extends Repository { options?: { detail?: boolean, includeSecrets?: boolean, + _hint_?: { + emojis: Emoji[] | null; + }; } ): Promise { const opts = Object.assign({ @@ -166,6 +170,34 @@ export class UserRepository extends Repository { }) : []; const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; + let emojis: Emoji[] = []; + if (user.emojis.length > 0) { + // 与えられたhintだけで十分(=新たにクエリする必要がない)かどうかを表すフラグ + let enough = true; + if (options?._hint_?.emojis) { + for (const name of user.emojis) { + const matched = options._hint_.emojis.find(x => x.name === name && x.host === user.host); + if (matched) { + emojis.push(matched); + } else { + enough = false; + } + } + } else { + enough = false; + } + + if (!enough) { + emojis = await Emojis.find({ + where: { + name: In(user.emojis), + host: user.host + }, + select: ['name', 'host', 'url', 'aliases'] + }); + } + } + const falsy = opts.detail ? false : undefined; const packed = { @@ -190,13 +222,7 @@ export class UserRepository extends Repository { } : undefined) : undefined, // カスタム絵文字添付 - emojis: user.emojis.length > 0 ? Emojis.find({ - where: { - name: In(user.emojis), - host: user.host - }, - select: ['name', 'host', 'url', 'aliases'] - }) : [], + emojis: emojis, ...(opts.detail ? { url: profile!.url, diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts index 750fc080c..2ea3e4374 100644 --- a/src/server/api/endpoints/antennas/notes.ts +++ b/src/server/api/endpoints/antennas/notes.ts @@ -74,6 +74,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ antennaQuery.getQuery() })`) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(antennaQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/channels/timeline.ts b/src/server/api/endpoints/channels/timeline.ts index acb34f124..292f21b63 100644 --- a/src/server/api/endpoints/channels/timeline.ts +++ b/src/server/api/endpoints/channels/timeline.ts @@ -88,6 +88,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); //#endregion diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts index 6a507e203..86d1e507f 100644 --- a/src/server/api/endpoints/clips/notes.ts +++ b/src/server/api/endpoints/clips/notes.ts @@ -72,6 +72,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ clipQuery.getQuery() })`) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(clipQuery.getParameters()); if (user) { diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index af49549d3..812a4bd1d 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -87,7 +87,11 @@ export default define(meta, async (ps, user) => { .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`); query.setParameters(mutingQuery.getParameters()); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index fab8455d7..708ce38c0 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -76,7 +76,11 @@ export default define(meta, async (ps) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.visibility = 'public'`) .andWhere(`note.localOnly = FALSE`) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (ps.local) { query.andWhere('note.userHost IS NULL'); diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index 0875e0f24..a8b239e44 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -64,7 +64,11 @@ export default define(meta, async (ps, user) => { })); })); })) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 4dda7d0ed..6c416b1c0 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -49,7 +49,11 @@ export default define(meta, async (ps, user) => { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 6d99f1fdb..64c2f5851 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -81,7 +81,11 @@ export default define(meta, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateRepliesQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 2b91b8c67..c01e170aa 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -130,6 +130,10 @@ export default define(meta, async (ps, user) => { .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); })) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 51e35e624..31abb2688 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -98,7 +98,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateChannelQuery(query, user); generateRepliesQuery(query, user); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 1e3014bd4..83e890d9a 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -63,7 +63,11 @@ export default define(meta, async (ps, user) => { .where(`:meId = ANY(note.mentions)`, { meId: user.id }) .orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id }); })) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 31c24f294..f52819722 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -68,7 +68,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.renoteId = :renoteId`, { renoteId: note.id }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 9fad74c78..5ac663d77 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -59,7 +59,11 @@ export const meta = { export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index e0f7f4d62..d878f379c 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -95,7 +95,11 @@ export const meta = { export default define(meta, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index 1aca05629..ae0e9242a 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -79,7 +79,11 @@ export default define(meta, async (ps, me) => { query .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index f09f3d173..c91bea0c1 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -123,6 +123,10 @@ export default define(meta, async (ps, user) => { if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index b0ff499d9..040f017fd 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -131,6 +131,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.userId IN (${ listQuery.getQuery() })`) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(listQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 33e3ecb03..91d1d7f4d 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -131,7 +131,11 @@ export default define(meta, async (ps, me) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me, user);