refactor(frontend): refactor popup api and make sure call dispose callback
Close #14122
This commit is contained in:
@@ -116,11 +116,13 @@ export function promiseDialog<T extends Promise<any>>(
|
||||
});
|
||||
|
||||
// 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<T> = {
|
||||
[K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K];
|
||||
};
|
||||
|
||||
export async function popup<T extends Component>(
|
||||
export function popup<T extends Component>(
|
||||
component: T,
|
||||
props: ComponentProps<T>,
|
||||
events: ComponentEmit<T> = {} as ComponentEmit<T>,
|
||||
disposeEvent?: keyof ComponentEmit<T>,
|
||||
): 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<T extends Component>(
|
||||
}
|
||||
|
||||
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<void> {
|
||||
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<T extends {
|
||||
canceled: false; result: T[number]['value'];
|
||||
}> {
|
||||
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<T extends {
|
||||
done: result => {
|
||||
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<C = any>(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<C = any>(props: {
|
||||
done: result => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
},
|
||||
}, 'closed');
|
||||
closed: () => dispose(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -497,53 +507,57 @@ export function success(): Promise<void> {
|
||||
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<void> {
|
||||
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<F extends Form>(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType<F> }> {
|
||||
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<Misskey.entities.UserDetailed> {
|
||||
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<Misskey.entities.DriveFile[]> {
|
||||
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<Misskey.entiti
|
||||
resolve(files);
|
||||
}
|
||||
},
|
||||
}, 'closed');
|
||||
closed: () => dispose(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function selectDriveFolder(multiple: boolean): Promise<Misskey.entities.DriveFolder[]> {
|
||||
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<Misskey.enti
|
||||
resolve(folders);
|
||||
}
|
||||
},
|
||||
}, 'closed');
|
||||
closed: () => dispose(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof MkEmojiPickerDialog>): Promise<string> {
|
||||
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<Misskey.entities.DriveFile> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
resolve();
|
||||
dispose();
|
||||
},
|
||||
}).then(res => {
|
||||
dispose = res.dispose;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -656,14 +668,11 @@ export function post(props: Record<string, any> = {}): Promise<void> {
|
||||
// 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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user