feat(upload): show progress bar in drop zone during upload
Some checks failed
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (push) Successful in 2m23s
CI / Backend Unit Tests (push) Successful in 2m13s
CI / E2E Tests (push) Failing after 29m59s
Some checks failed
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (push) Successful in 2m23s
CI / Backend Unit Tests (push) Successful in 2m13s
CI / E2E Tests (push) Failing after 29m59s
Replaces fetch with XMLHttpRequest to get upload progress events. The drop zone shows a filling progress bar and percentage while files are uploading, then reverts to the normal hint when done. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit was merged in pull request #74.
This commit is contained in:
@@ -25,6 +25,7 @@ let isDragging = $state(false);
|
|||||||
let windowDragging = $state(false);
|
let windowDragging = $state(false);
|
||||||
let dragCounter = 0;
|
let dragCounter = 0;
|
||||||
let isUploading = $state(false);
|
let isUploading = $state(false);
|
||||||
|
let uploadProgress = $state(0);
|
||||||
let uploadMessages = $state<{ text: string; isError: boolean; link?: string }[]>([]);
|
let uploadMessages = $state<{ text: string; isError: boolean; link?: string }[]>([]);
|
||||||
let fileInput: HTMLInputElement;
|
let fileInput: HTMLInputElement;
|
||||||
|
|
||||||
@@ -85,19 +86,26 @@ async function uploadFiles(files: File[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isUploading = true;
|
isUploading = true;
|
||||||
|
uploadProgress = 0;
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
for (const file of valid) {
|
for (const file of valid) {
|
||||||
formData.append('files', file);
|
formData.append('files', file);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch('/api/documents/quick-upload', {
|
const { ok, body } = await new Promise<{ ok: boolean; body: string }>((resolve, reject) => {
|
||||||
method: 'POST',
|
const xhr = new XMLHttpRequest();
|
||||||
body: formData
|
xhr.open('POST', '/api/documents/quick-upload');
|
||||||
|
xhr.upload.addEventListener('progress', (e) => {
|
||||||
|
if (e.lengthComputable) uploadProgress = Math.round((e.loaded / e.total) * 100);
|
||||||
|
});
|
||||||
|
xhr.addEventListener('load', () => resolve({ ok: xhr.status < 300, body: xhr.responseText }));
|
||||||
|
xhr.addEventListener('error', () => reject(new Error('Network error')));
|
||||||
|
xhr.send(formData);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (ok) {
|
||||||
const result = await res.json();
|
const result = JSON.parse(body);
|
||||||
if (result.created?.length > 0) {
|
if (result.created?.length > 0) {
|
||||||
messages.push({ text: m.upload_success({ count: result.created.length }), isError: false });
|
messages.push({ text: m.upload_success({ count: result.created.length }), isError: false });
|
||||||
}
|
}
|
||||||
@@ -122,6 +130,7 @@ async function uploadFiles(files: File[]) {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isUploading = false;
|
isUploading = false;
|
||||||
|
uploadProgress = 0;
|
||||||
uploadMessages = messages;
|
uploadMessages = messages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,10 +381,20 @@ $effect(() => {
|
|||||||
<polyline points="17 8 12 3 7 8" />
|
<polyline points="17 8 12 3 7 8" />
|
||||||
<line x1="12" y1="3" x2="12" y2="15" />
|
<line x1="12" y1="3" x2="12" y2="15" />
|
||||||
</svg>
|
</svg>
|
||||||
<span class="font-sans font-medium">
|
{#if isUploading}
|
||||||
{isUploading ? '…' : m.upload_drop_hint()}
|
<div class="flex w-48 flex-col items-center gap-1">
|
||||||
</span>
|
<div class="h-1.5 w-full overflow-hidden rounded-full bg-ink/10">
|
||||||
<span class="font-sans text-xs text-ink-3">{m.upload_accepted_types()}</span>
|
<div
|
||||||
|
class="h-full rounded-full bg-primary transition-all duration-200"
|
||||||
|
style="width: {uploadProgress}%"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span class="font-sans text-xs text-ink-3">{uploadProgress}%</span>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<span class="font-sans font-medium">{m.upload_drop_hint()}</span>
|
||||||
|
<span class="font-sans text-xs text-ink-3">{m.upload_accepted_types()}</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if uploadMessages.length > 0}
|
{#if uploadMessages.length > 0}
|
||||||
|
|||||||
Reference in New Issue
Block a user