diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue index ff4ba0d557..3b37f26a39 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue @@ -90,6 +90,7 @@ import MkPagingButtons from '@/components/MkPagingButtons.vue'; import { selectFile } from '@/utility/select-file.js'; import { copyGridDataToClipboard, removeDataFromGrid } from '@/components/grid/grid-utils.js'; import { useLoading } from '@/components/hook/useLoading.js'; +import { retryOnThrottled } from '@@/js/retry-on-throttled'; type GridItem = { checked: boolean; @@ -334,37 +335,29 @@ async function onUpdateButtonClicked() { err?: unknown; }; - const executeWithRetries = async (item: any, retries: number = 3): Promise => { - for (let attempt = 0; attempt <= retries; attempt++) { - try { - await misskeyApi('admin/emoji/update', { - id: item.id, - name: item.name, - category: emptyStrToNull(item.category), - aliases: emptyStrToEmptyArray(item.aliases), - license: emptyStrToNull(item.license), - isSensitive: item.isSensitive, - localOnly: item.localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: item.roleIdsThatCanBeUsedThisEmojiAsReaction.map((it: any) => it.id), - fileId: item.fileId, - }); - return { item, success: true }; - } catch (err) { - if (attempt < retries) { - console.warn(`Retrying ${item.name}, attempt ${attempt + 1}`); - await delay(1000 * (attempt + 1)); - } else { - return { item, success: false, err }; - } - } + const execute = async (item: any): Promise => { + try { + await retryOnThrottled(() => misskeyApi('admin/emoji/update', { + id: item.id, + name: item.name, + category: emptyStrToNull(item.category), + aliases: emptyStrToEmptyArray(item.aliases), + license: emptyStrToNull(item.license), + isSensitive: item.isSensitive, + localOnly: item.localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: item.roleIdsThatCanBeUsedThisEmojiAsReaction.map((it: any) => it.id), + fileId: item.fileId, + })); + return { item, success: true }; + } catch (error) { + return { item, success: false, err: error }; } - return { item, success: false, err: new Error('Unknown error') }; }; const action = async (): Promise => { const results: ApiResponse[] = []; for (const item of updatedItems) { - results.push(await executeWithRetries(item)); + results.push(await execute(item)); } return results; }; diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue index c5083263dd..78678342ea 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue @@ -75,6 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable @typescript-eslint/no-non-null-assertion */ import * as Misskey from 'misskey-js'; import { onMounted, ref, useCssModule } from 'vue'; +import { retryOnThrottled } from '@@/js/retry-on-throttled'; import type { RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js'; import type { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js'; import type { DroppedFile } from '@/utility/file-drop.js'; @@ -247,29 +248,19 @@ const registerButtonDisabled = ref(false); const requestLogs = ref([]); const isDragOver = ref(false); -const delay = (ms: number) => new Promise(resolve => window.setTimeout(resolve, ms)); - type ApiResponse = { item: any; success: boolean; err?: unknown; }; -const executeWithRetries = async (item: any, apiEndpoint: string, payload: any, retries = 3): Promise => { - for (let attempt = 0; attempt <= retries; attempt++) { - try { - await misskeyApi(apiEndpoint, payload); - return { item, success: true }; - } catch (err) { - if (attempt < retries) { - console.warn(`Retrying ${item.id || item.name}, attempt ${attempt + 1}`); - await delay(1000 * (attempt + 1)); // Exponential backoff - } else { - return { item, success: false, err }; - } - } +const execute = async (item: any, apiEndpoint: string, payload: any): Promise => { + try { + await retryOnThrottled(() => misskeyApi(apiEndpoint, payload)); + return { item, success: true }; + } catch (err) { + return { item, success: false, err }; } - return { item, success: false, err: new Error('Unknown error') }; // Ensures all code paths return a value }; const importEmojis = async (targets: any[]): Promise => { @@ -286,7 +277,7 @@ const importEmojis = async (targets: any[]): Promise => { async function action(): Promise { const results: ApiResponse[] = []; for (const item of targets) { - results.push(await executeWithRetries(item, 'admin/emoji/copy', { emojiId: item.id })); + results.push(await execute(item, 'admin/emoji/copy', { emojiId: item.id })); } return results; @@ -326,7 +317,7 @@ const onRegistryClicked = async (): Promise => { const results: ApiResponse[] = []; for (const item of items) { results.push( - await executeWithRetries(item, 'admin/emoji/add', { + await execute(item, 'admin/emoji/add', { name: item.name, category: emptyStrToNull(item.category), aliases: emptyStrToEmptyArray(item.aliases), diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue index 0d54a1faba..fb32ff4865 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue @@ -142,6 +142,7 @@ SPDX-License-Identifier: AGPL-3.0-only