---
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

<!-- SECTION:DESCRIPTION:BEGIN -->

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.
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria

<!-- AC:BEGIN -->

- [ ] #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
<!-- AC:END -->
