From 5bc52b6743d1d23d215aaa7b62ceee85f15c3502 Mon Sep 17 00:00:00 2001 From: taichan <40626578+tai-cha@users.noreply.github.com> Date: Mon, 12 May 2025 10:00:06 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(frontend):=20=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=82=92=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E3=81=AB=E3=81=99=E3=82=8B=E6=A9=9F=E8=83=BD=20(#1596?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 442742c371472f9c9e9372c5552cf73767aedecf. * Update Changelog --- CHANGELOG.md | 2 + locales/index.d.ts | 16 +++ locales/ja-JP.yml | 4 + packages/frontend/assets/unknown.png | Bin 0 -> 10770 bytes .../components/MkReactionsViewer.reaction.vue | 65 +++++++--- .../src/components/global/MkCustomEmoji.vue | 112 ++++++++++++++---- .../src/components/global/MkEmoji.vue | 47 +++++++- .../frontend/src/components/global/MkMfm.ts | 2 + .../pages/settings/mute-block.emoji-mute.vue | 105 ++++++++++++++++ .../src/pages/settings/mute-block.vue | 15 +++ packages/frontend/src/preferences/def.ts | 3 + packages/frontend/src/utility/emoji-mute.ts | 61 ++++++++++ packages/frontend/test/init.ts | 13 ++ 13 files changed, 409 insertions(+), 36 deletions(-) create mode 100644 packages/frontend/assets/unknown.png create mode 100644 packages/frontend/src/pages/settings/mute-block.emoji-mute.vue create mode 100644 packages/frontend/src/utility/emoji-mute.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9210a669ef..2d4fa47589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ - 何らの理由によりWebsocket接続が行えない環境でも快適に利用可能です - 従来のWebsocket接続を行うモードはリアルタイムモードとして再定義されました - チャットなど、一部の機能は引き続き設定に関わらずWebsocket接続が行われます +- Feat: 絵文字をミュート可能にする機能 + - 絵文字(ユニコードの絵文字・カスタム絵文字)毎にミュートし、不可視化することができるようになりました - Enhance: メモリ使用量を軽減しました - Enhance: 画像の高品質なプレースホルダを無効化してパフォーマンスを向上させるオプションを追加 - Enhance: 招待されているが参加していないルームを開いたときに、招待を承認するかどうか尋ねるように diff --git a/locales/index.d.ts b/locales/index.d.ts index afe99c6887..ed9d127a2e 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5425,6 +5425,22 @@ export interface Locale extends ILocale { * オフにする */ "turnItOff": string; + /** + * 絵文字ミュート + */ + "emojiMute": string; + /** + * 絵文字ミュート解除 + */ + "emojiUnmute": string; + /** + * {x}をミュート + */ + "muteX": ParameterizedString<"x">; + /** + * {x}のミュートを解除 + */ + "unmuteX": ParameterizedString<"x">; "_chat": { /** * まだメッセージはありません diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f1ffc19796..e318234087 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1351,6 +1351,10 @@ advice: "アドバイス" realtimeMode: "リアルタイムモード" turnItOn: "オンにする" turnItOff: "オフにする" +emojiMute: "絵文字ミュート" +emojiUnmute: "絵文字ミュート解除" +muteX: "{x}をミュート" +unmuteX: "{x}のミュートを解除" _chat: noMessagesYet: "まだメッセージはありません" diff --git a/packages/frontend/assets/unknown.png b/packages/frontend/assets/unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..d27bdfc8b3ce28b368d546e8b743edbe38ef9098 GIT binary patch literal 10770 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 YOY64zcfB9PT~EHFhfT>(4JcRs2Z75}B>(^b literal 0 HcmV?d00001 diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 9fc773b335..7d76dffa5a 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -22,6 +22,7 @@ import { computed, inject, onMounted, useTemplateRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import { getUnicodeEmoji } from '@@/js/emojilist.js'; import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue'; +import type { MenuItem } from '@/types/menu'; import XDetails from '@/components/MkReactionsViewer.details.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import * as os from '@/os.js'; @@ -36,6 +37,7 @@ import { customEmojisMap } from '@/custom-emojis.js'; import { prefer } from '@/preferences.js'; import { DI } from '@/di.js'; import { noteEvents } from '@/composables/use-note-capture.js'; +import { mute as muteEmoji, unmute as unmuteEmoji, checkMuted as isEmojiMuted } from '@/utility/emoji-mute.js'; const props = defineProps<{ noteId: Misskey.entities.Note['id']; @@ -63,6 +65,7 @@ const canToggle = computed(() => { return !props.reaction.match(/@\w/) && $i && emoji.value; }); const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':')); +const isLocalCustomEmoji = props.reaction[0] === ':' && props.reaction.includes('@.'); async function toggleReaction() { if (!canToggle.value) return; @@ -139,21 +142,55 @@ async function toggleReaction() { } async function menu(ev) { - if (!canGetInfo.value) return; + let menuItems: MenuItem[] = []; - os.popupMenu([{ - text: i18n.ts.info, - icon: 'ti ti-info-circle', - action: async () => { - const { dispose } = os.popup(MkCustomEmojiDetailedDialog, { - emoji: await misskeyApiGet('emoji', { - name: props.reaction.replace(/:/g, '').replace(/@\./, ''), - }), - }, { - closed: () => dispose(), - }); - }, - }], ev.currentTarget ?? ev.target); + if (canGetInfo.value) { + menuItems.push({ + text: i18n.ts.info, + icon: 'ti ti-info-circle', + action: async () => { + const { dispose } = os.popup(MkCustomEmojiDetailedDialog, { + emoji: await misskeyApiGet('emoji', { + name: props.reaction.replace(/:/g, '').replace(/@\./, ''), + }), + }, { + closed: () => dispose(), + }); + }, + }); + } + + if (isEmojiMuted(props.reaction).value) { + menuItems.push({ + text: i18n.ts.emojiUnmute, + icon: 'ti ti-mood-smile', + action: () => { + os.confirm({ + type: 'question', + title: i18n.tsx.unmuteX({ x: isLocalCustomEmoji ? `:${emojiName.value}:` : props.reaction }), + }).then(({ canceled }) => { + if (canceled) return; + unmuteEmoji(props.reaction); + }); + }, + }); + } else { + menuItems.push({ + text: i18n.ts.emojiMute, + icon: 'ti ti-mood-off', + action: () => { + os.confirm({ + type: 'question', + title: i18n.tsx.muteX({ x: isLocalCustomEmoji ? `:${emojiName.value}:` : props.reaction }), + }).then(({ canceled }) => { + if (canceled) return; + muteEmoji(props.reaction); + }); + }, + }); + } + + os.popupMenu(menuItems, ev.currentTarget ?? ev.target); } function anime() { diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index dda45ceaa2..ed114d8d31 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -5,7 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only