From 729abbef621aea5b8b697644181915935b74bbf8 Mon Sep 17 00:00:00 2001 From: おさむのひと <46447427+samunohito@users.noreply.github.com> Date: Fri, 7 Nov 2025 08:39:21 +0900 Subject: feat: チャンネルミュートの実装 (#14105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add channel_muting table and entities * add channel_muting services * タイムライン取得処理への組み込み * misskey-jsの型とインターフェース生成 * Channelスキーマにミュート情報を追加 * フロントエンドの実装 * 条件が逆だったのを修正 * 期限切れミュートを掃除する機能を実装 * TLの抽出条件調節 * 名前の変更と変更不要の差分をロールバック * 修正漏れ * isChannelRelatedの条件に誤りがあった * [wip] テスト追加 * テストの追加と検出した不備の修正 * fix test * fix CHANGELOG.md * 通常はFTTにしておく * 実装忘れ対応 * fix merge * fix merge * add channel tl test * fix CHANGELOG.md * remove unused import * fix lint * fix test * fix favorite -> favorited * exclude -> include * fix CHANGELOG.md * fix CHANGELOG.md * maintenance * fix CHANGELOG.md * fix * fix ci * regenerate * fix * Revert "fix" This reverts commit 699d50c6ec798777d8e9667cb5d45a26b06bfc93. * fixed --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- packages/frontend/src/pages/channel.vue | 82 +++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 8 deletions(-) (limited to 'packages/frontend/src/pages') diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 7c19e6798d..dff8be2a57 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -129,24 +129,25 @@ useInterval(() => { }); watch(() => props.channelId, async () => { - channel.value = await misskeyApi('channels/show', { + const _channel = await misskeyApi('channels/show', { channelId: props.channelId, }); - if (channel.value == null) return; // TSを黙らすため - favorited.value = channel.value.isFavorited ?? false; - if (favorited.value || channel.value.isFollowing) { + favorited.value = _channel.isFavorited ?? false; + if (favorited.value || _channel.isFollowing) { tab.value = 'timeline'; } - if ((favorited.value || channel.value.isFollowing) && channel.value.lastNotedAt) { - const lastReadedAt: number = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.value.id}`) ?? 0; - const lastNotedAt = Date.parse(channel.value.lastNotedAt); + if ((favorited.value || _channel.isFollowing) && _channel.lastNotedAt) { + const lastReadedAt: number = miLocalStorage.getItemAsJson(`channelLastReadedAt:${_channel.id}`) ?? 0; + const lastNotedAt = Date.parse(_channel.lastNotedAt); if (lastNotedAt > lastReadedAt) { - miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.value.id}`, lastNotedAt); + miLocalStorage.setItemAsJson(`channelLastReadedAt:${_channel.id}`, lastNotedAt); } } + + channel.value = _channel; }, { immediate: true }); function edit() { @@ -190,6 +191,53 @@ async function unfavorite() { }); } +async function mute() { + if (!channel.value) return; + const _channel = channel.value; + + const { canceled, result: period } = await os.select({ + title: i18n.ts.mutePeriod, + items: [{ + value: 'indefinitely', text: i18n.ts.indefinitely, + }, { + value: 'tenMinutes', text: i18n.ts.tenMinutes, + }, { + value: 'oneHour', text: i18n.ts.oneHour, + }, { + value: 'oneDay', text: i18n.ts.oneDay, + }, { + value: 'oneWeek', text: i18n.ts.oneWeek, + }], + default: 'indefinitely', + }); + if (canceled) return; + + const expiresAt = period === 'indefinitely' ? null + : period === 'tenMinutes' ? Date.now() + (1000 * 60 * 10) + : period === 'oneHour' ? Date.now() + (1000 * 60 * 60) + : period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24) + : period === 'oneWeek' ? Date.now() + (1000 * 60 * 60 * 24 * 7) + : null; + + os.apiWithDialog('channels/mute/create', { + channelId: _channel.id, + expiresAt, + }).then(() => { + _channel.isMuting = true; + }); +} + +async function unmute() { + if (!channel.value) return; + const _channel = channel.value; + + os.apiWithDialog('channels/mute/delete', { + channelId: _channel.id, + }).then(() => { + _channel.isMuting = false; + }); +} + async function search() { if (!channel.value) return; @@ -243,6 +291,24 @@ const headerActions = computed(() => { }); } + if (!channel.value.isMuting) { + headerItems.push({ + icon: 'ti ti-volume', + text: i18n.ts.mute, + handler: async (): Promise => { + await mute(); + }, + }); + } else { + headerItems.push({ + icon: 'ti ti-volume-off', + text: i18n.ts.unmute, + handler: async (): Promise => { + await unmute(); + }, + }); + } + if (($i && $i.id === channel.value.userId) || iAmModerator) { headerItems.push({ icon: 'ti ti-settings', -- cgit v1.2.3-freya