From 3c97c12e7fde60291bd26dc24217ec73eeed345b Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 21 Oct 2025 20:03:57 +0900
Subject: [PATCH] =?UTF-8?q?enhance(frontend):=20=E4=B8=8B=E6=9B=B8?=
=?UTF-8?q?=E3=81=8D/=E4=BA=88=E7=B4=84=E6=8A=95=E7=A8=BF=E4=B8=80?=
=?UTF-8?q?=E8=A6=A7=E3=81=AF=E6=8A=95=E7=A8=BF=E3=83=95=E3=82=A9=E3=83=BC?=
=?UTF-8?q?=E3=83=A0=E3=81=AE=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88?=
=?UTF-8?q?=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E5=86=85=E3=81=AB=E7=A7=BB?=
=?UTF-8?q?=E5=8B=95=E3=81=97=E3=80=81=E4=B8=8B=E6=9B=B8=E3=81=8D=E4=BF=9D?=
=?UTF-8?q?=E5=AD=98=E3=81=AF=E3=80=8C...=E3=80=8D=E3=83=A1=E3=83=8B?=
=?UTF-8?q?=E3=83=A5=E3=83=BC=E5=86=85=E3=81=AB=E7=A7=BB=E5=8B=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CHANGELOG.md | 1 +
packages/frontend/src/accounts.ts | 10 +-
.../frontend/src/components/MkPostForm.vue | 95 ++++++++-----------
.../src/components/global/MkPageHeader.vue | 11 ++-
.../frontend/src/ui/_common_/navbar-h.vue | 10 +-
packages/frontend/src/ui/_common_/navbar.vue | 10 +-
6 files changed, 63 insertions(+), 74 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46356ac754..9da1baab43 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
### Client
- Enhance: デッキのメインカラムのヘッダをクリックしてページ上部/下部にスクロールできるように
+- Enhance: 下書き/予約投稿一覧は投稿フォームのアカウントメニュー内に移動し、下書き保存は「...」メニュー内に移動されました
- Fix: カスタム絵文字画面(beta)のaliasesで使用される区切り文字が一致していないのを修正 #15614
- Fix: バナー画像の幅が表示領域と一致していない問題を修正
- Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正
diff --git a/packages/frontend/src/accounts.ts b/packages/frontend/src/accounts.ts
index 60f7cd0b4b..79086c2b39 100644
--- a/packages/frontend/src/accounts.ts
+++ b/packages/frontend/src/accounts.ts
@@ -211,13 +211,13 @@ export async function switchAccount(host: string, id: string) {
}
}
-export async function openAccountMenu(opts: {
+export async function getAccountMenu(opts: {
includeCurrentAccount?: boolean;
withExtraOperation: boolean;
active?: Misskey.entities.User['id'];
onChoose?: (account: Misskey.entities.MeDetailed) => void;
-}, ev: MouseEvent) {
- if (!$i) return;
+}) {
+ if ($i == null) throw new Error('No current account');
const me = $i;
const callback = opts.onChoose;
@@ -338,9 +338,7 @@ export async function openAccountMenu(opts: {
menuItems.push(...accountItems);
}
- popupMenu(menuItems, ev.currentTarget ?? ev.target, {
- align: 'left',
- });
+ return menuItems;
}
export function getAccountWithSigninDialog(): Promise<{ id: string, token: string } | null> {
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index c1b950a6c8..afa70cdbae 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -17,7 +17,6 @@ SPDX-License-Identifier: AGPL-3.0-only
-
@@ -141,7 +140,7 @@ import MkInfo from '@/components/MkInfo.vue';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { ensureSignin, notesCount, incNotesCount } from '@/i.js';
-import { getAccounts, openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { getAccounts, getAccountMenu } from '@/accounts.js';
import { deepClone } from '@/utility/clone.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { miLocalStorage } from '@/local-storage.js';
@@ -620,6 +619,19 @@ function showOtherSettings() {
action: () => {
toggleReactionAcceptance();
},
+ }, { type: 'divider' }, {
+ type: 'button',
+ text: i18n.ts._drafts.saveToDraft,
+ icon: 'ti ti-cloud-upload',
+ action: async () => {
+ if (!canSaveAsServerDraft.value) {
+ return os.alert({
+ type: 'error',
+ text: i18n.ts._drafts.cannotCreateDraft,
+ });
+ }
+ saveServerDraft();
+ },
}, ...($i.policies.scheduledNoteLimit > 0 ? [{
icon: 'ti ti-calendar-time',
text: i18n.ts.schedulePost + '...',
@@ -1159,34 +1171,9 @@ function showActions(ev: MouseEvent) {
const postAccount = ref(null);
-function openAccountMenu(ev: MouseEvent) {
+async function openAccountMenu(ev: MouseEvent) {
if (props.mock) return;
- openAccountMenu_({
- withExtraOperation: false,
- includeCurrentAccount: true,
- active: postAccount.value != null ? postAccount.value.id : $i.id,
- onChoose: (account) => {
- if (account.id === $i.id) {
- postAccount.value = null;
- } else {
- postAccount.value = account;
- }
- },
- }, ev);
-}
-
-function showPerUploadItemMenu(item: UploaderItem, ev: MouseEvent) {
- const menu = uploader.getMenu(item);
- os.popupMenu(menu, ev.currentTarget ?? ev.target);
-}
-
-function showPerUploadItemMenuViaContextmenu(item: UploaderItem, ev: MouseEvent) {
- const menu = uploader.getMenu(item);
- os.contextMenu(menu, ev);
-}
-
-function showDraftMenu(ev: MouseEvent) {
function showDraftsDialog(scheduled: boolean) {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkNoteDraftsDialog.vue')), {
scheduled,
@@ -1244,34 +1231,44 @@ function showDraftMenu(ev: MouseEvent) {
});
}
- os.popupMenu([{
- type: 'button',
- text: i18n.ts._drafts.saveToDraft,
- icon: 'ti ti-cloud-upload',
- action: async () => {
- if (!canSaveAsServerDraft.value) {
- return os.alert({
- type: 'error',
- text: i18n.ts._drafts.cannotCreateDraft,
- });
+ const items = await getAccountMenu({
+ withExtraOperation: false,
+ includeCurrentAccount: true,
+ active: postAccount.value != null ? postAccount.value.id : $i.id,
+ onChoose: (account) => {
+ if (account.id === $i.id) {
+ postAccount.value = null;
+ } else {
+ postAccount.value = account;
}
- saveServerDraft();
},
- }, {
+ });
+
+ os.popupMenu([{
type: 'button',
text: i18n.ts._drafts.listDrafts,
icon: 'ti ti-cloud-download',
action: () => {
showDraftsDialog(false);
},
- }, { type: 'divider' }, {
+ }, {
type: 'button',
text: i18n.ts._drafts.listScheduledNotes,
icon: 'ti ti-clock-down',
action: () => {
showDraftsDialog(true);
},
- }], (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
+ }, { type: 'divider' }, ...items], (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
+}
+
+function showPerUploadItemMenu(item: UploaderItem, ev: MouseEvent) {
+ const menu = uploader.getMenu(item);
+ os.popupMenu(menu, ev.currentTarget ?? ev.target);
+}
+
+function showPerUploadItemMenuViaContextmenu(item: UploaderItem, ev: MouseEvent) {
+ const menu = uploader.getMenu(item);
+ os.contextMenu(menu, ev);
}
async function schedule() {
@@ -1422,20 +1419,6 @@ defineExpose({
margin: auto;
}
-.draftButton {
- padding: 8px;
- font-size: 90%;
- border-radius: 6px;
-
- &:hover {
- background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
- }
-
- &:disabled {
- background: none;
- }
-}
-
.headerRight {
display: flex;
min-height: 48px;
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 2f4de840db..c4adf440cb 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -62,9 +62,10 @@ import { onMounted, onUnmounted, ref, inject, useTemplateRef, computed } from 'v
import { scrollToTop } from '@@/js/scroll.js';
import XTabs from './MkPageHeader.tabs.vue';
import { globalEvents } from '@/events.js';
-import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { getAccountMenu } from '@/accounts.js';
import { $i } from '@/i.js';
import { DI } from '@/di.js';
+import * as os from '@/os.js';
const props = withDefaults(defineProps(), {
tabs: () => ([] as Tab[]),
@@ -99,10 +100,12 @@ const top = () => {
}
};
-function openAccountMenu(ev: MouseEvent) {
- openAccountMenu_({
+async function openAccountMenu(ev: MouseEvent) {
+ const menuItems = await getAccountMenu({
withExtraOperation: true,
- }, ev);
+ });
+
+ os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
}
function onTabClick(): void {
diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue
index a78bdd52d1..b025dd4858 100644
--- a/packages/frontend/src/ui/_common_/navbar-h.vue
+++ b/packages/frontend/src/ui/_common_/navbar-h.vue
@@ -55,7 +55,7 @@ import MkButton from '@/components/MkButton.vue';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
-import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { getAccountMenu } from '@/accounts.js';
import { $i } from '@/i.js';
import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js';
@@ -84,10 +84,12 @@ async function more(ev: MouseEvent) {
});
}
-function openAccountMenu(ev: MouseEvent) {
- openAccountMenu_({
+async function openAccountMenu(ev: MouseEvent) {
+ const menuItems = await getAccountMenu({
withExtraOperation: true,
- }, ev);
+ });
+
+ os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
}
onMounted(() => {
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index 2e21587fcb..b0e45eafcd 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -109,7 +109,7 @@ import { instance } from '@/instance.js';
import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js';
import { useRouter } from '@/router.js';
import { prefer } from '@/preferences.js';
-import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { getAccountMenu } from '@/accounts.js';
import { $i } from '@/i.js';
const router = useRouter();
@@ -170,10 +170,12 @@ function toggleRealtimeMode(ev: MouseEvent) {
}], ev.currentTarget ?? ev.target);
}
-function openAccountMenu(ev: MouseEvent) {
- openAccountMenu_({
+async function openAccountMenu(ev: MouseEvent) {
+ const menuItems = await getAccountMenu({
withExtraOperation: true,
- }, ev);
+ });
+
+ os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
}
async function more(ev: MouseEvent) {