summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-07-19 09:57:01 +0900
committerGitHub <noreply@github.com>2024-07-19 09:57:01 +0900
commit1f24a8cb5a307c3ff621577189a2a618b9dcfdc4 (patch)
treee66eff1c2833757c2f7286b4b37a34fd6f374171 /packages/frontend/src
parentfix(frontend): 個人宛てダイアログお知らせが即時表示され... (diff)
downloadmisskey-1f24a8cb5a307c3ff621577189a2a618b9dcfdc4.tar.gz
misskey-1f24a8cb5a307c3ff621577189a2a618b9dcfdc4.tar.bz2
misskey-1f24a8cb5a307c3ff621577189a2a618b9dcfdc4.zip
enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように (#14115)
* enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように * Update Changelog
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/components/MkMediaAudio.vue14
-rw-r--r--packages/frontend/src/components/MkMediaBanner.vue26
-rw-r--r--packages/frontend/src/components/MkMediaImage.vue12
-rw-r--r--packages/frontend/src/components/MkMediaList.vue8
-rw-r--r--packages/frontend/src/components/MkMediaVideo.vue14
-rw-r--r--packages/frontend/src/pages/settings/general.vue3
-rw-r--r--packages/frontend/src/store.ts4
7 files changed, 64 insertions, 17 deletions
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index 582cf238c0..a080550ddf 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.stop
@keydown.stop
>
- <button v-if="hide" :class="$style.hidden" @click="hide = false">
+ <button v-if="hide" :class="$style.hidden" @click="show">
<div :class="$style.hiddenTextWrapper">
<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>
<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>();
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore'));
+async function show() {
+ if (props.audio.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.ts.sensitiveMediaRevealConfirm,
+ });
+ if (canceled) return;
+ }
+
+ hide.value = false;
+}
+
// Menu
const menuShowing = ref(false);
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index a219848b7f..11995e1f3b 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
- <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="hide = false">
+ <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show">
<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
<b>{{ i18n.ts.sensitive }}</b>
<span>{{ i18n.ts.clickToShow }}</span>
@@ -24,24 +24,30 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { shallowRef, watch, ref } from 'vue';
+import { ref } from 'vue';
import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
import MkMediaAudio from '@/components/MkMediaAudio.vue';
-const props = withDefaults(defineProps<{
+const props = defineProps<{
media: Misskey.entities.DriveFile;
-}>(), {
-});
+}>();
-const audioEl = shallowRef<HTMLAudioElement>();
const hide = ref(true);
-watch(audioEl, () => {
- if (audioEl.value) {
- audioEl.value.volume = 0.3;
+async function show() {
+ if (props.media.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.ts.sensitiveMediaRevealConfirm,
+ });
+ if (canceled) return;
}
-});
+
+ hide.value = false;
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 82f36fe5c4..0d1409e2c8 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -83,11 +83,21 @@ const url = computed(() => (props.raw || defaultStore.state.loadRawImages)
: props.image.thumbnailUrl,
);
-function onclick() {
+async function onclick(ev: MouseEvent) {
if (!props.controls) {
return;
}
+
if (hide.value) {
+ ev.stopPropagation();
+ if (props.image.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.ts.sensitiveMediaRevealConfirm,
+ });
+ if (canceled) return;
+ }
+
hide.value = false;
}
}
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 24b177d255..2300802dcf 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -138,15 +138,13 @@ onMounted(() => {
pswpModule: PhotoSwipe,
});
- lightbox.on('itemData', (ev) => {
- const { itemData } = ev;
-
+ lightbox.addFilter('itemData', (itemData) => {
// element is children
const { element } = itemData;
const id = element?.dataset.id;
const file = props.mediaList.find(media => media.id === id);
- if (!file) return;
+ if (!file) return itemData;
itemData.src = file.url;
itemData.w = Number(file.properties.width);
@@ -158,6 +156,8 @@ onMounted(() => {
itemData.alt = file.comment ?? file.name;
itemData.comment = file.comment ?? file.name;
itemData.thumbCropped = true;
+
+ return itemData;
});
lightbox.on('uiRegister', () => {
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 0ec0039df4..7c5a365148 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.stop
@keydown.stop
>
- <button v-if="hide" :class="$style.hidden" @click="hide = false">
+ <button v-if="hide" :class="$style.hidden" @click="show">
<div :class="$style.hiddenTextWrapper">
<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>
<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() {
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
+async function show() {
+ if (props.video.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.ts.sensitiveMediaRevealConfirm,
+ });
+ if (canceled) return;
+ }
+
+ hide.value = false;
+}
+
// Menu
const menuShowing = ref(false);
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index cfc63f2a08..9e429f8dbd 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -169,6 +169,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="disableStreamingTimeline">{{ i18n.ts.disableStreamingTimeline }}</MkSwitch>
<MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch>
<MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch>
+ <MkSwitch v-model="confirmWhenRevealingSensitiveMedia">{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</MkSwitch>
</div>
<MkSelect v-model="serverDisconnectedBehavior">
<template #label>{{ i18n.ts.whenServerDisconnected }}</template>
@@ -315,6 +316,7 @@ const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enabl
const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe'));
const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer'));
const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
+const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia'));
watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);
@@ -357,6 +359,7 @@ watch([
disableStreamingTimeline,
enableSeasonalScreenEffect,
alwaysConfirmFollow,
+ confirmWhenRevealingSensitiveMedia,
], async () => {
await reloadAsk();
});
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 9cb2742069..dbf6b8716f 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -454,6 +454,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: true,
},
+ confirmWhenRevealingSensitiveMedia: {
+ where: 'device',
+ default: false,
+ },
sound_masterVolume: {
where: 'device',