refactor: 絵文字URLを引き回すのをやめる (#9423)
This commit is contained in:
		
							parent
							
								
									510e6ec7e9
								
							
						
					
					
						commit
						912791b3ab
					
				|  | @ -6,8 +6,8 @@ import type { Packed } from '@/misc/schema.js'; | |||
| import type { } from '@/models/entities/Blocking.js'; | ||||
| import type { User } from '@/models/entities/User.js'; | ||||
| import type { Emoji } from '@/models/entities/Emoji.js'; | ||||
| import { UserEntityService } from './UserEntityService.js'; | ||||
| import { bindThis } from '@/decorators.js'; | ||||
| import { UserEntityService } from './UserEntityService.js'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class EmojiEntityService { | ||||
|  | @ -22,6 +22,7 @@ export class EmojiEntityService { | |||
| 	@bindThis | ||||
| 	public async pack( | ||||
| 		src: Emoji['id'] | Emoji, | ||||
| 		opts: { omitUrl?: boolean } = {}, | ||||
| 	): Promise<Packed<'Emoji'>> { | ||||
| 		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); | ||||
| 
 | ||||
|  | @ -32,15 +33,16 @@ export class EmojiEntityService { | |||
| 			category: emoji.category, | ||||
| 			host: emoji.host, | ||||
| 			// ?? emoji.originalUrl してるのは後方互換性のため
 | ||||
| 			url: emoji.publicUrl ?? emoji.originalUrl, | ||||
| 			url: opts.omitUrl ? undefined : (emoji.publicUrl ?? emoji.originalUrl), | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	public packMany( | ||||
| 		emojis: any[], | ||||
| 		opts: { omitUrl?: boolean } = {}, | ||||
| 	) { | ||||
| 		return Promise.all(emojis.map(x => this.pack(x))); | ||||
| 		return Promise.all(emojis.map(x => this.pack(x, opts))); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ export const packedEmojiSchema = { | |||
| 		}, | ||||
| 		url: { | ||||
| 			type: 'string', | ||||
| 			optional: false, nullable: false, | ||||
| 			optional: true, nullable: false, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  |  | |||
|  | @ -309,6 +309,7 @@ export const paramDef = { | |||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		detail: { type: 'boolean', default: true }, | ||||
| 		omitEmojiUrl: { type: 'boolean', default: false }, | ||||
| 	}, | ||||
| 	required: [], | ||||
| } as const; | ||||
|  | @ -390,7 +391,7 @@ 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), | ||||
| 				emojis: await this.emojiEntityService.packMany(emojis, { omitUrl: ps.omitEmojiUrl }), | ||||
| 				defaultLightTheme: instance.defaultLightTheme, | ||||
| 				defaultDarkTheme: instance.defaultDarkTheme, | ||||
| 				ads: ads.map(ad => ({ | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ import { PageEntityService } from '@/core/entities/PageEntityService.js'; | |||
| import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; | ||||
| import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; | ||||
| import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; | ||||
| import type { ChannelsRepository, ClipsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; | ||||
| import type { ChannelsRepository, ClipsRepository, EmojisRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; | ||||
| import { deepClone } from '@/misc/clone.js'; | ||||
| import { bindThis } from '@/decorators.js'; | ||||
| import manifest from './manifest.json' assert { type: 'json' }; | ||||
|  | @ -70,6 +70,9 @@ export class ClientServerService { | |||
| 		@Inject(DI.pagesRepository) | ||||
| 		private pagesRepository: PagesRepository, | ||||
| 
 | ||||
| 		@Inject(DI.emojisRepository) | ||||
| 		private emojisRepository: EmojisRepository, | ||||
| 
 | ||||
| 		private userEntityService: UserEntityService, | ||||
| 		private noteEntityService: NoteEntityService, | ||||
| 		private pageEntityService: PageEntityService, | ||||
|  | @ -217,6 +220,33 @@ export class ClientServerService { | |||
| 			return reply.sendFile('/apple-touch-icon.png', staticAssets); | ||||
| 		}); | ||||
| 
 | ||||
| 		fastify.get<{ Params: { path: string } }>('/emoji/:path(.*)', async (request, reply) => { | ||||
| 			const path = request.params.path; | ||||
| 
 | ||||
| 			if (!path.match(/^[a-zA-Z0-9\-_@\.]+?\.webp$/)) { | ||||
| 				reply.code(404); | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			const name = path.split('@')[0].replace('.webp', ''); | ||||
| 			const host = path.split('@')[1]?.replace('.webp', ''); | ||||
| 
 | ||||
| 			const emoji = await this.emojisRepository.findOneBy({ | ||||
| 				host: host == null ? IsNull() : host, | ||||
| 				name: name, | ||||
| 			}); | ||||
| 
 | ||||
| 			if (emoji == null) { | ||||
| 				reply.code(404); | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); | ||||
| 
 | ||||
| 			// ?? emoji.originalUrl してるのは後方互換性のため
 | ||||
| 			return await reply.redirect(301, emoji.publicUrl ?? emoji.originalUrl); | ||||
| 		}); | ||||
| 
 | ||||
| 		fastify.get<{ Params: { path: string } }>('/fluent-emoji/:path(.*)', async (request, reply) => { | ||||
| 			const path = request.params.path; | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,20 +37,20 @@ | |||
| 			<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/> | ||||
| 			<div class="body"> | ||||
| 				<p v-if="appearNote.cw != null" class="cw"> | ||||
| 					<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||
| 					<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i"/> | ||||
| 					<XCwButton v-model="showContent" :note="appearNote"/> | ||||
| 				</p> | ||||
| 				<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed, isLong }"> | ||||
| 					<div class="text"> | ||||
| 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> | ||||
| 						<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> | ||||
| 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||
| 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i"/> | ||||
| 						<a v-if="appearNote.renote != null" class="rp">RN:</a> | ||||
| 						<div v-if="translating || translation" class="translation"> | ||||
| 							<MkLoading v-if="translating" mini/> | ||||
| 							<div v-else class="translated"> | ||||
| 								<b>{{ $t('translatedFrom', { x: translation.sourceLang }) }}: </b> | ||||
| 								<Mfm :text="translation.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||
| 								<Mfm :text="translation.text" :author="appearNote.user" :i="$i"/> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
|  |  | |||
|  | @ -48,20 +48,20 @@ | |||
| 		<div class="main"> | ||||
| 			<div class="body"> | ||||
| 				<p v-if="appearNote.cw != null" class="cw"> | ||||
| 					<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||
| 					<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i"/> | ||||
| 					<XCwButton v-model="showContent" :note="appearNote"/> | ||||
| 				</p> | ||||
| 				<div v-show="appearNote.cw == null || showContent" class="content"> | ||||
| 					<div class="text"> | ||||
| 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> | ||||
| 						<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> | ||||
| 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||
| 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i"/> | ||||
| 						<a v-if="appearNote.renote != null" class="rp">RN:</a> | ||||
| 						<div v-if="translating || translation" class="translation"> | ||||
| 							<MkLoading v-if="translating" mini/> | ||||
| 							<div v-else class="translated"> | ||||
| 								<b>{{ $t('translatedFrom', { x: translation.sourceLang }) }}: </b> | ||||
| 								<Mfm :text="translation.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> | ||||
| 								<Mfm :text="translation.text" :author="appearNote.user" :i="$i"/> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| 		<XNoteHeader class="header" :note="note" :mini="true"/> | ||||
| 		<div class="body"> | ||||
| 			<p v-if="note.cw != null" class="cw"> | ||||
| 				<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/> | ||||
| 				<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i"/> | ||||
| 				<XCwButton v-model="showContent" :note="note"/> | ||||
| 			</p> | ||||
| 			<div v-show="note.cw == null || showContent" class="content"> | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 			<XNoteHeader class="header" :note="note" :mini="true"/> | ||||
| 			<div class="body"> | ||||
| 				<p v-if="note.cw != null" class="cw"> | ||||
| 					<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/> | ||||
| 					<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i"/> | ||||
| 					<XCwButton v-model="showContent" :note="note"/> | ||||
| 				</p> | ||||
| 				<div v-show="note.cw == null || showContent" class="content"> | ||||
|  |  | |||
|  | @ -34,31 +34,31 @@ | |||
| 		</header> | ||||
| 		<MkA v-if="notification.type === 'reaction'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full"/> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 		</MkA> | ||||
| 		<MkA v-if="notification.type === 'renote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)"> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 			<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.renote.emojis"/> | ||||
| 			<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="!full"/> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 		</MkA> | ||||
| 		<MkA v-if="notification.type === 'reply'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full"/> | ||||
| 		</MkA> | ||||
| 		<MkA v-if="notification.type === 'mention'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full"/> | ||||
| 		</MkA> | ||||
| 		<MkA v-if="notification.type === 'quote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full"/> | ||||
| 		</MkA> | ||||
| 		<MkA v-if="notification.type === 'pollVote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full"/> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 		</MkA> | ||||
| 		<MkA v-if="notification.type === 'pollEnded'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> | ||||
| 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full"/> | ||||
| 			<i class="ti ti-quote"></i> | ||||
| 		</MkA> | ||||
| 		<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span> | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| 			<div class="backdrop" :style="{ 'width': `${showResult ? (choice.votes / total * 100) : 0}%` }"></div> | ||||
| 			<span> | ||||
| 				<template v-if="choice.isVoted"><i class="ti ti-check"></i></template> | ||||
| 				<Mfm :text="choice.text" :plain="true" :custom-emojis="note.emojis"/> | ||||
| 				<Mfm :text="choice.text" :plain="true"/> | ||||
| 				<span v-if="showResult" class="votes">({{ $t('_poll.votesCount', { n: choice.votes }) }})</span> | ||||
| 			</span> | ||||
| 		</li> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <MkEmoji :emoji="reaction" :custom-emojis="customEmojis || []" :is-reaction="true" :normal="true" :no-style="noStyle"/> | ||||
| <MkEmoji :emoji="reaction" :is-reaction="true" :normal="true" :no-style="noStyle"/> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
|  | @ -7,7 +7,6 @@ import { } from 'vue'; | |||
| 
 | ||||
| const props = defineProps<{ | ||||
| 	reaction: string; | ||||
| 	customEmojis?: any[]; // TODO | ||||
| 	noStyle?: boolean; | ||||
| }>(); | ||||
| </script> | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| <template> | ||||
| <MkTooltip ref="tooltip" :showing="showing" :target-element="targetElement" :max-width="340" @closed="emit('closed')"> | ||||
| 	<div class="beeadbfb"> | ||||
| 		<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon" :no-style="true"/> | ||||
| 		<XReactionIcon :reaction="reaction" class="icon" :no-style="true"/> | ||||
| 		<div class="name">{{ reaction.replace('@.', '') }}</div> | ||||
| 	</div> | ||||
| </MkTooltip> | ||||
|  | @ -15,7 +15,6 @@ import XReactionIcon from '@/components/MkReactionIcon.vue'; | |||
| defineProps<{ | ||||
| 	showing: boolean; | ||||
| 	reaction: string; | ||||
| 	emojis: any[]; // TODO | ||||
| 	targetElement: HTMLElement; | ||||
| }>(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| <MkTooltip ref="tooltip" :showing="showing" :target-element="targetElement" :max-width="340" @closed="emit('closed')"> | ||||
| 	<div class="bqxuuuey"> | ||||
| 		<div class="reaction"> | ||||
| 			<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon" :no-style="true"/> | ||||
| 			<XReactionIcon :reaction="reaction" class="icon" :no-style="true"/> | ||||
| 			<div class="name">{{ getReactionName(reaction) }}</div> | ||||
| 		</div> | ||||
| 		<div class="users"> | ||||
|  | @ -27,7 +27,6 @@ defineProps<{ | |||
| 	reaction: string; | ||||
| 	users: any[]; // TODO | ||||
| 	count: number; | ||||
| 	emojis: any[]; // TODO | ||||
| 	targetElement: HTMLElement; | ||||
| }>(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
| 	:class="{ reacted: note.myReaction == reaction, canToggle }" | ||||
| 	@click="toggleReaction()" | ||||
| > | ||||
| 	<XReactionIcon class="icon" :reaction="reaction" :custom-emojis="note.emojis"/> | ||||
| 	<XReactionIcon class="icon" :reaction="reaction"/> | ||||
| 	<span class="count">{{ count }}</span> | ||||
| </button> | ||||
| </template> | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 		<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> | ||||
| 		<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deleted }})</span> | ||||
| 		<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> | ||||
| 		<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/> | ||||
| 		<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i"/> | ||||
| 		<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> | ||||
| 	</div> | ||||
| 	<details v-if="note.files.length > 0"> | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| 	<span v-if="$i && $i.id !== user.id && user.isFollowed" class="followed">{{ $ts.followsYou }}</span> | ||||
| 	<div class="description"> | ||||
| 		<div v-if="user.description" class="mfm"> | ||||
| 			<Mfm :text="user.description" :author="user" :i="$i" :custom-emojis="user.emojis"/> | ||||
| 			<Mfm :text="user.description" :author="user" :i="$i"/> | ||||
| 		</div> | ||||
| 		<span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span> | ||||
| 	</div> | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| 				<p class="username"><MkAcct :user="user"/></p> | ||||
| 			</div> | ||||
| 			<div class="description"> | ||||
| 				<Mfm v-if="user.description" :text="user.description" :author="user" :i="$i" :custom-emojis="user.emojis"/> | ||||
| 				<Mfm v-if="user.description" :text="user.description" :author="user" :i="$i"/> | ||||
| 			</div> | ||||
| 			<div class="status"> | ||||
| 				<div> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt" decoding="async"/> | ||||
| <img v-if="isCustom" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt" decoding="async"/> | ||||
| <img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" decoding="async" @pointerenter="computeTitle"/> | ||||
| <span v-else-if="char && useOsNativeEmojis" :alt="alt" @pointerenter="computeTitle">{{ char }}</span> | ||||
| <span v-else>{{ emoji }}</span> | ||||
|  | @ -7,44 +7,40 @@ | |||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { computed } from 'vue'; | ||||
| import { CustomEmoji } from 'misskey-js/built/entities'; | ||||
| import { getStaticImageUrl } from '@/scripts/get-static-image-url'; | ||||
| import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base'; | ||||
| import { defaultStore } from '@/store'; | ||||
| import { instance } from '@/instance'; | ||||
| import { getEmojiName } from '@/scripts/emojilist'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| 	emoji: string; | ||||
| 	normal?: boolean; | ||||
| 	noStyle?: boolean; | ||||
| 	customEmojis?: CustomEmoji[]; | ||||
| 	isReaction?: boolean; | ||||
| }>(); | ||||
| 
 | ||||
| const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; | ||||
| 
 | ||||
| const isCustom = computed(() => props.emoji.startsWith(':')); | ||||
| const customEmojiName = props.emoji.substr(1, props.emoji.length - 2); | ||||
| const char = computed(() => isCustom.value ? undefined : props.emoji); | ||||
| const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native' && !props.isReaction); | ||||
| const ce = computed(() => props.customEmojis ?? instance.emojis ?? []); | ||||
| const customEmoji = computed(() => isCustom.value ? ce.value.find(x => x.name === props.emoji.substr(1, props.emoji.length - 2)) : undefined); | ||||
| const url = computed(() => { | ||||
| 	if (char.value) { | ||||
| 		return char2path(char.value); | ||||
| 	} else { | ||||
| 		const rawUrl = (customEmoji.value as CustomEmoji).url; | ||||
| 		const rawUrl = '/emoji/' + customEmojiName + '.webp'; | ||||
| 		return defaultStore.state.disableShowingAnimatedImages | ||||
| 			? getStaticImageUrl(rawUrl) | ||||
| 			: rawUrl; | ||||
| 	} | ||||
| }); | ||||
| const alt = computed(() => customEmoji.value ? `:${customEmoji.value.name}:` : char.value); | ||||
| const alt = computed(() => isCustom.value ? `:${customEmojiName}:` : char.value); | ||||
| 
 | ||||
| // Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter | ||||
| function computeTitle(event: PointerEvent): void { | ||||
| 	const title = customEmoji.value | ||||
| 		? `:${customEmoji.value.name}:` | ||||
| 	const title = isCustom.value | ||||
| 		? `:${customEmojiName}:` | ||||
| 		: (getEmojiName(char.value as string) ?? char.value as string); | ||||
| 	(event.target as HTMLElement).title = title; | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <MfmCore :text="text" :plain="plain" :nowrap="nowrap" :author="author" :customEmojis="customEmojis" :isNote="isNote" class="havbbuyv" :class="{ nowrap }"/> | ||||
| <MfmCore :text="text" :plain="plain" :nowrap="nowrap" :author="author" :is-note="isNote" class="havbbuyv" :class="{ nowrap }"/> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
|  | @ -11,7 +11,6 @@ const props = withDefaults(defineProps<{ | |||
| 	plain?: boolean; | ||||
| 	nowrap?: boolean; | ||||
| 	author?: any; | ||||
| 	customEmojis?: any; | ||||
| 	isNote?: boolean; | ||||
| }>(), { | ||||
| 	plain: false, | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <Mfm :text="user.name || user.username" :plain="true" :nowrap="nowrap" :custom-emojis="user.emojis"/> | ||||
| <Mfm :text="user.name || user.username" :plain="true" :nowrap="nowrap"/> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
|  |  | |||
|  | @ -35,9 +35,6 @@ export default defineComponent({ | |||
| 			type: Object, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 		customEmojis: { | ||||
| 			required: false, | ||||
| 		}, | ||||
| 		isNote: { | ||||
| 			type: Boolean, | ||||
| 			default: true, | ||||
|  | @ -275,7 +272,6 @@ export default defineComponent({ | |||
| 					return [h(MkEmoji, { | ||||
| 						key: Math.random(), | ||||
| 						emoji: `:${token.props.name}:`, | ||||
| 						customEmojis: this.customEmojis, | ||||
| 						normal: this.plain, | ||||
| 					})]; | ||||
| 				} | ||||
|  | @ -284,7 +280,6 @@ export default defineComponent({ | |||
| 					return [h(MkEmoji, { | ||||
| 						key: Math.random(), | ||||
| 						emoji: token.props.emoji, | ||||
| 						customEmojis: this.customEmojis, | ||||
| 						normal: this.plain, | ||||
| 					})]; | ||||
| 				} | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ export const instance: Misskey.entities.InstanceMetadata = reactive(instanceData | |||
| export async function fetchInstance() { | ||||
| 	const meta = await api('meta', { | ||||
| 		detail: false, | ||||
| 		omitEmojiUrl: true, | ||||
| 	}); | ||||
| 
 | ||||
| 	for (const [k, v] of Object.entries(meta)) { | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| 					<img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/> | ||||
| 					<div class="misskey">Misskey</div> | ||||
| 					<div class="version">v{{ version }}</div> | ||||
| 					<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span> | ||||
| 					<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :is-reaction="false" :normal="true" :no-style="true"/></span> | ||||
| 				</div> | ||||
| 				<div class="_formBlock" style="text-align: center;"> | ||||
| 					{{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a> | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ | |||
| 								<p class="acct">@{{ acct(req.follower) }}</p> | ||||
| 							</div> | ||||
| 							<div v-if="req.follower.description" class="description" :title="req.follower.description"> | ||||
| 								<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/> | ||||
| 								<Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :plain="true" :nowrap="true"/> | ||||
| 							</div> | ||||
| 							<div class="actions"> | ||||
| 								<button class="_button" @click="accept(req.follower)"><i class="ti ti-check"></i></button> | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ | |||
| 						</div> | ||||
| 					</div> | ||||
| 					<div class="description"> | ||||
| 						<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/> | ||||
| 						<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i"/> | ||||
| 						<p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> | ||||
| 					</div> | ||||
| 					<div class="fields system"> | ||||
|  | @ -61,10 +61,10 @@ | |||
| 					<div v-if="user.fields.length > 0" class="fields"> | ||||
| 						<dl v-for="(field, i) in user.fields" :key="i" class="field"> | ||||
| 							<dt class="name"> | ||||
| 								<Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/> | ||||
| 								<Mfm :text="field.name" :plain="true" :colored="false"/> | ||||
| 							</dt> | ||||
| 							<dd class="value"> | ||||
| 								<Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/> | ||||
| 								<Mfm :text="field.value" :author="user" :i="$i" :colored="false"/> | ||||
| 							</dd> | ||||
| 						</dl> | ||||
| 					</div> | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 		<div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap afdcfbfb"> | ||||
| 			<div class="header"> | ||||
| 				<MkAvatar class="avatar" :user="user"/> | ||||
| 				<MkReactionIcon class="reaction" :reaction="item.type" :custom-emojis="item.note.emojis" :no-style="true"/> | ||||
| 				<MkReactionIcon class="reaction" :reaction="item.type" :no-style="true"/> | ||||
| 				<MkTime :time="item.createdAt" class="createdAt"/> | ||||
| 			</div> | ||||
| 			<MkNote :key="item.id" :note="item.note"/> | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| 			<div class="content _panel"> | ||||
| 				<div class="body"> | ||||
| 					<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> | ||||
| 					<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/> | ||||
| 					<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i"/> | ||||
| 					<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> | ||||
| 				</div> | ||||
| 				<div v-if="note.files.length > 0" class="richcontent"> | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 				<span v-for="note in notes" :key="note.id" class="item"> | ||||
| 					<img class="avatar" :src="note.user.avatarUrl" decoding="async"/> | ||||
| 					<MkA class="text" :to="notePage(note)"> | ||||
| 						<Mfm class="text" :text="getNoteSummary(note)" :plain="true" :nowrap="true" :custom-emojis="note.emojis"/> | ||||
| 						<Mfm class="text" :text="getNoteSummary(note)" :plain="true" :nowrap="true"/> | ||||
| 					</MkA> | ||||
| 					<span class="divider"></span> | ||||
| 				</span> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue