fix(frontend): ピン留め or 履歴に表示されるカスタム絵文字がサーバから削除されるとリアクションが出来なくなる (#13486)

* fix(frontend): ピン留めに登録されているカスタム絵文字がサーバから削除されるとリアクションが出来なくなる

* fix CHANGELOG.md

* fix Unicode Emojis

* fix Unicode Emojis

* fix
This commit is contained in:
おさむのひと 2024-03-02 13:28:10 +09:00 committed by GitHub
parent 114d3319e8
commit 32690f576f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 45 additions and 22 deletions

View file

@ -12,6 +12,19 @@
--> -->
## 202x.x.x (unreleased)
### General
-
### Client
- Fix: 絵文字関係の不具合を修正 (#13485)
- 履歴に残っている or ピン留めされた絵文字がコントロールパネルより削除されていた際にリアクションデッキが表示できなくなる
- Unicode絵文字が履歴に残っている or ピン留めされているとリアクションデッキが表示できなくなる
### Server
-
## 2024.3.0 ## 2024.3.0
### General ### General

View file

@ -77,7 +77,7 @@ const emojiDb = computed(() => {
unicodeEmojiDB.push({ unicodeEmojiDB.push({
emoji: emoji, emoji: emoji,
name: k, name: k,
aliasOf: getEmojiName(emoji)!, aliasOf: getEmojiName(emoji),
url: char2path(emoji), url: char2path(emoji),
}); });
} }

View file

@ -87,7 +87,7 @@ const shown = ref(!!props.initialShown);
function computeButtonTitle(ev: MouseEvent): void { function computeButtonTitle(ev: MouseEvent): void {
const elm = ev.target as HTMLElement; const elm = ev.target as HTMLElement;
const emoji = elm.dataset.emoji as string; const emoji = elm.dataset.emoji as string;
elm.title = getEmojiName(emoji) ?? emoji; elm.title = getEmojiName(emoji);
} }
function nestedChosen(emoji: any, ev: MouseEvent) { function nestedChosen(emoji: any, ev: MouseEvent) {

View file

@ -353,7 +353,7 @@ watch(q, () => {
searchResultUnicode.value = Array.from(searchUnicode()); searchResultUnicode.value = Array.from(searchUnicode());
}); });
function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean { function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji); return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji);
} }
@ -378,11 +378,14 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef):
return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
} }
function getDef(emoji: string) { function getDef(emoji: string): string | Misskey.entities.EmojiSimple | UnicodeEmojiDef {
if (emoji.includes(':')) { if (emoji.includes(':')) {
return customEmojisMap.get(emoji.replace(/:/g, ''))!; //
// undefined
const name = emoji.replaceAll(':', '');
return customEmojisMap.get(name) ?? emoji;
} else { } else {
return getUnicodeEmoji(emoji)!; return getUnicodeEmoji(emoji);
} }
} }
@ -390,7 +393,7 @@ function getDef(emoji: string) {
function computeButtonTitle(ev: MouseEvent): void { function computeButtonTitle(ev: MouseEvent): void {
const elm = ev.target as HTMLElement; const elm = ev.target as HTMLElement;
const emoji = elm.dataset.emoji as string; const emoji = elm.dataset.emoji as string;
elm.title = getEmojiName(emoji) ?? emoji; elm.title = getEmojiName(emoji);
} }
function chosen(emoji: any, ev?: MouseEvent) { function chosen(emoji: any, ev?: MouseEvent) {

View file

@ -44,7 +44,7 @@ function getReactionName(reaction: string): string {
if (trimLocal.startsWith(':')) { if (trimLocal.startsWith(':')) {
return trimLocal; return trimLocal;
} }
return getEmojiName(reaction) ?? reaction; return getEmojiName(reaction);
} }
</script> </script>

View file

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject } from 'vue'; import { computed, inject } from 'vue';
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js'; import { char2fluentEmojiFilePath, char2twemojiFilePath } from '@/scripts/emoji-base.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js'; import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -34,8 +34,7 @@ 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 // 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 { function computeTitle(event: PointerEvent): void {
const title = getEmojiName(props.emoji as string) ?? props.emoji as string; (event.target as HTMLElement).title = getEmojiName(props.emoji);
(event.target as HTMLElement).title = title;
} }
function onClick(ev: MouseEvent) { function onClick(ev: MouseEvent) {

View file

@ -1,10 +1,10 @@
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { UnicodeEmojiDef } from './emojilist.js'; import { UnicodeEmojiDef } from './emojilist.js';
export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean { export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
if (typeof emoji === 'string') return true; // UnicodeEmojiDefにも無い絵文字であれば文字列で来る。Unicode絵文字であることには変わりないので常にリアクション可能とする;
if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能 if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
emoji = emoji as Misskey.entities.EmojiSimple;
const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? []; const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
return !(emoji.localOnly && note.user.host !== me.host) return !(emoji.localOnly && note.user.host !== me.host)
&& !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote')) && !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))

View file

@ -39,21 +39,29 @@ for (let i = 0; i < emojilist.length; i++) {
export const emojiCharByCategory = _charGroupByCategory; export const emojiCharByCategory = _charGroupByCategory;
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null { export function getUnicodeEmoji(char: string): UnicodeEmojiDef | string {
// Colorize it because emojilist.json assumes that // Colorize it because emojilist.json assumes that
return unicodeEmojisMap.get(colorizeEmoji(char)) ?? unicodeEmojisMap.get(char) ?? null; return unicodeEmojisMap.get(colorizeEmoji(char))
// カラースタイル絵文字がjsonに無い場合はテキストスタイル絵文字にフォールバックする
?? unicodeEmojisMap.get(char)
// それでも見つからない場合はそのまま返す絵文字情報がjsonに無い場合、このフォールバックが無いとレンダリングに失敗する
?? char;
} }
export function getEmojiName(char: string): string | null { export function getEmojiName(char: string): string {
// Colorize it because emojilist.json assumes that // Colorize it because emojilist.json assumes that
const idx = _indexByChar.get(colorizeEmoji(char)); const idx = _indexByChar.get(colorizeEmoji(char)) ?? _indexByChar.get(char);
if (idx == null) { if (idx === undefined) {
return null; // 絵文字情報がjsonに無い場合は名前の取得が出来ないのでそのまま返すしか無い
return char;
} else { } else {
return emojilist[idx].name; return emojilist[idx].name;
} }
} }
/**
* U+260Eなどの1文字で表現される絵文字VS16:U+FE0Fを付与
*/
export function colorizeEmoji(char: string) { export function colorizeEmoji(char: string) {
return char.length === 1 ? `${char}\uFE0F` : char; return char.length === 1 ? `${char}\uFE0F` : char;
} }