diff --git a/resources/js/share-downloader.js b/resources/js/share-downloader.js
index a496a34..88012a7 100644
--- a/resources/js/share-downloader.js
+++ b/resources/js/share-downloader.js
@@ -68,9 +68,11 @@ document.addEventListener('alpine:init', () => {
/** How many of the run's files are in the folder. */
saved: 0,
- /** The file on its way. */
+ /** The file on its way: its path for the status line, its id for its row in the list. */
current: null,
+ currentId: null,
+
/** The files that failed, kept for the Retry button. */
failed: [],
@@ -140,6 +142,7 @@ document.addEventListener('alpine:init', () => {
for (const file of list) {
const before = this.received
this.current = file.path
+ this.currentId = file.id
try {
await this.save(file, controller.signal)
@@ -162,6 +165,7 @@ document.addEventListener('alpine:init', () => {
this.running = false
this.current = null
+ this.currentId = null
this.finished = ! controller.signal.aborted
},
diff --git a/resources/views/livewire/share-download.blade.php b/resources/views/livewire/share-download.blade.php
index fdc1909..f36dede 100644
--- a/resources/views/livewire/share-download.blade.php
+++ b/resources/views/livewire/share-download.blade.php
@@ -105,7 +105,16 @@
{{ Number::fileSize($file->file_size) }}
-
+ {{-- The file the folder download is on; its row, always shown, is brought into view in the list as it starts. --}}
+
+
+
+
diff --git a/tests/Browser/ShareFlowTest.php b/tests/Browser/ShareFlowTest.php
index 3e31a9e..a57a82f 100644
--- a/tests/Browser/ShareFlowTest.php
+++ b/tests/Browser/ShareFlowTest.php
@@ -313,3 +313,40 @@ test('a long file list scrolls on its own and keeps the download buttons on the
->assertScript("[...document.querySelectorAll('[data-md-button]')].filter((button) => button.offsetParent !== null && button.textContent.trim().toLowerCase().startsWith('download all')).every((button) => button.getBoundingClientRect().bottom <= window.innerHeight)")
->assertNoJavaScriptErrors();
});
+
+test('the folder download marks the file it is on and scrolls the list to it', function () {
+ $share = app(ShareService::class)->createShare(collect(range(1, 40))->map(fn (int $number): array => [
+ 'file' => UploadedFile::fake()->createWithContent("file-{$number}.txt", "file {$number}"),
+ 'relativePath' => null,
+ ])->all());
+ $last = $share->files->last();
+
+ $page = ready(visit(route('share.download', $share, false))->resize(1280, 800));
+
+ // Every file but the last is already in the folder, and the last one's request never answers,
+ // so the run stays on it.
+ $page->script("(async () => {
+ const root = await navigator.storage.getDirectory();
+ for await (const name of root.keys()) { await root.removeEntry(name, { recursive: true }); }
+ for (let number = 1; number < 40; number++) {
+ const writable = await (await root.getFileHandle(`file-\${number}.txt`, { create: true })).createWritable();
+ await writable.write(`file \${number}`);
+ await writable.close();
+ }
+ window.showDirectoryPicker = () => Promise.resolve(root);
+ window.fetch = () => new Promise(() => {});
+ Alpine.\$data(document.querySelector('[data-test=download-to-folder]')).supported = true;
+ })()");
+
+ $page->click('[data-test="download-to-folder"]');
+
+ $page->assertScript("[...document.querySelectorAll('[data-test=file-downloading]')].filter((mark) => mark.offsetParent !== null).length === 1")
+ ->assertScript("(() => {
+ const mark = [...document.querySelectorAll('[data-test=file-downloading]')].find((mark) => mark.offsetParent !== null);
+ const row = mark.closest('[data-md-list-item]');
+ const list = document.querySelector('[data-test=share-file-list]').getBoundingClientRect();
+ const rect = row.getBoundingClientRect();
+ return row.textContent.includes('{$last->original_name}') && rect.top >= list.top - 1 && rect.bottom <= list.bottom + 1;
+ })()")
+ ->assertNoJavaScriptErrors();
+});