enhance(frontend): エラー発生時のダイアログに詳細情報を載せる (MisskeyIO#543)

This commit is contained in:
まっちゃとーにゅ 2024-03-20 15:42:20 +09:00 committed by GitHub
parent 41ea486881
commit daf297c9c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 37 additions and 26 deletions

View file

@ -389,12 +389,10 @@ export class ApiCallService implements OnApplicationShutdown {
id: err.id, id: err.id,
}, },
{ {
e: {
message: err.message, message: err.message,
code: err.name, code: err.name,
id: err.id, id: err.id,
}, },
},
); );
} else { } else {
const errId = randomUUID(); const errId = randomUUID();
@ -416,12 +414,10 @@ export class ApiCallService implements OnApplicationShutdown {
kind: 'server', kind: 'server',
}, },
{ {
e: {
message: err.message, message: err.message,
code: err.name, code: err.name,
id: errId, id: errId,
}, },
},
); );
} }
}); });

View file

@ -63,7 +63,7 @@ export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> {
id: '3d81ceae-475f-4600-b2a8-2bc116157532', id: '3d81ceae-475f-4600-b2a8-2bc116157532',
}, { }, {
param: errors[0].schemaPath, param: errors[0].schemaPath,
reason: errors[0].message, reason: errors[0].message ?? 'Invalid',
}); });
return Promise.reject(err); return Promise.reject(err);
} }

View file

@ -85,11 +85,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
id: '5c77c4d7-0f68-48f9-8694-8453a2294840', id: '5c77c4d7-0f68-48f9-8694-8453a2294840',
}, },
{ {
e: {
message: err.message, message: err.message,
code: err.name, code: err.name,
} },
}
); );
} }

View file

@ -191,11 +191,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
id: '6708863c-6791-4487-aa01-2d682c6e7db0', id: '6708863c-6791-4487-aa01-2d682c6e7db0',
}, },
{ {
e: {
message: err.message, message: err.message,
code: err.name, code: err.name,
}, },
},
); );
} finally { } finally {
if (cleanup) cleanup(); if (cleanup) cleanup();

View file

@ -11,15 +11,15 @@ export class ApiError extends Error {
public id: string; public id: string;
public kind: string; public kind: string;
public httpStatusCode?: number; public httpStatusCode?: number;
public info?: any; public info?: Record<string, string>;
constructor(err: E, info?: any | null | undefined) { constructor(err: E, info?: Record<string, string> | null | undefined) {
super(err.message); super(err.message);
this.message = err.message; this.message = err.message;
this.code = err.code; this.code = err.code;
this.id = err.id; this.id = err.id;
this.kind = err.kind ?? 'client'; this.kind = err.kind ?? 'client';
this.httpStatusCode = err.httpStatusCode; this.httpStatusCode = err.httpStatusCode;
this.info = info; this.info = info ?? undefined;
} }
} }

View file

@ -48,6 +48,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option> <option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
</template> </template>
</MkSelect> </MkSelect>
<details v-if="details" class="_acrylic" style="margin: 1em 0;">
<summary>{{ i18n.ts.details }}</summary>
<div class="_gaps_s" style="text-align: initial;">
<MkKeyValue v-for="(value, key) in details" :key="key" :value="value">
<template #key>{{ key }}</template>
<template #value>{{ value }}</template>
</MkKeyValue>
</div>
</details>
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> <MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
@ -66,6 +75,7 @@ import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
type Input = { type Input = {
@ -89,11 +99,12 @@ type Result = string | number | true | null;
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting'; type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting';
icon?: string;
title?: string | null; title?: string | null;
text?: string | null; text?: string | null;
input?: Input; input?: Input;
select?: Select; select?: Select;
icon?: string; details?: Record<string, string>;
actions?: { actions?: {
text: string; text: string;
primary?: boolean, primary?: boolean,
@ -107,11 +118,12 @@ const props = withDefaults(defineProps<{
cancelText?: string; cancelText?: string;
}>(), { }>(), {
type: 'info', type: 'info',
icon: undefined,
title: undefined, title: undefined,
text: undefined, text: undefined,
input: undefined, input: undefined,
select: undefined, select: undefined,
icon: undefined, details: undefined,
actions: undefined, actions: undefined,
showOkButton: true, showOkButton: true,
showCancelButton: false, showCancelButton: false,

View file

@ -47,6 +47,7 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
type: 'error', type: 'error',
title, title,
text, text,
details: err.info,
actions: [{ actions: [{
value: 'ok', value: 'ok',
text: i18n.ts.gotIt, text: i18n.ts.gotIt,
@ -81,6 +82,7 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
type: 'error', type: 'error',
title, title,
text, text,
details: err.info,
}); });
}); });
@ -113,7 +115,9 @@ export function promiseDialog<T>(
} else { } else {
alert({ alert({
type: 'error', type: 'error',
text: err, title: err.message,
text: err.id,
details: err.info,
}); });
} }
}); });
@ -217,6 +221,7 @@ export function alert(props: {
type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
title?: string | null; title?: string | null;
text?: string | null; text?: string | null;
details?: Record<string, string>;
}): Promise<void> { }): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
popup(MkDialog, props, { popup(MkDialog, props, {
@ -231,6 +236,7 @@ export function confirm(props: {
type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
title?: string | null; title?: string | null;
text?: string | null; text?: string | null;
details?: Record<string, string>;
okText?: string; okText?: string;
cancelText?: string; cancelText?: string;
}): Promise<{ canceled: boolean }> { }): Promise<{ canceled: boolean }> {
@ -257,6 +263,7 @@ export function actions<T extends {
type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
title?: string | null; title?: string | null;
text?: string | null; text?: string | null;
details?: Record<string, string>;
actions: T; actions: T;
}): Promise<{ }): Promise<{
canceled: true; result: undefined; canceled: true; result: undefined;