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,73 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<span :class="$style.icon">
<i class="ti ti-info-circle"></i>
</span>
<span :class="$style.title">{{ i18n.ts._preferencesBackup.backupFound }}</span>
<span :class="$style.body"><button class="_textButton" @click="restore">{{ i18n.ts.restore }}</button> | <button class="_textButton" @click="skip">{{ i18n.ts.skip }}</button></span>
</div>
</template>
<script lang="ts" setup>
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { hideRestoreBackupSuggestion, restoreFromCloudBackup } from '@/preferences/utility.js';
function restore() {
restoreFromCloudBackup();
}
function skip() {
hideRestoreBackupSuggestion();
}
</script>
<style lang="scss" module>
.root {
--height: 24px;
font-size: 0.85em;
display: flex;
vertical-align: bottom;
width: 100%;
line-height: var(--height);
height: var(--height);
overflow: clip;
contain: strict;
background: var(--MI_THEME-panel);
@container (max-width: 1000px) {
display: block;
text-align: center;
> .body {
display: none;
}
}
}
.icon {
margin-left: 10px;
}
.title {
padding: 0 10px;
font-weight: bold;
&:empty {
display: none;
}
}
.body {
min-width: 0;
flex: 1;
overflow: clip;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>

View File

@@ -17,18 +17,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<TransitionGroup
tag="div"
:class="[$style.notifications, {
[$style.notificationsPosition_leftTop]: defaultStore.state.notificationPosition === 'leftTop',
[$style.notificationsPosition_leftBottom]: defaultStore.state.notificationPosition === 'leftBottom',
[$style.notificationsPosition_rightTop]: defaultStore.state.notificationPosition === 'rightTop',
[$style.notificationsPosition_rightBottom]: defaultStore.state.notificationPosition === 'rightBottom',
[$style.notificationsStackAxis_vertical]: defaultStore.state.notificationStackAxis === 'vertical',
[$style.notificationsStackAxis_horizontal]: defaultStore.state.notificationStackAxis === 'horizontal',
[$style.notificationsPosition_leftTop]: prefer.s.notificationPosition === 'leftTop',
[$style.notificationsPosition_leftBottom]: prefer.s.notificationPosition === 'leftBottom',
[$style.notificationsPosition_rightTop]: prefer.s.notificationPosition === 'rightTop',
[$style.notificationsPosition_rightBottom]: prefer.s.notificationPosition === 'rightBottom',
[$style.notificationsStackAxis_vertical]: prefer.s.notificationStackAxis === 'vertical',
[$style.notificationsStackAxis_horizontal]: prefer.s.notificationStackAxis === 'horizontal',
}]"
:moveClass="defaultStore.state.animation ? $style.transition_notification_move : ''"
:enterActiveClass="defaultStore.state.animation ? $style.transition_notification_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_notification_leaveActive : ''"
:enterFromClass="defaultStore.state.animation ? $style.transition_notification_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_notification_leaveTo : ''"
:moveClass="prefer.s.animation ? $style.transition_notification_move : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_notification_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_notification_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_notification_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_notification_leaveTo : ''"
>
<div v-for="notification in notifications" :key="notification.id" :class="$style.notification">
<XNotification :notification="notification"/>
@@ -56,7 +56,7 @@ import * as sound from '@/scripts/sound.js';
import { $i } from '@/account.js';
import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
import { globalEvents } from '@/events.js';
const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue'));

View File

