From db27623f2d672bbf9f3b070321e9233534ef5ad4 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:35:51 +0900 Subject: [PATCH] =?UTF-8?q?=E3=81=84=E3=82=8D=E3=81=84=E3=82=8D=E5=A4=89?= =?UTF-8?q?=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/os.ts | 106 ++++++++++-------- .../frontend/src/pages/settings/profile.vue | 47 ++++++-- packages/frontend/src/scripts/select-file.ts | 36 +++--- 3 files changed, 110 insertions(+), 79 deletions(-) diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index fd0ca331f0..bb10cd7895 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -27,66 +27,78 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; export const openingWindowsCount = ref(0); +export async function apiErrorAlert(err: Misskey.api.APIError, endpoint?: string) { + let title: string | undefined; + let text = err.message + '\n' + err.id; + if (err.code === 'INTERNAL_ERROR') { + title = i18n.ts.internalServerError; + text = i18n.ts.internalServerErrorDescription; + const date = new Date().toISOString(); + const { result } = await actions({ + type: 'error', + title, + text, + actions: [{ + value: 'ok', + text: i18n.ts.gotIt, + primary: true, + }, { + value: 'copy', + text: i18n.ts.copyErrorInfo, + }], + }); + if (result === 'copy') { + const errorReportText = [ + `Info: ${JSON.stringify(err.info)}`, + `Date: ${date}`, + ]; + if (endpoint) errorReportText.unshift(`Endpoint: ${endpoint}`); + + copyToClipboard(errorReportText.join('\n')); + success(); + } + return; + } else if (err.code === 'RATE_LIMIT_EXCEEDED') { + title = i18n.ts.cannotPerformTemporary; + text = i18n.ts.cannotPerformTemporaryDescription; + } else if (err.code === 'INVALID_PARAM') { + title = i18n.ts.invalidParamError; + text = i18n.ts.invalidParamErrorDescription; + } else if (err.code === 'ROLE_PERMISSION_DENIED') { + title = i18n.ts.permissionDeniedError; + text = i18n.ts.permissionDeniedErrorDescription; + } else if (err.code.startsWith('TOO_MANY')) { + title = i18n.ts.youCannotCreateAnymore; + text = `${i18n.ts.error}: ${err.id}`; + } else if (err.message.startsWith('Unexpected token')) { + title = i18n.ts.gotInvalidResponseError; + text = i18n.ts.gotInvalidResponseErrorDescription; + } + alert({ + type: 'error', + title, + text, + }); +} + export const apiWithDialog = (( endpoint: E, data: P = {} as any, token?: string | null | undefined, ) => { const promise = misskeyApi(endpoint, data, token); - promiseDialog(promise, null, async (err) => { - let title: string | undefined; - let text = err.message + '\n' + err.id; - if (err.code === 'INTERNAL_ERROR') { - title = i18n.ts.internalServerError; - text = i18n.ts.internalServerErrorDescription; - const date = new Date().toISOString(); - const { result } = await actions({ - type: 'error', - title, - text, - actions: [{ - value: 'ok', - text: i18n.ts.gotIt, - primary: true, - }, { - value: 'copy', - text: i18n.ts.copyErrorInfo, - }], - }); - if (result === 'copy') { - copyToClipboard(`Endpoint: ${endpoint}\nInfo: ${JSON.stringify(err.info)}\nDate: ${date}`); - success(); - } - return; - } else if (err.code === 'RATE_LIMIT_EXCEEDED') { - title = i18n.ts.cannotPerformTemporary; - text = i18n.ts.cannotPerformTemporaryDescription; - } else if (err.code === 'INVALID_PARAM') { - title = i18n.ts.invalidParamError; - text = i18n.ts.invalidParamErrorDescription; - } else if (err.code === 'ROLE_PERMISSION_DENIED') { - title = i18n.ts.permissionDeniedError; - text = i18n.ts.permissionDeniedErrorDescription; - } else if (err.code.startsWith('TOO_MANY')) { - title = i18n.ts.youCannotCreateAnymore; - text = `${i18n.ts.error}: ${err.id}`; - } else if (err.message.startsWith('Unexpected token')) { - title = i18n.ts.gotInvalidResponseError; - text = i18n.ts.gotInvalidResponseErrorDescription; - } - alert({ - type: 'error', - title, - text, - }); + promiseDialog(promise, null, (err) => { + apiErrorAlert(err); }); return promise; }) as typeof misskeyApi; +type Unpromise = T extends Promise ? U : never; + export function promiseDialog>( promise: T, - onSuccess?: ((res: any) => void) | null, + onSuccess?: ((res: Unpromise) => void) | null, onFailure?: ((err: Misskey.api.APIError) => void) | null, text?: string, ): T { diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 172fb454a4..398cb7fd58 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -128,6 +128,7 @@ import { defaultStore } from '@/store.js'; import { globalEvents } from '@/events.js'; import MkInfo from '@/components/MkInfo.vue'; import MkTextarea from '@/components/MkTextarea.vue'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const $i = signinRequired(); @@ -239,13 +240,27 @@ function changeAvatar(ev) { }); } - const i = await os.apiWithDialog('i/update', { + const updatePromise = misskeyApi('i/update', { avatarId: originalOrCropped.id, }); - $i.avatarId = i.avatarId; - $i.avatarUrl = i.avatarUrl; - globalEvents.emit('requestClearPageCache'); - claimAchievement('profileFilled'); + + os.promiseDialog(updatePromise, (updatedUser) => { + os.success(); + $i.avatarId = updatedUser.avatarId; + $i.avatarUrl = updatedUser.avatarUrl; + globalEvents.emit('requestClearPageCache'); + claimAchievement('profileFilled'); + }, (err) => { + if (err.code === 'AVATAR_IS_SENSITIVE') { + os.alert({ + type: 'error', + title: i18n.ts.cannotSelectSensitiveMedia, + text: i18n.ts.cannotSelectSensitiveMediaDescription, + }); + } else { + os.apiErrorAlert(err, 'i/update'); + } + }); }); } @@ -285,12 +300,26 @@ function changeBanner(ev) { }); } - const i = await os.apiWithDialog('i/update', { + const updatePromise = misskeyApi('i/update', { bannerId: originalOrCropped.id, }); - $i.bannerId = i.bannerId; - $i.bannerUrl = i.bannerUrl; - globalEvents.emit('requestClearPageCache'); + + os.promiseDialog(updatePromise, (updatedUser) => { + os.success(); + $i.bannerId = updatedUser.bannerId; + $i.bannerUrl = updatedUser.bannerUrl; + globalEvents.emit('requestClearPageCache'); + }, (err) => { + if (err.code === 'BANNER_IS_SENSITIVE') { + os.alert({ + type: 'error', + title: i18n.ts.cannotSelectSensitiveMedia, + text: i18n.ts.cannotSelectSensitiveMediaDescription, + }); + } else { + os.apiErrorAlert(err, 'i/update'); + } + }); }); } diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts index c1e9a5230f..308a42d9c4 100644 --- a/packages/frontend/src/scripts/select-file.ts +++ b/packages/frontend/src/scripts/select-file.ts @@ -12,6 +12,7 @@ import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { uploadFile } from '@/scripts/upload.js'; +import { deepMerge } from '@/scripts/merge.js'; type SelectFileOptions = { multiple?: boolean; @@ -88,29 +89,18 @@ export function chooseFileFromUrl(): Promise { } function select(src: any, label: string | null, options?: SelectFileOptions): Promise { - const _options = { + const _options = deepMerge(options ?? {}, { multiple: false, + + /** ドライブファイル選択時のみに適用 */ excludeSensitive: false, - additionalMenu: [], - ...options, - }; + + additionalMenu: [] as MenuItem[], + }); return new Promise((res, rej) => { const keepOriginal = ref(defaultStore.state.keepOriginalUploading); - function _resolve(files: Misskey.entities.DriveFile[]) { - if (_options.excludeSensitive && files.some(file => file.isSensitive)) { - os.alert({ - title: i18n.ts.cannotSelectSensitiveMedia, - text: i18n.ts.cannotSelectSensitiveMediaDescription, - }); - rej(new Error('Sensitive media is selected')); - return; - } - - res(files); - } - os.popupMenu([label ? { text: label, type: 'label', @@ -121,23 +111,23 @@ function select(src: any, label: string | null, options?: SelectFileOptions): Pr }, { text: i18n.ts.upload, icon: 'ti ti-upload', - action: () => chooseFileFromPc(_options.multiple, keepOriginal.value).then(files => _resolve(files)), + action: () => chooseFileFromPc(_options.multiple, keepOriginal.value).then(files => res(files)), }, { text: i18n.ts.fromDrive, icon: 'ti ti-cloud', - action: () => chooseFileFromDrive(_options.multiple, _options.excludeSensitive).then(files => _resolve(files)), + action: () => chooseFileFromDrive(_options.multiple, _options.excludeSensitive).then(files => res(files)), }, { text: i18n.ts.fromUrl, icon: 'ti ti-link', - action: () => chooseFileFromUrl().then(file => _resolve([file])), - }, ..._options.additionalMenu], src); + action: () => chooseFileFromUrl().then(file => res([file])), + }, ...(_options.additionalMenu)], src); }); } export function selectFile(src: any, label: string | null = null, options?: { excludeSensitive?: boolean; additionalMenu?: MenuItem[]; }): Promise { - return select(src, label, { ...options, multiple: false }).then(files => files[0]); + return select(src, label, { ...(options ? options : {}), multiple: false }).then(files => files[0]); } export function selectFiles(src: any, label: string | null = null, options?: { excludeSensitive?: boolean; additionalMenu?: MenuItem[]; }): Promise { - return select(src, label, { ...options, multiple: true }); + return select(src, label, { ...(options ? options : {}), multiple: true }); }