refactor: metadata file upload code cleanup

This commit is contained in:
Teemu Pöytäniemi
2026-04-01 14:59:49 +02:00
committed by Fernando Fernández
parent c7364638a0
commit a4195d3a1b
4 changed files with 58 additions and 24 deletions
@@ -503,14 +503,14 @@ function openUploadImgeEditor(): void {
}
/**
* Handle upload image editor opening
* Handle upload image editor closing
*/
function closeUploadImgeEditor(): void {
isImageDialogVisible.value = false;
}
/**
* Handle upload image editor opening
* Handle image upload
*/
async function onImageUpload(): Promise<void> {
await getData();
@@ -2,7 +2,7 @@
<VDialog
max-width="60%"
:model-value="isImageDialogVisible"
@update:model-value="emit('close')">
@update:model-value="closeDialog">
<VCard class="px-6">
<VCardTitle>{{ t('addImage') }}</VCardTitle>
<VDivider class="uno-mb-6" />
@@ -35,7 +35,7 @@
variant="flat"
width="8em"
class="mr-1"
@click="emit('close')">
@click="closeDialog">
{{ t('cancel') }}
</VBtn>
<VBtn
@@ -88,17 +88,28 @@ const imageTypes = computed(() => [
]);
/**
* Handles the file upload.
* Handle dialog closing.
*/
function closeDialog(): void {
selectedFile.value = undefined;
imageType.value = undefined;
emit('close');
}
/**
* Handle the file upload.
*/
async function onSave(): Promise<void> {
if (!selectedFile.value || !imageType.value) {
useSnackbar(t('failedToReadImage'), 'red');
emit('close');
return;
}
const base64FileContent = await ReadFileContent(selectedFile.value);
// According to the TypeScript typings, the SDK expects the body to be a File.
// However, sending a File causes the backend to return a 500 error due to a base64 parsing exception.
// When the File is converted to a base64 string, the backend works as expected.
const base64FileContent = await readFileContent(selectedFile.value);
const payload: ImageApiSetItemImageRequest = {
itemId,
@@ -119,16 +130,16 @@ async function onSave(): Promise<void> {
imageType.value = undefined;
emit('upload-image');
useSnackbar(t('imageUploadedSuccesfully'), 'green');
useSnackbar(t('imageUploadedSuccessfully'), 'green');
} catch {
useSnackbar(t('imageUploadFailed'), 'red');
}
}
/**
* Reads the file content in base64 format.
* Read the file content in base64 format.
*/
async function ReadFileContent(file: File): Promise<string> {
async function readFileContent(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
+1 -1
View File
@@ -144,7 +144,7 @@
"identifyInstructResult": "You can click on one of these cards to apply the result",
"imageSearchResult": "Image search result",
"imageType": "Image type",
"imageUploadedSuccesfully": "Image uploaded succesfully",
"imageUploadedSuccessfully": "Image uploaded successfully",
"imageUploadFailed": "Image upload failed",
"imageVotes": "{{votes}} votes",
"images": "Images",
@@ -8,7 +8,6 @@
:disabled="disabled"
@change="onInputChange">
<div
tabindex="0"
:class="[
'uno-min-h-84 uno-flex uno-flex-col items-center justify-center',
'uno-border-2 uno-border-dashed uno-rounded-xl uno-p-8 uno-text-center uno-bg-transparent',
@@ -35,7 +34,7 @@
block
size="large"
color="primary"
@click="onDropZoneClick">
@click="onBrowseButtonClick">
{{ t('browseFiles') }}
</VBtn>
</div>
@@ -55,7 +54,7 @@
v-if="preview"
:src="preview"
class="uno-w-12 uno-h-12 uno-object-cover uno-rounded-md uno-border uno-border-gray-200 dark:uno-border-gray-700">
<div class="flex flex-col">
<div>
<div class="uno-font-medium uno-text-gray-900 dark:uno-text-gray-100 uno-break-all">
{{ file.name }}
</div>
@@ -76,7 +75,7 @@
<script setup lang="ts">
import { useTranslation } from 'i18next-vue';
import { computed, ref, watch } from 'vue';
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import JIcon from './JIcon.vue';
const { accept, disabled } = defineProps<{
@@ -119,7 +118,7 @@ const acceptedFileRules = computed(() => {
});
/**
* Handles the file update and validation.
* Handle the file update and validation.
*/
function updateFile(value: File | undefined): void {
errorMessage.value = undefined;
@@ -149,7 +148,7 @@ function updateFile(value: File | undefined): void {
}
/**
* Handles the file input change.
* Handle the file input change.
*/
function onInputChange(event: Event): void {
const target = event.target as HTMLInputElement;
@@ -159,33 +158,43 @@ function onInputChange(event: Event): void {
}
/**
* Handles the file drop.
* Handle the file drop.
*/
function onDrop(e: DragEvent): void {
e.preventDefault();
dragging.value = false;
if (disabled) {
return;
}
updateFile(e.dataTransfer?.files[0]);
}
/**
* Handles the file drag over.
* Handle the file drag over.
*/
function onDragOver(e: DragEvent): void {
e.preventDefault();
if (disabled) {
return;
}
dragging.value = true;
}
/**
* Handles the file drag leave.
* Handle the file drag leave.
*/
function onDragLeave(): void {
dragging.value = false;
}
/**
* Handles the drop zone click.
* Handle the drop zone click.
*/
function onDropZoneClick(): void {
function onBrowseButtonClick(): void {
if (disabled) {
return;
}
@@ -194,20 +203,34 @@ function onDropZoneClick(): void {
}
/**
* Handles the image clear icon click.
* Handle the image clear icon click.
*/
function onClearButtonClick(): void {
file.value = undefined;
errorMessage.value = undefined;
}
watch(file, (newFile) => {
if (preview.value) {
URL.revokeObjectURL(preview.value);
preview.value = undefined;
}
if (!newFile) {
preview.value = undefined;
return;
}
preview.value = newFile.type.startsWith('image/') ? URL.createObjectURL(newFile) : undefined;
if (newFile.type.startsWith('image/')) {
preview.value = URL.createObjectURL(newFile);
}
});
onBeforeUnmount(() => {
if (preview.value) {
URL.revokeObjectURL(preview.value);
}
});
</script>