From f4167ae7f1df7c2cd4cf264fc4d79d51b8c51133 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 1 Jun 2025 10:44:45 +0900 Subject: [PATCH] =?UTF-8?q?enhance(frontend):=20=E9=9D=9E=E5=90=8C?= =?UTF-8?q?=E6=9C=9F=E7=9A=84=E3=81=AA=E3=82=B3=E3=83=B3=E3=83=9D=E3=83=BC?= =?UTF-8?q?=E3=83=8D=E3=83=B3=E3=83=88=E3=81=AE=E8=AA=AD=E3=81=BF=E8=BE=BC?= =?UTF-8?q?=E3=81=BF=E6=99=82=E3=81=AE=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E5=BC=B7=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../src/components/MkDrive.folder.vue | 4 +- .../src/components/MkPostFormAttaches.vue | 8 +-- .../src/components/MkUserSetupDialog.vue | 4 +- .../src/components/global/MkCustomEmoji.vue | 2 +- packages/frontend/src/os.ts | 50 ++++++++++++++++++- packages/frontend/src/pages/admin-user.vue | 8 +-- .../custom-emojis-manager.local.list.vue | 8 +-- .../frontend/src/pages/avatar-decorations.vue | 6 +-- packages/frontend/src/pages/chat/XMessage.vue | 4 +- .../src/pages/custom-emojis-manager.vue | 6 +-- .../frontend/src/pages/drive.file.info.vue | 4 +- packages/frontend/src/pages/emojis.emoji.vue | 2 +- packages/frontend/src/pages/flash/flash.vue | 4 +- packages/frontend/src/pages/gallery/post.vue | 4 +- packages/frontend/src/pages/page.vue | 4 +- .../frontend/src/pages/reset-password.vue | 4 +- packages/frontend/src/pages/settings/2fa.vue | 2 +- .../src/pages/settings/avatar-decoration.vue | 4 +- .../frontend/src/pages/settings/connect.vue | 4 +- packages/frontend/src/plugin.ts | 4 +- packages/frontend/src/ui/_common_/common.ts | 6 +-- .../frontend/src/ui/_common_/navbar-h.vue | 4 +- packages/frontend/src/ui/_common_/navbar.vue | 4 +- .../frontend/src/ui/deck/antenna-column.vue | 2 +- .../src/ui/deck/notifications-column.vue | 4 +- packages/frontend/src/utility/drive.ts | 8 +-- .../src/utility/get-drive-file-menu.ts | 4 +- .../frontend/src/utility/get-embed-code.ts | 4 +- .../frontend/src/utility/get-note-menu.ts | 4 +- .../frontend/src/utility/get-user-menu.ts | 4 +- packages/frontend/src/utility/please-login.ts | 7 ++- .../src/widgets/WidgetNotifications.vue | 4 +- 33 files changed, 120 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7baf724315..68a05a67e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - ### Client +- Enhance: 非同期的なコンポーネントの読み込み時のハンドリングを強化 - Fix: リアクションの一部の絵文字が重複して表示されることがある問題を修正 ### Server diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 8ba7520f35..d7dd12408c 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -282,8 +282,8 @@ function onContextmenu(ev: MouseEvent) { menu = [{ text: i18n.ts.openInWindow, icon: 'ti ti-app-window', - action: () => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkDriveWindow.vue')), { + action: async () => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveWindow.vue').then(x => x.default), { initialFolder: props.folder, }, { closed: () => dispose(), diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index dd594ef7f1..f429db94df 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -126,7 +126,7 @@ async function rename(file) { async function describe(file: Misskey.entities.DriveFile) { if (mock) return; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), { default: file.comment !== null ? file.comment : '', file: file, }, { @@ -168,9 +168,11 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar menuItems.push({ text: i18n.ts.preview, icon: 'ti ti-photo-search', - action: () => { - os.popup(defineAsyncComponent(() => import('@/components/MkImgPreviewDialog.vue')), { + action: async () => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkImgPreviewDialog.vue').then(x => x.default), { file: file, + }, { + closed: () => dispose(), }); }, }); diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index 82214ed5a5..b4ef35c221 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -174,8 +174,8 @@ function setupComplete() { function launchTutorial() { setupComplete(); - nextTick(() => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), { + nextTick(async () => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTutorialDialog.vue').then(x => x.default), { initialPage: 1, }, { closed: () => dispose(), diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index ed114d8d31..31c358eee7 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -185,7 +185,7 @@ async function edit(name: string) { const emoji = await misskeyApi('emoji', { name: name, }); - const { dispose } = os.popup(defineAsyncComponent(() => import('@/pages/emoji-edit-dialog.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/pages/emoji-edit-dialog.vue').then(x => x.default), { emoji: emoji, }, { closed: () => dispose(), diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 08291a5595..d50f50cf20 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -206,6 +206,52 @@ export function popup( }; } +export async function popupAsyncWithDialog( + componentFetching: Promise, + props: ComponentProps, + events: Partial> = {}, +): Promise<{ dispose: () => void }> { + const closeWaiting = waiting(); + + let component: T; + + try { + component = await componentFetching; + } catch (err) { + closeWaiting(); + alert({ + type: 'error', + title: i18n.ts.somethingHappened, + text: 'CODE: ASYNC_COMP_LOAD_FAIL', + }); + throw err; + } + + closeWaiting(); + + markRaw(component); + + const id = ++popupIdCount; + const dispose = () => { + // このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ? + window.setTimeout(() => { + popups.value = popups.value.filter(p => p.id !== id); + }, 0); + }; + const state = { + component, + props, + events, + id, + }; + + popups.value.push(state); + + return { + dispose, + }; +} + export function pageWindow(path: string) { const { dispose } = popup(MkPageWindow, { initialPath: path, @@ -787,9 +833,9 @@ export function launchUploader( multiple?: boolean; }, ): Promise { - return new Promise((res, rej) => { + return new Promise(async (res, rej) => { if (files.length === 0) return rej(); - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUploaderDialog.vue')), { + const { dispose } = await popupAsyncWithDialog(import('@/components/MkUploaderDialog.vue').then(x => x.default), { files: markRaw(files), folderId: options?.folderId, multiple: options?.multiple, diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 15cd219834..b0862ccaa4 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -477,16 +477,16 @@ function toggleRoleItem(role) { } } -function createAnnouncement() { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), { +async function createAnnouncement() { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkUserAnnouncementEditDialog.vue').then(x => x.default), { user: user.value, }, { closed: () => dispose(), }); } -function editAnnouncement(announcement) { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), { +async function editAnnouncement(announcement) { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkUserAnnouncementEditDialog.vue').then(x => x.default), { user: user.value, announcement, }, { diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue index 68c7048ae1..4c2c26ec45 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue @@ -525,10 +525,10 @@ const headerPageMetadata = computed(() => ({ const headerActions = computed(() => [{ icon: 'ti ti-search', text: i18n.ts.search, - handler: () => { + handler: async () => { if (searchWindowOpening) return; searchWindowOpening = true; - const { dispose } = os.popup(defineAsyncComponent(() => import('./custom-emojis-manager.local.list.search.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('./custom-emojis-manager.local.list.search.vue').then(x => x.default), { query: searchQuery.value, }, { queryUpdated: (query: EmojiSearchQuery) => { @@ -584,8 +584,8 @@ const headerActions = computed(() => [{ }, { icon: 'ti ti-notes', text: i18n.ts._customEmojisManager._gridCommon.registrationLogs, - handler: () => { - const { dispose } = os.popup(defineAsyncComponent(() => import('./custom-emojis-manager.local.list.logs.vue')), { + handler: async () => { + const { dispose } = await os.popupAsyncWithDialog(import('./custom-emojis-manager.local.list.logs.vue').then(x => x.default), { logs: requestLogs.value, }, { closed: () => { diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 675e558de9..f96c02a567 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -46,7 +46,7 @@ function load() { load(); async function add(ev: MouseEvent) { - const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration-edit-dialog.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration-edit-dialog.vue').then(x => x.default), { }, { done: result => { if (result.created) { @@ -57,8 +57,8 @@ async function add(ev: MouseEvent) { }); } -function edit(avatarDecoration) { - const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration-edit-dialog.vue')), { +async function edit(avatarDecoration) { + const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration-edit-dialog.vue').then(x => x.default), { avatarDecoration: avatarDecoration, }, { done: result => { diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue index 95f6d870dc..c5e8d0fdb6 100644 --- a/packages/frontend/src/pages/chat/XMessage.vue +++ b/packages/frontend/src/pages/chat/XMessage.vue @@ -181,9 +181,9 @@ function showMenu(ev: MouseEvent, contextmenu = false) { menu.push({ text: i18n.ts.reportAbuse, icon: 'ti ti-exclamation-circle', - action: () => { + action: async () => { const localUrl = `${url}/chat/messages/${props.message.id}`; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), { user: props.message.fromUser!, initialComment: `${localUrl}\n-----\n`, }, { diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index c2bc621f6a..5aba0f68a3 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -128,7 +128,7 @@ const toggleSelect = (emoji) => { }; const add = async (ev: MouseEvent) => { - const { dispose } = os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('./emoji-edit-dialog.vue').then(x => x.default), { }, { done: result => { if (result.created) { @@ -139,8 +139,8 @@ const add = async (ev: MouseEvent) => { }); }; -const edit = (emoji) => { - const { dispose } = os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { +const edit = async (emoji) => { + const { dispose } = await os.popupAsyncWithDialog(import('./emoji-edit-dialog.vue').then(x => x.default), { emoji: emoji, }, { done: result => { diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue index e8ac13c223..1def215afc 100644 --- a/packages/frontend/src/pages/drive.file.info.vue +++ b/packages/frontend/src/pages/drive.file.info.vue @@ -174,10 +174,10 @@ function rename() { }); } -function describe() { +async function describe() { if (!file.value) return; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), { default: file.value.comment ?? '', file: file.value, }, { diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index d5570eb20a..aaf433e78e 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -67,7 +67,7 @@ function menu(ev) { } const edit = async (emoji) => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/pages/emoji-edit-dialog.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/pages/emoji-edit-dialog.vue').then(x => x.default), { emoji: emoji, }, { closed: () => dispose(), diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index 8eb6521aac..1c9cb92bc2 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -238,12 +238,12 @@ async function run() { } } -function reportAbuse() { +async function reportAbuse() { if (!flash.value) return; const pageUrl = `${url}/play/${flash.value.id}`; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), { user: flash.value.user, initialComment: `Play: ${pageUrl}\n-----\n`, }, { diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue index 891f58ad08..3db003d9e2 100644 --- a/packages/frontend/src/pages/gallery/post.vue +++ b/packages/frontend/src/pages/gallery/post.vue @@ -153,12 +153,12 @@ function edit() { router.push(`/gallery/${post.value.id}/edit`); } -function reportAbuse() { +async function reportAbuse() { if (!post.value) return; const pageUrl = `${url}/gallery/${post.value.id}`; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), { user: post.value.user, initialComment: `Post: ${pageUrl}\n-----\n`, }, { diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index 82c953a2df..2cd8718968 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -245,12 +245,12 @@ function pin(pin) { }); } -function reportAbuse() { +async function reportAbuse() { if (!page.value) return; const pageUrl = `${url}/@${props.username}/pages/${props.pageName}`; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), { user: page.value.user, initialComment: `Page: ${pageUrl}\n-----\n`, }, { diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index 6584888148..9e2069f0e5 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -41,9 +41,9 @@ async function save() { mainRouter.push('/'); } -onMounted(() => { +onMounted(async () => { if (props.token == null) { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkForgotPassword.vue').then(x => x.default), {}, { closed: () => dispose(), }); mainRouter.push('/'); diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue index f47ffc984e..2f639cd090 100644 --- a/packages/frontend/src/pages/settings/2fa.vue +++ b/packages/frontend/src/pages/settings/2fa.vue @@ -117,7 +117,7 @@ async function registerTOTP(): Promise { token: auth.result.token, }); - const { dispose } = os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('./2fa.qrdialog.vue').then(x => x.default), { twoFactorData, }, { closed: () => dispose(), diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 14c3a03d2b..c58cd57c65 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -68,8 +68,8 @@ misskeyApi('get-avatar-decorations').then(_avatarDecorations => { loading.value = false; }); -function openDecoration(avatarDecoration, index?: number) { - const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), { +async function openDecoration(avatarDecoration, index?: number) { + const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration.dialog.vue').then(x => x.default), { decoration: avatarDecoration, usingIndex: index, }, { diff --git a/packages/frontend/src/pages/settings/connect.vue b/packages/frontend/src/pages/settings/connect.vue index 280ee546dc..959442d25f 100644 --- a/packages/frontend/src/pages/settings/connect.vue +++ b/packages/frontend/src/pages/settings/connect.vue @@ -81,8 +81,8 @@ const pagination = { noPaging: true, }; -function generateToken() { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, { +async function generateToken() { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTokenGenerateWindow.vue').then(x => x.default), {}, { done: async result => { const { name, permissions } = result; const { token } = await misskeyApi('miauth/gen-token', { diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts index 1b51850e77..f557d1047a 100644 --- a/packages/frontend/src/plugin.ts +++ b/packages/frontend/src/plugin.ts @@ -95,8 +95,8 @@ export async function authorizePlugin(plugin: Plugin) { if (plugin.permissions == null || plugin.permissions.length === 0) return; if (Object.hasOwn(store.s.pluginTokens, plugin.installId)) return; - const token = await new Promise((res, rej) => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), { + const token = await new Promise(async (res, rej) => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTokenGenerateWindow.vue').then(x => x.default), { title: i18n.ts.tokenRequested, information: i18n.ts.pluginTokenRequestedDescription, initialName: plugin.name, diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index 819e1fa42f..9872937d21 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -4,10 +4,10 @@ */ import { defineAsyncComponent } from 'vue'; +import { host } from '@@/js/config.js'; import type { MenuItem } from '@/types/menu.js'; import * as os from '@/os.js'; import { instance } from '@/instance.js'; -import { host } from '@@/js/config.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/i.js'; @@ -146,8 +146,8 @@ export function openInstanceMenu(ev: MouseEvent) { menuItems.push({ text: i18n.ts._initialTutorial.launchTutorial, icon: 'ti ti-presentation', - action: () => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), {}, { + action: async () => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTutorialDialog.vue').then(x => x.default), {}, { closed: () => dispose(), }); }, diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue index 24e2b28f1c..688e195ce6 100644 --- a/packages/frontend/src/ui/_common_/navbar-h.vue +++ b/packages/frontend/src/ui/_common_/navbar-h.vue @@ -71,8 +71,8 @@ const otherNavItemIndicated = computed(() => { return false; }); -function more(ev: MouseEvent) { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), { +async function more(ev: MouseEvent) { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkLaunchPad.vue').then(x => x.default), { anchorElement: ev.currentTarget ?? ev.target, anchor: { x: 'center', y: 'bottom' }, }, { diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index c8b7491895..28913a54ab 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -176,10 +176,10 @@ function openAccountMenu(ev: MouseEvent) { }, ev); } -function more(ev: MouseEvent) { +async function more(ev: MouseEvent) { const target = getHTMLElementOrNull(ev.currentTarget ?? ev.target); if (!target) return; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkLaunchPad.vue').then(x => x.default), { anchorElement: target, }, { closed: () => dispose(), diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue index 716f0ba995..8de894ee88 100644 --- a/packages/frontend/src/ui/deck/antenna-column.vue +++ b/packages/frontend/src/ui/deck/antenna-column.vue @@ -72,7 +72,7 @@ async function setAntenna() { if (canceled || antenna == null) return; if (antenna === '_CREATE_') { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAntennaEditorDialog.vue')), {}, { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAntennaEditorDialog.vue').then(x => x.default), {}, { created: (newAntenna: MisskeyEntities.Antenna) => { antennasCache.delete(); updateColumn(props.column.id, { diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue index 0e84ed3572..469e9472ab 100644 --- a/packages/frontend/src/ui/deck/notifications-column.vue +++ b/packages/frontend/src/ui/deck/notifications-column.vue @@ -27,8 +27,8 @@ const props = defineProps<{ const notificationsComponent = useTemplateRef('notificationsComponent'); -function func() { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), { +async function func() { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkNotificationSelectWindow.vue').then(x => x.default), { excludeTypes: props.column.excludeTypes, }, { done: async (res) => { diff --git a/packages/frontend/src/utility/drive.ts b/packages/frontend/src/utility/drive.ts index f171a4d14d..fcc847653d 100644 --- a/packages/frontend/src/utility/drive.ts +++ b/packages/frontend/src/utility/drive.ts @@ -172,8 +172,8 @@ export function chooseFileFromPcAndUpload( export function chooseDriveFile(options: { multiple?: boolean; } = {}): Promise { - return new Promise(resolve => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkDriveFileSelectDialog.vue')), { + return new Promise(async resolve => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFileSelectDialog.vue').then(x => x.default), { multiple: options.multiple ?? false, }, { done: files => { @@ -286,8 +286,8 @@ export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFi } export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise { - return new Promise(resolve => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkDriveFolderSelectDialog.vue')), { + return new Promise(async resolve => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFolderSelectDialog.vue').then(x => x.default), { initialFolder, }, { done: folders => { diff --git a/packages/frontend/src/utility/get-drive-file-menu.ts b/packages/frontend/src/utility/get-drive-file-menu.ts index 4b4bab3125..040cf8f976 100644 --- a/packages/frontend/src/utility/get-drive-file-menu.ts +++ b/packages/frontend/src/utility/get-drive-file-menu.ts @@ -28,8 +28,8 @@ function rename(file: Misskey.entities.DriveFile) { }); } -function describe(file: Misskey.entities.DriveFile) { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { +async function describe(file: Misskey.entities.DriveFile) { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), { default: file.comment ?? '', file: file, }, { diff --git a/packages/frontend/src/utility/get-embed-code.ts b/packages/frontend/src/utility/get-embed-code.ts index d458e64f19..de36314ac2 100644 --- a/packages/frontend/src/utility/get-embed-code.ts +++ b/packages/frontend/src/utility/get-embed-code.ts @@ -64,7 +64,7 @@ export function getEmbedCode(path: string, params?: EmbedParams): string { * * カスタマイズ機能がいらない場合(事前にパラメータを指定する場合)は getEmbedCode を直接使ってください */ -export function genEmbedCode(entity: EmbeddableEntity, id: string, params?: EmbedParams) { +export async function genEmbedCode(entity: EmbeddableEntity, id: string, params?: EmbedParams) { const _params = { ...params }; if (embedRouteWithScrollbar.includes(entity) && _params.maxHeight == null) { @@ -75,7 +75,7 @@ export function genEmbedCode(entity: EmbeddableEntity, id: string, params?: Embe if (window.innerWidth < MOBILE_THRESHOLD) { copyToClipboard(getEmbedCode(`/embed/${entity}/${id}`, _params)); } else { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkEmbedCodeGenDialog.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkEmbedCodeGenDialog.vue').then(x => x.default), { entity, id, params: _params, diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts index dc813cb78e..5b99be5fdb 100644 --- a/packages/frontend/src/utility/get-note-menu.ts +++ b/packages/frontend/src/utility/get-note-menu.ts @@ -135,12 +135,12 @@ export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): Men return { icon: 'ti ti-exclamation-circle', text, - action: (): void => { + action: async (): Promise => { const localUrl = `${url}/notes/${note.id}`; let noteInfo = ''; if (note.url ?? note.uri != null) noteInfo = `Note: ${note.url ?? note.uri}\n`; noteInfo += `Local Note: ${localUrl}\n`; - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), { user: note.user, initialComment: `${noteInfo}-----\n`, }, { diff --git a/packages/frontend/src/utility/get-user-menu.ts b/packages/frontend/src/utility/get-user-menu.ts index 563e45c446..5c08b8c462 100644 --- a/packages/frontend/src/utility/get-user-menu.ts +++ b/packages/frontend/src/utility/get-user-menu.ts @@ -94,8 +94,8 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router }); } - function reportAbuse() { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { + async function reportAbuse() { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), { user: user, }, { closed: () => dispose(), diff --git a/packages/frontend/src/utility/please-login.ts b/packages/frontend/src/utility/please-login.ts index 9253105f48..737e7d7c6e 100644 --- a/packages/frontend/src/utility/please-login.ts +++ b/packages/frontend/src/utility/please-login.ts @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { defineAsyncComponent } from 'vue'; import { $i } from '@/i.js'; import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; -import { popup } from '@/os.js'; +import { popupAsyncWithDialog } from '@/os.js'; export type OpenOnRemoteOptions = { /** @@ -45,7 +44,7 @@ export type OpenOnRemoteOptions = { params: Record; }; -export function pleaseLogin(opts: { +export async function pleaseLogin(opts: { path?: string; message?: string; openOnRemote?: OpenOnRemoteOptions; @@ -59,7 +58,7 @@ export function pleaseLogin(opts: { _openOnRemote = opts.openOnRemote; } - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), { + const { dispose } = await popupAsyncWithDialog(import('@/components/MkSigninDialog.vue').then(x => x.default), { autoSet: true, message: opts.message ?? (_openOnRemote ? i18n.ts.signinOrContinueOnRemote : i18n.ts.signinRequired), openOnRemote: _openOnRemote, diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue index b21a82179e..a1af5d084c 100644 --- a/packages/frontend/src/widgets/WidgetNotifications.vue +++ b/packages/frontend/src/widgets/WidgetNotifications.vue @@ -54,8 +54,8 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name, emit, ); -const configureNotification = () => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), { +const configureNotification = async () => { + const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkNotificationSelectWindow.vue').then(x => x.default), { excludeTypes: widgetProps.excludeTypes, }, { done: async (res) => {