Skip to content

Commit

Permalink
enhance/feat(frontend): データセーバーの改良・強化 (misskey-dev#12526)
Browse files Browse the repository at this point in the history
* enhance(frontend): データセーバーを個別で設定できるように

* Update Changelog

* fix design

* (fix) 設定が当たらない

* fix test(無理やり感)

* (fix) 設定がない状態ですべて有効・向操作が効かない

* fix

* tweak

---------

Co-authored-by: syuilo <[email protected]>
  • Loading branch information
2 people authored and camilla-ett committed Jan 2, 2024
1 parent f9dc028 commit 88f1043
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 26 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

### Client
- Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
- Feat: データセーバーでコードハイライトの読み込みを削減できるように
- Enhance: 絵文字のオートコンプリート機能強化 #12364
- Enhance: ユーザーのRawデータを表示するページが復活
- Enhance: リアクション選択時に音を鳴らせるように
Expand All @@ -31,6 +32,8 @@
- Enhance: Shareページで投稿を完了すると、親ウィンドウ(親フレーム)にpostMessageするように
- Enhance: チャンネル、クリップ、ページ、Play、ギャラリーにURLのコピーボタンを設置 #11305
- Enhance: ノートプレビューに「内容を隠す」が反映されるように
- Enhance: データセーバーの適用範囲を個別で設定できるように
- 従来のデータセーバーの設定はリセットされます
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
- Enhance: 絵文字の詳細ページに記載される情報を追加
Expand Down
20 changes: 20 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,8 @@ export interface Locale {
"signupPendingError": string;
"cwNotationRequired": string;
"doReaction": string;
"code": string;
"reloadRequiredToApplySettings": string;
"_announcement": {
"forExistingUsers": string;
"forExistingUsersDescription": string;
Expand Down Expand Up @@ -2502,6 +2504,24 @@ export interface Locale {
};
};
};
"_dataSaver": {
"_media": {
"title": string;
"description": string;
};
"_avatar": {
"title": string;
"description": string;
};
"_urlPreview": {
"title": string;
"description": string;
};
"_code": {
"title": string;
"description": string;
};
};
}
declare const locales: {
[lang: string]: Locale;
Expand Down
16 changes: 16 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,8 @@ useGroupedNotifications: "通知をグルーピングして表示する"
signupPendingError: "メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。"
cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。"
doReaction: "リアクションする"
code: "コード"
reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"

_announcement:
forExistingUsers: "既存ユーザーのみ"
Expand Down Expand Up @@ -2389,3 +2391,17 @@ _externalResourceInstaller:
_themeInstallFailed:
title: "テーマのインストールに失敗しました"
description: "テーマのインストール中に問題が発生しました。もう一度お試しください。エラーの詳細はJavascriptコンソールをご覧ください。"

_dataSaver:
_media:
title: "メディアの読み込み"
description: "画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。"
_avatar:
title: "アイコン画像"
description: "アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。"
_urlPreview:
title: "URLプレビューのサムネイル"
description: "URLプレビューのサムネイル画像が読み込まれなくなります。"
_code:
title: "コードハイライト"
description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。"
2 changes: 1 addition & 1 deletion packages/frontend/src/components/MkCode.core.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ watch(() => props.lang, (to) => {
padding: 1em;
margin: .5em 0;
overflow: auto;
border-radius: .3em;
border-radius: 8px;

& pre,
& code {
Expand Down
49 changes: 41 additions & 8 deletions packages/frontend/src/components/MkCode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,35 @@ SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<Suspense>
<template #fallback>
<MkLoading v-if="!inline ?? true" />
</template>
<code v-if="inline" :class="$style.codeInlineRoot">{{ code }}</code>
<XCode v-else :code="code" :lang="lang"/>
</Suspense>
<Suspense>
<template #fallback>
<MkLoading v-if="!inline ?? true"/>
</template>
<code v-if="inline" :class="$style.codeInlineRoot">{{ code }}</code>
<XCode v-else-if="show" :code="code" :lang="lang"/>
<button v-else :class="$style.codePlaceholderRoot" @click="show = true">
<div :class="$style.codePlaceholderContainer">
<div><i class="ti ti-code"></i> {{ i18n.ts.code }}</div>
<div>{{ i18n.ts.clickToShow }}</div>
</div>
</button>
</Suspense>
</template>

<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
import { defineAsyncComponent, ref } from 'vue';
import MkLoading from '@/components/global/MkLoading.vue';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';

defineProps<{
code: string;
lang?: string;
inline?: boolean;
}>();

const show = ref(!defaultStore.state.dataSaver.code);

const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'));
</script>

Expand All @@ -36,4 +46,27 @@ const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'))
padding: .1em;
border-radius: .3em;
}

.codePlaceholderRoot {
display: block;
width: 100%;
background: none;
border: none;
outline: none;
font: inherit;
color: inherit;
cursor: pointer;

box-sizing: border-box;
border-radius: 8px;
padding: 24px;
margin-top: 4px;
color: #D4D4D4;
background: #1E1E1E;
}

.codePlaceholderContainer {
text-align: center;
font-size: 0.8em;
}
</style>
8 changes: 4 additions & 4 deletions packages/frontend/src/components/MkMediaImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<ImgWithBlurhash
:hash="image.blurhash"
:src="(defaultStore.state.enableDataSaverMode && hide) ? null : url"
:src="(defaultStore.state.dataSaver.media && hide) ? null : url"
:forceBlurhash="hide"
:cover="hide || cover"
:alt="image.comment || image.name"
Expand All @@ -32,8 +32,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="hide">
<div :class="$style.hiddenText">
<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-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.dataSaver.media && image.size ? bytes(image.size) : i18n.ts.image }}</b>
<span v-if="controls" style="display: block;">{{ i18n.ts.clickToShow }}</span>
</div>
</div>
Expand Down Expand Up @@ -94,7 +94,7 @@ function onclick() {

// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
watch(() => props.image, () => {
hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
}, {
deep: true,
immediate: true,
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend/src/components/MkMediaVideo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="hide" :class="[$style.hidden, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]" @click="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>
<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ defaultStore.state.enableDataSaverMode && video.size ? bytes(video.size) : i18n.ts.video }}</b>
<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-alert-triangle"></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>
<span>{{ i18n.ts.clickToShow }}</span>
</div>
</div>
Expand Down Expand Up @@ -43,7 +43,7 @@ const props = defineProps<{
video: Misskey.entities.DriveFile;
}>();

const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));

const videoEl = shallowRef<HTMLVideoElement>();

Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/MkUrlPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<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">
<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.enableDataSaverMode ? '' : `background-image: url('${thumbnail}')`">
<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.dataSaver.urlPreview ? '' : `background-image: url('${thumbnail}')`">
</div>
<article :class="$style.body">
<header :class="$style.header">
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/global/MkAvatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const bound = $computed(() => props.link
? { to: userPage(props.user), target: props.target }
: {});

const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.enableDataSaverMode)
const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar)
? getStaticImageUrl(props.user.avatarUrl)
: props.user.avatarUrl);

Expand Down
57 changes: 55 additions & 2 deletions packages/frontend/src/pages/settings/general.vue
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch>
<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
<MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch>
<MkSwitch v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch>
</div>
<div>
<MkRadios v-model="emojiStyle">
Expand Down Expand Up @@ -165,6 +164,37 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.numberOfPageCache }}</template>
<template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template>
</MkRange>

<MkFolder>
<template #label>{{ i18n.ts.dataSaver }}</template>

<div class="_gaps_m">
<MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo>

<div class="_buttons">
<MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
<MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
</div>
<div class="_gaps_m">
<MkSwitch v-model="dataSaver.media">
{{ i18n.ts._dataSaver._media.title }}
<template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.avatar">
{{ i18n.ts._dataSaver._avatar.title }}
<template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.urlPreview">
{{ i18n.ts._dataSaver._urlPreview.title }}
<template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.code">
{{ i18n.ts._dataSaver._code.title }}
<template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
</MkSwitch>
</div>
</div>
</MkFolder>
</div>
</FormSection>

Expand Down Expand Up @@ -198,6 +228,7 @@ import MkButton from '@/components/MkButton.vue';
import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import MkLink from '@/components/MkLink.vue';
import MkInfo from '@/components/MkInfo.vue';
import { langs } from '@/config.js';
import { defaultStore } from '@/store.js';
import * as os from '@/os.js';
Expand All @@ -211,6 +242,7 @@ import { claimAchievement } from '@/scripts/achievements.js';
const lang = ref(miLocalStorage.getItem('lang'));
const fontSize = ref(miLocalStorage.getItem('fontSize'));
const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
const dataSaver = ref(defaultStore.state.dataSaver);

async function reloadAsk() {
const { canceled } = await os.confirm({
Expand Down Expand Up @@ -241,7 +273,6 @@ const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('dis
const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds'));
const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia'));
const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode'));
const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
Expand Down Expand Up @@ -374,6 +405,28 @@ function testNotification(): void {
}, 300);
}

function enableAllDataSaver() {
const g = { ...defaultStore.state.dataSaver };

Object.keys(g).forEach((key) => { g[key] = true; });

dataSaver.value = g;
}

function disableAllDataSaver() {
const g = { ...defaultStore.state.dataSaver };

Object.keys(g).forEach((key) => { g[key] = false; });

dataSaver.value = g;
}

watch(dataSaver, (to) => {
defaultStore.set('dataSaver', to);
}, {
deep: true,
});

const headerActions = $computed(() => []);

const headerTabs = $computed(() => []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'advancedMfm',
'loadRawImages',
'imageNewTab',
'enableDataSaverMode',
'dataSaver',
'disableShowingAnimatedImages',
'emojiStyle',
'disableDrawer',
Expand Down
13 changes: 9 additions & 4 deletions packages/frontend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,6 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: false,
},
enableDataSaverMode: {
where: 'device',
default: false,
},
disableShowingAnimatedImages: {
where: 'device',
default: window.matchMedia('(prefers-reduced-motion)').matches,
Expand Down Expand Up @@ -403,6 +399,15 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: true,
},
dataSaver: {
where: 'device',
default: {
media: false,
avatar: false,
urlPreview: false,
code: false,
} as Record<string, boolean>,
},

sound_masterVolume: {
where: 'device',
Expand Down
12 changes: 11 additions & 1 deletion packages/frontend/test/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ vi.stubGlobal('WebSocket', class WebSocket extends EventTarget { static CLOSING
vi.mock('@/store.js', () => {
return {
defaultStore: {
state: {},
state: {

// なんかtestがうまいこと動かないのでここに書く
dataSaver: {
media: false,
avatar: false,
urlPreview: false,
code: false,
},

},
},
};
});
Expand Down

0 comments on commit 88f1043

Please sign in to comment.