fix(frontend): ピン留め or 履歴に表示されるカスタム絵文字がサーバから削除されるとリアクションが出来なくなる (#13486)
* fix(frontend): ピン留めに登録されているカスタム絵文字がサーバから削除されるとリアクションが出来なくなる * fix CHANGELOG.md * fix Unicode Emojis * fix Unicode Emojis * fix
This commit is contained in:
parent
114d3319e8
commit
32690f576f
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -12,6 +12,19 @@
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 202x.x.x (unreleased)
|
||||||
|
|
||||||
|
### General
|
||||||
|
-
|
||||||
|
|
||||||
|
### Client
|
||||||
|
- Fix: 絵文字関係の不具合を修正 (#13485)
|
||||||
|
- 履歴に残っている or ピン留めされた絵文字がコントロールパネルより削除されていた際にリアクションデッキが表示できなくなる
|
||||||
|
- Unicode絵文字が履歴に残っている or ピン留めされているとリアクションデッキが表示できなくなる
|
||||||
|
|
||||||
|
### Server
|
||||||
|
-
|
||||||
|
|
||||||
## 2024.3.0
|
## 2024.3.0
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
|
@ -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),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
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 ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
|
if (typeof emoji === 'string') return true; // UnicodeEmojiDefにも無い絵文字であれば文字列で来る。Unicode絵文字であることには変わりないので常にリアクション可能とする;
|
||||||
|
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'))
|
||||||
&& (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)));
|
&& (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue