mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-06-14 16:05:19 +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:
@@ -5,7 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<template>
|
||||
<img
|
||||
v-if="errored && fallbackToImage"
|
||||
v-if="shouldMute"
|
||||
:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
|
||||
src="/client-assets/unknown.png"
|
||||
:title="alt"
|
||||
draggable="false"
|
||||
style="-webkit-user-drag: none;"
|
||||
@click="onClick"
|
||||
/>
|
||||
<img
|
||||
v-else-if="errored && fallbackToImage"
|
||||
:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
|
||||
src="/client-assets/dummy.png"
|
||||
:title="alt"
|
||||
@@ -40,6 +49,7 @@ import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialo
|
||||
import { $i } from '@/i.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { makeEmojiMuteKey, mute as muteEmoji, unmute as unmuteEmoji, checkMuted as checkEmojiMuted } from '@/utility/emoji-mute';
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
@@ -51,12 +61,16 @@ const props = defineProps<{
|
||||
menu?: boolean;
|
||||
menuReaction?: boolean;
|
||||
fallbackToImage?: boolean;
|
||||
ignoreMuted?: boolean;
|
||||
}>();
|
||||
|
||||
const react = inject(DI.mfmEmojiReactCallback);
|
||||
|
||||
const customEmojiName = computed(() => (props.name[0] === ':' ? props.name.substring(1, props.name.length - 1) : props.name).replace('@.', ''));
|
||||
const isLocal = computed(() => !props.host && (customEmojiName.value.endsWith('@.') || !customEmojiName.value.includes('@')));
|
||||
const emojiCodeToMute = makeEmojiMuteKey(props);
|
||||
const isMuted = checkEmojiMuted(emojiCodeToMute);
|
||||
const shouldMute = computed(() => !props.ignoreMuted && isMuted.value);
|
||||
|
||||
const rawUrl = computed(() => {
|
||||
if (props.url) {
|
||||
@@ -95,14 +109,18 @@ function onClick(ev: MouseEvent) {
|
||||
menuItems.push({
|
||||
type: 'label',
|
||||
text: `:${props.name}:`,
|
||||
}, {
|
||||
text: i18n.ts.copy,
|
||||
icon: 'ti ti-copy',
|
||||
action: () => {
|
||||
copyToClipboard(`:${props.name}:`);
|
||||
},
|
||||
});
|
||||
|
||||
if (isLocal.value) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.copy,
|
||||
icon: 'ti ti-copy',
|
||||
action: () => {
|
||||
copyToClipboard(`:${props.name}:`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (props.menuReaction && react) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.doReaction,
|
||||
@@ -113,21 +131,43 @@ function onClick(ev: MouseEvent) {
|
||||
});
|
||||
}
|
||||
|
||||
menuItems.push({
|
||||
text: i18n.ts.info,
|
||||
icon: 'ti ti-info-circle',
|
||||
action: async () => {
|
||||
const { dispose } = os.popup(MkCustomEmojiDetailedDialog, {
|
||||
emoji: await misskeyApiGet('emoji', {
|
||||
name: customEmojiName.value,
|
||||
}),
|
||||
}, {
|
||||
closed: () => dispose(),
|
||||
});
|
||||
},
|
||||
});
|
||||
if (isLocal.value) {
|
||||
menuItems.push({
|
||||
type: 'divider',
|
||||
}, {
|
||||
text: i18n.ts.info,
|
||||
icon: 'ti ti-info-circle',
|
||||
action: async () => {
|
||||
const { dispose } = os.popup(MkCustomEmojiDetailedDialog, {
|
||||
emoji: await misskeyApiGet('emoji', {
|
||||
name: customEmojiName.value,
|
||||
}),
|
||||
}, {
|
||||
closed: () => dispose(),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if ($i?.isModerator ?? $i?.isAdmin) {
|
||||
if (isMuted.value) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.emojiUnmute,
|
||||
icon: 'ti ti-mood-smile',
|
||||
action: async () => {
|
||||
await unmute();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
menuItems.push({
|
||||
text: i18n.ts.emojiMute,
|
||||
icon: 'ti ti-mood-off',
|
||||
action: async () => {
|
||||
await mute();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (($i?.isModerator ?? $i?.isAdmin) && isLocal.value) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.edit,
|
||||
icon: 'ti ti-pencil',
|
||||
@@ -152,6 +192,36 @@ async function edit(name: string) {
|
||||
});
|
||||
}
|
||||
|
||||
function mute() {
|
||||
const titleEmojiName = isLocal.value
|
||||
? `:${customEmojiName.value}:`
|
||||
: emojiCodeToMute;
|
||||
os.confirm({
|
||||
type: 'question',
|
||||
title: i18n.tsx.muteX({ x: titleEmojiName }),
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
muteEmoji(emojiCodeToMute);
|
||||
});
|
||||
}
|
||||
|
||||
function unmute() {
|
||||
const titleEmojiName = isLocal.value
|
||||
? `:${customEmojiName.value}:`
|
||||
: emojiCodeToMute;
|
||||
os.confirm({
|
||||
type: 'question',
|
||||
title: i18n.tsx.unmuteX({ x: titleEmojiName }),
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
unmuteEmoji(emojiCodeToMute);
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
Reference in New Issue
Block a user