From 5e71418d5caca1cea333ee1b8629987cc69c4fbc Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight <saschanaz@outlook.com> Date: Sun, 7 Jan 2024 08:02:53 +0100 Subject: [PATCH] fix(frontend/emoji) restore U+FE0F for simple emojis (#12866) * fix(frontend/emoji) restore U+FE0F for simple emojis * Update CHANGELOG.md --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> --- CHANGELOG.md | 1 + .../src/components/global/MkEmoji.vue | 10 ++--- packages/frontend/src/scripts/emojilist.ts | 7 +++- packages/frontend/test/emoji.test.ts | 41 +++++++++++++++++++ packages/frontend/test/init.ts | 24 ++++++----- 5 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 packages/frontend/test/emoji.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d2fb4ccd5..474fcad674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ ### Client - Feat: æ–°ã—ã„ã‚²ãƒ¼ãƒ ã‚’è¿½åŠ - Enhance: ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°å…¥åŠ›æ™‚ã«ã€æœ¬æ–‡ã®æœ«å°¾ã®è¡Œã«ä½•ã‚‚書ã‹ã‚Œã¦ã„ãªã„å ´åˆã¯æ–°ãŸã«ã‚¹ãƒšãƒ¼ã‚¹ã‚’è¿½åŠ ã—ãªã„よã†ã« +- Fix: ãƒã‚¤ãƒ†ã‚£ãƒ–モードã®çµµæ–‡å—ãŒãƒ¢ãƒŽã‚¯ãƒã«ãªã‚‰ãªã„よã†ã« - Fix: v2023.12.0ã§è¿½åŠ ã•ã‚ŒãŸã€Œãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ãŒãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚‚ã—ãã¯ãƒãƒŠãƒ¼ç”»åƒã‚’未è¨å®šçŠ¶æ…‹ã«ã§ãる機能ã€ãŒç®¡ç†ç”»é¢ä¸Šã§æ£ã—ã表示ã•ã‚Œã¦ã„ãªã„å•é¡Œã‚’ä¿®æ£ - Enhance: ãƒãƒ£ãƒ³ãƒãƒ«ãƒŽãƒ¼ãƒˆã®ãƒ”ン留ã‚をノートã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‹ã‚‰ã§ãるよ diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index 76ca8688d1..f6b21343b6 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -5,15 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <img v-if="!useOsNativeEmojis" :class="$style.root" :src="url" :alt="props.emoji" decoding="async" @pointerenter="computeTitle" @click="onClick"/> -<span v-else-if="useOsNativeEmojis" :alt="props.emoji" @pointerenter="computeTitle" @click="onClick">{{ props.emoji }}</span> -<span v-else>{{ emoji }}</span> +<span v-else :alt="props.emoji" @pointerenter="computeTitle" @click="onClick">{{ colorizedNativeEmoji }}</span> </template> <script lang="ts" setup> import { computed, inject } from 'vue'; import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js'; import { defaultStore } from '@/store.js'; -import { getEmojiName } from '@/scripts/emojilist.js'; +import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js'; import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import * as sound from '@/scripts/sound.js'; @@ -30,9 +29,8 @@ const react = inject<((name: string) => void) | null>('react', null); const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native'); -const url = computed(() => { - return char2path(props.emoji); -}); +const url = computed(() => char2path(props.emoji)); +const colorizedNativeEmoji = computed(() => colorizeEmoji(props.emoji)); // 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 { diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts index 8885bf4b7f..4bd8bf94be 100644 --- a/packages/frontend/src/scripts/emojilist.ts +++ b/packages/frontend/src/scripts/emojilist.ts @@ -36,7 +36,8 @@ for (let i = 0; i < emojilist.length; i++) { export const emojiCharByCategory = _charGroupByCategory; export function getEmojiName(char: string): string | null { - const idx = _indexByChar.get(char); + // Colorize it because emojilist.json assumes that + const idx = _indexByChar.get(colorizeEmoji(char)); if (idx == null) { return null; } else { @@ -44,6 +45,10 @@ export function getEmojiName(char: string): string | null { } } +export function colorizeEmoji(char: string) { + return char.length === 1 ? `${char}\uFE0F` : char; +} + export interface CustomEmojiFolderTree { value: string; category: string; diff --git a/packages/frontend/test/emoji.test.ts b/packages/frontend/test/emoji.test.ts new file mode 100644 index 0000000000..a1782a4913 --- /dev/null +++ b/packages/frontend/test/emoji.test.ts @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { describe, test, assert, afterEach } from 'vitest'; +import { render, cleanup, type RenderResult } from '@testing-library/vue'; +import { defaultStoreState } from './init.js'; +import { getEmojiName } from '@/scripts/emojilist.js'; +import { components } from '@/components/index.js'; +import { directives } from '@/directives/index.js'; +import MkEmoji from '@/components/global/MkEmoji.vue'; + +describe('Emoji', () => { + const renderEmoji = (emoji: string): RenderResult => { + return render(MkEmoji, { + props: { emoji }, + global: { directives, components }, + }); + }; + + afterEach(() => { + cleanup(); + defaultStoreState.emojiStyle = ''; + }); + + describe('MkEmoji', () => { + test('Should render selector-less heart with color in native mode', async () => { + defaultStoreState.emojiStyle = 'native'; + const mkEmoji = await renderEmoji('\u2764'); // monochrome heart + assert.ok(mkEmoji.queryByText('\u2764\uFE0F')); // colored heart + assert.ok(!mkEmoji.queryByText('\u2764')); + }); + }); + + describe('Emoji list', () => { + test('Should get the name of the heart', () => { + assert.strictEqual(getEmojiName('\u2764'), 'heart'); + }); + }); +}); diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts index 6d93ff8cb0..f21248cfee 100644 --- a/packages/frontend/test/init.ts +++ b/packages/frontend/test/init.ts @@ -17,21 +17,23 @@ updateI18n(locales['en-US']); // XXX: misskey-js panics if WebSocket is not defined vi.stubGlobal('WebSocket', class WebSocket extends EventTarget { static CLOSING = 2; }); +export const defaultStoreState: Record<string, unknown> = { + + // ãªã‚“ã‹testãŒã†ã¾ã„ã“ã¨å‹•ã‹ãªã„ã®ã§ã“ã“ã«æ›¸ã + dataSaver: { + media: false, + avatar: false, + urlPreview: false, + code: false, + }, + +}; + // XXX: defaultStore somehow becomes undefined in vitest? vi.mock('@/store.js', () => { return { defaultStore: { - state: { - - // ãªã‚“ã‹testãŒã†ã¾ã„ã“ã¨å‹•ã‹ãªã„ã®ã§ã“ã“ã«æ›¸ã - dataSaver: { - media: false, - avatar: false, - urlPreview: false, - code: false, - }, - - }, + state: defaultStoreState, }, }; }); -- GitLab