summaryrefslogtreecommitdiff
path: root/packages/backend
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-05-04 10:08:41 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-05-08 16:20:29 -0400
commit6c9dcb84abc1a11e21d03b6e82b46d1743fe0c03 (patch)
tree381821546669606d16e3b9a89afeb7d86f12f75f /packages/backend
parentmerge: merge the two post-form menus (!995) (diff)
downloadsharkey-6c9dcb84abc1a11e21d03b6e82b46d1743fe0c03.tar.gz
sharkey-6c9dcb84abc1a11e21d03b6e82b46d1743fe0c03.tar.bz2
sharkey-6c9dcb84abc1a11e21d03b6e82b46d1743fe0c03.zip
resolve mentioned user handles on the backend
Diffstat (limited to 'packages/backend')
-rw-r--r--packages/backend/src/core/entities/NoteEntityService.ts58
-rw-r--r--packages/backend/src/models/json-schema/note.ts10
2 files changed, 66 insertions, 2 deletions
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index eceb972a91..94fe9e97ef 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -402,7 +402,8 @@ export class NoteEntityService implements OnModuleInit {
bufferedReactions: Map<MiNote['id'], { deltas: Record<string, number>; pairs: ([MiUser['id'], string])[] }> | null;
myReactions: Map<MiNote['id'], string | null>;
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
- packedUsers: Map<MiUser['id'], Packed<'UserLite'>>
+ packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
+ mentionHandles: Record<string, string | undefined>;
};
},
): Promise<Packed<'Note'>> {
@@ -443,6 +444,9 @@ export class NoteEntityService implements OnModuleInit {
const packedFiles = options?._hint_?.packedFiles;
const packedUsers = options?._hint_?.packedUsers;
+ // Do not await - defer until the awaitAll below
+ const mentionHandles = this.getUserHandles(note.mentions, options?._hint_?.mentionHandles);
+
const packed: Packed<'Note'> = await awaitAll({
id: note.id,
createdAt: this.idService.parse(note.id).date.toISOString(),
@@ -476,7 +480,8 @@ export class NoteEntityService implements OnModuleInit {
allowRenoteToExternal: channel.allowRenoteToExternal,
userId: channel.userId,
} : undefined,
- mentions: note.mentions && note.mentions.length > 0 ? note.mentions : undefined,
+ mentions: note.mentions.length > 0 ? note.mentions : undefined,
+ mentionHandles: note.mentions.length > 0 ? mentionHandles : undefined,
uri: note.uri ?? undefined,
url: note.url ?? undefined,
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
@@ -594,6 +599,22 @@ export class NoteEntityService implements OnModuleInit {
const packedUsers = await this.userEntityService.packMany(users, me)
.then(users => new Map(users.map(u => [u.id, u])));
+ // Recursively add all mentioned users from all notes + replies + renotes
+ const allMentionedUsers = notes.reduce((users, note) => {
+ function add(n: MiNote) {
+ for (const user of n.mentions) {
+ users.add(user);
+ }
+
+ if (n.reply) add(n.reply);
+ if (n.renote) add(n.renote);
+ }
+
+ add(note);
+ return users;
+ }, new Set<string>());
+ const mentionHandles = await this.getUserHandles(Array.from(allMentionedUsers));
+
return await Promise.all(notes.map(n => this.pack(n, me, {
...options,
_hint_: {
@@ -601,6 +622,7 @@ export class NoteEntityService implements OnModuleInit {
myReactions: myReactionsMap,
packedFiles,
packedUsers,
+ mentionHandles,
},
})));
}
@@ -636,4 +658,36 @@ export class NoteEntityService implements OnModuleInit {
relations: ['user'],
});
}
+
+ private async getUserHandles(userIds: string[], hint?: Record<string, string | undefined>): Promise<Record<string, string | undefined>> {
+ if (userIds.length < 1) return {};
+
+ // Hint is provided by packMany to avoid N+1 queries.
+ // It should already include all existing mentioned users.
+ if (hint) {
+ const handles = {} as Record<string, string | undefined>;
+ for (const id of userIds) {
+ handles[id] = hint[id];
+ }
+ return handles;
+ }
+
+ const users = await this.usersRepository.find({
+ select: {
+ id: true,
+ username: true,
+ host: true,
+ },
+ where: {
+ id: In(userIds),
+ },
+ });
+
+ return users.reduce((map, user) => {
+ map[user.id] = user.host
+ ? `@${user.username}@${user.host}`
+ : `@${user.username}`;
+ return map;
+ }, {} as Record<string, string | undefined>);
+ }
}
diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index 16e240ab11..b19c8f7c06 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -85,6 +85,16 @@ export const packedNoteSchema = {
format: 'id',
},
},
+ mentionHandles: {
+ type: 'object',
+ optional: true, nullable: false,
+ additionalProperties: {
+ anyOf: [{
+ type: 'string',
+ }],
+ optional: true, nullable: false,
+ },
+ },
visibleUserIds: {
type: 'array',
optional: true, nullable: false,