feat: collapse sensitive channel notes

This commit is contained in:
anatawa12 2025-12-17 00:06:38 +09:00
parent 0b931daefd
commit 99a4553923
No known key found for this signature in database
GPG Key ID: 9CA909848B8E4EA6
12 changed files with 59 additions and 8 deletions

View File

@ -10,7 +10,7 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
### Client
- Fix: バージョン表記のないPlayが正しく動作しない問題を修正
- Enhance: センシティブチャンネルの投稿が多くの場所で畳まれるようにになりました
## 2025.12.1

View File

@ -839,6 +839,7 @@ emailNotification: "メール通知"
publish: "公開"
inChannelSearch: "チャンネル内検索"
useReactionPickerForContextMenu: "右クリックでリアクションピッカーを開く"
collapseSensitiveChannel: "ほとんどのタイムラインでセンシティブチャンネルの投稿を隠す"
typingUsers: "{users}が入力中"
jumpToSpecifiedDate: "特定の日付にジャンプ"
showingPastTimeline: "過去のタイムラインを表示しています"
@ -1281,6 +1282,7 @@ backToTitle: "タイトルへ"
hemisphere: "お住まいの地域"
withSensitive: "センシティブなファイルを含むノートを表示"
userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿"
userSaysSomethingInSensitiveChannel: "{name}のセンシティブチャンネルでの投稿"
enableHorizontalSwipe: "スワイプしてタブを切り替える"
loading: "読み込み中"
surrender: "やめる"

View File

@ -169,6 +169,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</template>
</I18n>
<I18n v-else-if="muted === 'sensitiveChannel'" :src="i18n.ts.userSaysSomethingInSensitiveChannel" tag="small">
<template #name>
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
<MkUserName :user="appearNote.user"/>
</MkA>
</template>
</I18n>
<I18n v-else-if="showSoftWordMutedWord !== true" :src="i18n.ts.userSaysSomething" tag="small">
<template #name>
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
@ -261,6 +268,9 @@ const emit = defineEmits<{
(ev: 'removeReaction', emoji: string): void;
}>();
// for some timelines, like home timeline which only shows the following channels,
// we never collapse sensitive channel notes so we allow inject to override the preference
const collapseSensitiveChannel: boolean = inject<boolean | undefined>('collapseSensitiveChannel', undefined) ?? prefer.s.collapseSensitiveChannel;
const inTimeline = inject<boolean>('inTimeline', false);
const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(true));
const inChannel = inject('inChannel', null);
@ -330,9 +340,9 @@ const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
/* eslint-disable no-redeclare */
/** checkOnlyでは純粋なワードミュート結果をbooleanで返却する */
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly?: false): Array<string | string[]> | false | 'sensitiveMute';
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly?: false): Array<string | string[]> | false | 'sensitiveMute' | 'sensitiveChannel';
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): Array<string | string[]> | boolean | 'sensitiveMute' {
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): Array<string | string[]> | boolean | 'sensitiveMute' | 'sensitiveChannel' {
if (mutedWords != null) {
const result = checkWordMute(noteToCheck, $i, mutedWords);
if (Array.isArray(result)) {
@ -352,6 +362,7 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string
if (checkOnly) return false;
if (collapseSensitiveChannel && noteToCheck.channel?.isSensitive) return 'sensitiveChannel';
if (inTimeline && tl_withSensitive.value === false && noteToCheck.files?.some((v) => v.isSensitive)) {
return 'sensitiveMute';
}

View File

@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, watch, ref, markRaw, shallowRef } from 'vue';
import { computed, watch, ref, markRaw, shallowRef, provide } from 'vue';
import * as Misskey from 'misskey-js';
import { url } from '@@/js/config.js';
import { useInterval } from '@@/js/use-interval.js';
@ -106,6 +106,9 @@ const props = defineProps<{
channelId: string;
}>();
//
provide('collapseSensitiveChannel', false);
const tab = ref('overview');
const channel = ref<Misskey.entities.Channel | null>(null);

View File

@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, markRaw, ref } from 'vue';
import { computed, markRaw, provide, ref } from 'vue';
import { notificationTypes } from 'misskey-js';
import MkStreamingNotificationsTimeline from '@/components/MkStreamingNotificationsTimeline.vue';
import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
@ -33,6 +33,9 @@ const tab = ref('all');
const includeTypes = ref<string[] | null>(null);
const excludeTypes = computed(() => includeTypes.value ? notificationTypes.filter(t => !includeTypes.value!.includes(t)) : null);
// 稿
provide('collapseSensitiveChannel', false);
const mentionsPaginator = markRaw(new Paginator('notes/mentions', {
limit: 10,
}));

View File

@ -236,6 +236,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['collapse', 'sensitive', 'channel', 'fold']">
<MkPreferenceContainer k="collapseSensitiveChannel">
<MkSwitch v-model="collapseSensitiveChannel">
<template #label><SearchLabel>{{ i18n.ts.collapseSensitiveChannel }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
</div>
<SearchMarker :keywords="['reaction', 'size', 'scale', 'display']">
@ -871,6 +879,7 @@ const showReactionsCount = prefer.model('showReactionsCount');
const enableQuickAddMfmFunction = prefer.model('enableQuickAddMfmFunction');
const forceShowAds = prefer.model('forceShowAds');
const loadRawImages = prefer.model('loadRawImages');
const collapseSensitiveChannel = prefer.model('collapseSensitiveChannel');
const imageNewTab = prefer.model('imageNewTab');
const showFixedPostForm = prefer.model('showFixedPostForm');
const showFixedPostFormInChannel = prefer.model('showFixedPostFormInChannel');

View File

@ -47,6 +47,9 @@ import { prefer } from '@/preferences.js';
const tlComponent = useTemplateRef('tlComponent');
//
provide('collapseSensitiveChannel', false);
type TimelinePageSrc = BasicTimelineType | `list:${string}`;
const srcWhenNotSignin = ref<'local' | 'global'>(isAvailableBasicTimeline('local') ? 'local' : 'global');

View File

@ -209,6 +209,9 @@ export const PREF_DEF = definePreferences({
nsfw: {
default: 'respect' as 'respect' | 'force' | 'ignore',
},
collapseSensitiveChannel: {
default: true,
},
highlightSensitiveMedia: {
default: false,
},

View File

@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, ref, shallowRef, watch, useTemplateRef } from 'vue';
import { onMounted, ref, shallowRef, watch, useTemplateRef, provide } from 'vue';
import * as Misskey from 'misskey-js';
import XColumn from './column.vue';
import type { Column } from '@/deck.js';
@ -39,6 +39,9 @@ const props = defineProps<{
isStacked: boolean;
}>();
//
provide('collapseSensitiveChannel', false);
const timeline = useTemplateRef('timeline');
const channel = shallowRef<Misskey.entities.Channel>();
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });

