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 41bffd3a13.

* 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>
This commit is contained in:
syuilo
2025-03-09 12:34:08 +09:00
committed by GitHub
parent 05cdc095c0
commit d30ddd4c2e
181 changed files with 3437 additions and 2463 deletions

View File

@@ -0,0 +1,222 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ref, watch } from 'vue';
import type { PreferencesProfile } from './profile.js';
import type { MenuItem } from '@/types/menu.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js';
import { prefer, profileManager } from '@/preferences.js';
import * as os from '@/os.js';
import { store } from '@/store.js';
import { $i } from '@/account.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { unisonReload } from '@/scripts/unison-reload.js';
export function getPreferencesProfileMenu(): MenuItem[] {
const autoBackupEnabled = ref(store.state.enablePreferencesAutoCloudBackup);
watch(autoBackupEnabled, () => {
if (autoBackupEnabled.value) {
if (profileManager.profile.name == null || profileManager.profile.name.trim() === '') {
autoBackupEnabled.value = false;
os.alert({
type: 'warning',
title: i18n.ts._preferencesBackup.youNeedToNameYourProfileToEnableAutoBackup,
});
return;
}
store.set('enablePreferencesAutoCloudBackup', true);
} else {
store.set('enablePreferencesAutoCloudBackup', false);
}
});
const menu: MenuItem[] = [{
type: 'label',
text: profileManager.profile.name || `(${i18n.ts.noName})`,
}, {
text: i18n.ts.rename,
icon: 'ti ti-pencil',
action: () => {
renameProfile();
},
}, {
type: 'switch',
icon: 'ti ti-cloud-up',
text: i18n.ts._preferencesBackup.autoBackup,
ref: autoBackupEnabled,
}, {
text: i18n.ts.export,
icon: 'ti ti-download',
action: () => {
exportCurrentProfile();
},
}, {
type: 'divider',
}, {
text: i18n.ts._preferencesBackup.restoreFromBackup,
icon: 'ti ti-cloud-down',
action: () => {
restoreFromCloudBackup();
},
}, {
text: i18n.ts.import,
icon: 'ti ti-upload',
action: () => {
importProfile();
},
}];
if (prefer.s.devMode) {
menu.push({
type: 'divider',
}, {
text: 'Copy profile as text',
icon: 'ti ti-clipboard',
action: () => {
copyToClipboard(JSON.stringify(profileManager.profile, null, '\t'));
},
});
}
return menu;
}
async function renameProfile() {
const { canceled, result: name } = await os.inputText({
title: i18n.ts._preferencesProfile.profileName,
text: i18n.ts._preferencesProfile.profileNameDescription + '\n' + i18n.ts._preferencesProfile.profileNameDescription2,
placeholder: profileManager.profile.name || null,
default: profileManager.profile.name || null,
});
if (canceled || name == null || name.trim() === '') return;
profileManager.renameProfile(name);
}
function exportCurrentProfile() {
const p = profileManager.profile;
const txtBlob = new Blob([JSON.stringify(p)], { type: 'text/plain' });
const dummya = document.createElement('a');
dummya.href = URL.createObjectURL(txtBlob);
dummya.download = `${p.name || p.id}.misskeypreferences`;
dummya.click();
}
function importProfile() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.misskeypreferences';
input.onchange = async () => {
if (input.files == null || input.files.length === 0) return;
const file = input.files[0];
const txt = await file.text();
const profile = JSON.parse(txt) as PreferencesProfile;
miLocalStorage.setItem('preferences', JSON.stringify(profile));
miLocalStorage.setItem('hidePreferencesRestoreSuggestion', 'true');
shouldSuggestRestoreBackup.value = false;
unisonReload();
};
input.click();
}
export async function cloudBackup() {
if ($i == null) return;
if (profileManager.profile.name == null || profileManager.profile.name.trim() === '') {
throw new Error('Profile name is not set');
}
await misskeyApi('i/registry/set', {
scope: ['client', 'preferences', 'backups'],
key: profileManager.profile.name,
value: profileManager.profile,
});
}
export async function restoreFromCloudBackup() {
if ($i == null) return;
// TODO: 更新日時でソートして取得したい
const keys = await misskeyApi('i/registry/keys', {
scope: ['client', 'preferences', 'backups'],
});
console.log(keys);
if (keys.length === 0) {
os.alert({
type: 'warning',
title: i18n.ts._preferencesBackup.noBackupsFoundTitle,
text: i18n.ts._preferencesBackup.noBackupsFoundDescription,
});
return;
}
const select = await os.select({
title: i18n.ts._preferencesBackup.selectBackupToRestore,
items: keys.map(k => ({
text: k,
value: k,
})),
});
if (select.canceled) return;
if (select.result == null) return;
const profile = await misskeyApi('i/registry/get', {
scope: ['client', 'preferences', 'backups'],
key: select.result,
});
console.log(profile);
miLocalStorage.setItem('preferences', JSON.stringify(profile));
miLocalStorage.setItem('hidePreferencesRestoreSuggestion', 'true');
store.set('enablePreferencesAutoCloudBackup', true);
shouldSuggestRestoreBackup.value = false;
unisonReload();
}
export async function enableAutoBackup() {
if (profileManager.profile.name == null || profileManager.profile.name.trim() === '') {
await renameProfile();
}
if (profileManager.profile.name == null || profileManager.profile.name.trim() === '') {
return;
}
store.set('enablePreferencesAutoCloudBackup', true);
}
export const shouldSuggestRestoreBackup = ref(false);
if ($i != null) {
if (new Date($i.createdAt).getTime() < (Date.now() - 1000 * 60 * 30)) { // アカウント作成直後は意味ないので除外
miLocalStorage.setItem('hidePreferencesRestoreSuggestion', 'true');
} else {
if (miLocalStorage.getItem('hidePreferencesRestoreSuggestion') !== 'true') {
misskeyApi('i/registry/keys', {
scope: ['client', 'preferences', 'backups'],
}).then(keys => {
if (keys.length === 0) {
miLocalStorage.setItem('hidePreferencesRestoreSuggestion', 'true');
} else {
shouldSuggestRestoreBackup.value = true;
}
});
}
}
}
export function hideRestoreBackupSuggestion() {
miLocalStorage.setItem('hidePreferencesRestoreSuggestion', 'true');
shouldSuggestRestoreBackup.value = false;
}