@@ -54,11 +54,11 @@ import { openInstanceMenu } from './common.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
const menu = toRef(defaultStore.state, 'menu');
const menu = toRef(prefer.s, 'menu');
const otherMenuItemIndicated = computed(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;

View File

@@ -94,20 +94,21 @@ import { openInstanceMenu } from './common.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
import { defaultStore } from '@/store.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
import { useRouter } from '@/router/supplier.js';
import { prefer } from '@/preferences.js';
const router = useRouter();
const forceIconOnly = ref(window.innerWidth <= 1279);
const iconOnly = computed(() => {
return forceIconOnly.value || (defaultStore.reactiveState.menuDisplay.value === 'sideIcon');
return forceIconOnly.value || (store.reactiveState.menuDisplay.value === 'sideIcon');
});
const menu = computed(() => defaultStore.state.menu);
const menu = computed(() => prefer.s.menu);
const otherMenuItemIndicated = computed(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;
@@ -122,12 +123,12 @@ function calcViewState() {
window.addEventListener('resize', calcViewState);
watch(defaultStore.reactiveState.menuDisplay, () => {
watch(store.reactiveState.menuDisplay, () => {
calcViewState();
});
function toggleIconOnly() {
defaultStore.set('menuDisplay', iconOnly.value ? 'sideFull' : 'sideIcon');
store.set('menuDisplay', iconOnly.value ? 'sideFull' : 'sideIcon');
}
function openAccountMenu(ev: MouseEvent) {

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<div
v-for="x in defaultStore.reactiveState.statusbars.value" :key="x.id" :class="[$style.item, { [$style.black]: x.black,
v-for="x in prefer.r.statusbars.value" :key="x.id" :class="[$style.item, { [$style.black]: x.black,
[$style.verySmall]: x.size === 'verySmall',
[$style.small]: x.size === 'small',
[$style.large]: x.size === 'large',
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent } from 'vue';
import { instance } from '@/instance.js';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
const XRss = defineAsyncComponent(() => import('./statusbar-rss.vue'));
const XFederation = defineAsyncComponent(() => import('./statusbar-federation.vue'));
const XUserList = defineAsyncComponent(() => import('./statusbar-user-list.vue'));

View File

@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div v-if="hasDisconnected && defaultStore.state.serverDisconnectedBehavior === 'quiet'" :class="$style.root" class="_panel _shadow" @click="resetDisconnected">
<div v-if="hasDisconnected && prefer.s.serverDisconnectedBehavior === 'quiet'" :class="$style.root" class="_panel _shadow" @click="resetDisconnected">
<div><i class="ti ti-alert-triangle"></i> {{ i18n.ts.disconnectedFromServer }}</div>
<div :class="$style.command" class="_buttons">
<MkButton small primary @click="reload">{{ i18n.ts.reload }}</MkButton>
@@ -19,7 +19,7 @@ import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
const zIndex = os.claimZIndex('high');

View File

@@ -53,15 +53,16 @@ import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { openAccountMenu as openAccountMenu_, $i } from '@/account.js';
import MkButton from '@/components/MkButton.vue';
import { defaultStore } from '@/store.js';
import { store } from '@/store.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
const WINDOW_THRESHOLD = 1400;
const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
const menu = ref(defaultStore.state.menu);
// const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
const menu = ref(prefer.s.menu);
// const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
const otherNavItemIndicated = computed<boolean>(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;

View File

@@ -59,14 +59,15 @@ import MkButton from '@/components/MkButton.vue';
// import { StickySidebar } from '@/scripts/sticky-sidebar.js';
// import { mainRouter } from '@/router.js';
//import MisskeyLogo from '@assets/client/misskey.svg';
import { defaultStore } from '@/store.js';
import { store } from '@/store.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
const WINDOW_THRESHOLD = 1400;
const menu = ref(defaultStore.state.menu);
const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
const menu = ref(prefer.s.menu);
const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
const otherNavItemIndicated = computed<boolean>(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;
@@ -99,7 +100,7 @@ function openAccountMenu(ev: MouseEvent) {
}, ev);
}
watch(defaultStore.reactiveState.menuDisplay, () => {
watch(store.reactiveState.menuDisplay, () => {
calcViewState();
});

View File

@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<Transition :name="defaultStore.state.animation ? 'tray-back' : ''">
<Transition :name="prefer.s.animation ? 'tray-back' : ''">
<div
v-if="widgetsShowing"
class="tray-back _modalBg"
@@ -35,11 +35,11 @@ SPDX-License-Identifier: AGPL-3.0-only
></div>
</Transition>
<Transition :name="defaultStore.state.animation ? 'tray' : ''">
<Transition :name="prefer.s.animation ? 'tray' : ''">
<XWidgets v-if="widgetsShowing" class="tray"/>
</Transition>
<iframe v-if="defaultStore.state.aiChanMode" ref="live2d" class="ivnzpscs" src="https://misskey-dev.github.io/mascot-web/?scale=2&y=1.4"></iframe>
<iframe v-if="prefer.s.aiChanMode" ref="live2d" class="ivnzpscs" src="https://misskey-dev.github.io/mascot-web/?scale=2&y=1.4"></iframe>
<XCommon/>
</div>
@@ -47,18 +47,19 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, onMounted, provide, ref, computed, shallowRef } from 'vue';
import { instanceName } from '@@/js/config.js';
import { isLink } from '@@/js/is-link.js';
import XSidebar from './classic.sidebar.vue';
import XCommon from './_common_/common.vue';
import { instanceName } from '@@/js/config.js';
import type { PageMetadata } from '@/scripts/page-metadata.js';
import { StickySidebar } from '@/scripts/sticky-sidebar.js';
import * as os from '@/os.js';
import { provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import type { PageMetadata } from '@/scripts/page-metadata.js';
import { defaultStore } from '@/store.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js';
import { mainRouter } from '@/router/main.js';
import { isLink } from '@@/js/is-link.js';
import { prefer } from '@/preferences.js';
const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
@@ -74,7 +75,7 @@ const widgetsShowing = ref(false);
const fullView = ref(false);
const globalHeaderHeight = ref(0);
const wallpaper = miLocalStorage.getItem('wallpaper') != null;
const showMenuOnTop = computed(() => defaultStore.state.menuDisplay === 'top');
const showMenuOnTop = computed(() => store.state.menuDisplay === 'top');
const live2d = shallowRef<HTMLIFrameElement>();
const widgetsLeft = ref<HTMLElement>();
const widgetsRight = ref<HTMLElement>();
@@ -96,7 +97,7 @@ provide('shouldHeaderThin', showMenuOnTop.value);
provide('forceSpacerMin', true);
function attachSticky(el: HTMLElement) {
const sticky = new StickySidebar(el, 0, defaultStore.state.menuDisplay === 'top' ? 60 : 0); // TODO: ヘッダーの高さを60pxと決め打ちしているのを直す
const sticky = new StickySidebar(el, 0, store.state.menuDisplay === 'top' ? 60 : 0); // TODO: ヘッダーの高さを60pxと決め打ちしているのを直す
window.addEventListener('scroll', () => {
sticky.calc(window.scrollY);
}, { passive: true });
@@ -142,9 +143,9 @@ if (window.innerWidth < 1024) {
document.documentElement.style.overflowY = 'scroll';
defaultStore.loaded.then(() => {
if (defaultStore.state.widgets.length === 0) {
defaultStore.set('widgets', [{
store.loaded.then(() => {
if (store.state.widgets.length === 0) {
store.set('widgets', [{
name: 'calendar',
id: 'a', place: null, data: {},
}, {
@@ -162,7 +163,7 @@ onMounted(() => {
isDesktop.value = (window.innerWidth >= DESKTOP_THRESHOLD);
}, { passive: true });
if (defaultStore.state.aiChanMode) {
if (prefer.s.aiChanMode) {
const iframeRect = live2d.value.getBoundingClientRect();
window.addEventListener('mousemove', ev => {
live2d.value.contentWindow.postMessage({

View File

@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.main">
<XAnnouncements v-if="$i"/>
<XStatusBars/>
<div ref="columnsEl" :class="[$style.sections, { [$style.center]: deckStore.reactiveState.columnAlign.value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.self="onWheel">
<div ref="columnsEl" :class="[$style.sections, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.self="onWheel">
<!-- sectionを利用しているのはdeck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
<section
v-for="ids in layout"
@@ -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}: ${deckStore.state.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}: ${store.state['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.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
</div>
<div :class="$style.sideMenuMiddle">
@@ -62,10 +62,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<Transition
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
>
<div
v-if="drawerMenuShowing"
@@ -77,10 +77,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</Transition>
<Transition
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''"
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawer_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawer_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawer_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawer_leaveTo : ''"
>
<div v-if="drawerMenuShowing" :class="$style.menu">
<XDrawerMenu/>
@@ -95,8 +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 { deckStore, columnTypes, addColumn as addColumnToStore, forceSaveDeck, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
import type { ColumnType } from './deck/deck-store.js';
import type { MenuItem } from '@/types/menu.js';
import XSidebar from '@/ui/_common_/navbar.vue';
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
@@ -107,7 +105,7 @@ import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { unisonReload } from '@/scripts/unison-reload.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { defaultStore } from '@/store.js';
import { prefer } from '@/preferences.js';
import XMainColumn from '@/ui/deck/main-column.vue';
import XTlColumn from '@/ui/deck/tl-column.vue';
import XAntennaColumn from '@/ui/deck/antenna-column.vue';
@@ -119,6 +117,8 @@ 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';
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
@@ -137,8 +137,8 @@ const columnComponents = {
mainRouter.navHook = (path, flag): boolean => {
if (flag === 'forcePage') return false;
const noMainColumn = !deckStore.state.columns.some(x => x.type === 'main');
if (deckStore.state.navWindow || noMainColumn) {
const noMainColumn = !store.state['deck.columns'].some(x => x.type === 'main');
if (prefer.s['deck.navWindow'] || noMainColumn) {
os.pageWindow(path);
return true;
}
@@ -160,8 +160,8 @@ watch(route, () => {
});
*/
const columns = deckStore.reactiveState.columns;
const layout = deckStore.reactiveState.layout;
const columns = store.reactiveState['deck.columns'];
const layout = store.reactiveState['deck.layout'];
const menuIndicated = computed(() => {
if ($i == null) return false;
for (const def in navbarItemDef) {
@@ -214,15 +214,15 @@ loadDeck();
function changeProfile(ev: MouseEvent) {
let items: MenuItem[] = [{
text: deckStore.state.profile,
text: store.state['deck.profile'],
active: true,
action: () => {},
}];
getProfiles().then(profiles => {
items.push(...(profiles.filter(k => k !== deckStore.state.profile).map(k => ({
items.push(...(profiles.filter(k => k !== store.state['deck.profile']).map(k => ({
text: k,
action: () => {
deckStore.set('profile', k);
store.set('deck.profile', k);
unisonReload();
},
}))), { type: 'divider' as const }, {
@@ -237,7 +237,7 @@ function changeProfile(ev: MouseEvent) {
if (canceled || name == null) return;
os.promiseDialog((async () => {
await deckStore.set('profile', name);
await store.set('deck.profile', name);
await forceSaveDeck();
})(), () => {
unisonReload();
@@ -252,19 +252,19 @@ function changeProfile(ev: MouseEvent) {
async function deleteProfile() {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.tsx.deleteAreYouSure({ x: deckStore.state.profile }),
text: i18n.tsx.deleteAreYouSure({ x: store.state['deck.profile'] }),
});
if (canceled) return;
os.promiseDialog((async () => {
if (deckStore.state.profile === 'default') {
await deckStore.set('columns', []);
await deckStore.set('layout', []);
if (store.state['deck.profile'] === 'default') {
await store.set('deck.columns', []);
await store.set('deck.layout', []);
await forceSaveDeck();
} else {
await deleteProfile_(deckStore.state.profile);
await deleteProfile_(store.state['deck.profile']);
}
await deckStore.set('profile', 'default');
await store.set('deck.profile', 'default');
})(), () => {
unisonReload();
});

View File

@@ -15,17 +15,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, ref, shallowRef, watch, defineAsyncComponent } from 'vue';
import type { entities as MisskeyEntities } from 'misskey-js';
import XColumn from './column.vue';
import { updateColumn } from './deck-store.js';
import type { Column } from './deck-store.js';
import type { entities as MisskeyEntities } from 'misskey-js';
import type { Column } from '@/deck.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/preferences/def.js';
import { updateColumn } from '@/deck.js';
import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import type { MenuItem } from '@/types/menu.js';
import { antennasCache } from '@/cache.js';
import type { SoundStore } from '@/store.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
import * as sound from '@/scripts/sound.js';

View File

@@ -22,16 +22,16 @@ SPDX-License-Identifier: AGPL-3.0-only
import { onMounted, ref, shallowRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import XColumn from './column.vue';
import { updateColumn } from './deck-store.js';
import type { Column } from './deck-store.js';
import type { Column } from '@/deck.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/preferences/def.js';
import { updateColumn } from '@/deck.js';
import MkTimeline from '@/components/MkTimeline.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { favoritedChannelsCache } from '@/cache.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/store.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
import * as sound from '@/scripts/sound.js';

View File

@@ -43,11 +43,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, provide, watch, shallowRef, ref, computed } from 'vue';
import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn } from './deck-store.js';
import type { Column } from '@/deck.js';
import type { MenuItem } from '@/types/menu.js';
import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn } from '@/deck.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import type { Column } from './deck-store.js';
import type { MenuItem } from '@/types/menu.js';
provide('shouldHeaderThin', true);
provide('shouldOmitHeaderTitle', true);

View File

@@ -3,57 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { throttle } from 'throttle-debounce';
import { markRaw } from 'vue';
import { notificationTypes } from 'misskey-js';
import type { BasicTimelineType } from '@/timelines.js';
import type { Column } from '@/deck.js';
import { Storage } from '@/pizzax.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { deepClone } from '@/scripts/clone.js';
import type { SoundStore } from '@/store.js';
type ColumnWidget = {
name: string;
id: string;
data: Record<string, any>;
};
export const columnTypes = [
'main',
'widgets',
'notifications',
'tl',
'antenna',
'list',
'channel',
'mentions',
'direct',
'roleTimeline',
] as const;
export type ColumnType = typeof columnTypes[number];
export type Column = {
id: string;
type: ColumnType;
name: string | null;
width: number;
widgets?: ColumnWidget[];
active?: boolean;
flexible?: boolean;
antennaId?: string;
listId?: string;
channelId?: string;
roleId?: string;
excludeTypes?: typeof notificationTypes[number][];
tl?: BasicTimelineType;
withRenotes?: boolean;
withReplies?: boolean;
withSensitive?: boolean;
onlyFiles?: boolean;
soundSetting?: SoundStore;
};
// TODO: 消す(移行済みのため)
export const deckStore = markRaw(new Storage('deck', {
profile: {
where: 'deviceAccount',
@@ -67,272 +21,4 @@ export const deckStore = markRaw(new Storage('deck', {
where: 'deviceAccount',
default: [] as Column['id'][][],
},
columnAlign: {
where: 'deviceAccount',
default: 'left' as 'left' | 'right' | 'center',
},
alwaysShowMainColumn: {
where: 'deviceAccount',
default: true,
},
navWindow: {
where: 'deviceAccount',
default: true,
},
useSimpleUiForNonRootPages: {
where: 'deviceAccount',
default: true,
},
}));
export const loadDeck = async () => {
let deck;
try {
deck = await misskeyApi('i/registry/get', {
scope: ['client', 'deck', 'profiles'],
key: deckStore.state.profile,
});
} catch (err) {
if (typeof err === 'object' && err != null && 'code' in err && err.code === 'NO_SUCH_KEY') {
// 後方互換性のため
if (deckStore.state.profile === 'default') {
saveDeck();
return;
}
deckStore.set('columns', []);
deckStore.set('layout', []);
return;
}
throw err;
}
deckStore.set('columns', deck.columns);
deckStore.set('layout', deck.layout);
};
export async function forceSaveDeck() {
await misskeyApi('i/registry/set', {
scope: ['client', 'deck', 'profiles'],
key: deckStore.state.profile,
value: {
columns: deckStore.reactiveState.columns.value,
layout: deckStore.reactiveState.layout.value,
},
});
}
// TODO: deckがloadされていない状態でsaveすると意図せず上書きが発生するので対策する
export const saveDeck = throttle(1000, () => {
forceSaveDeck();
});
export async function getProfiles(): Promise<string[]> {
return await misskeyApi('i/registry/keys', {
scope: ['client', 'deck', 'profiles'],
});
}
export async function deleteProfile(key: string): Promise<void> {
return await misskeyApi('i/registry/remove', {
scope: ['client', 'deck', 'profiles'],
key: key,
});
}
export function addColumn(column: Column) {
if (column.name === undefined) column.name = null;
deckStore.push('columns', column);
deckStore.push('layout', [column.id]);
saveDeck();
}
export function removeColumn(id: Column['id']) {
deckStore.set('columns', deckStore.state.columns.filter(c => c.id !== id));
deckStore.set('layout', deckStore.state.layout
.map(ids => ids.filter(_id => _id !== id))
.filter(ids => ids.length > 0));
saveDeck();
}
export function swapColumn(a: Column['id'], b: Column['id']) {
const aX = deckStore.state.layout.findIndex(ids => ids.indexOf(a) !== -1);
const aY = deckStore.state.layout[aX].findIndex(id => id === a);
const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) !== -1);
const bY = deckStore.state.layout[bX].findIndex(id => id === b);
const layout = deepClone(deckStore.state.layout);
layout[aX][aY] = b;
layout[bX][bY] = a;
deckStore.set('layout', layout);
saveDeck();
}
export function swapLeftColumn(id: Column['id']) {
const layout = deepClone(deckStore.state.layout);
deckStore.state.layout.some((ids, i) => {
if (ids.includes(id)) {
const left = deckStore.state.layout[i - 1];
if (left) {
layout[i - 1] = deckStore.state.layout[i];
layout[i] = left;
deckStore.set('layout', layout);
}
return true;
}
return false;
});
saveDeck();
}
export function swapRightColumn(id: Column['id']) {
const layout = deepClone(deckStore.state.layout);
deckStore.state.layout.some((ids, i) => {
if (ids.includes(id)) {
const right = deckStore.state.layout[i + 1];
if (right) {
layout[i + 1] = deckStore.state.layout[i];
layout[i] = right;
deckStore.set('layout', layout);
}
return true;
}
return false;
});
saveDeck();
}
export function swapUpColumn(id: Column['id']) {
const layout = deepClone(deckStore.state.layout);
const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id));
const ids = deepClone(deckStore.state.layout[idsIndex]);
ids.some((x, i) => {
if (x === id) {
const up = ids[i - 1];
if (up) {
ids[i - 1] = id;
ids[i] = up;
layout[idsIndex] = ids;
deckStore.set('layout', layout);
}
return true;
}
return false;
});
saveDeck();
}
export function swapDownColumn(id: Column['id']) {
const layout = deepClone(deckStore.state.layout);
const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id));
const ids = deepClone(deckStore.state.layout[idsIndex]);
ids.some((x, i) => {
if (x === id) {
const down = ids[i + 1];
if (down) {
ids[i + 1] = id;
ids[i] = down;
layout[idsIndex] = ids;
deckStore.set('layout', layout);
}
return true;
}
return false;
});
saveDeck();
}
export function stackLeftColumn(id: Column['id']) {
let layout = deepClone(deckStore.state.layout);
const i = deckStore.state.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);
deckStore.set('layout', layout);
saveDeck();
}
export function popRightColumn(id: Column['id']) {
let layout = deepClone(deckStore.state.layout);
const i = deckStore.state.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);
deckStore.set('layout', layout);
const columns = deepClone(deckStore.state.columns);
for (const column of columns) {
if (affected.includes(column.id)) {
column.active = true;
}
}
deckStore.set('columns', columns);
saveDeck();
}
export function addColumnWidget(id: Column['id'], widget: ColumnWidget) {
const columns = deepClone(deckStore.state.columns);
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = deepClone(deckStore.state.columns[columnIndex]);
if (column == null) return;
if (column.widgets == null) column.widgets = [];
column.widgets.unshift(widget);
columns[columnIndex] = column;
deckStore.set('columns', columns);
saveDeck();
}
export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) {
const columns = deepClone(deckStore.state.columns);
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = deepClone(deckStore.state.columns[columnIndex]);
if (column == null) return;
if (column.widgets == null) column.widgets = [];
column.widgets = column.widgets.filter(w => w.id !== widget.id);
columns[columnIndex] = column;
deckStore.set('columns', columns);
saveDeck();
}
export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) {
const columns = deepClone(deckStore.state.columns);
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = deepClone(deckStore.state.columns[columnIndex]);
if (column == null) return;
column.widgets = widgets;
columns[columnIndex] = column;
deckStore.set('columns', columns);
saveDeck();
}
export function updateColumnWidget(id: Column['id'], widgetId: string, widgetData: any) {
const columns = deepClone(deckStore.state.columns);
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = deepClone(deckStore.state.columns[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;
deckStore.set('columns', columns);
saveDeck();
}
export function updateColumn(id: Column['id'], column: Partial<Column>) {
const columns = deepClone(deckStore.state.columns);
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const currentColumn = deepClone(deckStore.state.columns[columnIndex]);
if (currentColumn == null) return;
for (const [k, v] of Object.entries(column)) {
currentColumn[k] = v;
}
columns[columnIndex] = currentColumn;
deckStore.set('columns', columns);
saveDeck();
}

View File

@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import XColumn from './column.vue';
import type { Column } from './deck-store.js';
import type { Column } from '@/deck.js';
import MkNotes from '@/components/MkNotes.vue';
import { i18n } from '@/i18n.js';

View File

@@ -15,16 +15,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { watch, shallowRef, ref, onMounted } from 'vue';
import type { entities as MisskeyEntities } from 'misskey-js';
import XColumn from './column.vue';
import { updateColumn } from './deck-store.js';
import type { Column } from './deck-store.js';
import type { entities as MisskeyEntities } from 'misskey-js';
import type { Column } from '@/deck.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/preferences/def.js';
import { updateColumn } from '@/deck.js';
import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/store.js';
import { userListsCache } from '@/cache.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
import * as sound from '@/scripts/sound.js';

View File

@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<XColumn v-if="deckStore.state.alwaysShowMainColumn || mainRouter.currentRoute.value.name !== 'index'" :column="column" :isStacked="isStacked">
<XColumn v-if="prefer.s['deck.alwaysShowMainColumn'] || mainRouter.currentRoute.value.name !== 'index'" :column="column" :isStacked="isStacked">
<template #header>
<template v-if="pageMetadata">
<i :class="pageMetadata.icon"></i>
@@ -20,17 +20,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { provide, shallowRef, ref } from 'vue';
import { getScrollContainer } from '@@/js/scroll.js';
import { isLink } from '@@/js/is-link.js';
import XColumn from './column.vue';
import { deckStore } from '@/ui/deck/deck-store.js';
import type { Column } from '@/ui/deck/deck-store.js';
import type { Column } from '@/deck.js';
import type { PageMetadata } from '@/scripts/page-metadata.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import type { PageMetadata } from '@/scripts/page-metadata.js';
import { useScrollPositionManager } from '@/nirax.js';
import { getScrollContainer } from '@@/js/scroll.js';
import { isLink } from '@@/js/is-link.js';
import { mainRouter } from '@/router/main.js';
import { prefer } from '@/preferences.js';
defineProps<{
column: Column;

View File

@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import XColumn from './column.vue';
import type { Column } from './deck-store.js';
import type { Column } from '@/deck.js';
import MkNotes from '@/components/MkNotes.vue';
import { i18n } from '../../i18n.js';

View File

@@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, shallowRef } from 'vue';
import XColumn from './column.vue';
import { updateColumn } from './deck-store.js';
import type { Column } from './deck-store.js';
import { updateColumn } from '@/deck.js';
import type { Column } from '@/deck.js';
import XNotifications from '@/components/MkNotifications.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';

View File

@@ -16,14 +16,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, onMounted, ref, shallowRef, watch } from 'vue';
import XColumn from './column.vue';
import { updateColumn } from './deck-store.js';
import type { Column } from './deck-store.js';
import type { Column } from '@/deck.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/preferences/def.js';
import { updateColumn } from '@/deck.js';
import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/store.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
import * as sound from '@/scripts/sound.js';

View File

@@ -34,15 +34,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, watch, ref, shallowRef, computed } from 'vue';
import XColumn from './column.vue';
import { removeColumn, updateColumn } from './deck-store.js';
import type { Column } from './deck-store.js';
import type { Column } from '@/deck.js';
import type { MenuItem } from '@/types/menu.js';
import type { SoundStore } from '@/preferences/def.js';
import { removeColumn, updateColumn } from '@/deck.js';
import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { hasWithReplies, isAvailableBasicTimeline, basicTimelineIconClass } from '@/timelines.js';
import { instance } from '@/instance.js';
import type { SoundStore } from '@/store.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
import * as sound from '@/scripts/sound.js';

View File

@@ -5,8 +5,8 @@
import * as Misskey from 'misskey-js';
import type { Ref } from 'vue';
import type { SoundStore } from '@/store.js';
import type { SoundType } from '@/scripts/sound.js';
import type { SoundStore } from '@/preferences/def.js';
import { getSoundDuration, playMisskeySfxFile, soundsTypes } from '@/scripts/sound.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';

View File

@@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import XColumn from './column.vue';
import { addColumnWidget, removeColumnWidget, setColumnWidgets, updateColumnWidget } from './deck-store.js';
import type { Column } from './deck-store.js';
import { addColumnWidget, removeColumnWidget, setColumnWidgets, updateColumnWidget } from '@/deck.js';
import type { Column } from '@/deck.js';
import XWidgets from '@/components/MkWidgets.vue';
import { i18n } from '@/i18n.js';

View File

@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkStickyContainer ref="contents" :class="$style.contents" style="container-type: inline-size;" @contextmenu.stop="onContextmenu">
<template #header>
<div>
<XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
<XAnnouncements v-if="$i"/>
<XStatusBars :class="$style.statusbars"/>
</div>
@@ -38,10 +39,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<Transition
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
>
<div
v-if="drawerMenuShowing"
@@ -53,10 +54,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</Transition>
<Transition
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''"
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawer_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawer_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawer_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawer_leaveTo : ''"
>
<div v-if="drawerMenuShowing" :class="$style.menuDrawer">
<XDrawerMenu/>
@@ -64,10 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</Transition>
<Transition
:enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''"
:enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''"
>
<div
v-if="widgetsShowing"
@@ -79,10 +80,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</Transition>
<Transition
:enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''"
:leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''"
:enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''"
:leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_widgetsDrawer_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_widgetsDrawer_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_widgetsDrawer_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_widgetsDrawer_leaveTo : ''"
>
<div v-if="widgetsShowing" :class="$style.widgetsDrawer">
<button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i class="ti ti-x"></i></button>
@@ -105,7 +106,7 @@ import type MkStickyContainer from '@/components/global/MkStickyContainer.vue';
import type { PageMetadata } from '@/scripts/page-metadata.js';
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
import { store } from '@/store.js';
import { navbarItemDef } from '@/navbar.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
@@ -114,11 +115,14 @@ import { deviceKind } from '@/scripts/device-kind.js';
import { miLocalStorage } from '@/local-storage.js';
import { useScrollPositionManager } from '@/nirax.js';
import { mainRouter } from '@/router/main.js';
import { prefer } from '@/preferences.js';
import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue'));
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
const XPreferenceRestore = defineAsyncComponent(() => import('@/ui/_common_/PreferenceRestore.vue'));
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
@@ -174,9 +178,9 @@ if (window.innerWidth > 1024) {
}
}
defaultStore.loaded.then(() => {
if (defaultStore.state.widgets.length === 0) {
defaultStore.set('widgets', [{
store.loaded.then(() => {
if (store.state.widgets.length === 0) {
store.set('widgets', [{
name: 'calendar',
id: 'a', place: 'right', data: {},
}, {

View File

@@ -19,7 +19,7 @@ const editMode = ref(false);
<script lang="ts" setup>
import XWidgets from '@/components/MkWidgets.vue';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { store } from '@/store.js';
const props = withDefaults(defineProps<{
// null = 全てのウィジェットを表示
@@ -31,24 +31,24 @@ const props = withDefaults(defineProps<{
});
const widgets = computed(() => {
if (props.place === null) return defaultStore.reactiveState.widgets.value;
if (props.place === 'left') return defaultStore.reactiveState.widgets.value.filter(w => w.place === 'left');
return defaultStore.reactiveState.widgets.value.filter(w => w.place !== 'left');
if (props.place === null) return store.reactiveState.widgets.value;
if (props.place === 'left') return store.reactiveState.widgets.value.filter(w => w.place === 'left');
return store.reactiveState.widgets.value.filter(w => w.place !== 'left');
});
function addWidget(widget) {
defaultStore.set('widgets', [{
store.set('widgets', [{
...widget,
place: props.place,
}, ...defaultStore.state.widgets]);
}, ...store.state.widgets]);
}
function removeWidget(widget) {
defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id !== widget.id));
store.set('widgets', store.state.widgets.filter(w => w.id !== widget.id));
}
function updateWidget({ id, data }) {
defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? {
store.set('widgets', store.state.widgets.map(w => w.id === id ? {
...w,
data,
place: props.place,
@@ -57,18 +57,18 @@ function updateWidget({ id, data }) {
function updateWidgets(thisWidgets) {
if (props.place === null) {
defaultStore.set('widgets', thisWidgets);
store.set('widgets', thisWidgets);
return;
}
if (props.place === 'left') {
defaultStore.set('widgets', [
store.set('widgets', [
...thisWidgets.map(w => ({ ...w, place: 'left' })),
...defaultStore.state.widgets.filter(w => w.place !== 'left' && !thisWidgets.some(t => w.id === t.id)),
...store.state.widgets.filter(w => w.place !== 'left' && !thisWidgets.some(t => w.id === t.id)),
]);
return;
}
defaultStore.set('widgets', [
...defaultStore.state.widgets.filter(w => w.place === 'left' && !thisWidgets.some(t => w.id === t.id)),
store.set('widgets', [
...store.state.widgets.filter(w => w.place === 'left' && !thisWidgets.some(t => w.id === t.id)),
...thisWidgets.map(w => ({ ...w, place: 'right' })),
]);
}

View File

@@ -70,15 +70,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, provide, ref, computed } from 'vue';
import XCommon from './_common_/common.vue';
import { instanceName } from '@@/js/config.js';
import XCommon from './_common_/common.vue';
import type { PageMetadata } from '@/scripts/page-metadata.js';
import * as os from '@/os.js';
import { instance } from '@/instance.js';
import XSigninDialog from '@/components/MkSigninDialog.vue';
import XSignupDialog from '@/components/MkSignupDialog.vue';
import { ColdDeviceStorage, defaultStore } from '@/store.js';
import { provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import type { PageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js';
import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
import { mainRouter } from '@/router/main.js';
@@ -108,7 +107,7 @@ const announcements = {
limit: 10,
};
const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
const isTimelineAvailable = ref(instance.policies.ltlAvailable || instance.policies.gtlAvailable);
const showMenu = ref(false);
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
@@ -116,10 +115,6 @@ const narrow = ref(window.innerWidth < 1280);
const keymap = computed(() => {
return {
'd': () => {
if (ColdDeviceStorage.get('syncDeviceDarkMode')) return;
defaultStore.set('darkMode', !defaultStore.state.darkMode);
},
's': () => {
mainRouter.push('/search');
},