enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように (#14115)
* enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように * Update Changelog
This commit is contained in:
		
							parent
							
								
									54d0a46378
								
							
						
					
					
						commit
						1f24a8cb5a
					
				
					 10 changed files with 75 additions and 17 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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', () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -454,6 +454,10 @@ export const defaultStore = markRaw(new Storage('base', {
 | 
			
		|||
		where: 'device',
 | 
			
		||||
		default: true,
 | 
			
		||||
	},
 | 
			
		||||
	confirmWhenRevealingSensitiveMedia: {
 | 
			
		||||
		where: 'device',
 | 
			
		||||
		default: false,
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	sound_masterVolume: {
 | 
			
		||||
		where: 'device',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue