Skip to content

Commit d30ddd4

Browse files
syuilotai-chakakkokari-gtyih
authored
Refine preferences (#15597)
* wip * wip * wip * test * wip rollup pluginでsearchIndexの情報生成 * wip * SPDX * wip: markerIdを自動付与 * rollupでビルド時・devモード時に毎回uuidを生成するように * 開発サーバーでだけ必要な挙動は開発サーバーのみで * 条件が逆 * wip: childrenの生成 * update comment * update comment * rename auto generated file * hashをパスと行数から決定 * Update privacy.vue * Update privacy.vue * wip * Update general.vue * Update general.vue * wip * wip * Update SearchMarker.vue * wip * Update profile.vue * Update mute-block.vue * Update mute-block.vue * Update general.vue * Update general.vue * childrenがduplicate key errorを吐く問題をいったん解決 * マーカーの形を成形 * loggerを置きかえ * とりあえず省略記法に対応 * Refactor and Format codes * wip * Update settings-search-index.ts * wip * wip * とりあえず不確定要因の仮置きidを削除 * hashの生成を正規化(絶対パスになっていたのを緩和) * pathの入力を省略可能に * adminでもパス生成できるように * Update settings-search-index.ts * Update privacy.vue * wip * build searchIndex * wip * build * Update general.vue * build * Update sounds.vue * build * build * Update sounds.vue * 🎨 * 🎨 * Update privacy.vue * Update privacy.vue * Update security.vue * create-search-indexを多少改善 * build * Update 2fa.vue * wip * 必ずtransformCodeCacheを利用するように, キャッシュの明確な受け渡しを定義 * キャッシュはdevServerでなくても更新 * Revert "wip" This reverts commit 41bffd3. * inlining * wip * Update theme.vue * 🎨 * wip normalize * Update theme.vue * キャッシュのパス変換 * build * wip * wip * Update SearchMarker.vue * i18n.ts['key'] の形式が取り出せない問題のFix * build * 仮でpath入れ * 必ず絶対パスが使われるように * wip * 🎨 * storybookビルド時はcreateSearchIndexをしない * inliningの構造化 * format code * Update index.vue * wip * wip * 🎨 * wip * wip * wip * wip * wip * wip * wip * wip * clean up * wip * wip * wip * Update rollup-plugin-unwind-css-module-class-name.test.ts * Update navbar.vue * clean up * wip * wip * wip * wip * wip * Update preferences-backups.vue * Update common.ts * Update preferences.ts * wip * wip * wip * wip * Update MkPreferenceContainer.vue * Update MkPreferenceContainer.vue * Update MkPreferenceContainer.vue * enhance: 検索で上下矢印を使用することで検索結果を移動できるように * Update main-boot.ts * refactor * wip * Update sounds.vue * fix(frontend): PageWindowでSearchMarkerが動作するように * enhance(frontend): SearchMarkerの点滅を一定時間で止める * wip * lint fix * fix: 子要素監視が抜けていたのを修正 * アニメーションの回数はCSSで制御するように * refactor * enhance(frontend): 検索インデックス作成時のログを削減 * revert * fix * fix * Update preferences.ts * Update preferences.ts * wip * Update preferences.ts * wip * 🎨 * wip * Update MkPreferenceContainer.vue * wip * Update preferences.ts * wip * Update preferences.ts * Update preferences.ts * wip * wip * Update preferences.ts * wip * wip * Update preferences.ts * Update CHANGELOG.md * Update preferences.ts * Update deck-store.ts * deckStoreをdefaultStoreに統合 * wip * defaultStore -> store * Update profile.ts * wip * refactor * wip: plugin * plugin * plugin * plugin * Update plugin.ts * wip * Update plugin.vue * Update preferences.ts * Update main-boot.ts * wip * fix test * Update plugin.vue * Update plugin.vue * Update utility.ts * wip * wip * Update utility.ts * wip * wip * clean up * Update utility.ts --------- Co-authored-by: tai-cha <dev@taichan.site> Co-authored-by: taichan <40626578+tai-cha@users.noreply.github.com> Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
1 parent 05cdc09 commit d30ddd4

File tree

181 files changed

+3441
-2467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

181 files changed

+3441
-2467
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
-
55

66
### Client
7-
-
7+
- Feat: 設定の管理が強化されました
8+
- 自動でバックアップされるように
89

910
### Server
1011
-

locales/index.d.ts

+80
Original file line numberDiff line numberDiff line change
@@ -5278,6 +5278,86 @@ export interface Locale extends ILocale {
52785278
* アクセシビリティ
52795279
*/
52805280
"accessibility": string;
5281+
/**
5282+
* 設定のプロファイル
5283+
*/
5284+
"preferencesProfile": string;
5285+
/**
5286+
* 設定IDをコピー
5287+
*/
5288+
"copyPreferenceId": string;
5289+
/**
5290+
* 初期値に戻す
5291+
*/
5292+
"resetToDefaultValue": string;
5293+
/**
5294+
* アカウントで上書き
5295+
*/
5296+
"overrideByAccount": string;
5297+
/**
5298+
* 無題
5299+
*/
5300+
"untitled": string;
5301+
/**
5302+
* 名前はありません
5303+
*/
5304+
"noName": string;
5305+
/**
5306+
* スキップ
5307+
*/
5308+
"skip": string;
5309+
/**
5310+
* 復元
5311+
*/
5312+
"restore": string;
5313+
"_preferencesProfile": {
5314+
/**
5315+
* プロファイル名
5316+
*/
5317+
"profileName": string;
5318+
/**
5319+
* このデバイスを識別する名前を設定してください。
5320+
*/
5321+
"profileNameDescription": string;
5322+
/**
5323+
* 例: 「メインPC」、「スマホ」など
5324+
*/
5325+
"profileNameDescription2": string;
5326+
};
5327+
"_preferencesBackup": {
5328+
/**
5329+
* 自動バックアップ
5330+
*/
5331+
"autoBackup": string;
5332+
/**
5333+
* バックアップから復元
5334+
*/
5335+
"restoreFromBackup": string;
5336+
/**
5337+
* バックアップが見つかりませんでした
5338+
*/
5339+
"noBackupsFoundTitle": string;
5340+
/**
5341+
* 自動で作成されたバックアップは見つかりませんでしたが、バックアップファイルを手動で保存している場合、それをインポートして復元することはできます。
5342+
*/
5343+
"noBackupsFoundDescription": string;
5344+
/**
5345+
* 復元するバックアップを選択してください
5346+
*/
5347+
"selectBackupToRestore": string;
5348+
/**
5349+
* 自動バックアップを有効にするにはプロファイル名の設定が必要です。
5350+
*/
5351+
"youNeedToNameYourProfileToEnableAutoBackup": string;
5352+
/**
5353+
* このデバイスで設定の自動バックアップは有効になっていません。
5354+
*/
5355+
"autoPreferencesBackupIsNotEnabledForThisDevice": string;
5356+
/**
5357+
* 設定のバックアップが見つかりました
5358+
*/
5359+
"backupFound": string;
5360+
};
52815361
"_accountSettings": {
52825362
/**
52835363
* コンテンツの表示にログインを必須にする

locales/ja-JP.yml

+23
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,29 @@ markAsSensitiveConfirm: "このメディアをセンシティブとして設定
13151315
unmarkAsSensitiveConfirm: "このメディアのセンシティブ指定を解除しますか?"
13161316
preferences: "環境設定"
13171317
accessibility: "アクセシビリティ"
1318+
preferencesProfile: "設定のプロファイル"
1319+
copyPreferenceId: "設定IDをコピー"
1320+
resetToDefaultValue: "初期値に戻す"
1321+
overrideByAccount: "アカウントで上書き"
1322+
untitled: "無題"
1323+
noName: "名前はありません"
1324+
skip: "スキップ"
1325+
restore: "復元"
1326+
1327+
_preferencesProfile:
1328+
profileName: "プロファイル名"
1329+
profileNameDescription: "このデバイスを識別する名前を設定してください。"
1330+
profileNameDescription2: "例: 「メインPC」、「スマホ」など"
1331+
1332+
_preferencesBackup:
1333+
autoBackup: "自動バックアップ"
1334+
restoreFromBackup: "バックアップから復元"
1335+
noBackupsFoundTitle: "バックアップが見つかりませんでした"
1336+
noBackupsFoundDescription: "自動で作成されたバックアップは見つかりませんでしたが、バックアップファイルを手動で保存している場合、それをインポートして復元することはできます。"
1337+
selectBackupToRestore: "復元するバックアップを選択してください"
1338+
youNeedToNameYourProfileToEnableAutoBackup: "自動バックアップを有効にするにはプロファイル名の設定が必要です。"
1339+
autoPreferencesBackupIsNotEnabledForThisDevice: "このデバイスで設定の自動バックアップは有効になっていません。"
1340+
backupFound: "設定のバックアップが見つかりました"
13181341

13191342
_accountSettings:
13201343
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"

packages/frontend/.storybook/preview.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ queueMicrotask(() => {
6868
import('../src/directives'),
6969
import('../src/widgets'),
7070
import('../src/scripts/theme'),
71-
import('../src/store'),
71+
import('../src/preferences'),
7272
import('../src/os'),
73-
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { defaultStore }, os]) => {
73+
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => {
7474
setup((app) => {
7575
moduleInitialized = true;
7676
if (app[appInitialized]) {
@@ -83,7 +83,7 @@ queueMicrotask(() => {
8383
widgets(app);
8484
misskeyOS = os;
8585
if (isChromatic()) {
86-
defaultStore.set('animation', false);
86+
prefer.set('animation', false);
8787
}
8888
});
8989
});
@@ -104,9 +104,9 @@ const preview = {
104104
}
105105
}).catch(() => {})
106106
: Promise.resolve();
107-
const resetDefaultStorePromise = import('../src/store').then(({ defaultStore }) => {
107+
const resetDefaultStorePromise = import('../src/store').then(({ store }) => {
108108
// @ts-expect-error
109-
defaultStore.init();
109+
store.init();
110110
}).catch(() => {});
111111
Promise.all([resetIndexedDBPromise, resetDefaultStorePromise]).then(() => {
112112
initLocalStorage();

packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe(normalizeClass.name, () => {
5858

5959
it('Composition API (standard)', () => {
6060
const ast = parse(`
61-
import { c as api, d as defaultStore, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js';
61+
import { c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js';
6262
import { M as MkContainer } from './MkContainer-!~{03M}~.js';
6363
import { b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode } from './vue-!~{002}~.js';
6464
import './photoswipe-!~{003}~.js';
@@ -74,7 +74,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
7474
let fetching = ref(true);
7575
let images = ref([]);
7676
function thumbnail(image) {
77-
return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
77+
return store.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
7878
}
7979
onMounted(() => {
8080
const image = [
@@ -173,7 +173,7 @@ export { index_photos as default };
173173
`.slice(1), { ecmaVersion: 'latest', sourceType: 'module' });
174174
unwindCssModuleClassName(ast);
175175
expect(generate(ast)).toBe(`
176-
import {c as api, d as defaultStore, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc} from './app-!~{001}~.js';
176+
import {c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc} from './app-!~{001}~.js';
177177
import {M as MkContainer} from './MkContainer-!~{03M}~.js';
178178
import {b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode} from './vue-!~{002}~.js';
179179
import './photoswipe-!~{003}~.js';
@@ -190,7 +190,7 @@ const index_photos = defineComponent({
190190
let fetching = ref(true);
191191
let images = ref([]);
192192
function thumbnail(image) {
193-
return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
193+
return store.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
194194
}
195195
onMounted(() => {
196196
const image = ["image/jpeg", "image/webp", "image/avif", "image/png", "image/gif", "image/apng", "image/vnd.mozilla.apng"];
@@ -268,7 +268,7 @@ export {index_photos as default};
268268
it('Composition API (with `useCssModule()`)', () => {
269269
const ast = parse(`
270270
import { a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup } from './!~{002}~.js';
271-
import { d as defaultStore, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js';
271+
import { d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js';
272272
273273
function isDebuggerEnabled(id) {
274274
try {
@@ -393,7 +393,7 @@ const _sfc_main = defineComponent({
393393
el.style.left = "";
394394
}
395395
return () => h(
396-
defaultStore.state.animation ? TransitionGroup : "div",
396+
prefer.s.animation ? TransitionGroup : "div",
397397
{
398398
class: {
399399
[$style["date-separated-list"]]: true,
@@ -402,7 +402,7 @@ const _sfc_main = defineComponent({
402402
[$style["direction-down"]]: props.direction === "down",
403403
[$style["direction-up"]]: props.direction === "up"
404404
},
405-
...defaultStore.state.animation ? {
405+
...prefer.s.animation ? {
406406
name: "list",
407407
tag: "div",
408408
onBeforeLeave,
@@ -441,7 +441,7 @@ export { MkDateSeparatedList as M };
441441
unwindCssModuleClassName(ast);
442442
expect(generate(ast)).toBe(`
443443
import {a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup} from './!~{002}~.js';
444-
import {d as defaultStore, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc} from './app-!~{001}~.js';
444+
import {d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc} from './app-!~{001}~.js';
445445
function isDebuggerEnabled(id) {
446446
try {
447447
return localStorage.getItem(\`DEBUG_\${id}\`) !== null;
@@ -555,15 +555,15 @@ const _sfc_main = defineComponent({
555555
el.style.top = "";
556556
el.style.left = "";
557557
}
558-
return () => h(defaultStore.state.animation ? TransitionGroup : "div", {
558+
return () => h(prefer.s.animation ? TransitionGroup : "div", {
559559
class: {
560560
[$style["date-separated-list"]]: true,
561561
[$style["date-separated-list-nogap"]]: props.noGap,
562562
[$style["reversed"]]: props.reversed,
563563
[$style["direction-down"]]: props.direction === "down",
564564
[$style["direction-up"]]: props.direction === "up"
565565
},
566-
...defaultStore.state.animation ? {
566+
...prefer.s.animation ? {
567567
name: "list",
568568
tag: "div",
569569
onBeforeLeave,

packages/frontend/src/boot/common.ts

+35-25
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import { computed, watch, version as vueVersion } from 'vue';
77
import { compareVersions } from 'compare-versions';
88
import { version, lang, updateLocale, locale } from '@@/js/config.js';
9+
import defaultLightTheme from '@@/themes/l-light.json5';
10+
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
911
import type { App } from 'vue';
1012
import widgets from '@/widgets/index.js';
1113
import directives from '@/directives/index.js';
@@ -14,7 +16,7 @@ import { applyTheme } from '@/scripts/theme.js';
1416
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
1517
import { updateI18n, i18n } from '@/i18n.js';
1618
import { $i, refreshAccount, login } from '@/account.js';
17-
import { defaultStore, ColdDeviceStorage } from '@/store.js';
19+
import { store } from '@/store.js';
1820
import { fetchInstance, instance } from '@/instance.js';
1921
import { deviceKind, updateDeviceKind } from '@/scripts/device-kind.js';
2022
import { reloadChannel } from '@/scripts/unison-reload.js';
@@ -26,6 +28,7 @@ import { miLocalStorage } from '@/local-storage.js';
2628
import { fetchCustomEmojis } from '@/custom-emojis.js';
2729
import { setupRouter } from '@/router/main.js';
2830
import { createMainRouter } from '@/router/definition.js';
31+
import { prefer } from '@/preferences.js';
2932

3033
export async function common(createVue: () => App<Element>) {
3134
console.info(`Misskey v${version}`);
@@ -38,7 +41,7 @@ export async function common(createVue: () => App<Element>) {
3841
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3942
(window as any).$i = $i;
4043
// eslint-disable-next-line @typescript-eslint/no-explicit-any
41-
(window as any).$store = defaultStore;
44+
(window as any).$store = store;
4245

4346
window.addEventListener('error', event => {
4447
console.error(event);
@@ -123,7 +126,7 @@ export async function common(createVue: () => App<Element>) {
123126
html.setAttribute('lang', lang);
124127
//#endregion
125128

126-
await defaultStore.ready;
129+
await store.ready;
127130
await deckStore.ready;
128131

129132
const fetchInstanceMetaPromise = fetchInstance();
@@ -151,56 +154,63 @@ export async function common(createVue: () => App<Element>) {
151154
//#endregion
152155

153156
// NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため)
154-
watch(defaultStore.reactiveState.darkMode, (darkMode) => {
155-
applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme'));
157+
watch(store.reactiveState.darkMode, (darkMode) => {
158+
applyTheme(darkMode
159+
? (prefer.s.darkTheme ?? defaultDarkTheme)
160+
: (prefer.s.lightTheme ?? defaultLightTheme),
161+
);
156162
}, { immediate: miLocalStorage.getItem('theme') == null });
157163

158-
document.documentElement.dataset.colorScheme = defaultStore.state.darkMode ? 'dark' : 'light';
164+
document.documentElement.dataset.colorScheme = store.state.darkMode ? 'dark' : 'light';
159165

160-
const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme'));
161-
const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme'));
166+
const darkTheme = prefer.model('darkTheme');
167+
const lightTheme = prefer.model('lightTheme');
162168

163169
watch(darkTheme, (theme) => {
164-
if (defaultStore.state.darkMode) {
165-
applyTheme(theme);
170+
if (store.state.darkMode) {
171+
applyTheme(theme ?? defaultDarkTheme);
166172
}
167173
});
168174

169175
watch(lightTheme, (theme) => {
170-
if (!defaultStore.state.darkMode) {
171-
applyTheme(theme);
176+
if (!store.state.darkMode) {
177+
applyTheme(theme ?? defaultLightTheme);
172178
}
173179
});
174180

175181
//#region Sync dark mode
176-
if (ColdDeviceStorage.get('syncDeviceDarkMode')) {
177-
defaultStore.set('darkMode', isDeviceDarkmode());
182+
if (prefer.s.syncDeviceDarkMode) {
183+
store.set('darkMode', isDeviceDarkmode());
178184
}
179185

180186
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (mql) => {
181-
if (ColdDeviceStorage.get('syncDeviceDarkMode')) {
182-
defaultStore.set('darkMode', mql.matches);
187+
if (prefer.s.syncDeviceDarkMode) {
188+
store.set('darkMode', mql.matches);
183189
}
184190
});
185191
//#endregion
186192

193+
if (prefer.s.darkTheme && store.state.darkMode) {
194+
if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme);
195+
} else if (prefer.s.lightTheme && !store.state.darkMode) {
196+
if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme);
197+
}
198+
187199
fetchInstanceMetaPromise.then(() => {
188-
if (defaultStore.state.themeInitial) {
189-
if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(instance.defaultLightTheme));
190-
if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(instance.defaultDarkTheme));
191-
defaultStore.set('themeInitial', false);
192-
}
200+
// TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア
201+
if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.set('lightTheme', JSON.parse(instance.defaultLightTheme));
202+
if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.set('darkTheme', JSON.parse(instance.defaultDarkTheme));
193203
});
194204

195-
watch(defaultStore.reactiveState.overridedDeviceKind, (kind) => {
205+
watch(store.reactiveState.overridedDeviceKind, (kind) => {
196206
updateDeviceKind(kind);
197207
}, { immediate: true });
198208

199-
watch(defaultStore.reactiveState.useBlurEffectForModal, v => {
209+
watch(prefer.r.useBlurEffectForModal, v => {
200210
document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none');
201211
}, { immediate: true });
202212

203-
watch(defaultStore.reactiveState.useBlurEffect, v => {
213+
watch(prefer.r.useBlurEffect, v => {
204214
if (v) {
205215
document.documentElement.style.removeProperty('--MI-blur');
206216
} else {
@@ -214,7 +224,7 @@ export async function common(createVue: () => App<Element>) {
214224
navigator.wakeLock.request('screen');
215225
}
216226
});
217-
if (defaultStore.state.keepScreenOn && 'wakeLock' in navigator) {
227+
if (prefer.s.keepScreenOn && 'wakeLock' in navigator) {
218228
navigator.wakeLock.request('screen')
219229
.then(onVisibilityChange)
220230
.catch(() => {

0 commit comments

Comments
 (0)