navidrome/ui/src
Deluan Quintão 23548f40a0
fix(share): give visual feedback when downloading from a share (#5865)
* fix(share): give visual feedback when downloading a share

The share page handed the download URL to navidrome-music-player, which fell
through to downloadjs and buffered the whole ZIP into memory via XHR before
saving it. Nothing was handed to the browser until the last byte arrived, so a
large share produced a long silent window with no player feedback and no
browser download UI, inviting repeat clicks that each spawn another server-side
zip+transcode.

Use the player's customDownloader prop to trigger a synthetic anchor instead,
so the browser performs the download and reports its own progress. An anchor
rather than assigning window.location.href: the share page's service worker
registers a NavigationRoute over all navigations, which intercepts the streamed
archive and fails it into the offline fallback (observed as HTTP 503 in Chrome).

handleDownloads now loads the share before streaming so it can set
Content-Disposition and Content-Type. This also fixes error reporting: ZipShare
previously wrote to the ResponseWriter before checkShareError ran, locking the
status at 200, so expired, missing and non-downloadable shares all returned 200.
They now correctly return 410, 404 and 403.

* feat(share): acknowledge the download click in the player

The browser's download UI is the real progress indicator, but nothing in the
page itself reacted to the click, so the moment before the browser catches up
still read as unresponsive. Dim the download button and make it unclickable for
two seconds after a download starts, reusing the JSS function-value pattern the
existing single-track styling already uses.

A repeat download restarts the window instead of extending the original, and
the timer is cleared on unmount. This also blunts repeat clicking, where every
extra click costs another server-side zip and transcode.

Add SharePlayer tests covering the download mechanism and this state machine.
The dimming itself is verified in a browser rather than jsdom: JSS function
values are not evaluated there, so the rule is never emitted and a CSS
assertion would pass or fail for the wrong reason.

Signed-off-by: Deluan <deluan@navidrome.org>

* test(share): assert render counts in SharePlayer feedback tests

The two acknowledgement tests compared the props object across renders, which
React may reuse, so they passed without proving anything and then failed once
the surrounding assertions changed. Count renders instead, and let the pending
timer run out rather than advancing exactly to its deadline, which does not
cross it.

The repeat-download test now also asserts that no render happens at the
original deadline, proving the timer was replaced rather than merely that one
eventually fired.

* fix(share): count one visit per share download

The preflight share load added in this branch made every download record two
visits: handleDownloads called Share.Load, and ZipShare then loaded the share
again internally. Share.Load increments and persists VisitCount, so the counter
advanced twice per download and the repository work was duplicated.

Pass the already-loaded share into ZipShare instead of its id. handleDownloads
is its only production caller, and it now has the share in hand for the
Content-Disposition header anyway.

The archiver test asserts Load is not called, so the double-load cannot come
back unnoticed. Verified against a running server: the counter now advances by
one per download.

* test(share): derive feedback-window timings from the constant

The acknowledgement tests hardcoded clock advances tuned to a 2000ms window.
Raising DOWNLOAD_FEEDBACK_MS to 5000 left them advancing 1500ms and 1001ms,
which no longer reach the deadline they are meant to cross, so the repeat-
download test passed without proving the timer had been replaced.

Export the constant and derive the advances from it, and let the pending timer
run out in the unmount test rather than advancing a fixed amount. Changing the
duration can no longer silently strand a test short of its deadline.

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2026-07-28 16:13:44 -04:00
..
actions feat(ui): playlist favourites (heart, list filter, sidebar favourites-only toggle) (#5805) 2026-07-18 14:45:47 -04:00
album feat(ui): remember 'items per page' selection across sessions (#5819) 2026-07-19 18:59:37 -04:00
artist feat(ui): remember 'items per page' selection across sessions (#5819) 2026-07-19 18:59:37 -04:00
audioplayer fix(ui): report playback when restarting current track via prev 2026-05-22 22:23:36 -03:00
common feat(ui): remember 'items per page' selection across sessions (#5819) 2026-07-19 18:59:37 -04:00
dataProvider fix(ui): make self-service profile edits report their outcome (#5699) 2026-07-01 21:31:29 -04:00
dialogs feat(ui): add download config toml link, disable copy when clipboard not available (#5035) 2026-02-12 10:54:04 -05:00
i18n feat(ui): playlist favourites (heart, list filter, sidebar favourites-only toggle) (#5805) 2026-07-18 14:45:47 -04:00
icons build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
layout feat(ui): playlist favourites (heart, list filter, sidebar favourites-only toggle) (#5805) 2026-07-18 14:45:47 -04:00
library feat(ui): conditionally display 'path' field in LibraryList for desktop view 2026-03-01 10:50:37 -05:00
missing feat(ui): remember 'items per page' selection across sessions (#5819) 2026-07-19 18:59:37 -04:00
personal feat(ui): add Radio to Default View options (#5801) 2026-07-17 21:53:17 -04:00
player fix(transcoding): honor player forced format on the WebUI transcode flow (#5613) 2026-06-14 20:52:19 -04:00
playlist feat(ui): remember 'items per page' selection across sessions (#5819) 2026-07-19 18:59:37 -04:00
plugin fix(ui): add Rescan button to plugin list empty state (#5471) 2026-05-05 18:49:24 -04:00
radio feat(ui): remember 'items per page' selection across sessions (#5819) 2026-07-19 18:59:37 -04:00
reducers feat(ui): playlist favourites (heart, list filter, sidebar favourites-only toggle) (#5805) 2026-07-18 14:45:47 -04:00
share fix(share): give visual feedback when downloading from a share (#5865) 2026-07-28 16:13:44 -04:00
song feat(ui): remember 'items per page' selection across sessions (#5819) 2026-07-19 18:59:37 -04:00
store feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
subsonic feat(ui): replace UI scrobble with reportPlayback and redesign NowPlaying panel (#5448) 2026-05-01 15:27:32 -04:00
themes feat(ui): add rose pine themes (#5664) 2026-07-03 17:53:57 -04:00
transcode fix(ui): improve browser codec detection and limit Safari transcoding to mp3 (#5171) 2026-03-12 08:21:49 -04:00
transcoding fix(ui): minor icon inconsistencies and "no missing files" translation (#3837) 2025-03-16 19:39:19 -04:00
user fix(ui): make self-service profile edits report their outcome (#5699) 2026-07-01 21:31:29 -04:00
utils fix(ui): use div for fragment, check lastfm url for artist page (#4980) 2026-02-04 17:34:26 -05:00
App.jsx fix(ui): load ND_DEFAULTLANGUAGE on app startup (#4000) 2026-06-09 19:27:15 -04:00
authProvider.js feat(server): add ExtAuth logout URL configuration (#5074) 2026-02-23 20:28:38 -05:00
config.js feat(ui): replace UI scrobble with reportPlayback and redesign NowPlaying panel (#5448) 2026-05-01 15:27:32 -04:00
consts.js fix(artwork): address WebP performance regression on low-power hardware (#5286) 2026-04-04 15:17:01 -04:00
eventStream.js fix(ui): keep the NowPlayingPanel badge in sync. 2025-06-29 11:35:10 -04:00
eventStream.test.js chore(deps): update all dependencies (#4618) 2025-10-25 17:05:16 -04:00
hotkeys.js Change "Go to current song" hotkey. 2022-12-18 20:58:01 -05:00
index.css chore(deps): bump JS dependencies 2025-03-07 19:47:08 -05:00
index.jsx fix(ui): service worker does not load new version of ui (#3402) 2024-10-16 21:39:14 -04:00
routes.jsx build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
setupTests.js Use the default import path for jest-dom. 2024-01-17 17:07:43 -05:00
sw.js fix(ui): service worker crashing on precacheAndRoute (#3528) 2024-12-08 17:43:34 -05:00
useChangeThemeColor.jsx build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
vite-env.d.ts build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00