カスタム絵文字一覧情報をmetaから分離
This commit is contained in:
		
							parent
							
								
									e4144a17a4
								
							
						
					
					
						commit
						462acc9eee
					
				
					 17 changed files with 212 additions and 151 deletions
				
			
		|  | @ -40,6 +40,8 @@ You should also include the user name that made the change. | |||
| - Firefox109以下はサポートされなくなりました | ||||
| 
 | ||||
| #### For app developers | ||||
| - API: metaのレスポンスに`emojis`プロパティが含まれなくなりました | ||||
| 	- カスタム絵文字一覧情報を取得するには、`emojis`エンドポイントにリクエストします | ||||
| - API: カスタム絵文字エンティティに`url`プロパティが含まれなくなりました | ||||
| 	- 絵文字画像を表示するには、`<instance host>/emoji/<emoji name>.webp`にリクエストすると画像が返ります。 | ||||
| 	- e.g. `https://p1.a9z.dev/emoji/misskey.webp` | ||||
|  |  | |||
|  | @ -22,23 +22,25 @@ export class EmojiEntityService { | |||
| 	@bindThis | ||||
| 	public async pack( | ||||
| 		src: Emoji['id'] | Emoji, | ||||
| 		opts: { omitHost?: boolean; omitId?: boolean; } = {}, | ||||
| 	): Promise<Packed<'Emoji'>> { | ||||
| 		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); | ||||
| 
 | ||||
| 		return { | ||||
| 			id: emoji.id, | ||||
| 			id: opts.omitId ? undefined : emoji.id, | ||||
| 			aliases: emoji.aliases, | ||||
| 			name: emoji.name, | ||||
| 			category: emoji.category, | ||||
| 			host: emoji.host, | ||||
| 			host: opts.omitHost ? undefined : emoji.host, | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	public packMany( | ||||
| 		emojis: any[], | ||||
| 		opts: { omitHost?: boolean; omitId?: boolean; } = {}, | ||||
| 	) { | ||||
| 		return Promise.all(emojis.map(x => this.pack(x))); | ||||
| 		return Promise.all(emojis.map(x => this.pack(x, opts))); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ export const packedEmojiSchema = { | |||
| 	properties: { | ||||
| 		id: { | ||||
| 			type: 'string', | ||||
| 			optional: false, nullable: false, | ||||
| 			optional: true, nullable: false, | ||||
| 			format: 'id', | ||||
| 			example: 'xxxxxxxxxx', | ||||
| 		}, | ||||
|  | @ -26,12 +26,8 @@ export const packedEmojiSchema = { | |||
| 		}, | ||||
| 		host: { | ||||
| 			type: 'string', | ||||
| 			optional: false, nullable: true, | ||||
| 			optional: true, nullable: true, | ||||
| 			description: 'The local host is represented with `null`.', | ||||
| 		}, | ||||
| 		url: { | ||||
| 			type: 'string', | ||||
| 			optional: true, nullable: false, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  |  | |||
|  | @ -220,6 +220,7 @@ import * as ep___messaging_messages_create from './endpoints/messaging/messages/ | |||
| import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; | ||||
| import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; | ||||
| import * as ep___meta from './endpoints/meta.js'; | ||||
| import * as ep___emojis from './endpoints/emojis.js'; | ||||
| import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; | ||||
| import * as ep___mute_create from './endpoints/mute/create.js'; | ||||
| import * as ep___mute_delete from './endpoints/mute/delete.js'; | ||||
|  | @ -550,6 +551,7 @@ const $messaging_messages_create: Provider = { provide: 'ep:messaging/messages/c | |||
| const $messaging_messages_delete: Provider = { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }; | ||||
| const $messaging_messages_read: Provider = { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }; | ||||
| const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; | ||||
| const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; | ||||
| const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; | ||||
| const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; | ||||
| const $mute_delete: Provider = { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }; | ||||
|  | @ -884,6 +886,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention | |||
| 		$messaging_messages_delete, | ||||
| 		$messaging_messages_read, | ||||
| 		$meta, | ||||
| 		$emojis, | ||||
| 		$miauth_genToken, | ||||
| 		$mute_create, | ||||
| 		$mute_delete, | ||||
|  | @ -1212,6 +1215,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention | |||
| 		$messaging_messages_delete, | ||||
| 		$messaging_messages_read, | ||||
| 		$meta, | ||||
| 		$emojis, | ||||
| 		$miauth_genToken, | ||||
| 		$mute_create, | ||||
| 		$mute_delete, | ||||
|  |  | |||
|  | @ -219,6 +219,7 @@ import * as ep___messaging_messages_create from './endpoints/messaging/messages/ | |||
| import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; | ||||
| import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; | ||||
| import * as ep___meta from './endpoints/meta.js'; | ||||
| import * as ep___emojis from './endpoints/emojis.js'; | ||||
| import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; | ||||
| import * as ep___mute_create from './endpoints/mute/create.js'; | ||||
| import * as ep___mute_delete from './endpoints/mute/delete.js'; | ||||
|  | @ -547,6 +548,7 @@ const eps = [ | |||
| 	['messaging/messages/delete', ep___messaging_messages_delete], | ||||
| 	['messaging/messages/read', ep___messaging_messages_read], | ||||
| 	['meta', ep___meta], | ||||
| 	['emojis', ep___emojis], | ||||
| 	['miauth/gen-token', ep___miauth_genToken], | ||||
| 	['mute/create', ep___mute_create], | ||||
| 	['mute/delete', ep___mute_delete], | ||||
|  |  | |||
							
								
								
									
										91
									
								
								packages/backend/src/server/api/endpoints/emojis.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								packages/backend/src/server/api/endpoints/emojis.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | |||
| import { IsNull, MoreThan } from 'typeorm'; | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import type { EmojisRepository } from '@/models/index.js'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; | ||||
| import type { Config } from '@/config.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['meta'], | ||||
| 
 | ||||
| 	requireCredential: false, | ||||
| 
 | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 		optional: false, nullable: false, | ||||
| 		properties: { | ||||
| 			emojis: { | ||||
| 				type: 'array', | ||||
| 				optional: false, nullable: false, | ||||
| 				items: { | ||||
| 					type: 'object', | ||||
| 					optional: false, nullable: false, | ||||
| 					properties: { | ||||
| 						id: { | ||||
| 							type: 'string', | ||||
| 							optional: false, nullable: false, | ||||
| 							format: 'id', | ||||
| 						}, | ||||
| 						aliases: { | ||||
| 							type: 'array', | ||||
| 							optional: false, nullable: false, | ||||
| 							items: { | ||||
| 								type: 'string', | ||||
| 								optional: false, nullable: false, | ||||
| 							}, | ||||
| 						}, | ||||
| 						category: { | ||||
| 							type: 'string', | ||||
| 							optional: false, nullable: true, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
| 
 | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 	}, | ||||
| 	required: [], | ||||
| } as const; | ||||
| 
 | ||||
| // eslint-disable-next-line import/no-default-export
 | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 	constructor( | ||||
| 		@Inject(DI.config) | ||||
| 		private config: Config, | ||||
| 	 | ||||
| 		@Inject(DI.emojisRepository) | ||||
| 		private emojisRepository: EmojisRepository, | ||||
| 
 | ||||
| 		private emojiEntityService: EmojiEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const emojis = await this.emojisRepository.find({ | ||||
| 				where: { | ||||
| 					host: IsNull(), | ||||
| 				}, | ||||
| 				order: { | ||||
| 					category: 'ASC', | ||||
| 					name: 'ASC', | ||||
| 				}, | ||||
| 				cache: { | ||||
| 					id: 'meta_emojis', | ||||
| 					milliseconds: 3600000,	// 1 hour
 | ||||
| 				}, | ||||
| 			}); | ||||
| 
 | ||||
| 			return { | ||||
| 				emojis: await this.emojiEntityService.packMany(emojis, { | ||||
| 					omitId: true, | ||||
| 					omitHost: true, | ||||
| 				}), | ||||
| 			}; | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | @ -4,7 +4,6 @@ import type { AdsRepository, EmojisRepository, UsersRepository } from '@/models/ | |||
| import { MAX_NOTE_TEXT_LENGTH, DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||
| import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; | ||||
| import { MetaService } from '@/core/MetaService.js'; | ||||
| import type { Config } from '@/config.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
|  | @ -152,43 +151,6 @@ export const meta = { | |||
| 				type: 'number', | ||||
| 				optional: false, nullable: false, | ||||
| 			}, | ||||
| 			emojis: { | ||||
| 				type: 'array', | ||||
| 				optional: false, nullable: false, | ||||
| 				items: { | ||||
| 					type: 'object', | ||||
| 					optional: false, nullable: false, | ||||
| 					properties: { | ||||
| 						id: { | ||||
| 							type: 'string', | ||||
| 							optional: false, nullable: false, | ||||
| 							format: 'id', | ||||
| 						}, | ||||
| 						aliases: { | ||||
| 							type: 'array', | ||||
| 							optional: false, nullable: false, | ||||
| 							items: { | ||||
| 								type: 'string', | ||||
| 								optional: false, nullable: false, | ||||
| 							}, | ||||
| 						}, | ||||
| 						category: { | ||||
| 							type: 'string', | ||||
| 							optional: false, nullable: true, | ||||
| 						}, | ||||
| 						host: { | ||||
| 							type: 'string', | ||||
| 							optional: false, nullable: true, | ||||
| 							description: 'The local host is represented with `null`.', | ||||
| 						}, | ||||
| 						url: { | ||||
| 							type: 'string', | ||||
| 							optional: false, nullable: false, | ||||
| 							format: 'url', | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			ads: { | ||||
| 				type: 'array', | ||||
| 				optional: false, nullable: false, | ||||
|  | @ -326,30 +288,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | |||
| 		@Inject(DI.adsRepository) | ||||
| 		private adsRepository: AdsRepository, | ||||
| 
 | ||||
| 		@Inject(DI.emojisRepository) | ||||
| 		private emojisRepository: EmojisRepository, | ||||
| 
 | ||||
| 		private userEntityService: UserEntityService, | ||||
| 		private emojiEntityService: EmojiEntityService, | ||||
| 		private metaService: MetaService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const instance = await this.metaService.fetch(true); | ||||
| 
 | ||||
| 			const emojis = await this.emojisRepository.find({ | ||||
| 				where: { | ||||
| 					host: IsNull(), | ||||
| 				}, | ||||
| 				order: { | ||||
| 					category: 'ASC', | ||||
| 					name: 'ASC', | ||||
| 				}, | ||||
| 				cache: { | ||||
| 					id: 'meta_emojis', | ||||
| 					milliseconds: 3600000,	// 1 hour
 | ||||
| 				}, | ||||
| 			}); | ||||
| 
 | ||||
| 			const ads = await this.adsRepository.find({ | ||||
| 				where: { | ||||
| 					expiresAt: MoreThan(new Date()), | ||||
|  | @ -390,7 +334,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | |||
| 				backgroundImageUrl: instance.backgroundImageUrl, | ||||
| 				logoImageUrl: instance.logoImageUrl, | ||||
| 				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
 | ||||
| 				emojis: await this.emojiEntityService.packMany(emojis), | ||||
| 				defaultLightTheme: instance.defaultLightTheme, | ||||
| 				defaultDarkTheme: instance.defaultDarkTheme, | ||||
| 				ads: ads.map(ad => ({ | ||||
|  |  | |||
|  | @ -47,6 +47,9 @@ import { emojilist } from '@/scripts/emojilist'; | |||
| import { instance } from '@/instance'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import { miLocalStorage } from '@/local-storage'; | ||||
| import { getCustomEmojis } from '@/custom-emojis'; | ||||
| 
 | ||||
| const customEmojis = await getCustomEmojis(); | ||||
| 
 | ||||
| type EmojiDef = { | ||||
| 	emoji: string; | ||||
|  | @ -86,7 +89,6 @@ for (const x of lib) { | |||
| emjdb.sort((a, b) => a.name.length - b.name.length); | ||||
| 
 | ||||
| //#region Construct Emoji DB | ||||
| const customEmojis = instance.emojis; | ||||
| const emojiDefinitions: EmojiDef[] = []; | ||||
| 
 | ||||
| for (const x of customEmojis) { | ||||
|  | @ -117,7 +119,6 @@ export default { | |||
| 	emojiDb, | ||||
| 	emojiDefinitions, | ||||
| 	emojilist, | ||||
| 	customEmojis, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 			<div v-if="searchResultCustom.length > 0" class="body"> | ||||
| 				<button | ||||
| 					v-for="emoji in searchResultCustom" | ||||
| 					:key="emoji.id" | ||||
| 					:key="emoji.name" | ||||
| 					class="_button item" | ||||
| 					:title="emoji.name" | ||||
| 					tabindex="0" | ||||
|  | @ -85,9 +85,10 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; | |||
| import * as os from '@/os'; | ||||
| import { isTouchUsing } from '@/scripts/touch'; | ||||
| import { deviceKind } from '@/scripts/device-kind'; | ||||
| import { emojiCategories, instance } from '@/instance'; | ||||
| import { instance } from '@/instance'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import { defaultStore } from '@/store'; | ||||
| import { getCustomEmojiCategories, getCustomEmojis } from '@/custom-emojis'; | ||||
| 
 | ||||
| const props = withDefaults(defineProps<{ | ||||
| 	showPinned?: boolean; | ||||
|  | @ -103,6 +104,7 @@ const emit = defineEmits<{ | |||
| 	(ev: 'chosen', v: string): void; | ||||
| }>(); | ||||
| 
 | ||||
| const customEmojis = await getCustomEmojis(); | ||||
| const search = shallowRef<HTMLInputElement>(); | ||||
| const emojis = shallowRef<HTMLDivElement>(); | ||||
| 
 | ||||
|  | @ -118,8 +120,7 @@ const { | |||
| const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1); | ||||
| const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3); | ||||
| const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2); | ||||
| const customEmojiCategories = emojiCategories; | ||||
| const customEmojis = instance.emojis; | ||||
| const customEmojiCategories = await getCustomEmojiCategories(); | ||||
| const q = ref<string>(''); | ||||
| const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]); | ||||
| const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); | ||||
|  |  | |||
							
								
								
									
										48
									
								
								packages/frontend/src/custom-emojis.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								packages/frontend/src/custom-emojis.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| import { api } from './os'; | ||||
| import { miLocalStorage } from './local-storage'; | ||||
| 
 | ||||
| const storageCache = miLocalStorage.getItem('emojis'); | ||||
| let cached = storageCache ? JSON.parse(storageCache) : null; | ||||
| export async function getCustomEmojis() { | ||||
| 	const now = Date.now(); | ||||
| 	const lastFetchedAt = miLocalStorage.getItem('lastEmojisFetchedAt'); | ||||
| 	if (cached && lastFetchedAt && (now - parseInt(lastFetchedAt)) < 1000 * 60 * 60) return cached; | ||||
| 
 | ||||
| 	const res = await api('emojis', {}); | ||||
| 
 | ||||
| 	cached = res.emojis; | ||||
| 	miLocalStorage.setItem('emojis', JSON.stringify(cached)); | ||||
| 	miLocalStorage.setItem('lastEmojisFetchedAt', now.toString()); | ||||
| } | ||||
| 
 | ||||
| let cachedCategories; | ||||
| export async function getCustomEmojiCategories() { | ||||
| 	if (cachedCategories) return cachedCategories; | ||||
| 
 | ||||
| 	const customEmojis = await getCustomEmojis(); | ||||
| 
 | ||||
| 	const categories = new Set(); | ||||
| 	for (const emoji of customEmojis) { | ||||
| 		categories.add(emoji.category); | ||||
| 	} | ||||
| 	const res = Array.from(categories); | ||||
| 	cachedCategories = res; | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| let cachedTags; | ||||
| export async function getCustomEmojiTags() { | ||||
| 	if (cachedTags) return cachedTags; | ||||
| 
 | ||||
| 	const customEmojis = await getCustomEmojis(); | ||||
| 
 | ||||
| 	const tags = new Set(); | ||||
| 	for (const emoji of customEmojis) { | ||||
| 		for (const tag of emoji.aliases) { | ||||
| 			tags.add(tag); | ||||
| 		} | ||||
| 	} | ||||
| 	const res = Array.from(tags); | ||||
| 	cachedTags = res; | ||||
| 	return res; | ||||
| } | ||||
|  | @ -5,11 +5,11 @@ import { miLocalStorage } from './local-storage'; | |||
| 
 | ||||
| // TODO: 他のタブと永続化されたstateを同期
 | ||||
| 
 | ||||
| const instanceData = miLocalStorage.getItem('instance'); | ||||
| const cached = miLocalStorage.getItem('instance'); | ||||
| 
 | ||||
| // TODO: instanceをリアクティブにするかは再考の余地あり
 | ||||
| 
 | ||||
| export const instance: Misskey.entities.InstanceMetadata = reactive(instanceData ? JSON.parse(instanceData) : { | ||||
| export const instance: Misskey.entities.InstanceMetadata = reactive(cached ? JSON.parse(cached) : { | ||||
| 	// TODO: set default values
 | ||||
| }); | ||||
| 
 | ||||
|  | @ -24,23 +24,3 @@ export async function fetchInstance() { | |||
| 
 | ||||
| 	miLocalStorage.setItem('instance', JSON.stringify(instance)); | ||||
| } | ||||
| 
 | ||||
| export const emojiCategories = computed(() => { | ||||
| 	if (instance.emojis == null) return []; | ||||
| 	const categories = new Set(); | ||||
| 	for (const emoji of instance.emojis) { | ||||
| 		categories.add(emoji.category); | ||||
| 	} | ||||
| 	return Array.from(categories); | ||||
| }); | ||||
| 
 | ||||
| export const emojiTags = computed(() => { | ||||
| 	if (instance.emojis == null) return []; | ||||
| 	const tags = new Set(); | ||||
| 	for (const emoji of instance.emojis) { | ||||
| 		for (const tag of emoji.aliases) { | ||||
| 			tags.add(tag); | ||||
| 		} | ||||
| 	} | ||||
| 	return Array.from(tags); | ||||
| }); | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ type Keys = | |||
| 	'v' | | ||||
| 	'lastVersion' | | ||||
| 	'instance' | | ||||
| 	'emojis' | // TODO: indexed db
 | ||||
| 	'lastEmojisFetchedAt' | | ||||
| 	'account' | | ||||
| 	'accounts' | | ||||
| 	'latestDonationInfoShownAt' | | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| 
 | ||||
| 		<!-- たくさんあると邪魔 | ||||
| 		<div class="tags"> | ||||
| 			<span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> | ||||
| 			<span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> | ||||
| 		</div> | ||||
| 		--> | ||||
| 	</div> | ||||
|  | @ -28,8 +28,8 @@ | |||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { defineComponent, computed } from 'vue'; | ||||
| <script lang="ts" setup> | ||||
| import { defineComponent, computed, watch } from 'vue'; | ||||
| import XEmoji from './emojis.emoji.vue'; | ||||
| import MkButton from '@/components/MkButton.vue'; | ||||
| import MkInput from '@/components/MkInput.vue'; | ||||
|  | @ -37,62 +37,43 @@ import MkSelect from '@/components/MkSelect.vue'; | |||
| import MkFoldableSection from '@/components/MkFoldableSection.vue'; | ||||
| import MkTab from '@/components/MkTab.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { emojiCategories, emojiTags } from '@/instance'; | ||||
| import { getCustomEmojis, getCustomEmojiCategories, getCustomEmojiTags } from '@/custom-emojis'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		MkButton, | ||||
| 		MkInput, | ||||
| 		MkSelect, | ||||
| 		MkFoldableSection, | ||||
| 		MkTab, | ||||
| 		XEmoji, | ||||
| 	}, | ||||
| const customEmojis = await getCustomEmojis(); | ||||
| const customEmojiCategories = await getCustomEmojiCategories(); | ||||
| const customEmojiTags = await getCustomEmojiTags(); | ||||
| let q = $ref(''); | ||||
| let searchEmojis = $ref(null); | ||||
| let selectedTags = $ref(new Set()); | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			q: '', | ||||
| 			customEmojiCategories: emojiCategories, | ||||
| 			customEmojis: this.$instance.emojis, | ||||
| 			tags: emojiTags, | ||||
| 			selectedTags: new Set(), | ||||
| 			searchEmojis: null, | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
| 		q() { this.search(); }, | ||||
| 		selectedTags: { | ||||
| 			handler() { | ||||
| 				this.search(); | ||||
| 			}, | ||||
| 			deep: true, | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		search() { | ||||
| 			if ((this.q === '' || this.q == null) && this.selectedTags.size === 0) { | ||||
| 				this.searchEmojis = null; | ||||
| function search() { | ||||
| 	if ((q === '' || q == null) && selectedTags.size === 0) { | ||||
| 		searchEmojis = null; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 			if (this.selectedTags.size === 0) { | ||||
| 				this.searchEmojis = this.customEmojis.filter(emoji => emoji.name.includes(this.q) || emoji.aliases.includes(this.q)); | ||||
| 	if (selectedTags.size === 0) { | ||||
| 		searchEmojis = customEmojis.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q)); | ||||
| 	} else { | ||||
| 				this.searchEmojis = this.customEmojis.filter(emoji => (emoji.name.includes(this.q) || emoji.aliases.includes(this.q)) && [...this.selectedTags].every(t => emoji.aliases.includes(t))); | ||||
| 		searchEmojis = customEmojis.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t))); | ||||
| 	} | ||||
| } | ||||
| 		}, | ||||
| 
 | ||||
| 		toggleTag(tag) { | ||||
| 			if (this.selectedTags.has(tag)) { | ||||
| 				this.selectedTags.delete(tag); | ||||
| function toggleTag(tag) { | ||||
| 	if (selectedTags.has(tag)) { | ||||
| 		selectedTags.delete(tag); | ||||
| 	} else { | ||||
| 				this.selectedTags.add(tag); | ||||
| 		selectedTags.add(tag); | ||||
| 	} | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| watch($$(q), () => { | ||||
| 	search(); | ||||
| }); | ||||
| 
 | ||||
| watch($$(selectedTags), () => { | ||||
| 	search(); | ||||
| }, { deep: true }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ import MkInput from '@/components/MkInput.vue'; | |||
| import * as os from '@/os'; | ||||
| import { unique } from '@/scripts/array'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import { emojiCategories } from '@/instance'; | ||||
| import { getCustomEmojiCategories } from '@/custom-emojis'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| 	emoji: any, | ||||
|  | @ -46,7 +46,7 @@ let dialog = $ref(null); | |||
| let name: string = $ref(props.emoji.name); | ||||
| let category: string = $ref(props.emoji.category); | ||||
| let aliases: string = $ref(props.emoji.aliases.join(' ')); | ||||
| let categories: string[] = $ref(emojiCategories); | ||||
| const categories = await getCustomEmojiCategories(); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'done', v: { deleted?: boolean, updated?: any }): void, | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ | |||
| 				<div class="icon"><i class="ti ti-icons"></i></div> | ||||
| 				<div class="body"> | ||||
| 					<div class="value"> | ||||
| 						<MkNumber :value="$instance.emojis.length" style="margin-right: 0.5em;"/> | ||||
| 						<MkNumber :value="customEmojis.length" style="margin-right: 0.5em;"/> | ||||
| 					</div> | ||||
| 					<div class="label">Custom emojis</div> | ||||
| 				</div> | ||||
|  | @ -63,6 +63,9 @@ import number from '@/filters/number'; | |||
| import MkNumberDiff from '@/components/MkNumberDiff.vue'; | ||||
| import MkNumber from '@/components/MkNumber.vue'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import { getCustomEmojis } from '@/custom-emojis'; | ||||
| 
 | ||||
| const customEmojis = await getCustomEmojis(); | ||||
| 
 | ||||
| let stats: any = $ref(null); | ||||
| let usersComparedToThePrevDay = $ref<number>(); | ||||
|  |  | |||
|  | @ -317,12 +317,15 @@ import MkTextarea from '@/components/MkTextarea.vue'; | |||
| import { definePageMetadata } from '@/scripts/page-metadata'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import { instance } from '@/instance'; | ||||
| import { getCustomEmojis } from '@/custom-emojis'; | ||||
| 
 | ||||
| const customEmojis = await getCustomEmojis(); | ||||
| 
 | ||||
| let preview_mention = $ref('@example'); | ||||
| let preview_hashtag = $ref('#test'); | ||||
| let preview_url = $ref('https://example.com'); | ||||
| let preview_link = $ref(`[${i18n.ts._mfm.dummy}](https://example.com)`); | ||||
| let preview_emoji = $ref(instance.emojis.length ? `:${instance.emojis[0].name}:` : ':emojiname:'); | ||||
| let preview_emoji = $ref(customEmojis.length ? `:${customEmojis[0].name}:` : ':emojiname:'); | ||||
| let preview_bold = $ref(`**${i18n.ts._mfm.dummy}**`); | ||||
| let preview_small = $ref(`<small>${i18n.ts._mfm.dummy}</small>`); | ||||
| let preview_center = $ref(`<center>${i18n.ts._mfm.dummy}</center>`); | ||||
|  |  | |||
|  | @ -183,6 +183,8 @@ const menuDef = computed(() => [{ | |||
| 		action: () => { | ||||
| 			miLocalStorage.removeItem('locale'); | ||||
| 			miLocalStorage.removeItem('theme'); | ||||
| 			miLocalStorage.removeItem('emojis'); | ||||
| 			miLocalStorage.removeItem('lastEmojisFetchedAt'); | ||||
| 			unisonReload(); | ||||
| 		}, | ||||
| 	}, { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue