Merge pull request #392 from qwreey75/develop

feat: double click to open NSFW media
This commit is contained in:
NoriDev 2023-11-03 16:03:40 +09:00 committed by GitHub
commit 1dc0f88c3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 73 additions and 7 deletions

View file

@ -1247,6 +1247,7 @@ additionalPermissionsForFlash: "Allow to add permission to Play"
thisFlashRequiresTheFollowingPermissions: "This Play requires the following permissions"
doYouWantToAllowThisPlayToAccessYourAccount: "Do you want to allow this Play to access your account?"
translateProfile: "Translate profile"
nsfwOpenBehavior: "NSFW media open behavior"
_vibrations:
click: "When an element is clicked"
note: "When a new note is posted on the timeline"
@ -2522,3 +2523,6 @@ _imageCompressionMode:
noResizeCompress: "Compression without resize"
resizeCompressLossy: "Resize and lossy compression"
noResizeCompressLossy: "Lossy compression without resize"
_nsfwOpenBehavior:
click: "Click to open"
doubleClick: "Double click to open"

5
locales/index.d.ts vendored
View file

@ -1252,6 +1252,7 @@ export interface Locale {
"thisFlashRequiresTheFollowingPermissions": string;
"doYouWantToAllowThisPlayToAccessYourAccount": string;
"translateProfile": string;
"nsfwOpenBehavior": string;
"_vibrations": {
"click": string;
"note": string;
@ -2712,6 +2713,10 @@ export interface Locale {
};
};
};
"_nsfwOpenBehavior": {
"click": string;
"doubleClick": string;
};
}
declare const locales: {
[lang: string]: Locale;

View file

@ -1249,6 +1249,7 @@ additionalPermissionsForFlash: "Playへの追加許可"
thisFlashRequiresTheFollowingPermissions: "このPlayは以下の権限を要求しています"
doYouWantToAllowThisPlayToAccessYourAccount: "このPlayによるアカウントへのアクセスを許可しますか"
translateProfile: "プロフィールを翻訳する"
nsfwOpenBehavior: "閲覧注意の開き方"
_vibrations:
click: "要素をクリックしたとき"
@ -2607,3 +2608,7 @@ _externalResourceInstaller:
_themeInstallFailed:
title: "テーマのインストールに失敗しました"
description: "テーマのインストール中に問題が発生しました。もう一度お試しください。エラーの詳細はJavascriptコンソールをご覧ください。"
_nsfwOpenBehavior:
click: "タップして開く"
doubleClick: "二回タップして開く"

View file

@ -1223,6 +1223,7 @@ additionalPermissionsForFlash: "Play에 대한 추가 권한"
thisFlashRequiresTheFollowingPermissions: "이 Play는 다음 권한을 요구해요"
doYouWantToAllowThisPlayToAccessYourAccount: "이 Play가 계정에 접근하도록 허용할까요?"
translateProfile: "프로필 번역하기"
nsfwOpenBehavior: "열람주의 여는 방법"
_vibrations:
click: "요소를 클릭했을 때"
note: "타임라인에 새 노트가 올라왔을 때"
@ -2447,3 +2448,6 @@ _externalResourceInstaller:
_themeInstallFailed:
title: "테마 설치에 실패했어요"
description: "테마를 설치하는 동안 문제가 발생했어요. 다시 시도해 주세요. 오류에 대한 자세한 내용은 자바스크립트 콘솔을 참고해 주세요."
_nsfwOpenBehavior:
click: "탭하여 열기"
doubleClick: "더블 탭 하여 열기"

View file

@ -4,10 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitive]" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
<div :data-is-hidden="hide ? 'true' : 'false'" :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitive]" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onClick" @dblclick="onDblclick">
<component
:is="disableImageLink ? 'div' : 'a'"
v-bind="disableImageLink ? {
:is="(disableImageLink || hide) ? 'div' : 'a'"
v-bind="(disableImageLink || hide) ? {
title: image.name,
class: $style.imageContainer,
} : {
@ -64,6 +64,7 @@ import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { iAmModerator } from '@/account.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
const props = withDefaults(defineProps<{
image: Misskey.entities.DriveFile;
@ -90,11 +91,24 @@ const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
: props.image.thumbnailUrl,
);
function onclick() {
function onClick(ev: MouseEvent) {
if (!props.controls) {
return;
}
if (hide) {
if (!hide) return;
if (defaultStore.state.nsfwOpenBehavior === 'doubleClick') {
os.popup(MkRippleEffect, { x: ev.clientX, y: ev.clientY }, {}, 'end');
}
if (defaultStore.state.nsfwOpenBehavior === 'click') {
hide = false;
}
}
function onDblclick() {
if (!props.controls) {
return;
}
if (hide && defaultStore.state.nsfwOpenBehavior === 'doubleClick') {
hide = false;
}
}
@ -150,6 +164,7 @@ onUnmounted(() => {
<style lang="scss" module>
.hidden {
position: relative;
-webkit-tap-highlight-color: transparent;
}
.sensitive {
@ -205,6 +220,7 @@ onUnmounted(() => {
.visible {
position: relative;
-webkit-tap-highlight-color: transparent;
//box-shadow: 0 0 0 1px var(--divider) inset;
background: var(--bg);
background-image: linear-gradient(45deg, var(--c) 16.67%, var(--bg) 16.67%, var(--bg) 50%, var(--c) 50%, var(--c) 66.67%, var(--bg) 66.67%, var(--bg) 100%);

View file

@ -214,6 +214,14 @@ onMounted(() => {
itemData.thumbCropped = true;
});
// prevent to open hidden media
lightbox.addFilter('clickedIndex', (clickedIndex) => {
if ((gallery.value?.children[clickedIndex] as HTMLElement|undefined)?.dataset.isHidden === 'true') {
return -1;
}
return clickedIndex;
});
lightbox.on('uiRegister', () => {
lightbox.pswp.ui.registerElement({
name: 'altText',

View file

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div v-if="hide" :class="[$style.hidden, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]" @click="hide = false">
<div v-if="hide" :class="[$style.hidden, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]" data-is-hidden="true" @click="onClick" @dblclick="defaultStore.state.nsfwOpenBehavior === 'doubleClick' ? hide = false : ''">
<!-- 注意dataSaverMode が有効になっている際にはhide false になるまでサムネイルや動画を読み込まないようにすること -->
<div :class="$style.sensitive">
<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span>{{ i18n.ts.clickToShow }}</span>
</div>
</div>
<div v-else :class="[$style.visible, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]">
<div v-else :class="[$style.visible, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]" data-is-hidden="false">
<video
ref="videoEl"
:class="$style.video"
@ -37,6 +37,8 @@ import * as Misskey from 'cherrypick-js';
import bytes from '@/filters/bytes.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
const props = defineProps<{
video: Misskey.entities.DriveFile;
@ -44,6 +46,16 @@ const props = defineProps<{
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
function onClick(ev: MouseEvent) {
if (!hide.value) return;
if (defaultStore.state.nsfwOpenBehavior === 'doubleClick') {
os.popup(MkRippleEffect, { x: ev.clientX, y: ev.clientY }, {}, 'end');
}
if (defaultStore.state.nsfwOpenBehavior === 'click') {
hide.value = false;
}
}
const videoEl = shallowRef<HTMLVideoElement>();
watch(videoEl, () => {
@ -107,6 +119,7 @@ watch(videoEl, () => {
align-items: center;
background: #111;
color: #fff;
-webkit-tap-highlight-color: transparent;
}
.sensitive {

View file

@ -93,6 +93,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="force">{{ i18n.ts._displayOfSensitiveMedia.force }}</option>
</MkSelect>
<MkSelect v-model="nsfwOpenBehavior">
<template #label>{{ i18n.ts.nsfwOpenBehavior }} <span class="_beta">CherryPick</span></template>
<option value="click">{{ i18n.ts._nsfwOpenBehavior.click }}</option>
<option value="doubleClick">{{ i18n.ts._nsfwOpenBehavior.doubleClick }}</option>
</MkSelect>
<MkRadios v-model="mediaListWithOneImageAppearance">
<template #label>{{ i18n.ts.mediaListWithOneImageAppearance }}</template>
<option value="expand">{{ i18n.ts.default }}</option>
@ -381,6 +387,7 @@ const renoteQuoteButtonSeparation = computed(defaultStore.makeGetterSetter('reno
const showFixedPostFormInReplies = computed(defaultStore.makeGetterSetter('showFixedPostFormInReplies'));
const showingAnimatedImages = computed(defaultStore.makeGetterSetter('showingAnimatedImages'));
const allMediaNoteCollapse = computed(defaultStore.makeGetterSetter('allMediaNoteCollapse'));
const nsfwOpenBehavior = computed(defaultStore.makeGetterSetter('nsfwOpenBehavior'));
watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);

View file

@ -477,6 +477,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: false,
},
nsfwOpenBehavior: {
where: 'device',
default: 'click' as 'click' | 'doubleClick',
},
// - Settings/Timeline
enableHomeTimeline: {