1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-30 21:33:56 +02:00

feat(frontend): 絵文字をミュート可能にする機能 (#15966)

* wip ( 絵文字ミュートの基礎実装, PoC )

* refactor: 絵文字のmute/unmute処理の共通化

* SPDX

* リアクションからも絵文字ミュート可能に

* emojiMute/emojiUnmute

* replace resource of emojiMute

* add vitest preferstate for mutedEmojis

* add vitest to preferReactive

* 混入削除

* Fix typo (mutedEmojis -> mutingEmojis)

* reactiveやめる

* add時の判定ミスを修正

* Add CHANGELOG

* Revert "reactiveやめる"

This reverts commit 442742c371.

* Update Changelog
This commit is contained in:
taichan
2025-05-12 10:00:06 +09:00
committed by GitHub
parent b18d6b4cef
commit 5bc52b6743
13 changed files with 409 additions and 36 deletions

View File

@@ -4,7 +4,8 @@ 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"/>
<img v-if="shouldMute" :class="$style.root" src="/client-assets/unknown.png" :alt="props.emoji" decoding="async" @pointerenter="computeTitle" @click="onClick"/>
<img v-else-if="!useOsNativeEmojis" :class="$style.root" :src="url" :alt="props.emoji" decoding="async" @pointerenter="computeTitle" @click="onClick"/>
<span v-else :alt="props.emoji" @pointerenter="computeTitle" @click="onClick">{{ colorizedNativeEmoji }}</span>
</template>
@@ -18,11 +19,13 @@ import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
import { DI } from '@/di.js';
import { mute as muteEmoji, unmute as unmuteEmoji, checkMuted as checkMutedEmoji } from '@/utility/emoji-mute.js';
const props = defineProps<{
emoji: string;
menu?: boolean;
menuReaction?: boolean;
ignoreMuted?: boolean;
}>();
const react = inject(DI.mfmEmojiReactCallback, null);
@@ -32,12 +35,38 @@ const char2path = prefer.s.emojiStyle === 'twemoji' ? char2twemojiFilePath : cha
const useOsNativeEmojis = computed(() => prefer.s.emojiStyle === 'native');
const url = computed(() => char2path(props.emoji));
const colorizedNativeEmoji = computed(() => colorizeEmoji(props.emoji));
const isMuted = checkMutedEmoji(props.emoji);
const shouldMute = computed(() => isMuted.value && !props.ignoreMuted);
// 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 {
(event.target as HTMLElement).title = getEmojiName(props.emoji);
}
function mute() {
os.confirm({
type: 'question',
title: i18n.tsx.muteX({ x: props.emoji }),
}).then(({ canceled }) => {
if (canceled) {
return;
}
muteEmoji(props.emoji);
});
}
function unmute() {
os.confirm({
type: 'question',
title: i18n.tsx.unmuteX({ x: props.emoji }),
}).then(({ canceled }) => {
if (canceled) {
return;
}
unmuteEmoji(props.emoji);
});
}
function onClick(ev: MouseEvent) {
if (props.menu) {
const menuItems: MenuItem[] = [];
@@ -63,6 +92,22 @@ function onClick(ev: MouseEvent) {
});
}
menuItems.push({
type: 'divider',
}, isMuted.value ? {
text: i18n.ts.emojiUnmute,
icon: 'ti ti-mood-smile',
action: () => {
unmute();
},
} : {
text: i18n.ts.emojiMute,
icon: 'ti ti-mood-off',
action: () => {
mute();
},
});
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
}
}