Add upload queue UI with ExifTool metadata preview on pick.

Users can select images within configured limits, see per-row load status, and inspect raw ExifTool output before upload work continues.
This commit is contained in:
2026-10-17 21:39:59 +02:00
parent 5b6304709e
commit 18597f4127
8 changed files with 274 additions and 32 deletions
+28 -22
View File
@@ -5,8 +5,20 @@ import type { MetadataPreviewEntry } from '@/types/metadata/MetadataPreview';
const EXIFTOOL_PREPARE_ERROR_MESSAGE = 'Could not prepare ExifTool';
const EXIFTOOL_INITIALIZING_MESSAGE = 'Initializing ExifTool';
const METADATA_READING_MESSAGE = 'Reading metadata';
const METADATA_READ_ERROR_MESSAGE = 'Could not read image metadata';
const METADATA_LOADING_MESSAGE = 'Loading metadata';
const METADATA_LOAD_ERROR_MESSAGE = 'Could not load image metadata';
const METADATA_IDLE_MESSAGE = 'Waiting to load metadata';
const METADATA_READY_MESSAGE = 'Metadata loaded';
const METADATA_IDLE_PREVIEW: MetadataPreviewEntry = {
status: 'idle',
message: METADATA_IDLE_MESSAGE
};
const METADATA_LOADING_PREVIEW: MetadataPreviewEntry = {
status: 'loading',
message: METADATA_LOADING_MESSAGE
};
export const useMetadataStore = defineStore('metadata', () => {
const previewsByKey = ref<Record<string, MetadataPreviewEntry>>({});
@@ -18,10 +30,14 @@ export const useMetadataStore = defineStore('metadata', () => {
const makeKeyFromFile = (file: File): string => `${file.name}\0${file.size}\0${file.lastModified}`;
const getPreview = (file: File): MetadataPreviewEntry => {
const getPreview = (file?: File | null): MetadataPreviewEntry => {
if (!file) {
return METADATA_IDLE_PREVIEW;
}
const key = makeKeyFromFile(file);
return previewsByKey.value[key] ?? { status: 'idle' };
return previewsByKey.value[key] ?? METADATA_IDLE_PREVIEW;
};
const loadPreview = async (file: File): Promise<MetadataPreviewEntry> => {
@@ -41,7 +57,7 @@ export const useMetadataStore = defineStore('metadata', () => {
if (exiftoolInitStatus.value === 'ready') {
previewsByKey.value = {
...previewsByKey.value,
[key]: { status: 'loading', message: METADATA_READING_MESSAGE }
[key]: METADATA_LOADING_PREVIEW
};
} else {
previewsByKey.value = {
@@ -54,7 +70,7 @@ export const useMetadataStore = defineStore('metadata', () => {
previewsByKey.value = {
...previewsByKey.value,
[key]: { status: 'loading', message: METADATA_READING_MESSAGE }
[key]: METADATA_LOADING_PREVIEW
};
} catch {
const entry: MetadataPreviewEntry = {
@@ -71,7 +87,11 @@ export const useMetadataStore = defineStore('metadata', () => {
try {
const output = await readMetadata(file);
const entry: MetadataPreviewEntry = { status: 'ready', output };
const entry: MetadataPreviewEntry = {
status: 'ready',
message: METADATA_READY_MESSAGE,
output
};
previewsByKey.value = { ...previewsByKey.value, [key]: entry };
@@ -79,7 +99,7 @@ export const useMetadataStore = defineStore('metadata', () => {
} catch {
const entry: MetadataPreviewEntry = {
status: 'error',
message: METADATA_READ_ERROR_MESSAGE
message: METADATA_LOAD_ERROR_MESSAGE
};
previewsByKey.value = { ...previewsByKey.value, [key]: entry };
@@ -91,19 +111,6 @@ export const useMetadataStore = defineStore('metadata', () => {
const loadPreviews = (files: readonly File[]): Promise<MetadataPreviewEntry[]> =>
Promise.all(files.map(file => loadPreview(file)));
const clearPreview = (file: File): void => {
const key = makeKeyFromFile(file);
if (!(key in previewsByKey.value)) {
return;
}
const next = { ...previewsByKey.value };
delete next[key];
previewsByKey.value = next;
};
const clearAllPreviews = (): void => {
previewsByKey.value = {};
};
@@ -114,7 +121,6 @@ export const useMetadataStore = defineStore('metadata', () => {
getPreview,
loadPreview,
loadPreviews,
clearPreview,
clearAllPreviews
};
});