summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/entities/EmojiEntityService.ts
blob: 490d3f2511cc74086bfdc8543732e4c7621c162a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * SPDX-FileCopyrightText: syuilo and misskey-project
 * SPDX-License-Identifier: AGPL-3.0-only
 */

import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { EmojisRepository, MiRole, RolesRepository } from '@/models/_.js';
import type { Packed } from '@/misc/json-schema.js';
import type { MiEmoji } from '@/models/Emoji.js';
import { bindThis } from '@/decorators.js';

@Injectable()
export class EmojiEntityService {
	constructor(
		@Inject(DI.emojisRepository)
		private emojisRepository: EmojisRepository,
		@Inject(DI.rolesRepository)
		private rolesRepository: RolesRepository,
	) {
	}

	@bindThis
	public async packSimple(
		src: MiEmoji['id'] | MiEmoji,
	): Promise<Packed<'EmojiSimple'>> {
		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });

		return {
			aliases: emoji.aliases,
			name: emoji.name,
			category: emoji.category,
			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
			url: emoji.publicUrl || emoji.originalUrl,
			localOnly: emoji.localOnly ? true : undefined,
			isSensitive: emoji.isSensitive ? true : undefined,
			roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined,
		};
	}

	@bindThis
	public packSimpleMany(
		emojis: any[],
	) {
		return Promise.all(emojis.map(x => this.packSimple(x)));
	}

	@bindThis
	public async packDetailed(
		src: MiEmoji['id'] | MiEmoji,
	): Promise<Packed<'EmojiDetailed'>> {
		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });

		return {
			id: emoji.id,
			aliases: emoji.aliases,
			name: emoji.name,
			category: emoji.category,
			host: emoji.host,
			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
			url: emoji.publicUrl || emoji.originalUrl,
			license: emoji.license,
			isSensitive: emoji.isSensitive,
			localOnly: emoji.localOnly,
			roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
		};
	}

	@bindThis
	public packDetailedMany(
		emojis: any[],
	): Promise<Packed<'EmojiDetailed'>[]> {
		return Promise.all(emojis.map(x => this.packDetailed(x)));
	}

	@bindThis
	public async packDetailedAdmin(
		src: MiEmoji['id'] | MiEmoji,
		hint?: {
			roles?: Map<MiRole['id'], MiRole>
		},
	): Promise<Packed<'EmojiDetailedAdmin'>> {
		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });

		const roles = Array.of<MiRole>();
		if (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0) {
			if (hint?.roles) {
				const hintRoles = hint.roles;
				roles.push(
					...emoji.roleIdsThatCanBeUsedThisEmojiAsReaction
						.filter(x => hintRoles.has(x))
						.map(x => hintRoles.get(x)!),
				);
			} else {
				roles.push(
					...await this.rolesRepository.findBy({ id: In(emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) }),
				);
			}

			roles.sort((a, b) => {
				if (a.displayOrder !== b.displayOrder) {
					return b.displayOrder - a.displayOrder;
				}

				return a.id.localeCompare(b.id);
			});
		}

		return {
			id: emoji.id,
			updatedAt: emoji.updatedAt?.toISOString() ?? null,
			name: emoji.name,
			host: emoji.host,
			uri: emoji.uri,
			type: emoji.type,
			aliases: emoji.aliases,
			category: emoji.category,
			publicUrl: emoji.publicUrl,
			originalUrl: emoji.originalUrl,
			license: emoji.license,
			localOnly: emoji.localOnly,
			isSensitive: emoji.isSensitive,
			roleIdsThatCanBeUsedThisEmojiAsReaction: roles.map(it => ({ id: it.id, name: it.name })),
		};
	}

	@bindThis
	public async packDetailedAdminMany(
		emojis: MiEmoji['id'][] | MiEmoji[],
		hint?: {
			roles?: Map<MiRole['id'], MiRole>
		},
	): Promise<Packed<'EmojiDetailedAdmin'>[]> {
		// IDのみの要素をピックアップし、DBからレコードを取り出して他の値を補完する
		const emojiEntities = emojis.filter(x => typeof x === 'object') as MiEmoji[];
		const emojiIdOnlyList = emojis.filter(x => typeof x === 'string') as string[];
		if (emojiIdOnlyList.length > 0) {
			emojiEntities.push(...await this.emojisRepository.findBy({ id: In(emojiIdOnlyList) }));
		}

		// 特定ロール専用の絵文字である場合、そのロール情報をあらかじめまとめて取得しておく(pack側で都度取得も出来るが負荷が高いので)
		let hintRoles: Map<MiRole['id'], MiRole>;
		if (hint?.roles) {
			hintRoles = hint.roles;
		} else {
			const roles = Array.of<MiRole>();
			const roleIds = [...new Set(emojiEntities.flatMap(x => x.roleIdsThatCanBeUsedThisEmojiAsReaction))];
			if (roleIds.length > 0) {
				roles.push(...await this.rolesRepository.findBy({ id: In(roleIds) }));
			}

			hintRoles = new Map(roles.map(x => [x.id, x]));
		}

		return Promise.all(emojis.map(x => this.packDetailedAdmin(x, { roles: hintRoles })));
	}
}