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>
This commit is contained in:
		
							parent
							
								
									c6a4caa8be
								
							
						
					
					
						commit
						5e71418d5c
					
				|  | @ -21,6 +21,7 @@ | |||
| ### Client | ||||
| - Feat: 新しいゲームを追加 | ||||
| - Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように | ||||
| - Fix: ネイティブモードの絵文字がモノクロにならないように | ||||
| - Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正 | ||||
| - Enhance: チャンネルノートのピン留めをノートのメニューからできるよ | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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'); | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
|  | @ -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, | ||||
| 		}, | ||||
| 	}; | ||||
| }); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue