diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index bf3ab12682..86efd48c4e 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -6,9 +6,11 @@ import { createApp, defineAsyncComponent, markRaw } from 'vue'; import { ui } from '@@/js/config.js'; import * as Misskey from 'misskey-js'; +import { v4 as uuid } from 'uuid'; import { common } from './common.js'; import type { Component } from 'vue'; import type { Keymap } from '@/utility/hotkey.js'; +import type { DeckProfile } from '@/deck.js'; import { i18n } from '@/i18n.js'; import { alert, confirm, popup, post, toast } from '@/os.js'; import { useStream } from '@/stream.js'; @@ -143,12 +145,34 @@ export async function mainBoot() { if (themes.length > 0) { prefer.commit('themes', themes); } + const plugins = ColdDeviceStorage.get('plugins'); prefer.commit('plugins', plugins.map(p => ({ ...p, installId: (p as any).id, id: undefined, }))); + + prefer.commit('deck.profile', deckStore.s.profile); + misskeyApi('i/registry/keys', { + scope: ['client', 'deck', 'profiles'], + }).then(async keys => { + const profiles: DeckProfile[] = []; + for (const key of keys) { + const deck = await misskeyApi('i/registry/get', { + scope: ['client', 'deck', 'profiles'], + key: key, + }); + profiles.push({ + id: uuid(), + name: key, + columns: deck.columns, + layout: deck.layout, + }); + } + prefer.commit('deck.profiles', profiles); + }); + prefer.commit('lightTheme', ColdDeviceStorage.get('lightTheme')); prefer.commit('darkTheme', ColdDeviceStorage.get('darkTheme')); prefer.commit('syncDeviceDarkMode', ColdDeviceStorage.get('syncDeviceDarkMode')); @@ -223,9 +247,6 @@ export async function mainBoot() { prefer.commit('sound.on.noteMy', store.s.sound_noteMy as any); prefer.commit('sound.on.notification', store.s.sound_notification as any); prefer.commit('sound.on.reaction', store.s.sound_reaction as any); - store.set('deck.profile', deckStore.s.profile); - store.set('deck.columns', deckStore.s.columns); - store.set('deck.layout', deckStore.s.layout); store.set('menu', []); } diff --git a/packages/frontend/src/deck.ts b/packages/frontend/src/deck.ts index f158db2f85..9df56c52df 100644 --- a/packages/frontend/src/deck.ts +++ b/packages/frontend/src/deck.ts @@ -3,13 +3,23 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { throttle } from 'throttle-debounce'; import { notificationTypes } from 'misskey-js'; +import { ref } from 'vue'; +import { v4 as uuid } from 'uuid'; +import { i18n } from './i18n.js'; import type { BasicTimelineType } from '@/timelines.js'; import type { SoundStore } from '@/preferences/def.js'; -import { misskeyApi } from '@/utility/misskey-api.js'; +import type { MenuItem } from '@/types/menu.js'; import { deepClone } from '@/utility/clone.js'; -import { store } from '@/store.js'; +import { prefer } from '@/preferences.js'; +import * as os from '@/os.js'; + +export type DeckProfile = { + name: string; + id: string; + columns: Column[]; + layout: Column['id'][][]; +}; type ColumnWidget = { name: string; @@ -53,127 +63,132 @@ export type Column = { soundSetting?: SoundStore; }; -export const loadDeck = async () => { - let deck; +const _currentProfile = prefer.s['deck.profiles'].find(p => p.name === prefer.s['deck.profile']); +const __currentProfile = _currentProfile ? deepClone(_currentProfile) : null; +export const columns = ref(__currentProfile ? __currentProfile.columns : []); +export const layout = ref(__currentProfile ? __currentProfile.layout : []); - try { - deck = await misskeyApi('i/registry/get', { - scope: ['client', 'deck', 'profiles'], - key: store.s['deck.profile'], - }); - } catch (err) { - if (typeof err === 'object' && err != null && 'code' in err && err.code === 'NO_SUCH_KEY') { - // 後方互換性のため - if (store.s['deck.profile'] === 'default') { - saveDeck(); - return; - } +if (prefer.s['deck.profile'] == null) { + addProfile('Main'); +} - store.set('deck.columns', []); - store.set('deck.layout', []); - return; - } - throw err; - } +export function forceSaveCurrentDeckProfile() { + const currentProfile = prefer.s['deck.profiles'].find(p => p.name === prefer.s['deck.profile']); + if (currentProfile == null) return; - store.set('deck.columns', deck.columns); - store.set('deck.layout', deck.layout); + const newProfile = deepClone(currentProfile); + newProfile.columns = columns.value; + newProfile.layout = layout.value; + + const newProfiles = prefer.s['deck.profiles'].filter(p => p.name !== prefer.s['deck.profile']); + newProfiles.push(newProfile); + prefer.commit('deck.profiles', newProfiles); +} + +export const saveCurrentDeckProfile = () => { + forceSaveCurrentDeckProfile(); }; -export async function forceSaveDeck() { - await misskeyApi('i/registry/set', { - scope: ['client', 'deck', 'profiles'], - key: store.s['deck.profile'], - value: { - columns: store.r['deck.columns'].value, - layout: store.r['deck.layout'].value, - }, - }); +function switchProfile(profile: DeckProfile) { + prefer.commit('deck.profile', profile.name); + const currentProfile = deepClone(profile); + columns.value = currentProfile.columns; + layout.value = currentProfile.layout; + forceSaveCurrentDeckProfile(); } -// TODO: deckがloadされていない状態でsaveすると意図せず上書きが発生するので対策する -export const saveDeck = throttle(1000, () => { - forceSaveDeck(); -}); +function addProfile(name: string) { + if (name.trim() === '') return; + if (prefer.s['deck.profiles'].find(p => p.name === name)) return; -export async function getProfiles(): Promise<string[]> { - return await misskeyApi('i/registry/keys', { - scope: ['client', 'deck', 'profiles'], - }); + const newProfile: DeckProfile = { + id: uuid(), + name, + columns: [], + layout: [], + }; + prefer.commit('deck.profiles', [...prefer.s['deck.profiles'], newProfile]); + switchProfile(newProfile); } -export async function deleteProfile(key: string): Promise<void> { - return await misskeyApi('i/registry/remove', { - scope: ['client', 'deck', 'profiles'], - key: key, - }); +function createFirstProfile() { + addProfile('Main'); +} + +export function deleteProfile(name: string): void { + const newProfiles = prefer.s['deck.profiles'].filter(p => p.name !== name); + prefer.commit('deck.profiles', newProfiles); + + if (prefer.s['deck.profiles'].length === 0) { + createFirstProfile(); + } else { + switchProfile(prefer.s['deck.profiles'][0]); + } } export function addColumn(column: Column) { if (column.name === undefined) column.name = null; - store.push('deck.columns', column); - store.push('deck.layout', [column.id]); - saveDeck(); + columns.value.push(column); + layout.value.push([column.id]); + saveCurrentDeckProfile(); } export function removeColumn(id: Column['id']) { - store.set('deck.columns', store.s['deck.columns'].filter(c => c.id !== id)); - store.set('deck.layout', store.s['deck.layout'] - .map(ids => ids.filter(_id => _id !== id)) - .filter(ids => ids.length > 0)); - saveDeck(); + columns.value = columns.value.filter(c => c.id !== id); + layout.value = layout.value.map(ids => ids.filter(_id => _id !== id)).filter(ids => ids.length > 0); + saveCurrentDeckProfile(); } export function swapColumn(a: Column['id'], b: Column['id']) { - const aX = store.s['deck.layout'].findIndex(ids => ids.indexOf(a) !== -1); - const aY = store.s['deck.layout'][aX].findIndex(id => id === a); - const bX = store.s['deck.layout'].findIndex(ids => ids.indexOf(b) !== -1); - const bY = store.s['deck.layout'][bX].findIndex(id => id === b); - const layout = deepClone(store.s['deck.layout']); - layout[aX][aY] = b; - layout[bX][bY] = a; - store.set('deck.layout', layout); - saveDeck(); + const aX = layout.value.findIndex(ids => ids.indexOf(a) !== -1); + const aY = layout.value[aX].findIndex(id => id === a); + const bX = layout.value.findIndex(ids => ids.indexOf(b) !== -1); + const bY = layout.value[bX].findIndex(id => id === b); + const newLayout = deepClone(layout.value); + newLayout[aX][aY] = b; + newLayout[bX][bY] = a; + layout.value = newLayout; + saveCurrentDeckProfile(); } export function swapLeftColumn(id: Column['id']) { - const layout = deepClone(store.s['deck.layout']); - store.s['deck.layout'].some((ids, i) => { + const newLayout = deepClone(layout.value); + layout.value.some((ids, i) => { if (ids.includes(id)) { - const left = store.s['deck.layout'][i - 1]; + const left = layout.value[i - 1]; if (left) { - layout[i - 1] = store.s['deck.layout'][i]; - layout[i] = left; - store.set('deck.layout', layout); + newLayout[i - 1] = layout.value[i]; + newLayout[i] = left; + layout.value = newLayout; } return true; } return false; }); - saveDeck(); + saveCurrentDeckProfile(); } export function swapRightColumn(id: Column['id']) { - const layout = deepClone(store.s['deck.layout']); - store.s['deck.layout'].some((ids, i) => { + const newLayout = deepClone(layout.value); + layout.value.some((ids, i) => { if (ids.includes(id)) { - const right = store.s['deck.layout'][i + 1]; + const right = layout.value[i + 1]; if (right) { - layout[i + 1] = store.s['deck.layout'][i]; - layout[i] = right; - store.set('deck.layout', layout); + newLayout[i + 1] = layout.value[i]; + newLayout[i] = right; + layout.value = newLayout; } return true; } return false; }); - saveDeck(); + saveCurrentDeckProfile(); } export function swapUpColumn(id: Column['id']) { - const layout = deepClone(store.s['deck.layout']); - const idsIndex = store.s['deck.layout'].findIndex(ids => ids.includes(id)); - const ids = deepClone(store.s['deck.layout'][idsIndex]); + const newLayout = deepClone(layout.value); + const idsIndex = layout.value.findIndex(ids => ids.includes(id)); + const ids = deepClone(layout.value[idsIndex]); ids.some((x, i) => { if (x === id) { const up = ids[i - 1]; @@ -181,20 +196,20 @@ export function swapUpColumn(id: Column['id']) { ids[i - 1] = id; ids[i] = up; - layout[idsIndex] = ids; - store.set('deck.layout', layout); + newLayout[idsIndex] = ids; + layout.value = newLayout; } return true; } return false; }); - saveDeck(); + saveCurrentDeckProfile(); } export function swapDownColumn(id: Column['id']) { - const layout = deepClone(store.s['deck.layout']); - const idsIndex = store.s['deck.layout'].findIndex(ids => ids.includes(id)); - const ids = deepClone(store.s['deck.layout'][idsIndex]); + const newLayout = deepClone(layout.value); + const idsIndex = layout.value.findIndex(ids => ids.includes(id)); + const ids = deepClone(layout.value[idsIndex]); ids.some((x, i) => { if (x === id) { const down = ids[i + 1]; @@ -202,105 +217,137 @@ export function swapDownColumn(id: Column['id']) { ids[i + 1] = id; ids[i] = down; - layout[idsIndex] = ids; - store.set('deck.layout', layout); + newLayout[idsIndex] = ids; + layout.value = newLayout; } return true; } return false; }); - saveDeck(); + saveCurrentDeckProfile(); } export function stackLeftColumn(id: Column['id']) { - let layout = deepClone(store.s['deck.layout']); - const i = store.s['deck.layout'].findIndex(ids => ids.includes(id)); - layout = layout.map(ids => ids.filter(_id => _id !== id)); - layout[i - 1].push(id); - layout = layout.filter(ids => ids.length > 0); - store.set('deck.layout', layout); - saveDeck(); + let newLayout = deepClone(layout.value); + const i = layout.value.findIndex(ids => ids.includes(id)); + newLayout = newLayout.map(ids => ids.filter(_id => _id !== id)); + newLayout[i - 1].push(id); + newLayout = newLayout.filter(ids => ids.length > 0); + layout.value = newLayout; + saveCurrentDeckProfile(); } export function popRightColumn(id: Column['id']) { - let layout = deepClone(store.s['deck.layout']); - const i = store.s['deck.layout'].findIndex(ids => ids.includes(id)); - const affected = layout[i]; - layout = layout.map(ids => ids.filter(_id => _id !== id)); - layout.splice(i + 1, 0, [id]); - layout = layout.filter(ids => ids.length > 0); - store.set('deck.layout', layout); + let newLayout = deepClone(layout.value); + const i = layout.value.findIndex(ids => ids.includes(id)); + const affected = newLayout[i]; + newLayout = newLayout.map(ids => ids.filter(_id => _id !== id)); + newLayout.splice(i + 1, 0, [id]); + newLayout = newLayout.filter(ids => ids.length > 0); + layout.value = newLayout; - const columns = deepClone(store.s['deck.columns']); - for (const column of columns) { + const newColumns = deepClone(columns.value); + for (const column of newColumns) { if (affected.includes(column.id)) { column.active = true; } } - store.set('deck.columns', columns); + columns.value = newColumns; - saveDeck(); + saveCurrentDeckProfile(); } export function addColumnWidget(id: Column['id'], widget: ColumnWidget) { - const columns = deepClone(store.s['deck.columns']); - const columnIndex = store.s['deck.columns'].findIndex(c => c.id === id); - const column = deepClone(store.s['deck.columns'][columnIndex]); + const newColumns = deepClone(columns.value); + const columnIndex = columns.value.findIndex(c => c.id === id); + const column = deepClone(columns.value[columnIndex]); if (column == null) return; if (column.widgets == null) column.widgets = []; column.widgets.unshift(widget); - columns[columnIndex] = column; - store.set('deck.columns', columns); - saveDeck(); + newColumns[columnIndex] = column; + columns.value = newColumns; + saveCurrentDeckProfile(); } export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) { - const columns = deepClone(store.s['deck.columns']); - const columnIndex = store.s['deck.columns'].findIndex(c => c.id === id); - const column = deepClone(store.s['deck.columns'][columnIndex]); + const newColumns = deepClone(columns.value); + const columnIndex = columns.value.findIndex(c => c.id === id); + const column = deepClone(columns.value[columnIndex]); if (column == null) return; if (column.widgets == null) column.widgets = []; column.widgets = column.widgets.filter(w => w.id !== widget.id); - columns[columnIndex] = column; - store.set('deck.columns', columns); - saveDeck(); + newColumns[columnIndex] = column; + columns.value = newColumns; + saveCurrentDeckProfile(); } export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { - const columns = deepClone(store.s['deck.columns']); - const columnIndex = store.s['deck.columns'].findIndex(c => c.id === id); - const column = deepClone(store.s['deck.columns'][columnIndex]); + const newColumns = deepClone(columns.value); + const columnIndex = columns.value.findIndex(c => c.id === id); + const column = deepClone(columns.value[columnIndex]); if (column == null) return; column.widgets = widgets; - columns[columnIndex] = column; - store.set('deck.columns', columns); - saveDeck(); + newColumns[columnIndex] = column; + columns.value = newColumns; + saveCurrentDeckProfile(); } export function updateColumnWidget(id: Column['id'], widgetId: string, widgetData: any) { - const columns = deepClone(store.s['deck.columns']); - const columnIndex = store.s['deck.columns'].findIndex(c => c.id === id); - const column = deepClone(store.s['deck.columns'][columnIndex]); + const newColumns = deepClone(columns.value); + const columnIndex = columns.value.findIndex(c => c.id === id); + const column = deepClone(columns.value[columnIndex]); if (column == null) return; if (column.widgets == null) column.widgets = []; column.widgets = column.widgets.map(w => w.id === widgetId ? { ...w, data: widgetData, } : w); - columns[columnIndex] = column; - store.set('deck.columns', columns); - saveDeck(); + newColumns[columnIndex] = column; + columns.value = newColumns; + saveCurrentDeckProfile(); } export function updateColumn(id: Column['id'], column: Partial<Column>) { - const columns = deepClone(store.s['deck.columns']); - const columnIndex = store.s['deck.columns'].findIndex(c => c.id === id); - const currentColumn = deepClone(store.s['deck.columns'][columnIndex]); + const newColumns = deepClone(columns.value); + const columnIndex = columns.value.findIndex(c => c.id === id); + const currentColumn = deepClone(columns.value[columnIndex]); if (currentColumn == null) return; for (const [k, v] of Object.entries(column)) { currentColumn[k] = v; } - columns[columnIndex] = currentColumn; - store.set('deck.columns', columns); - saveDeck(); + newColumns[columnIndex] = currentColumn; + columns.value = newColumns; + saveCurrentDeckProfile(); +} + +export function switchProfileMenu(ev: MouseEvent) { + const items: MenuItem[] = prefer.s['deck.profile'] ? [{ + text: prefer.s['deck.profile'], + active: true, + action: () => {}, + }] : []; + + const profiles = prefer.s['deck.profiles']; + + items.push(...(profiles.filter(p => p.name !== prefer.s['deck.profile']).map(p => ({ + text: p.name, + action: () => { + switchProfile(p); + }, + }))), { 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 || name.trim() === '') return; + + addProfile(name); + }, + }); + + os.popupMenu(items, ev.currentTarget ?? ev.target); } 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<string, any>; }[], }, + '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/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<string, string>, // 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/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 </div> <div :class="$style.sideMenu"> <div :class="$style.sideMenuTop"> - <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${store.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="changeProfile"><i class="ti ti-caret-down"></i></button> + <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button> <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button> </div> <div :class="$style.sideMenuMiddle"> @@ -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(); } + </script> <style> diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue index fc208197a0..7fdf75c026 100644 --- a/packages/frontend/src/ui/deck/column.vue +++ b/packages/frontend/src/ui/deck/column.vue @@ -100,7 +100,7 @@ function onOtherDragEnd() { function toggleActive() { if (!props.isStacked) return; updateColumn(props.column.id, { - active: !props.column.active, + active: props.column.active == null ? false : !props.column.active, }); }