Skip to content

Commit 1f24a8c

Browse files
enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように (#14115)
* enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように * Update Changelog
1 parent 54d0a46 commit 1f24a8c

File tree

10 files changed

+75
-17
lines changed

10 files changed

+75
-17
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
(Cherry-picked from https://github.com/taiyme/misskey/pull/238)
3131
- Enhance: AiScriptを0.19.0にアップデート
3232
- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
33+
- Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように
3334
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
3435
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
3536
- Fix: リバーシの対局を正しく共有できないことがある問題を修正

locales/index.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -5008,6 +5008,14 @@ export interface Locale extends ILocale {
50085008
* もう一度お試しください。
50095009
*/
50105010
"tryAgain": string;
5011+
/**
5012+
* センシティブなメディアを表示するとき確認する
5013+
*/
5014+
"confirmWhenRevealingSensitiveMedia": string;
5015+
/**
5016+
* センシティブなメディアです。表示しますか?
5017+
*/
5018+
"sensitiveMediaRevealConfirm": string;
50115019
"_delivery": {
50125020
/**
50135021
* 配信状態

locales/ja-JP.yml

+2
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,8 @@ noDescription: "説明文はありません"
12481248
alwaysConfirmFollow: "フォローの際常に確認する"
12491249
inquiry: "お問い合わせ"
12501250
tryAgain: "もう一度お試しください。"
1251+
confirmWhenRevealingSensitiveMedia: "センシティブなメディアを表示するとき確認する"
1252+
sensitiveMediaRevealConfirm: "センシティブなメディアです。表示しますか?"
12511253

12521254
_delivery:
12531255
status: "配信状態"

packages/frontend/src/components/MkMediaAudio.vue

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
1515
@contextmenu.stop
1616
@keydown.stop
1717
>
18-
<button v-if="hide" :class="$style.hidden" @click="hide = false">
18+
<button v-if="hide" :class="$style.hidden" @click="show">
1919
<div :class="$style.hiddenTextWrapper">
2020
<b v-if="audio.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.audio}${audio.size ? ' ' + bytes(audio.size) : ''})` : '' }}</b>
2121
<b v-else style="display: block;"><i class="ti ti-music"></i> {{ defaultStore.state.dataSaver.media && audio.size ? bytes(audio.size) : i18n.ts.audio }}</b>
@@ -156,6 +156,18 @@ const audioEl = shallowRef<HTMLAudioElement>();
156156
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
157157
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore'));
158158

159+
async function show() {
160+
if (props.audio.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
161+
const { canceled } = await os.confirm({
162+
type: 'question',
163+
text: i18n.ts.sensitiveMediaRevealConfirm,
164+
});
165+
if (canceled) return;
166+
}
167+
168+
hide.value = false;
169+
}
170+
159171
// Menu
160172
const menuShowing = ref(false);
161173

packages/frontend/src/components/MkMediaBanner.vue

+16-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
66
<template>
77
<div :class="$style.root">
88
<MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
9-
<div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="hide = false">
9+
<div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show">
1010
<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
1111
<b>{{ i18n.ts.sensitive }}</b>
1212
<span>{{ i18n.ts.clickToShow }}</span>
@@ -24,24 +24,30 @@ SPDX-License-Identifier: AGPL-3.0-only
2424
</template>
2525

2626
<script lang="ts" setup>
27-
import { shallowRef, watch, ref } from 'vue';
27+
import { ref } from 'vue';
2828
import * as Misskey from 'misskey-js';
2929
import { i18n } from '@/i18n.js';
30+
import { defaultStore } from '@/store.js';
31+
import * as os from '@/os.js';
3032
import MkMediaAudio from '@/components/MkMediaAudio.vue';
3133

32-
const props = withDefaults(defineProps<{
34+
const props = defineProps<{
3335
media: Misskey.entities.DriveFile;
34-
}>(), {
35-
});
36+
}>();
3637

37-
const audioEl = shallowRef<HTMLAudioElement>();
3838
const hide = ref(true);
3939

40-
watch(audioEl, () => {
41-
if (audioEl.value) {
42-
audioEl.value.volume = 0.3;
40+
async function show() {
41+
if (props.media.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
42+
const { canceled } = await os.confirm({
43+
type: 'question',
44+
text: i18n.ts.sensitiveMediaRevealConfirm,
45+
});
46+
if (canceled) return;
4347
}
44-
});
48+
49+
hide.value = false;
50+
}
4551
</script>
4652

4753
<style lang="scss" module>

packages/frontend/src/components/MkMediaImage.vue

+11-1
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,21 @@ const url = computed(() => (props.raw || defaultStore.state.loadRawImages)
8383
: props.image.thumbnailUrl,
8484
);
8585

86-
function onclick() {
86+
async function onclick(ev: MouseEvent) {
8787
if (!props.controls) {
8888
return;
8989
}
90+
9091
if (hide.value) {
92+
ev.stopPropagation();
93+
if (props.image.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
94+
const { canceled } = await os.confirm({
95+
type: 'question',
96+
text: i18n.ts.sensitiveMediaRevealConfirm,
97+
});
98+
if (canceled) return;
99+
}
100+
91101
hide.value = false;
92102
}
93103
}

packages/frontend/src/components/MkMediaList.vue

+4-4
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,13 @@ onMounted(() => {
138138
pswpModule: PhotoSwipe,
139139
});
140140

141-
lightbox.on('itemData', (ev) => {
142-
const { itemData } = ev;
143-
141+
lightbox.addFilter('itemData', (itemData) => {
144142
// element is children
145143
const { element } = itemData;
146144

147145
const id = element?.dataset.id;
148146
const file = props.mediaList.find(media => media.id === id);
149-
if (!file) return;
147+
if (!file) return itemData;
150148

151149
itemData.src = file.url;
152150
itemData.w = Number(file.properties.width);
@@ -158,6 +156,8 @@ onMounted(() => {
158156
itemData.alt = file.comment ?? file.name;
159157
itemData.comment = file.comment ?? file.name;
160158
itemData.thumbCropped = true;
159+
160+
return itemData;
161161
});
162162

163163
lightbox.on('uiRegister', () => {

packages/frontend/src/components/MkMediaVideo.vue

+13-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
1818
@contextmenu.stop
1919
@keydown.stop
2020
>
21-
<button v-if="hide" :class="$style.hidden" @click="hide = false">
21+
<button v-if="hide" :class="$style.hidden" @click="show">
2222
<div :class="$style.hiddenTextWrapper">
2323
<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
2424
<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ defaultStore.state.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
@@ -176,6 +176,18 @@ function hasFocus() {
176176
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
177177
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
178178

179+
async function show() {
180+
if (props.video.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
181+
const { canceled } = await os.confirm({
182+
type: 'question',
183+
text: i18n.ts.sensitiveMediaRevealConfirm,
184+
});
185+
if (canceled) return;
186+
}
187+
188+
hide.value = false;
189+
}
190+
179191
// Menu
180192
const menuShowing = ref(false);
181193

packages/frontend/src/pages/settings/general.vue

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ SPDX-License-Identifier: AGPL-3.0-only
169169
<MkSwitch v-model="disableStreamingTimeline">{{ i18n.ts.disableStreamingTimeline }}</MkSwitch>
170170
<MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch>
171171
<MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch>
172+
<MkSwitch v-model="confirmWhenRevealingSensitiveMedia">{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</MkSwitch>
172173
</div>
173174
<MkSelect v-model="serverDisconnectedBehavior">
174175
<template #label>{{ i18n.ts.whenServerDisconnected }}</template>
@@ -315,6 +316,7 @@ const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enabl
315316
const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe'));
316317
const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer'));
317318
const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
319+
const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia'));
318320

319321
watch(lang, () => {
320322
miLocalStorage.setItem('lang', lang.value as string);
@@ -357,6 +359,7 @@ watch([
357359
disableStreamingTimeline,
358360
enableSeasonalScreenEffect,
359361
alwaysConfirmFollow,
362+
confirmWhenRevealingSensitiveMedia,
360363
], async () => {
361364
await reloadAsk();
362365
});

packages/frontend/src/store.ts

+4
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,10 @@ export const defaultStore = markRaw(new Storage('base', {
454454
where: 'device',
455455
default: true,
456456
},
457+
confirmWhenRevealingSensitiveMedia: {
458+
where: 'device',
459+
default: false,
460+
},
457461

458462
sound_masterVolume: {
459463
where: 'device',

0 commit comments

Comments
 (0)