summaryrefslogtreecommitdiff
path: root/packages/frontend/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/ui')
-rw-r--r--packages/frontend/src/ui/_common_/common.vue22
-rw-r--r--packages/frontend/src/ui/_common_/navbar-for-mobile.vue273
-rw-r--r--packages/frontend/src/ui/_common_/navbar.vue198
-rw-r--r--packages/frontend/src/ui/_common_/stream-indicator.vue11
-rw-r--r--packages/frontend/src/ui/deck/antenna-column.vue9
-rw-r--r--packages/frontend/src/ui/deck/channel-column.vue9
-rw-r--r--packages/frontend/src/ui/deck/direct-column.vue10
-rw-r--r--packages/frontend/src/ui/deck/list-column.vue9
-rw-r--r--packages/frontend/src/ui/deck/mentions-column.vue12
-rw-r--r--packages/frontend/src/ui/deck/notifications-column.vue4
-rw-r--r--packages/frontend/src/ui/deck/role-timeline-column.vue9
-rw-r--r--packages/frontend/src/ui/deck/tl-column.vue10
12 files changed, 191 insertions, 385 deletions
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 5fe99e0d14..fcf9fb234d 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawer_leaveTo : ''"
>
<div v-if="drawerMenuShowing" :class="$style.menuDrawer">
- <XDrawerMenu/>
+ <XNavbar style="height: 100%;" :asDrawer="true" :showWidgetButton="false"/>
</div>
</Transition>
@@ -112,7 +112,8 @@ import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
import { globalEvents } from '@/events.js';
-import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
+import { store } from '@/store.js';
+import XNavbar from '@/ui/_common_/navbar.vue';
const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue'));
const XUpload = defineAsyncComponent(() => import('./upload.vue'));
@@ -129,7 +130,9 @@ function onNotification(notification: Misskey.entities.Notification, isClient =
if (window.document.visibilityState === 'visible') {
if (!isClient && notification.type !== 'test') {
// サーバーサイドのテスト通知の際は自動で既読をつけない(テストできないので)
- useStream().send('readNotification');
+ if (store.s.realtimeMode) {
+ useStream().send('readNotification');
+ }
}
notifications.value.unshift(notification);
@@ -146,11 +149,12 @@ function onNotification(notification: Misskey.entities.Notification, isClient =
}
if ($i) {
- const connection = useStream().useChannel('main', null, 'UI');
- connection.on('notification', onNotification);
+ if (store.s.realtimeMode) {
+ const connection = useStream().useChannel('main');
+ connection.on('notification', onNotification);
+ }
globalEvents.on('clientNotification', notification => onNotification(notification, true));
- //#region Listen message from SW
if ('serviceWorker' in navigator) {
swInject();
}
@@ -226,12 +230,6 @@ if ($i) {
left: 0;
z-index: 1001;
height: 100dvh;
- width: 240px;
- box-sizing: border-box;
- contain: strict;
- overflow: auto;
- overscroll-behavior: contain;
- background: var(--MI_THEME-navBg);
}
.widgetsDrawerBg {
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
deleted file mode 100644
index 826e03751a..0000000000
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ /dev/null
@@ -1,273 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and misskey-project
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<div :class="$style.root">
- <div :class="$style.top">
- <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div>
- <button class="_button" :class="$style.instance" @click="openInstanceMenu">
- <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/>
- </button>
- </div>
- <div :class="$style.middle">
- <MkA :class="$style.item" :activeClass="$style.active" to="/" exact>
- <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span>
- </MkA>
- <template v-for="item in prefer.r.menu.value">
- <div v-if="item === '-'" :class="$style.divider"></div>
- <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
- <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span>
- <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator" class="_blink">
- <span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
- <i v-else class="_indicatorCircle"></i>
- </span>
- </component>
- </template>
- <div :class="$style.divider"></div>
- <MkA v-if="$i.isAdmin || $i.isModerator" :class="$style.item" :activeClass="$style.active" to="/admin">
- <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span>
- </MkA>
- <button :class="$style.item" class="_button" @click="more">
- <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span>
- <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator" class="_blink"><i class="_indicatorCircle"></i></span>
- </button>
- <MkA :class="$style.item" :activeClass="$style.active" to="/settings">
- <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span>
- </MkA>
- </div>
- <div :class="$style.bottom">
- <button class="_button" :class="$style.post" data-cy-open-post-form @click="os.post">
- <i :class="$style.postIcon" class="ti ti-pencil ti-fw"></i><span style="position: relative;">{{ i18n.ts.note }}</span>
- </button>
- <button class="_button" :class="$style.account" @click="openAccountMenu">
- <MkAvatar :user="$i" :class="$style.avatar"/><MkAcct :class="$style.acct" class="_nowrap" :user="$i"/>
- </button>
- </div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { computed, defineAsyncComponent } from 'vue';
-import { openInstanceMenu } from './common.js';
-import * as os from '@/os.js';
-import { navbarItemDef } from '@/navbar.js';
-import { prefer } from '@/preferences.js';
-import { i18n } from '@/i18n.js';
-import { instance } from '@/instance.js';
-import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
-import { $i } from '@/i.js';
-
-const otherMenuItemIndicated = computed(() => {
- for (const def in navbarItemDef) {
- if (prefer.r.menu.value.includes(def)) continue;
- if (navbarItemDef[def].indicated) return true;
- }
- return false;
-});
-
-function openAccountMenu(ev: MouseEvent) {
- openAccountMenu_({
- withExtraOperation: true,
- }, ev);
-}
-
-function more() {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {}, {
- closed: () => dispose(),
- });
-}
-</script>
-
-<style lang="scss" module>
-.root {
- --nav-bg-transparent: color(from var(--MI_THEME-navBg) srgb r g b / 0.5);
-
- display: flex;
- flex-direction: column;
-}
-
-.top {
- position: sticky;
- top: 0;
- z-index: 1;
- padding: 20px 0;
- background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--MI-blur, blur(8px));
- backdrop-filter: var(--MI-blur, blur(8px));
-}
-
-.banner {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-size: cover;
- background-position: center center;
- -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%);
- mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%);
-}
-
-.instance {
- position: relative;
- display: block;
- text-align: center;
- width: 100%;
-}
-
-.instanceIcon {
- display: inline-block;
- width: 38px;
- aspect-ratio: 1;
- border-radius: 8px;
-}
-
-.bottom {
- position: sticky;
- bottom: 0;
- padding: 20px 0;
- background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--MI-blur, blur(8px));
- backdrop-filter: var(--MI-blur, blur(8px));
-}
-
-.post {
- position: relative;
- display: block;
- width: 100%;
- height: 40px;
- color: var(--MI_THEME-fgOnAccent);
- font-weight: bold;
- text-align: left;
-
- &::before {
- content: "";
- display: block;
- width: calc(100% - 38px);
- height: 100%;
- margin: auto;
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- border-radius: 999px;
- background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
- }
-
- &:hover, &.active {
- &::before {
- background: hsl(from var(--MI_THEME-accent) h s calc(l + 10));
- }
- }
-}
-
-.postIcon {
- position: relative;
- margin-left: 30px;
- margin-right: 8px;
- width: 32px;
-}
-
-.account {
- position: relative;
- display: flex;
- align-items: center;
- padding-left: 30px;
- width: 100%;
- text-align: left;
- box-sizing: border-box;
- margin-top: 16px;
-}
-
-.avatar {
- display: block;
- flex-shrink: 0;
- position: relative;
- width: 32px;
- aspect-ratio: 1;
- margin-right: 8px;
-}
-
-.acct {
- display: block;
- flex-shrink: 1;
- padding-right: 8px;
-}
-
-.middle {
- flex: 1;
-}
-
-.divider {
- margin: 16px 16px;
- border-top: solid 0.5px var(--MI_THEME-divider);
-}
-
-.item {
- position: relative;
- display: block;
- padding-left: 24px;
- line-height: 2.85rem;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
- width: 100%;
- text-align: left;
- box-sizing: border-box;
- color: var(--MI_THEME-navFg);
-
- &:hover {
- text-decoration: none;
- color: light-dark(hsl(from var(--MI_THEME-navFg) h s calc(l - 17)), hsl(from var(--MI_THEME-navFg) h s calc(l + 17)));
- }
-
- &.active {
- color: var(--MI_THEME-navActive);
- }
-
- &:hover, &.active {
- &::before {
- content: "";
- display: block;
- width: calc(100% - 24px);
- height: 100%;
- margin: auto;
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- border-radius: 999px;
- background: var(--MI_THEME-accentedBg);
- }
- }
-}
-
-.itemIcon {
- position: relative;
- width: 32px;
- margin-right: 8px;
-}
-
-.itemIndicator {
- position: absolute;
- top: 0;
- left: 20px;
- color: var(--MI_THEME-navIndicator);
- font-size: 8px;
-
- &:has(.itemIndicateValueIcon) {
- animation: none;
- left: auto;
- right: 20px;
- }
-}
-
-.itemText {
- position: relative;
- font-size: 0.9em;
-}
-</style>
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index ce8efa3324..7cfedc939f 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -10,6 +10,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu">
<img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/>
</button>
+ <button v-if="!iconOnly" v-tooltip.noDelay.right="i18n.ts.realtimeMode" class="_button" :class="[$style.realtimeMode, store.r.realtimeMode.value ? $style.on : null]" @click="toggleRealtimeMode">
+ <i class="ti ti-bolt ti-fw"></i>
+ </button>
</div>
<div :class="$style.middle">
<MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact>
@@ -50,6 +53,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-if="showWidgetButton" class="_button" :class="[$style.widget]" @click="() => emit('widgetButtonClick')">
<i class="ti ti-apps ti-fw"></i>
</button>
+ <button v-if="iconOnly" v-tooltip.noDelay.right="i18n.ts.realtimeMode" class="_button" :class="[$style.realtimeMode, store.r.realtimeMode.value ? $style.on : null]" @click="toggleRealtimeMode">
+ <i class="ti ti-bolt ti-fw"></i>
+ </button>
<button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form @click="() => { os.post(); }">
<i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span>
</button>
@@ -76,16 +82,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</svg>
<button class="_button" :class="$style.subButtonClickable" @click="menuEdit"><i :class="$style.subButtonIcon" class="ti ti-settings-2"></i></button>
</div>
- <div :class="$style.subButtonGapFill"></div>
- <div :class="$style.subButtonGapFillDivider"></div>
- <div :class="[$style.subButton, $style.toggleButton]">
- <svg viewBox="0 0 16 64" :class="$style.subButtonShape">
- <g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)">
- <path d="M47.488,7.995C47.79,10.11 47.943,12.266 47.943,14.429C47.997,26.989 47.997,84 47.997,84C47.997,84 44.018,118.246 23.997,133.5C-0.374,152.07 -0.003,192 -0.003,192L-0.003,-96C-0.003,-96 0.151,-56.216 23.997,-37.5C40.861,-24.265 46.043,-1.243 47.488,7.995Z" style="fill:var(--MI_THEME-navBg);"/>
- </g>
- </svg>
- <button class="_button" :class="$style.subButtonClickable" @click="toggleIconOnly"><i v-if="iconOnly" class="ti ti-chevron-right" :class="$style.subButtonIcon"></i><i v-else class="ti ti-chevron-left" :class="$style.subButtonIcon"></i></button>
- </div>
+ <template v-if="!props.asDrawer">
+ <div :class="$style.subButtonGapFill"></div>
+ <div :class="$style.subButtonGapFillDivider"></div>
+ <div :class="[$style.subButton, $style.toggleButton]">
+ <svg viewBox="0 0 16 64" :class="$style.subButtonShape">
+ <g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)">
+ <path d="M47.488,7.995C47.79,10.11 47.943,12.266 47.943,14.429C47.997,26.989 47.997,84 47.997,84C47.997,84 44.018,118.246 23.997,133.5C-0.374,152.07 -0.003,192 -0.003,192L-0.003,-96C-0.003,-96 0.151,-56.216 23.997,-37.5C40.861,-24.265 46.043,-1.243 47.488,7.995Z" style="fill:var(--MI_THEME-navBg);"/>
+ </g>
+ </svg>
+ <button class="_button" :class="$style.subButtonClickable" @click="toggleIconOnly"><i v-if="iconOnly" class="ti ti-chevron-right" :class="$style.subButtonIcon"></i><i v-else class="ti ti-chevron-left" :class="$style.subButtonIcon"></i></button>
+ </div>
+ </template>
</div>
</div>
</template>
@@ -108,15 +116,16 @@ const router = useRouter();
const props = defineProps<{
showWidgetButton?: boolean;
+ asDrawer?: boolean;
}>();
const emit = defineEmits<{
(ev: 'widgetButtonClick'): void;
}>();
-const forceIconOnly = ref(window.innerWidth <= 1279);
+const forceIconOnly = ref(!props.asDrawer && window.innerWidth <= 1279);
const iconOnly = computed(() => {
- return forceIconOnly.value || (store.r.menuDisplay.value === 'sideIcon');
+ return !props.asDrawer && (forceIconOnly.value || (store.r.menuDisplay.value === 'sideIcon'));
});
const otherMenuItemIndicated = computed(() => {
@@ -147,6 +156,20 @@ function toggleIconOnly() {
}
}
+function toggleRealtimeMode(ev: MouseEvent) {
+ os.popupMenu([{
+ type: 'label',
+ text: i18n.ts.realtimeMode,
+ }, {
+ text: store.s.realtimeMode ? i18n.ts.turnItOff : i18n.ts.turnItOn,
+ icon: store.s.realtimeMode ? 'ti ti-bolt-off' : 'ti ti-bolt',
+ action: () => {
+ store.set('realtimeMode', !store.s.realtimeMode);
+ window.location.reload();
+ },
+ }], ev.currentTarget ?? ev.target);
+}
+
function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({
withExtraOperation: true,
@@ -191,21 +214,108 @@ function menuEdit() {
overscroll-behavior: contain;
background: var(--MI_THEME-navBg);
contain: strict;
+
+ /* 画面が縦に長い、設置している項目数が少ないなどの環境においても確実にbottomを最下部に表示するため */
display: flex;
flex-direction: column;
- direction: rtl; // スクロールバーを左に表示したいため
+
+ direction: rtl; /* スクロールバーを左に表示したいため */
}
.top {
+ flex-shrink: 0;
direction: ltr;
+
+ /* 疑似progressive blur */
+ &::before {
+ position: absolute;
+ z-index: -1;
+ inset: 0;
+ content: "";
+ backdrop-filter: blur(8px);
+ mask-image: linear-gradient(
+ to top,
+ rgb(0 0 0 / 0%) 0%,
+ rgb(0 0 0 / 4.9%) 7.75%,
+ rgb(0 0 0 / 10.4%) 11.25%,
+ rgb(0 0 0 / 45%) 23.55%,
+ rgb(0 0 0 / 55%) 26.45%,
+ rgb(0 0 0 / 89.6%) 38.75%,
+ rgb(0 0 0 / 95.1%) 42.25%,
+ rgb(0 0 0 / 100%) 50%
+ );
+ }
+
+ &::after {
+ position: absolute;
+ z-index: -1;
+ inset: 0;
+ bottom: 25%;
+ content: "";
+ backdrop-filter: blur(16px);
+ mask-image: linear-gradient(
+ to top,
+ rgb(0 0 0 / 0%) 0%,
+ rgb(0 0 0 / 4.9%) 15.5%,
+ rgb(0 0 0 / 10.4%) 22.5%,
+ rgb(0 0 0 / 45%) 47.1%,
+ rgb(0 0 0 / 55%) 52.9%,
+ rgb(0 0 0 / 89.6%) 77.5%,
+ rgb(0 0 0 / 95.1%) 91.9%,
+ rgb(0 0 0 / 100%) 100%
+ );
+ }
}
.middle {
+ flex: 1;
direction: ltr;
}
.bottom {
+ flex-shrink: 0;
direction: ltr;
+
+ /* 疑似progressive blur */
+ &::before {
+ position: absolute;
+ z-index: -1;
+ inset: -30px 0 0 0;
+ content: "";
+ backdrop-filter: blur(8px);
+ mask-image: linear-gradient(
+ to bottom,
+ rgb(0 0 0 / 0%) 0%,
+ rgb(0 0 0 / 4.9%) 7.75%,
+ rgb(0 0 0 / 10.4%) 11.25%,
+ rgb(0 0 0 / 45%) 23.55%,
+ rgb(0 0 0 / 55%) 26.45%,
+ rgb(0 0 0 / 89.6%) 38.75%,
+ rgb(0 0 0 / 95.1%) 42.25%,
+ rgb(0 0 0 / 100%) 50%
+ );
+ pointer-events: none;
+ }
+
+ &::after {
+ position: absolute;
+ z-index: -1;
+ inset: 0;
+ top: 25%;
+ content: "";
+ backdrop-filter: blur(16px);
+ mask-image: linear-gradient(
+ to bottom,
+ rgb(0 0 0 / 0%) 0%,
+ rgb(0 0 0 / 4.9%) 15.5%,
+ rgb(0 0 0 / 10.4%) 22.5%,
+ rgb(0 0 0 / 45%) 47.1%,
+ rgb(0 0 0 / 55%) 52.9%,
+ rgb(0 0 0 / 89.6%) 77.5%,
+ rgb(0 0 0 / 95.1%) 91.9%,
+ rgb(0 0 0 / 100%) 100%
+ );
+ }
}
.subButtons {
@@ -290,29 +400,18 @@ function menuEdit() {
}
.top {
+ --top-height: 80px;
+
position: sticky;
top: 0;
z-index: 1;
- padding: 20px 0;
- background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--MI-blur, blur(8px));
- backdrop-filter: var(--MI-blur, blur(8px));
+ display: flex;
+ height: var(--top-height);
}
.instance {
position: relative;
- display: block;
- text-align: center;
- width: 100%;
-
- &:focus-visible {
- outline: none;
-
- > .instanceIcon {
- outline: 2px solid var(--MI_THEME-focus);
- outline-offset: 2px;
- }
- }
+ width: var(--top-height);
}
.instanceIcon {
@@ -322,13 +421,20 @@ function menuEdit() {
border-radius: 8px;
}
+ .realtimeMode {
+ display: inline-block;
+ width: var(--top-height);
+ margin-left: auto;
+
+ &.on {
+ color: var(--MI_THEME-accent);
+ }
+ }
+
.bottom {
position: sticky;
bottom: 0;
padding-top: 20px;
- background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--MI-blur, blur(8px));
- backdrop-filter: var(--MI-blur, blur(8px));
}
.post {
@@ -416,10 +522,6 @@ function menuEdit() {
padding-right: 8px;
}
- .middle {
- flex: 1;
- }
-
.divider {
margin: 16px 16px;
border-top: solid 0.5px var(--MI_THEME-divider);
@@ -520,9 +622,6 @@ function menuEdit() {
top: 0;
z-index: 1;
padding: 20px 0;
- background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--MI-blur, blur(8px));
- backdrop-filter: var(--MI-blur, blur(8px));
}
.instance {
@@ -551,9 +650,6 @@ function menuEdit() {
position: sticky;
bottom: 0;
padding-top: 20px;
- background: var(--nav-bg-transparent);
- -webkit-backdrop-filter: var(--MI-blur, blur(8px));
- backdrop-filter: var(--MI-blur, blur(8px));
}
.widget {
@@ -564,6 +660,18 @@ function menuEdit() {
text-align: center;
}
+ .realtimeMode {
+ display: block;
+ position: relative;
+ width: 100%;
+ height: 52px;
+ text-align: center;
+
+ &.on {
+ color: var(--MI_THEME-accent);
+ }
+ }
+
.post {
display: block;
position: relative;
@@ -637,10 +745,6 @@ function menuEdit() {
display: none;
}
- .middle {
- flex: 1;
- }
-
.divider {
margin: 8px auto;
width: calc(100% - 32px);
@@ -650,7 +754,7 @@ function menuEdit() {
.item {
display: block;
position: relative;
- padding: 18px 0;
+ padding: 16px 0;
width: 100%;
text-align: center;
diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue
index 5f7600881f..35508b7ce6 100644
--- a/packages/frontend/src/ui/_common_/stream-indicator.vue
+++ b/packages/frontend/src/ui/_common_/stream-indicator.vue
@@ -20,6 +20,7 @@ import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { prefer } from '@/preferences.js';
+import { store } from '@/store.js';
const zIndex = os.claimZIndex('high');
@@ -37,11 +38,13 @@ function reload() {
window.location.reload();
}
-useStream().on('_disconnected_', onDisconnected);
+if (store.s.realtimeMode) {
+ useStream().on('_disconnected_', onDisconnected);
-onUnmounted(() => {
- useStream().off('_disconnected_', onDisconnected);
-});
+ onUnmounted(() => {
+ useStream().off('_disconnected_', onDisconnected);
+ });
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index 194b56c842..716f0ba995 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name || antennaName || i18n.ts._deck._columns.antenna }}</span>
</template>
- <MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @note="onNote"/>
+ <MkStreamingNotesTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId"/>
</XColumn>
</template>
@@ -21,13 +21,12 @@ 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 MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { antennasCache } from '@/cache.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
-import * as sound from '@/utility/sound.js';
const props = defineProps<{
column: Column;
@@ -96,10 +95,6 @@ function editAntenna() {
os.pageWindow('my/antennas/' + props.column.antennaId);
}
-function onNote() {
- sound.playMisskeySfxFile(soundSetting.value);
-}
-
const menu: MenuItem[] = [
{
icon: 'ti ti-pencil',
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index c2644da707..3439a2a56e 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div style="padding: 8px; text-align: center;">
<MkButton primary gradate rounded inline small @click="post"><i class="ti ti-pencil"></i></MkButton>
</div>
- <MkTimeline ref="timeline" src="channel" :channel="column.channelId" @note="onNote"/>
+ <MkStreamingNotesTimeline ref="timeline" src="channel" :channel="column.channelId"/>
</template>
</XColumn>
</template>
@@ -26,14 +26,13 @@ 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 MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { favoritedChannelsCache } from '@/cache.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
-import * as sound from '@/utility/sound.js';
const props = defineProps<{
column: Column;
@@ -90,10 +89,6 @@ async function post() {
});
}
-function onNote() {
- sound.playMisskeySfxFile(soundSetting.value);
-}
-
const menu: MenuItem[] = [{
icon: 'ti ti-pencil',
text: i18n.ts.selectChannel,
diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue
index 772188d773..c8b174da09 100644
--- a/packages/frontend/src/ui/deck/direct-column.vue
+++ b/packages/frontend/src/ui/deck/direct-column.vue
@@ -7,15 +7,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<XColumn :column="column" :isStacked="isStacked" :refresher="() => reloadTimeline()">
<template #header><i class="ti ti-mail" style="margin-right: 8px;"></i>{{ column.name || i18n.ts._deck._columns.direct }}</template>
- <MkNotes ref="tlComponent" :pagination="pagination"/>
+ <MkNotesTimeline ref="tlComponent" :pagination="pagination"/>
</XColumn>
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, useTemplateRef } from 'vue';
import XColumn from './column.vue';
import type { Column } from '@/deck.js';
-import MkNotes from '@/components/MkNotes.vue';
+import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
import { i18n } from '@/i18n.js';
defineProps<{
@@ -31,11 +31,11 @@ const pagination = {
},
};
-const tlComponent = ref<InstanceType<typeof MkNotes>>();
+const tlComponent = useTemplateRef('tlComponent');
function reloadTimeline() {
return new Promise<void>((res) => {
- tlComponent.value?.pagingComponent?.reload().then(() => {
+ tlComponent.value?.reload().then(() => {
res();
});
});
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index a8f17feb23..5b7390b1b2 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-list"></i><span style="margin-left: 8px;">{{ (column.name || listName) ?? i18n.ts._deck._columns.list }}</span>
</template>
- <MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :withRenotes="withRenotes" @note="onNote"/>
+ <MkStreamingNotesTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :withRenotes="withRenotes"/>
</XColumn>
</template>
@@ -21,13 +21,12 @@ 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 MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { userListsCache } from '@/cache.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
-import * as sound from '@/utility/sound.js';
const props = defineProps<{
column: Column;
@@ -102,10 +101,6 @@ function editList() {
os.pageWindow('my/lists/' + props.column.listId);
}
-function onNote() {
- sound.playMisskeySfxFile(soundSetting.value);
-}
-
const menu: MenuItem[] = [
{
icon: 'ti ti-pencil',
diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue
index ffd0307940..640e933f23 100644
--- a/packages/frontend/src/ui/deck/mentions-column.vue
+++ b/packages/frontend/src/ui/deck/mentions-column.vue
@@ -7,27 +7,27 @@ SPDX-License-Identifier: AGPL-3.0-only
<XColumn :column="column" :isStacked="isStacked" :refresher="() => reloadTimeline()">
<template #header><i class="ti ti-at" style="margin-right: 8px;"></i>{{ column.name || i18n.ts._deck._columns.mentions }}</template>
- <MkNotes ref="tlComponent" :pagination="pagination"/>
+ <MkNotesTimeline ref="tlComponent" :pagination="pagination"/>
</XColumn>
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, useTemplateRef } from 'vue';
import XColumn from './column.vue';
import type { Column } from '@/deck.js';
-import MkNotes from '@/components/MkNotes.vue';
-import { i18n } from '../../i18n.js';
+import { i18n } from '@/i18n.js';
+import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
defineProps<{
column: Column;
isStacked: boolean;
}>();
-const tlComponent = ref<InstanceType<typeof MkNotes>>();
+const tlComponent = useTemplateRef('tlComponent');
function reloadTimeline() {
return new Promise<void>((res) => {
- tlComponent.value?.pagingComponent?.reload().then(() => {
+ tlComponent.value?.reload().then(() => {
res();
});
});
diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue
index 8378dddfef..0e84ed3572 100644
--- a/packages/frontend/src/ui/deck/notifications-column.vue
+++ b/packages/frontend/src/ui/deck/notifications-column.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XColumn :column="column" :isStacked="isStacked" :menu="menu" :refresher="async () => { await notificationsComponent?.reload() }">
<template #header><i class="ti ti-bell" style="margin-right: 8px;"></i>{{ column.name || i18n.ts._deck._columns.notifications }}</template>
- <XNotifications ref="notificationsComponent" :excludeTypes="props.column.excludeTypes"/>
+ <MkStreamingNotificationsTimeline ref="notificationsComponent" :excludeTypes="props.column.excludeTypes"/>
</XColumn>
</template>
@@ -16,7 +16,7 @@ import { defineAsyncComponent, useTemplateRef } from 'vue';
import XColumn from './column.vue';
import type { Column } from '@/deck.js';
import { updateColumn } from '@/deck.js';
-import XNotifications from '@/components/MkNotifications.vue';
+import MkStreamingNotificationsTimeline from '@/components/MkStreamingNotificationsTimeline.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index 468b3e49e0..ff00dfa6e0 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-badge"></i><span style="margin-left: 8px;">{{ column.name || roleName || i18n.ts._deck._columns.roleTimeline }}</span>
</template>
- <MkTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId" @note="onNote"/>
+ <MkStreamingNotesTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId"/>
</XColumn>
</template>
@@ -20,12 +20,11 @@ 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 MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
-import * as sound from '@/utility/sound.js';
const props = defineProps<{
column: Column;
@@ -68,10 +67,6 @@ async function setRole() {
});
}
-function onNote() {
- sound.playMisskeySfxFile(soundSetting.value);
-}
-
const menu: MenuItem[] = [{
icon: 'ti ti-pencil',
text: i18n.ts.role,
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 6759135654..97208f1c6a 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</p>
<p :class="$style.disabledDescription">{{ i18n.ts._disabledTimeline.description }}</p>
</div>
- <MkTimeline
+ <MkStreamingNotesTimeline
v-else-if="column.tl"
ref="timeline"
:key="column.tl + withRenotes + withReplies + onlyFiles"
@@ -26,7 +26,6 @@ SPDX-License-Identifier: AGPL-3.0-only
:withReplies="withReplies"
:withSensitive="withSensitive"
:onlyFiles="onlyFiles"
- @note="onNote"
/>
</XColumn>
</template>
@@ -38,12 +37,11 @@ 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 MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { hasWithReplies, isAvailableBasicTimeline, basicTimelineIconClass } from '@/timelines.js';
import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
-import * as sound from '@/utility/sound.js';
const props = defineProps<{
column: Column;
@@ -117,10 +115,6 @@ async function setType() {
});
}
-function onNote() {
- sound.playMisskeySfxFile(soundSetting.value);
-}
-
const menu = computed<MenuItem[]>(() => {
const menuItems: MenuItem[] = [];