@@ -63,6 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue
deleted file mode 100644
index bf8af8cdce..0000000000
--- a/packages/frontend/src/pages/settings/webhook.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
- {{ i18n.ts._webhookSettings.createWebhook }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ webhook.name || webhook.url }}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index 9ceea6b79b..68e0b08f92 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -9,6 +9,7 @@ import type { Theme } from '@/theme.js';
import type { SoundType } from '@/utility/sound.js';
import type { Plugin } from '@/plugin.js';
import type { DeviceKind } from '@/utility/device-kind.js';
+import type { Column, DeckProfile } from '@/deck.js';
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
/** サウンド設定 */
@@ -45,6 +46,14 @@ export const PREF_DEF = {
data: Record
;
}[],
},
+ 'deck.profile': {
+ accountDependent: true,
+ default: null as string | null,
+ },
+ 'deck.profiles': {
+ accountDependent: true,
+ default: [] as DeckProfile[],
+ },
overridedDeviceKind: {
default: null as DeviceKind | null,
diff --git a/packages/frontend/src/preferences/profile.ts b/packages/frontend/src/preferences/profile.ts
index c1320b0dcc..defa2747eb 100644
--- a/packages/frontend/src/preferences/profile.ts
+++ b/packages/frontend/src/preferences/profile.ts
@@ -42,6 +42,8 @@ export type PreferencesProfile = {
syncByAccount: [Account, keyof PREF][],
};
+// TODO: 任意のプロパティをデバイス間で同期できるようにする?
+
export class ProfileManager extends EventEmitter<{
updated: (ctx: {
profile: PreferencesProfile
diff --git a/packages/frontend/src/preferences/store.ts b/packages/frontend/src/preferences/store.ts
index e10afcc308..e061021be3 100644
--- a/packages/frontend/src/preferences/store.ts
+++ b/packages/frontend/src/preferences/store.ts
@@ -45,12 +45,14 @@ export class Store> extends EventEmitter(key: K, value: Data[K]) {
- this.r[key].value = this.s[key] = value;
- this.emit('updated', { key, value });
+ const v = JSON.parse(JSON.stringify(value)); // deep copy 兼 vueのプロキシ解除
+ this.r[key].value = this.s[key] = v;
+ this.emit('updated', { key, value: v });
}
public rewrite(key: K, value: Data[K]) {
- this.r[key].value = this.s[key] = value;
+ const v = JSON.parse(JSON.stringify(value)); // deep copy 兼 vueのプロキシ解除
+ this.r[key].value = this.s[key] = v;
}
/**
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index 7e0efa2e89..93dd081127 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -134,33 +134,29 @@ const routes: RouteDef[] = [{
name: 'plugin',
component: page(() => import('@/pages/settings/plugin.vue')),
}, {
- path: '/import-export',
- name: 'import-export',
- component: page(() => import('@/pages/settings/import-export.vue')),
+ path: '/account-data',
+ name: 'account-data',
+ component: page(() => import('@/pages/settings/account-data.vue')),
}, {
path: '/mute-block',
name: 'mute-block',
component: page(() => import('@/pages/settings/mute-block.vue')),
}, {
- path: '/api',
- name: 'api',
- component: page(() => import('@/pages/settings/api.vue')),
+ path: '/connect',
+ name: 'connect',
+ component: page(() => import('@/pages/settings/connect.vue')),
}, {
path: '/apps',
- name: 'api',
+ name: 'connect',
component: page(() => import('@/pages/settings/apps.vue')),
}, {
path: '/webhook/edit/:webhookId',
- name: 'webhook',
+ name: 'connect',
component: page(() => import('@/pages/settings/webhook.edit.vue')),
}, {
path: '/webhook/new',
- name: 'webhook',
+ name: 'connect',
component: page(() => import('@/pages/settings/webhook.new.vue')),
- }, {
- path: '/webhook',
- name: 'webhook',
- component: page(() => import('@/pages/settings/webhook.vue')),
}, {
path: '/deck',
name: 'deck',
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 8f718ebcdf..738a57d233 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -10,7 +10,6 @@ import darkTheme from '@@/themes/d-green-lime.json5';
import { hemisphere } from '@@/js/intl-const.js';
import type { DeviceKind } from '@/utility/device-kind.js';
import type { Plugin } from '@/plugin.js';
-import type { Column } from '@/deck.js';
import { miLocalStorage } from '@/local-storage.js';
import { Storage } from '@/pizzax.js';
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
@@ -117,18 +116,6 @@ export const store = markRaw(new Storage('base', {
where: 'deviceAccount',
default: {} as Record, // plugin id, token
},
- 'deck.profile': {
- where: 'deviceAccount',
- default: 'default',
- },
- 'deck.columns': {
- where: 'deviceAccount',
- default: [] as Column[],
- },
- 'deck.layout': {
- where: 'deviceAccount',
- default: [] as Column['id'][][],
- },
enablePreferencesAutoCloudBackup: {
where: 'device',
diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts
index ed2f1d3164..970d143b97 100644
--- a/packages/frontend/src/theme.ts
+++ b/packages/frontend/src/theme.ts
@@ -114,7 +114,7 @@ export function applyTheme(theme: Theme, persist = true) {
globalEvents.emit('themeChanging');
}
-function compile(theme: Theme): Record {
+export function compile(theme: Theme): Record {
function getColor(val: string): tinycolor.Instance {
if (val[0] === '@') { // ref (prop)
return getColor(theme.props[val.substring(1)]);
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 08f8ca4f65..337b0dac94 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
@@ -95,7 +95,6 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, defineAsyncComponent, ref, watch, shallowRef } from 'vue';
import { v4 as uuid } from 'uuid';
import XCommon from './_common_/common.vue';
-import type { MenuItem } from '@/types/menu.js';
import XSidebar from '@/ui/_common_/navbar.vue';
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import MkButton from '@/components/MkButton.vue';
@@ -103,7 +102,6 @@ import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
-import { unisonReload } from '@/utility/unison-reload.js';
import { deviceKind } from '@/utility/device-kind.js';
import { prefer } from '@/preferences.js';
import XMainColumn from '@/ui/deck/main-column.vue';
@@ -117,8 +115,7 @@ import XMentionsColumn from '@/ui/deck/mentions-column.vue';
import XDirectColumn from '@/ui/deck/direct-column.vue';
import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
import { mainRouter } from '@/router/main.js';
-import { store } from '@/store.js';
-import { columnTypes, forceSaveDeck, getProfiles, loadDeck, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js';
+import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js';
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
@@ -137,7 +134,7 @@ const columnComponents = {
mainRouter.navHook = (path, flag): boolean => {
if (flag === 'forcePage') return false;
- const noMainColumn = !store.s['deck.columns'].some(x => x.type === 'main');
+ const noMainColumn = !columns.value.some(x => x.type === 'main');
if (prefer.s['deck.navWindow'] || noMainColumn) {
os.pageWindow(path);
return true;
@@ -160,8 +157,6 @@ watch(route, () => {
});
*/
-const columns = store.r['deck.columns'];
-const layout = store.r['deck.layout'];
const menuIndicated = computed(() => {
if ($i == null) return false;
for (const def in navbarItemDef) {
@@ -210,65 +205,20 @@ function onWheel(ev: WheelEvent) {
document.documentElement.style.overflowY = 'hidden';
document.documentElement.style.scrollBehavior = 'auto';
-loadDeck();
-
-function changeProfile(ev: MouseEvent) {
- let items: MenuItem[] = [{
- text: store.s['deck.profile'],
- active: true,
- action: () => {},
- }];
- getProfiles().then(profiles => {
- items.push(...(profiles.filter(k => k !== store.s['deck.profile']).map(k => ({
- text: k,
- action: () => {
- store.set('deck.profile', k);
- unisonReload();
- },
- }))), { type: 'divider' as const }, {
- text: i18n.ts._deck.newProfile,
- icon: 'ti ti-plus',
- action: async () => {
- const { canceled, result: name } = await os.inputText({
- title: i18n.ts._deck.profile,
- minLength: 1,
- });
-
- if (canceled || name == null) return;
-
- os.promiseDialog((async () => {
- await store.set('deck.profile', name);
- await forceSaveDeck();
- })(), () => {
- unisonReload();
- });
- },
- });
- }).then(() => {
- os.popupMenu(items, ev.currentTarget ?? ev.target);
- });
-}
-
async function deleteProfile() {
+ if (prefer.s['deck.profile'] == null) return;
+
const { canceled } = await os.confirm({
type: 'warning',
- text: i18n.tsx.deleteAreYouSure({ x: store.s['deck.profile'] }),
+ text: i18n.tsx.deleteAreYouSure({ x: prefer.s['deck.profile'] }),
});
if (canceled) return;
- os.promiseDialog((async () => {
- if (store.s['deck.profile'] === 'default') {
- await store.set('deck.columns', []);
- await store.set('deck.layout', []);
- await forceSaveDeck();
- } else {
- await deleteProfile_(store.s['deck.profile']);
- }
- await store.set('deck.profile', 'default');
- })(), () => {
- unisonReload();
- });
+ await deleteProfile_(prefer.s['deck.profile']);
+
+ os.success();
}
+