From 6dd2e9fc0b1eeea6b5f04ccac93ccfab658f976d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 4 Jul 2024 13:14:49 +0900 Subject: refactor(frontend): refactor popup api and make sure call dispose callback Close #14122 --- packages/frontend/src/os.ts | 125 ++++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 58 deletions(-) (limited to 'packages/frontend/src/os.ts') diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index f656a52371..560f692acf 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -116,11 +116,13 @@ export function promiseDialog>( }); // NOTE: dynamic importすると挙動がおかしくなる(showingの変更が伝播しない) - popup(MkWaitingDialog, { + const { dispose } = popup(MkWaitingDialog, { success: success, showing: showing, text: text, - }, {}, 'closed'); + }, { + closed: () => dispose(), + }); return promise; } @@ -166,28 +168,24 @@ type EmitsExtractor = { [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize : K extends string ? never : K]: T[K]; }; -export async function popup( +export function popup( component: T, props: ComponentProps, events: ComponentEmit = {} as ComponentEmit, - disposeEvent?: keyof ComponentEmit, -): Promise<{ dispose: () => void }> { +): { dispose: () => void } { markRaw(component); const id = ++popupIdCount; const dispose = () => { // このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ? window.setTimeout(() => { - popups.value = popups.value.filter(popup => popup.id !== id); + popups.value = popups.value.filter(p => p.id !== id); }, 0); }; const state = { component, props, - events: disposeEvent ? { - ...events, - [disposeEvent]: dispose, - } : events, + events, id, }; @@ -199,15 +197,19 @@ export async function popup( } export function pageWindow(path: string) { - popup(MkPageWindow, { + const { dispose } = popup(MkPageWindow, { initialPath: path, - }, {}, 'closed'); + }, { + closed: () => dispose(), + }); } export function toast(message: string) { - popup(MkToast, { + const { dispose } = popup(MkToast, { message, - }, {}, 'closed'); + }, { + closed: () => dispose(), + }); } export function alert(props: { @@ -216,11 +218,12 @@ export function alert(props: { text?: string; }): Promise { return new Promise(resolve => { - popup(MkDialog, props, { + const { dispose } = popup(MkDialog, props, { done: () => { resolve(); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -232,14 +235,15 @@ export function confirm(props: { cancelText?: string; }): Promise<{ canceled: boolean }> { return new Promise(resolve => { - popup(MkDialog, { + const { dispose } = popup(MkDialog, { ...props, showCancelButton: true, }, { done: result => { resolve(result ? result : { canceled: true }); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -261,7 +265,7 @@ export function actions { return new Promise(resolve => { - popup(MkDialog, { + const { dispose } = popup(MkDialog, { ...props, actions: props.actions.map(a => ({ text: a.text, @@ -275,7 +279,8 @@ export function actions { resolve(result ? result : { canceled: true }); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -323,7 +328,7 @@ export function inputText(props: { canceled: false; result: string | null; }> { return new Promise(resolve => { - popup(MkDialog, { + const { dispose } = popup(MkDialog, { title: props.title, text: props.text, input: { @@ -338,7 +343,8 @@ export function inputText(props: { done: result => { resolve(result ? result : { canceled: true }); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -377,7 +383,7 @@ export function inputNumber(props: { canceled: false; result: number | null; }> { return new Promise(resolve => { - popup(MkDialog, { + const { dispose } = popup(MkDialog, { title: props.title, text: props.text, input: { @@ -390,7 +396,8 @@ export function inputNumber(props: { done: result => { resolve(result ? result : { canceled: true }); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -405,7 +412,7 @@ export function inputDate(props: { canceled: false; result: Date; }> { return new Promise(resolve => { - popup(MkDialog, { + const { dispose } = popup(MkDialog, { title: props.title, text: props.text, input: { @@ -417,7 +424,8 @@ export function inputDate(props: { done: result => { resolve(result ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true }); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -427,11 +435,12 @@ export function authenticateDialog(): Promise<{ canceled: false; result: { password: string; token: string | null; }; }> { return new Promise(resolve => { - popup(MkPasswordDialog, {}, { + const { dispose } = popup(MkPasswordDialog, {}, { done: result => { resolve(result ? { canceled: false, result } : { canceled: true, result: undefined }); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -476,7 +485,7 @@ export function select(props: { canceled: false; result: C | null; }> { return new Promise(resolve => { - popup(MkDialog, { + const { dispose } = popup(MkDialog, { title: props.title, text: props.text, select: { @@ -487,7 +496,8 @@ export function select(props: { done: result => { resolve(result ? result : { canceled: true }); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -497,53 +507,57 @@ export function success(): Promise { window.setTimeout(() => { showing.value = false; }, 1000); - popup(MkWaitingDialog, { + const { dispose } = popup(MkWaitingDialog, { success: true, showing: showing, }, { done: () => resolve(), - }, 'closed'); + closed: () => dispose(), + }); }); } export function waiting(): Promise { return new Promise(resolve => { const showing = ref(true); - popup(MkWaitingDialog, { + const { dispose } = popup(MkWaitingDialog, { success: false, showing: showing, }, { done: () => resolve(), - }, 'closed'); + closed: () => dispose(), + }); }); } export function form(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType }> { return new Promise(resolve => { - popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, { + const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, { done: result => { resolve(result); }, - }, 'closed'); + closed: () => dispose(), + }); }); } export async function selectUser(opts: { includeSelf?: boolean; localOnly?: boolean; } = {}): Promise { return new Promise(resolve => { - popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { + const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { includeSelf: opts.includeSelf, localOnly: opts.localOnly, }, { ok: user => { resolve(user); }, - }, 'closed'); + closed: () => dispose(), + }); }); } export async function selectDriveFile(multiple: boolean): Promise { return new Promise(resolve => { - popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { + const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'file', multiple, }, { @@ -552,13 +566,14 @@ export async function selectDriveFile(multiple: boolean): Promise dispose(), + }); }); } export async function selectDriveFolder(multiple: boolean): Promise { return new Promise(resolve => { - popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { + const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'folder', multiple, }, { @@ -567,20 +582,22 @@ export async function selectDriveFolder(multiple: boolean): Promise dispose(), + }); }); } export async function pickEmoji(src: HTMLElement, opts: ComponentProps): Promise { return new Promise(resolve => { - popup(MkEmojiPickerDialog, { + const { dispose } = popup(MkEmojiPickerDialog, { src, ...opts, }, { done: emoji => { resolve(emoji); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -589,7 +606,7 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { uploadFolder?: string | null; }): Promise { return new Promise(resolve => { - popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), { + const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), { file: image, aspectRatio: options.aspectRatio, uploadFolder: options.uploadFolder, @@ -597,7 +614,8 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { ok: x => { resolve(x); }, - }, 'closed'); + closed: () => dispose(), + }); }); } @@ -608,8 +626,7 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n onClosing?: () => void; }): Promise { return new Promise(resolve => { - let dispose; - popup(MkPopupMenu, { + const { dispose } = popup(MkPopupMenu, { items, src, width: options?.width, @@ -623,8 +640,6 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n closing: () => { if (options?.onClosing) options.onClosing(); }, - }).then(res => { - dispose = res.dispose; }); }); } @@ -632,8 +647,7 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise { ev.preventDefault(); return new Promise(resolve => { - let dispose; - popup(MkContextMenu, { + const { dispose } = popup(MkContextMenu, { items, ev, }, { @@ -641,8 +655,6 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise { resolve(); dispose(); }, - }).then(res => { - dispose = res.dispose; }); }); } @@ -656,14 +668,11 @@ export function post(props: Record = {}): Promise { // Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、 // 複数のpost formを開いたときに場合によってはエラーになる // もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが - let dispose; - popup(MkPostFormDialog, props, { + const { dispose } = popup(MkPostFormDialog, props, { closed: () => { resolve(); dispose(); }, - }).then(res => { - dispose = res.dispose; }); }); } -- cgit v1.2.3-freya