PronounsPage/composables/useDialogue.ts
2024-12-28 00:03:28 +01:00

120 lines
4.0 KiB
TypeScript

import { useNuxtApp } from 'nuxt/app';
import type { FetchOptions } from 'ofetch';
import { getCurrentInstance, h, render } from 'vue';
import DialogueBox from '../components/DialogueBox.vue';
import type { DialogueMessage } from '../components/DialogueBox.vue';
import type { Color, ModalSize } from '../src/bootstrap.ts';
export default (to: string = 'body') => {
const { $translator: translator } = useNuxtApp();
const self = getCurrentInstance()!;
const show = (
choice: boolean,
message: string | DialogueMessage,
colour: Color,
value: string | string[] | undefined,
size: ModalSize,
): Promise<string | string[] | undefined> => {
return new Promise((resolve, reject) => {
if (typeof message === 'string') {
message = { message };
}
const vnode = h(DialogueBox, {
choice,
icon: message.icon || (choice ? 'map-marker-question' : undefined),
header: message.header,
message: message.message || (choice ? translator.translate('confirm.header') : undefined),
margin: message.margin ?? true,
colour,
value,
size: message.size ?? size,
onConfirm: (value) => {
hide();
resolve(value);
},
onCancel: () => {
hide();
reject();
},
});
vnode.key = Math.random();
vnode.appContext = self.appContext;
render(vnode, document.querySelector(to)!);
});
};
const hide = () => {
render(null, document.querySelector(to)!);
};
const alert = (
message: string | DialogueMessage,
color: Color = 'primary',
): Promise<void> => {
return show(false, message, color, undefined, undefined).then(() => undefined);
};
const confirm = (
message: string | DialogueMessage = '',
colour: Color = 'primary',
): Promise<void> => {
return show(true, message, colour, undefined, undefined).then(() => undefined);
};
const editor = (
value: string | string[],
message: string | DialogueMessage = '',
color: Color = 'primary',
): Promise<string | string[] | undefined> => {
return show(false, message, color, value, 'lg');
};
const alertRaw = (
message: string | DialogueMessage,
color: Color = 'primary',
): Promise<void> => {
return show(false, `<pre class="text-start"><code>${message}</code></pre>`, color, undefined, 'lg').then(() => undefined);
};
const postWithAlertOnError = async <T = unknown>(
url: string,
data: BodyInit | Record<string, unknown> | null | undefined = undefined,
options: Omit<FetchOptions, 'method' | 'data' | 'timeout'> = {},
timeout = 30000,
): Promise<T> => {
return $fetch<T>(url, {
method: 'POST',
body: data,
timeout,
...options,
})
.catch(async (error) => {
let errorMessage = translator.translate('error.generic');
if (typeof error.data?.message === 'string') {
errorMessage = error.data.message;
}
if (typeof error.data?.error === 'string') {
errorMessage = translator.translate(error.data?.error);
// in case no translatable key was provided
if (errorMessage === undefined) {
errorMessage = error.data?.error;
}
}
await alert(errorMessage, 'danger');
throw new Error(`POST to ${url} failed: ${error.data?.error || 'unknown error'}`);
});
};
return {
alert,
confirm,
editor,
alertRaw,
postWithAlertOnError,
};
};