7.8 KiB
id, title, status, assignee, created_date, labels, dependencies, references, documentation, priority
| id | title | status | assignee | created_date | labels | dependencies | references | documentation | priority | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ML-145 | Restructure /scrobble routes; reuse Release component on scrobble page | To Do | 2026-04-23 06:11 |
|
|
|
medium |
Description
Why
/scrobble/:release_id (MusicLibraryWeb.ScrobbleLive.Show) re-implements scrobbling UI that already exists as a LiveComponent used by collection/wishlist show pages (MusicLibraryWeb.Components.Release). Today the scrobble page imports medium/1 and scrobble_button_label/1 from that component but reimplements scrobble_release / scrobble_medium / scrobble_selected_tracks with hard-coded DateTime.utc_now/0, no Finished at picker, and no selection bar. ML-142.1 proposed porting those affordances into ScrobbleLive.Show in parallel — which would leave two drifting implementations.
This task takes the opposite approach: make the Release LiveComponent reusable outside the sheet (currently tightly coupled to %Records.Record{} and wrapped in <.sheet>), restructure the scrobble routes into a bookmarkable hierarchy, and delete the custom scrobble UI in favour of reuse.
Supersedes ML-142.1 — the ML-142.1 ACs become free once the component is reused on the new scrobble page. ML-142.1 can be archived as superseded.
What
Three routes:
/scrobble— search only. Release-group clicks<.link navigate>to/scrobble/:rg_id(no more inline release-loading state)./scrobble/:rg_id(new) — release-group header (cover, title, primary artist, type badge, first-release date, release count) + full list of releases; each release links to/scrobble/:rg_id/releases/:release_id./scrobble/:rg_id/releases/:release_id(new, replaces/scrobble/:release_id) — the scrobble page itself, rendered by the same LiveComponent that powers the collection/wishlist show sheet.
Old /scrobble/:release_id → 404 (personal app; bookmark churn is acceptable, not worth 301-redirect plumbing).
Key refactor decisions (working design doc: .claude/plans/scrobble-route-restructure/design.md, local-only)
- Release LiveComponent input contract — takes
release_id(string) instead ofrecord; addsshow_print?: boolean. Header title/artists derive from the loaded%MusicBrainz.Release{}. <.sheet>moves to callsites — single render path for the component. Collection/wishlist show pages wrap in their own<.sheet>; the scrobble release page renders it directly under<Layouts.app>.- Selection bar stickiness — switches to
position: stickyso it works both inside a sheet (pins to sheet bottom) and on a page (pins to viewport bottom) with single markup, no mode flag. TracklistPdfsignatures —generate/1andgenerate_medium/2take a%MusicBrainz.Release{}; no%Records.Record{}required (both fields used by the PDF —titleandartists— already exist on the release struct).- Module names — new
MusicLibraryWeb.ScrobbleLive.ReleaseGroupShowat/scrobble/:rg_id; renameMusicLibraryWeb.ScrobbleLive.Show→MusicLibraryWeb.ScrobbleLive.ReleaseShowat/scrobble/:rg_id/releases/:release_id, removing its custom scrobble UI in favour of embedding the LiveComponent. - Unused after refactor —
MusicLibraryWeb.Components.Release.scrobble_button_label/1(delete).
Non-goals
- Scrobble-rule picker on the scrobble page (fresh MB releases have no misrecognised tracks yet).
- Cross-link to
/collection/:idwhen the release is already collected. - 301-redirecting old
/scrobble/:release_idURLs. - Breadcrumbs on the scrobble page.
- Preserving
?query=Xacross nested URLs (browser back button already works). - Splitting the Release LiveComponent into separate
Content/Sheetmodules. - Changes to
MusicLibrary.ScrobbleActivity.
Acceptance Criteria
- #1 /scrobble/:rg_id renders a release-group header (cover, release-group title, primary artist, type badge, first-release date, release count) and the full list of releases returned by MusicBrainz for that group.
- #2 Each release in the /scrobble/:rg_id list is a navigate link to /scrobble/:rg_id/releases/:release_id.
- #3 /scrobble/:rg_id shows an error toast and redirects to /scrobble if fetching the release group or its releases fails.
- #4 /scrobble/:rg_id/releases/:release_id renders a scrobble page with picker-driven
Finished at, release-level scrobble, per-medium scrobble, track-selection + selection-bar scrobble, and Print tracklist — behaviour identical to the collection/wishlist show sheet. - #5 /scrobble/:rg_id/releases/:release_id has a
Back to releaseslink that navigates to /scrobble/:rg_id. - #6 Old /scrobble/:release_id returns 404 (not redirected, not aliased).
- #7 /scrobble no longer renders releases inline; release-group list items are navigate links to /scrobble/:rg_id. The
?query=Xsearch param still works as before. - #8 The Release LiveComponent on the scrobble page and on the collection/wishlist sheet share a single implementation — there is no parallel reimplementation of scrobble handlers, picker, or selection bar.
- #9 The selection bar stays visible at the bottom of the visible scroll region in both contexts (scrobble page viewport, collection/wishlist sheet inner scroll) while tracks scroll, using a single markup path.
- #10 MusicLibrary.Records.TracklistPdf generates tracklist PDFs from a MusicBrainz release struct alone (no Records.Record required). Output for collection/wishlist records is visually unchanged.
- #11 The Release LiveComponent supports suppressing both the release-level and per-medium Print tracklist dropdown entries via a single input (both hidden together); the suppression state is covered by a component test.
- #12 MusicLibraryWeb.ScrobbleLive.Show's custom scrobble UI and handlers (scrobble_release, scrobble_medium, scrobble_selected_tracks, validate, recover_form) are deleted. MusicLibraryWeb.Components.Release.scrobble_button_label/1 is deleted as unused.
- #13 All new user-facing strings are wrapped in gettext; .pot/.po files regenerated via
mix gettext.extract --merge. - #14 Tests updated: test/music_library_web/components/release_test.exs covers the new
release_idinput contract and both states of the print-suppression input; test/music_library/records/tracklist_pdf_test.exs covers the new generate/1 and generate_medium/2 signatures. - #15 Tests added: test/music_library_web/live/scrobble_live/release_group_show_test.exs covers happy path (header fields + releases list + link targets) and fetch failure (toast + redirect); test/music_library_web/live/scrobble_live/release_show_test.exs (replacing show_test.exs) smoke-tests mount, component render, and back-link target.
- #16 Tests updated: test/music_library_web/live/scrobble_live/index_test.exs asserts release-group clicks navigate (no inline state); collection/wishlist show tests updated for the new component input shape and callsite sheet markup.
- #17 Manual UI verification via
iex -S mix phx.server: sheet selection-bar still pins correctly on collection/wishlist show pages; page selection-bar pins to viewport bottom on scrobble page while tracks list scrolls; navigation loop /scrobble → /scrobble/:rg_id → /scrobble/:rg_id/releases/:release_id → back → back works, and?query=Xsurvives the browser back button.