From decde50c865afb287d8c0d67e2dba0af65b1d69e Mon Sep 17 00:00:00 2001
From: Kagami Sascha Rosylight <saschanaz@outlook.com>
Date: Sun, 25 Dec 2022 15:52:52 +0900
Subject: [PATCH] enhance(client): show Unicode emoji tooltip with its name
 (#9399)

* enhance(client): show Unicode emoji tooltip with its name

* Update CHANGELOG.md

* Update CHANGELOG.md

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 .../components/MkReactionsViewer.details.vue  | 11 ++++++++-
 .../client/src/components/global/MkEmoji.vue  | 23 +++++++++++++------
 packages/client/src/scripts/emojilist.ts      |  4 ++++
 3 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/packages/client/src/components/MkReactionsViewer.details.vue b/packages/client/src/components/MkReactionsViewer.details.vue
index 10e82cb9f9..fb8d74ad4b 100644
--- a/packages/client/src/components/MkReactionsViewer.details.vue
+++ b/packages/client/src/components/MkReactionsViewer.details.vue
@@ -3,7 +3,7 @@
 	<div class="bqxuuuey">
 		<div class="reaction">
 			<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon" :no-style="true"/>
-			<div class="name">{{ reaction.replace('@.', '') }}</div>
+			<div class="name">{{ getReactionName(reaction) }}</div>
 		</div>
 		<div class="users">
 			<div v-for="u in users" :key="u.id" class="user">
@@ -20,6 +20,7 @@
 import { } from 'vue';
 import MkTooltip from './MkTooltip.vue';
 import XReactionIcon from '@/components/MkReactionIcon.vue';
+import { getEmojiName } from '@/scripts/emojilist';
 
 defineProps<{
 	showing: boolean;
@@ -33,6 +34,14 @@ defineProps<{
 const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
+
+function getReactionName(reaction: string): string {
+	const trimLocal = reaction.replace('@.', '');
+	if (trimLocal.startsWith(':')) {
+		return trimLocal;
+	}
+	return getEmojiName(reaction) ?? reaction;
+}
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/components/global/MkEmoji.vue b/packages/client/src/components/global/MkEmoji.vue
index 106778aee2..440ef6220d 100644
--- a/packages/client/src/components/global/MkEmoji.vue
+++ b/packages/client/src/components/global/MkEmoji.vue
@@ -1,17 +1,18 @@
 <template>
 <img v-if="customEmoji" 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" :title="alt" decoding="async"/>
-<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
+<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" decoding="async" @pointerenter="computeTitle"/>
+<span v-else-if="char && useOsNativeEmojis" :alt="alt" @pointerenter="computeTitle">{{ char }}</span>
 <span v-else>{{ emoji }}</span>
 </template>
 
 <script lang="ts" setup>
-import { computed, ref, watch } from 'vue';
+import { computed } from 'vue';
 import { CustomEmoji } from 'misskey-js/built/entities';
 import { getStaticImageUrl } from '@/scripts/get-static-image-url';
 import { char2filePath } from '@/scripts/twemoji-base';
 import { defaultStore } from '@/store';
 import { instance } from '@/instance';
+import { getEmojiName } from '@/scripts/emojilist';
 
 const props = defineProps<{
 	emoji: string;
@@ -22,20 +23,28 @@ const props = defineProps<{
 }>();
 
 const isCustom = computed(() => props.emoji.startsWith(':'));
-const char = computed(() => isCustom.value ? null : props.emoji);
+const char = computed(() => isCustom.value ? undefined : props.emoji);
 const useOsNativeEmojis = computed(() => defaultStore.state.useOsNativeEmojis && !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)) : null);
+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 char2filePath(char.value);
 	} else {
+		const rawUrl = (customEmoji.value as CustomEmoji).url;
 		return defaultStore.state.disableShowingAnimatedImages
-			? getStaticImageUrl(customEmoji.value.url)
-			: customEmoji.value.url;
+			? getStaticImageUrl(rawUrl)
+			: rawUrl;
 	}
 });
 const alt = computed(() => customEmoji.value ? `:${customEmoji.value.name}:` : char.value);
+
+function computeTitle(event: PointerEvent): void {
+	const title = customEmoji.value
+		? `:${customEmoji.value.name}:`
+		: (getEmojiName(char.value as string) ?? char.value as string);
+	(event.target as HTMLElement).title = title;
+}
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/scripts/emojilist.ts b/packages/client/src/scripts/emojilist.ts
index 4ce63dc7e7..bc52fa7a43 100644
--- a/packages/client/src/scripts/emojilist.ts
+++ b/packages/client/src/scripts/emojilist.ts
@@ -11,3 +11,7 @@ export type UnicodeEmojiDef = {
 import _emojilist from '../emojilist.json';
 
 export const emojilist = _emojilist as UnicodeEmojiDef[];
+
+export function getEmojiName(char: string): string | undefined {
+	return emojilist.find(emo => emo.char === char)?.name;
+}
-- 
GitLab