enhance(frontend): データセーバーモードで隠れる画像を増やす等 (#11779)
* enhance datasaver mode * サムネイルがないとき変な角丸にならんようにする * Avoid using wildcard selector * Avoid wildcard * Update MkMediaImage.vue --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
parent
bf303238f0
commit
22d966e92d
7 changed files with 75 additions and 42 deletions
|
@ -35,6 +35,7 @@
|
||||||
- Enhance: ノート検索にローカルのみ検索可能なオプションの追加
|
- Enhance: ノート検索にローカルのみ検索可能なオプションの追加
|
||||||
- Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように
|
- Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように
|
||||||
- Enhance: Renote自体を通報できるように
|
- Enhance: Renote自体を通報できるように
|
||||||
|
- Enhance: データセーバーモードの強化
|
||||||
- Enhance: Renoteを管理者権限で削除可能に
|
- Enhance: Renoteを管理者権限で削除可能に
|
||||||
- `$[rainbow ]`記法が、動きのあるMFMが無効になっていても使用できるようになりました
|
- `$[rainbow ]`記法が、動きのあるMFMが無効になっていても使用できるようになりました
|
||||||
- Playの操作を行うAPI TokenをAPIコンソールから発行できるように
|
- Playの操作を行うAPI TokenをAPIコンソールから発行できるように
|
||||||
|
|
|
@ -4,34 +4,41 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="hide ? $style.hidden : $style.visible" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
|
<div :class="hide ? $style.hidden : $style.visible" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click.stop="onclick">
|
||||||
<a
|
<component
|
||||||
:class="$style.imageContainer"
|
:is="disableImageLink ? 'div' : 'a'"
|
||||||
:href="image.url"
|
v-bind="disableImageLink ? {
|
||||||
:title="image.name"
|
title: image.name,
|
||||||
|
class: $style.imageContainer,
|
||||||
|
} : {
|
||||||
|
title: image.name,
|
||||||
|
class: $style.imageContainer,
|
||||||
|
href: image.url,
|
||||||
|
style: 'cursor: zoom-in;'
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<ImgWithBlurhash
|
<ImgWithBlurhash
|
||||||
:hash="image.blurhash"
|
:hash="image.blurhash"
|
||||||
:src="(defaultStore.state.enableDataSaverMode && hide) ? null : url"
|
:src="(defaultStore.state.enableDataSaverMode && hide) ? null : url"
|
||||||
:forceBlurhash="hide"
|
:forceBlurhash="hide"
|
||||||
:cover="hide"
|
:cover="hide || cover"
|
||||||
:alt="image.comment || image.name"
|
:alt="image.comment || image.name"
|
||||||
:title="image.comment || image.name"
|
:title="image.comment || image.name"
|
||||||
:width="image.properties.width"
|
:width="image.properties.width"
|
||||||
:height="image.properties.height"
|
:height="image.properties.height"
|
||||||
:style="hide ? 'filter: brightness(0.5);' : null"
|
:style="hide ? 'filter: brightness(0.5);' : null"
|
||||||
/>
|
/>
|
||||||
</a>
|
</component>
|
||||||
<template v-if="hide">
|
<template v-if="hide">
|
||||||
<div :class="$style.hiddenText">
|
<div :class="$style.hiddenText">
|
||||||
<div :class="$style.hiddenTextWrapper">
|
<div :class="$style.hiddenTextWrapper">
|
||||||
<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
|
<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
|
||||||
<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
|
<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
|
||||||
<span style="display: block;">{{ i18n.ts.clickToShow }}</span>
|
<span v-if="controls" style="display: block;">{{ i18n.ts.clickToShow }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else-if="controls">
|
||||||
<div :class="$style.indicators">
|
<div :class="$style.indicators">
|
||||||
<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
|
<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
|
||||||
<div v-if="image.comment" :class="$style.indicator">ALT</div>
|
<div v-if="image.comment" :class="$style.indicator">ALT</div>
|
||||||
|
@ -54,10 +61,17 @@ import { i18n } from '@/i18n';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { iAmModerator } from '@/account';
|
import { iAmModerator } from '@/account';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
image: Misskey.entities.DriveFile;
|
image: Misskey.entities.DriveFile;
|
||||||
raw?: boolean;
|
raw?: boolean;
|
||||||
}>();
|
cover?: boolean;
|
||||||
|
disableImageLink?: boolean;
|
||||||
|
controls?: boolean;
|
||||||
|
}>(), {
|
||||||
|
cover: false,
|
||||||
|
disableImageLink: false,
|
||||||
|
controls: true,
|
||||||
|
});
|
||||||
|
|
||||||
let hide = $ref(true);
|
let hide = $ref(true);
|
||||||
let darkMode: boolean = $ref(defaultStore.state.darkMode);
|
let darkMode: boolean = $ref(defaultStore.state.darkMode);
|
||||||
|
@ -70,6 +84,9 @@ const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
|
||||||
);
|
);
|
||||||
|
|
||||||
function onclick() {
|
function onclick() {
|
||||||
|
if (!props.controls) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (hide) {
|
if (hide) {
|
||||||
hide = false;
|
hide = false;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +184,6 @@ function showMenu(ev: MouseEvent) {
|
||||||
|
|
||||||
.imageContainer {
|
.imageContainer {
|
||||||
display: block;
|
display: block;
|
||||||
cursor: zoom-in;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -5,7 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
|
<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
|
||||||
<div v-if="page.eyeCatchingImage" class="thumbnail" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
|
<div v-if="page.eyeCatchingImage" class="thumbnail">
|
||||||
|
<MediaImage
|
||||||
|
:image="page.eyeCatchingImage"
|
||||||
|
:disableImageLink="true"
|
||||||
|
:controls="false"
|
||||||
|
:cover="true"
|
||||||
|
:class="$style.eyeCatchingImageRoot"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<header>
|
||||||
<h1 :title="page.title">{{ page.title }}</h1>
|
<h1 :title="page.title">{{ page.title }}</h1>
|
||||||
|
@ -23,12 +31,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { } from 'vue';
|
import { } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { userName } from '@/filters/user';
|
import { userName } from '@/filters/user';
|
||||||
|
import MediaImage from '@/components/MkMediaImage.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
page: Misskey.entities.Page;
|
page: Misskey.entities.Page;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style module>
|
||||||
|
.eyeCatchingImageRoot {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: var(--radius) var(--radius) 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.vhpxefrj {
|
.vhpxefrj {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -39,32 +57,15 @@ const props = defineProps<{
|
||||||
}
|
}
|
||||||
|
|
||||||
> .thumbnail {
|
> .thumbnail {
|
||||||
width: 100%;
|
|
||||||
height: 200px;
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> button {
|
|
||||||
font-size: 3.5em;
|
|
||||||
opacity: 0.7;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
font-size: 4em;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& + article {
|
& + article {
|
||||||
left: 100px;
|
border-radius: 0 0 var(--radius) var(--radius);
|
||||||
width: calc(100% - 100px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> article {
|
> article {
|
||||||
|
background-color: var(--panel);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
|
||||||
> header {
|
> header {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
|
@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="self ? url.substring(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
|
<component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="self ? url.substring(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
|
||||||
<div v-if="thumbnail" :class="$style.thumbnail" :style="`background-image: url('${thumbnail}')`">
|
<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.enableDataSaverMode ? '' : `background-image: url('${thumbnail}')`">
|
||||||
</div>
|
</div>
|
||||||
<article :class="$style.body">
|
<article :class="$style.body">
|
||||||
<header :class="$style.header">
|
<header :class="$style.header">
|
||||||
|
@ -260,6 +260,7 @@ onUnmounted(() => {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-color: var(--bg);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -5,20 +5,24 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<ImgWithBlurhash v-if="image" style="max-width: 100%;" :hash="image.blurhash" :src="image.url" :alt="image.comment" :title="image.comment" :width="image.properties.width" :height="image.properties.height" :cover="false"/>
|
<MediaImage
|
||||||
|
v-if="image"
|
||||||
|
:image="image"
|
||||||
|
:disableImageLink="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { ImageBlock } from './block.type';
|
import { ImageBlock } from './block.type';
|
||||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
import MediaImage from '@/components/MkMediaImage.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
block: ImageBlock,
|
block: ImageBlock,
|
||||||
page: Misskey.entities.Page,
|
page: Misskey.entities.Page,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const image = props.page.attachedFiles.find(x => x.id === props.block.fileId);
|
const image = ref<Misskey.entities.DriveFile>(props.page.attachedFiles.find(x => x.id === props.block.fileId));
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }">
|
<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps_s">
|
||||||
<XBlock v-for="child in page.content" :key="child.id" :page="page" :block="child" :h="2"/>
|
<XBlock v-for="child in page.content" :key="child.id" :page="page" :block="child" :h="2"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -16,7 +16,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
<img v-if="page.eyeCatchingImageId" :src="page.eyeCatchingImage.url"/>
|
<MkMediaImage
|
||||||
|
v-if="page.eyeCatchingImageId"
|
||||||
|
:image="page.eyeCatchingImage"
|
||||||
|
:cover="true"
|
||||||
|
:disableImageLink="true"
|
||||||
|
class="thumbnail"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<XPage :page="page"/>
|
<XPage :page="page"/>
|
||||||
|
@ -74,6 +80,7 @@ import XPage from '@/components/page/page.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { url } from '@/config';
|
import { url } from '@/config';
|
||||||
|
import MkMediaImage from '@/components/MkMediaImage.vue';
|
||||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||||
import MkContainer from '@/components/MkContainer.vue';
|
import MkContainer from '@/components/MkContainer.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
@ -204,11 +211,14 @@ definePageMetadata(computed(() => page ? {
|
||||||
}
|
}
|
||||||
|
|
||||||
> .banner {
|
> .banner {
|
||||||
> img {
|
> .thumbnail {
|
||||||
// TODO: 良い感じのアスペクト比で表示
|
// TODO: 良い感じのアスペクト比で表示
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 150px;
|
height: auto;
|
||||||
|
aspect-ratio: 3/1;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
overflow: hidden;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue