diff --git a/src/extensions/core/load3d/Load3dUtils.ts b/src/extensions/core/load3d/Load3dUtils.ts index 6720838ce..dd5c15fba 100644 --- a/src/extensions/core/load3d/Load3dUtils.ts +++ b/src/extensions/core/load3d/Load3dUtils.ts @@ -42,7 +42,9 @@ class Load3dUtils { ) if (!result.success || !result.response) { - const err = `Error uploading temp file: ${result.error}` + const err = t('toastMessages.tempUploadFailed', { + error: result.error || '' + }) useToastStore().addAlert(err) throw new Error(err) } diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 17f4b8268..938f7d6d7 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -1806,6 +1806,8 @@ "pleaseSelectNodesToGroup": "Please select the nodes (or other groups) to create a group for", "emptyCanvas": "Empty canvas", "fileUploadFailed": "File upload failed", + "uploadFailed": "Upload failed", + "tempUploadFailed": "Error uploading temp file: {error}", "fileTooLarge": "File too large ({size} MB). Maximum supported size is {maxSize} MB", "unableToGetModelFilePath": "Unable to get model file path", "couldNotDetermineFileType": "Could not determine file type", diff --git a/src/platform/assets/services/uploadService.test.ts b/src/platform/assets/services/uploadService.test.ts index 83f708398..b3e523d25 100644 --- a/src/platform/assets/services/uploadService.test.ts +++ b/src/platform/assets/services/uploadService.test.ts @@ -26,7 +26,9 @@ describe('uploadService', () => { }) } - vi.mocked(api.fetchApi).mockResolvedValue(mockResponse as any) + vi.mocked(api.fetchApi).mockResolvedValue( + mockResponse as Partial as Response + ) const result = await uploadMedia({ source: mockFile }) @@ -46,7 +48,9 @@ describe('uploadService', () => { }) } - vi.mocked(api.fetchApi).mockResolvedValue(mockResponse as any) + vi.mocked(api.fetchApi).mockResolvedValue( + mockResponse as Partial as Response + ) const result = await uploadMedia({ source: mockBlob }) @@ -68,7 +72,9 @@ describe('uploadService', () => { }) } - vi.mocked(api.fetchApi).mockResolvedValue(mockResponse as any) + vi.mocked(api.fetchApi).mockResolvedValue( + mockResponse as Partial as Response + ) try { const result = await uploadMedia({ source: dataURL }) @@ -94,7 +100,9 @@ describe('uploadService', () => { json: vi.fn().mockResolvedValue({ name: 'test.png' }) } - vi.mocked(api.fetchApi).mockResolvedValue(mockResponse as any) + vi.mocked(api.fetchApi).mockResolvedValue( + mockResponse as Partial as Response + ) await uploadMedia( { source: mockFile }, @@ -131,7 +139,9 @@ describe('uploadService', () => { statusText: 'Internal Server Error' } - vi.mocked(api.fetchApi).mockResolvedValue(mockResponse as any) + vi.mocked(api.fetchApi).mockResolvedValue( + mockResponse as Partial as Response + ) const result = await uploadMedia({ source: mockFile }) @@ -157,7 +167,9 @@ describe('uploadService', () => { json: vi.fn().mockResolvedValue({ name: 'mask.png' }) } - vi.mocked(api.fetchApi).mockResolvedValue(mockResponse as any) + vi.mocked(api.fetchApi).mockResolvedValue( + mockResponse as Partial as Response + ) const originalRef = { filename: 'original.png', @@ -194,8 +206,8 @@ describe('uploadService', () => { } vi.mocked(api.fetchApi) - .mockResolvedValueOnce(mockResponse1 as any) - .mockResolvedValueOnce(mockResponse2 as any) + .mockResolvedValueOnce(mockResponse1 as Partial as Response) + .mockResolvedValueOnce(mockResponse2 as Partial as Response) const results = await uploadMediaBatch( mockFiles.map((source) => ({ source })) diff --git a/src/platform/assets/services/uploadService.ts b/src/platform/assets/services/uploadService.ts index 72f638969..7b020c7db 100644 --- a/src/platform/assets/services/uploadService.ts +++ b/src/platform/assets/services/uploadService.ts @@ -51,7 +51,7 @@ async function convertToFile( // dataURL string if (!isDataURL(source)) { - throw new Error(`Invalid data URL: ${source.substring(0, 50)}...`) + throw new Error('Invalid data URL') } try { diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue index 267464635..3b20990f2 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue @@ -4,7 +4,7 @@ import { computed, provide, ref, toRef, watch } from 'vue' import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps' import { t } from '@/i18n' -import { uploadMedia } from '@/platform/assets/services/uploadService' +import { uploadMediaBatch } from '@/platform/assets/services/uploadService' import { useToastStore } from '@/platform/updates/common/toastStore' import FormDropdown from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue' import { AssetKindKey } from '@/renderer/extensions/vueNodes/widgets/components/form/dropdown/types' @@ -307,29 +307,30 @@ function updateSelectedItems(selectedItems: Set) { modelValue.value = name } -// Handle multiple file uploads using shared uploadMedia service +// Handle multiple file uploads using shared uploadMediaBatch service const uploadFiles = async (files: File[]): Promise => { const folder = props.uploadFolder ?? 'input' const assetsStore = useAssetsStore() - const uploadPromises = files.map(async (file) => { - const result = await uploadMedia({ source: file }, { type: folder }) + const results = await uploadMediaBatch( + files.map((file) => ({ source: file })), + { type: folder } + ) - if (!result.success) { - toastStore.addAlert(result.error || 'Upload failed') - return null - } + // Report failed uploads + const failedUploads = results.filter((r) => !r.success) + for (const failed of failedUploads) { + toastStore.addAlert(failed.error || t('toastMessages.uploadFailed')) + } - // Update AssetsStore when uploading to input folder - if (folder === 'input') { - await assetsStore.updateInputs() - } + // Update AssetsStore once after all uploads complete (not per-file) + const successfulPaths = results.filter((r) => r.success).map((r) => r.path) - return result.path - }) + if (folder === 'input' && successfulPaths.length > 0) { + await assetsStore.updateInputs() + } - const results = await Promise.all(uploadPromises) - return results.filter((path): path is string => path !== null) + return successfulPaths } async function handleFilesUpdate(files: File[]) { @@ -340,7 +341,7 @@ async function handleFilesUpdate(files: File[]) { const uploadedPaths = await uploadFiles(files) if (uploadedPaths.length === 0) { - toastStore.addAlert('File upload failed') + toastStore.addAlert(t('toastMessages.uploadFailed')) return }