--- id: ML-142.1 title: Port Finished at picker and selection bar to ScrobbleLive.Show status: To Do assignee: [] created_date: "2026-04-22 16:30" labels: - ui - scrobble dependencies: [] references: - lib/music_library_web/live/scrobble_live/show.ex - lib/music_library_web/components/release.ex - backlog/ml-142/plan.md parent_task_id: ML-142 priority: medium --- ## Description Bring the `/scrobble/:release_id` page (`MusicLibraryWeb.ScrobbleLive.Show`) up to ML-142 parity for the two affordances that affect scrobbling outcomes: the `Finished at` date/time picker and the pinned selection-bar footer. Today the page imports `medium/1` and `scrobble_button_label/1` from `MusicLibraryWeb.Components.Release` — so the per-medium UI stays visually consistent — but reimplements its own `scrobble_release`, `scrobble_medium`, and `scrobble_selected_tracks` handlers, each hard-coding `DateTime.utc_now/0`. There is no picker (users cannot back-date or forward-date a scrobble) and no selection bar (when tracks are selected, the user sees no count/duration summary and the only scrobble affordance is an icon-only header button whose behaviour depends on hidden selection state via `scrobble_button_label/1`). `ScrobbleLive.Show` was explicitly called out as out-of-scope in ML-142 — see `backlog/ml-142/plan.md`: "A full sticky-bar + picker port to `ScrobbleLive.Show`. That page uses its own layout (not the sheet) and can be iterated on separately once the LiveComponent pattern is settled." That deferred work is the subject of this task. ## Out of scope - `⋯` overflow menu on this page (Print tracklist for release + per-medium). - Connect Last.fm dropdown entry — the page already has a top-of-page alert banner for that state. - Extracting a shared function component or LiveHelper is an implementation decision, not a requirement. Implementer may duplicate or refactor; the acceptance criteria are outcome-based. ## Reusable pieces the implementer can draw on - `MusicLibraryWeb.Components.Release.apply_form_params/3` — already public, already used by this LiveView. - `MusicLibraryWeb.Components.Release.medium/1` — already imported. - `MusicLibrary.ScrobbleActivity.scrobble_release/3`, `scrobble_medium/4`, `scrobble_tracks/4` — already used. - Private helpers in `Release` that may need to be promoted (implementer's choice): `parse_finished_at/1`, `selection_bar/1`, `selected_tracks_summary/2`. - `scrobble_button_label/1` will likely become unused after AC#8 — remove if so. - `lib/music_library_web/live_helpers/` pattern (e.g. `record_actions.ex`) is a possible refactor target but not required. ## Acceptance Criteria - [ ] #1 The page renders a `Finished at` date/time picker in the tracks section header when Last.fm is connected, initialised to the current time at mount - [ ] #2 The picker has a `Now` reset affordance that resets the value back to the current time - [ ] #3 `Scrobble release` uses the picker value; no longer calls `DateTime.utc_now/0` directly inside the handler - [ ] #4 Per-medium `Scrobble disc`/`Scrobble side` uses the picker value; no longer calls `DateTime.utc_now/0` directly inside the handler - [ ] #5 `Scrobble selected tracks` uses the picker value; no longer calls `DateTime.utc_now/0` directly inside the handler - [ ] #6 A selection bar renders at the bottom of the tracks section whenever `MapSet.size(@selected_tracks) > 0`, showing track count, medium count (when > 1), aggregate duration, and a `Scrobble selected` button - [ ] #7 The selection bar is not rendered when no tracks are selected - [ ] #8 The selection bar stays visible while the tracks list is scrolled on narrow viewports (`position: sticky` or equivalent layout — implementer's choice given this page sits inside `Layouts.app`, not the sheet) - [ ] #9 The icon-only header scrobble button (which currently toggles via `scrobble_button_label/1`) is replaced by a solid `Scrobble release` button; the selection bar owns the scrobble-selected role - [ ] #10 All new user-facing strings wrapped in `gettext`; `.pot`/`.po` files regenerated via `mix gettext.extract --merge` - [ ] #11 LiveView tests cover: picker default state initialised to `utc_now`, picker value propagating to all three scrobble handlers, `reset_to_now` event, selection-bar hidden when no tracks selected, selection-bar shown with correct copy when tracks selected - [ ] #12 The existing AC#14 regression test (per-medium scrobble works with cross-medium tracks selected, at `test/music_library_web/live/scrobble_live/show_test.exs:115`) continues to pass