Fix upload UI stuck on "Processing files..."

The `uploading` flag was only cleared by `x-init="uploading = false"` on the
file list, which Alpine runs when the element is initialized. Adding a second
batch of files to the same share only patches that existing element, so the
flag stayed true forever: the spinner never went away, the drop zone stayed
disabled and the submit button stayed disabled, making the share impossible
to create.

`updatedFiles()` now dispatches `files-processed`, which runs for every batch,
and the form resets its upload state on that event instead.

Also fixes two adjacent bugs in the drag & drop path: `uploadMultiple()` was
called without callbacks so dropped files showed no progress at all, and
`relativePaths` was replaced instead of appended, shifting every earlier
file's path onto the wrong file on a second drop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Reinhold / reini
2026-07-23 11:44:35 +02:00
co-authored by Claude Opus 4.8
parent 084cefe023
commit d2e8a135c5
3 changed files with 61 additions and 6 deletions
@@ -38,16 +38,36 @@
}
setTimeout(() => {
if (! files.length) {
return;
}
const dt = new DataTransfer();
const paths = [];
files.forEach(f => {
dt.items.add(f.file);
paths.push(f.path);
});
$wire.relativePaths = paths;
$wire.uploadMultiple('files', dt.files);
$wire.relativePaths = [...($wire.relativePaths ?? []), ...paths];
this.uploading = true;
this.progress = 0;
$wire.uploadMultiple(
'files',
dt.files,
() => this.progress = 100,
() => this.resetUpload(),
(event) => this.progress = event.detail.progress,
() => this.resetUpload(),
);
}, 500);
},
resetUpload() {
this.uploading = false;
this.progress = 0;
},
traverseEntry(entry, path, files) {
if (entry.isFile) {
entry.file(file => {
@@ -61,10 +81,11 @@
}
}
}"
x-init="$wire.$on('files-processed', () => resetUpload())"
x-on:livewire-upload-start="uploading = true; progress = 0"
x-on:livewire-upload-finish="progress = 100"
x-on:livewire-upload-cancel="uploading = false"
x-on:livewire-upload-error="uploading = false"
x-on:livewire-upload-cancel="resetUpload()"
x-on:livewire-upload-error="resetUpload()"
x-on:livewire-upload-progress="progress = $event.detail.progress"
>
{{-- Drop Zone --}}
@@ -118,12 +139,12 @@
{{-- Errors --}}
@error('files')
<div x-init="uploading = false" class="alert alert-error mb-4">{{ $message }}</div>
<div class="alert alert-error mb-4">{{ $message }}</div>
@enderror
{{-- File List --}}
@if (count($files))
<div x-init="uploading = false" class="mb-6">
<div class="mb-6">
<h3 class="font-semibold mb-2">{{ __('Selected Files') }} ({{ count($files) }})</h3>
<div class="space-y-1 max-h-60 overflow-y-auto">
@foreach ($files as $index => $file)