View File

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { defineAsyncComponent, useTemplateRef } from 'vue';
import { defineAsyncComponent, provide, useTemplateRef } from 'vue';
import XColumn from './column.vue';
import type { Column } from '@/deck.js';
import { updateColumn } from '@/deck.js';
@ -25,6 +25,9 @@ const props = defineProps<{
isStacked: boolean;
}>();
// 稿
provide('collapseSensitiveChannel', false);
const notificationsComponent = useTemplateRef('notificationsComponent');
async function func() {

View File

@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, watch, ref, useTemplateRef, computed } from 'vue';
import { onMounted, watch, ref, useTemplateRef, computed, provide } from 'vue';
import XColumn from './column.vue';
import type { Column } from '@/deck.js';
import type { MenuItem } from '@/types/menu.js';
@ -50,6 +50,9 @@ const props = defineProps<{
isStacked: boolean;
}>();
//
provide('collapseSensitiveChannel', false);
const timeline = useTemplateRef('timeline');
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });

View File

@ -3368,6 +3368,10 @@ export interface Locale extends ILocale {
*
*/
"useReactionPickerForContextMenu": string;
/**
* 稿
*/
"collapseSensitiveChannel": string;
/**
* {users}
*/
@ -5136,6 +5140,10 @@ export interface Locale extends ILocale {
* {name}稿
*/
"userSaysSomethingSensitive": ParameterizedString<"name">;
/**
* {name}稿
*/
"userSaysSomethingInSensitiveChannel": ParameterizedString<"name">;
/**
*
*/