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
|
@ -30,6 +30,7 @@
|
|||
(Cherry-picked from https://github.com/taiyme/misskey/pull/238)
|
||||
- Enhance: AiScriptを0.19.0にアップデート
|
||||
- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
|
||||
- Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように
|
||||
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
|
||||
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
|
||||
- Fix: リバーシの対局を正しく共有できないことがある問題を修正
|
||||
|
|
8
locales/index.d.ts
vendored
8
locales/index.d.ts
vendored
|
@ -5008,6 +5008,14 @@ export interface Locale extends ILocale {
|
|||
* もう一度お試しください。
|
||||
*/
|
||||
"tryAgain": string;
|
||||
/**
|
||||
* センシティブなメディアを表示するとき確認する
|
||||
*/
|
||||
"confirmWhenRevealingSensitiveMedia": string;
|
||||
/**
|
||||
* センシティブなメディアです。表示しますか?
|
||||
*/
|
||||
"sensitiveMediaRevealConfirm": string;
|
||||
"_delivery": {
|
||||
/**
|
||||
* 配信状態
|
||||
|
|
|
@ -1248,6 +1248,8 @@ noDescription: "説明文はありません"
|
|||
alwaysConfirmFollow: "フォローの際常に確認する"
|
||||
inquiry: "お問い合わせ"
|
||||
tryAgain: "もう一度お試しください。"
|
||||
confirmWhenRevealingSensitiveMedia: "センシティブなメディアを表示するとき確認する"
|
||||
sensitiveMediaRevealConfirm: "センシティブなメディアです。表示しますか?"
|
||||
|
||||
_delivery:
|
||||
status: "配信状態"
|
||||
|
|
|
@ -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…
Reference in a new issue