Refactor Release component to take release_id

This commit is contained in:
Claudio Ortolina
2026-04-23 08:03:36 +01:00
parent 0d1f580379
commit 4c48bda7b2
5 changed files with 274 additions and 181 deletions
+29 -37
View File
@@ -20,16 +20,14 @@ defmodule MusicLibraryWeb.Components.Release do
|> assign(:release_with_tracks, AsyncResult.loading())
|> assign(:already_scrobbled, false)
|> assign(:selected_tracks, MapSet.new())
|> assign(:timezone, MusicLibrary.default_timezone())
|> assign(:finished_at, nil)
|> assign(:form, to_form(%{"finished_at" => nil}, as: :release))
|> assign(:pending_form_params, nil)}
end
@impl true
def update(%{record: record} = assigns, socket) do
def update(%{release_id: release_id} = assigns, socket) do
socket = assign(socket, assigns)
current_time = DateTime.utc_now() |> DateTime.shift_zone!(socket.assigns.timezone)
timezone = socket.assigns.timezone
current_time = DateTime.utc_now() |> DateTime.shift_zone!(timezone)
{:ok,
socket
@@ -37,7 +35,7 @@ defmodule MusicLibraryWeb.Components.Release do
|> assign(:form, to_form(%{"finished_at" => DateTime.to_naive(current_time)}, as: :release))
|> assign(:release_with_tracks, AsyncResult.loading())
|> start_async(:release_with_tracks, fn ->
load_release_with_tracks(record.selected_release_id)
load_release_with_tracks(release_id)
end)}
end
@@ -116,12 +114,6 @@ defmodule MusicLibraryWeb.Components.Release do
def render(assigns) do
~H"""
<div>
<.sheet
:if={@record.selected_release_id}
id={@sheet_id}
placement="right"
class="flex min-w-xs flex-col overflow-hidden p-0 sm:min-w-sm"
>
<.form
for={@form}
id={"#{@sheet_id}-form"}
@@ -137,10 +129,10 @@ defmodule MusicLibraryWeb.Components.Release do
<div class="mt-6 flex flex-wrap items-start justify-between gap-3">
<div class="min-w-0 flex-1">
<h2 class="truncate text-lg font-semibold text-zinc-900 dark:text-zinc-100">
{@record.title}
{header_title(@release_with_tracks)}
</h2>
<p
:if={artist_names = header_subtitle(@record)}
:if={artist_names = header_subtitle(@release_with_tracks)}
class="truncate text-xs text-zinc-500 dark:text-zinc-400"
>
{artist_names}
@@ -198,7 +190,7 @@ defmodule MusicLibraryWeb.Components.Release do
</:toggle>
<.focus_wrap id={"#{@sheet_id}-release-actions-focus-wrap"}>
<.dropdown_link
:if={@release_with_tracks.ok?}
:if={@show_print? && @release_with_tracks.ok?}
phx-click="print_tracklist"
phx-target={@myself}
>
@@ -251,7 +243,7 @@ defmodule MusicLibraryWeb.Components.Release do
media_count={MusicBrainz.Release.media_count(release_with_tracks)}
selected_tracks={@selected_tracks}
myself={@myself}
record={@record}
show_print?={@show_print?}
/>
</.async_result>
</div>
@@ -265,7 +257,6 @@ defmodule MusicLibraryWeb.Components.Release do
myself={@myself}
/>
</.form>
</.sheet>
</div>
"""
end
@@ -277,7 +268,7 @@ defmodule MusicLibraryWeb.Components.Release do
attr :already_scrobbled, :boolean, required: true
attr :selected_tracks, :any, required: true
attr :myself, :any, required: true
attr :record, :any, default: nil
attr :show_print?, :boolean, required: true
def medium(assigns) do
~H"""
@@ -317,7 +308,7 @@ defmodule MusicLibraryWeb.Components.Release do
<span class="sr-only sm:hidden">{medium_scrobble_label(@medium.format)}</span>
</.button>
<.dropdown
:if={@record}
:if={@show_print?}
id={"medium-actions-#{@medium.number}"}
placement="bottom-end"
>
@@ -435,7 +426,7 @@ defmodule MusicLibraryWeb.Components.Release do
)
~H"""
<div class="flex flex-wrap items-center justify-between gap-3 border-t border-zinc-200 bg-white px-6 py-3 shadow-[0_-4px_14px_rgba(0,0,0,0.06)] dark:border-zinc-700 dark:bg-zinc-900">
<div class="sticky bottom-0 z-10 flex flex-wrap items-center justify-between gap-3 border-t border-zinc-200 bg-white px-6 py-3 shadow-[0_-4px_14px_rgba(0,0,0,0.06)] dark:border-zinc-700 dark:bg-zinc-900">
<div class="min-w-0 flex-1 leading-tight">
<p class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
{ngettext("%{count} track selected", "%{count} tracks selected", @count, count: @count)}
@@ -618,11 +609,10 @@ defmodule MusicLibraryWeb.Components.Release do
def handle_event("print_tracklist", _params, socket) when release_loaded?(socket.assigns) do
release = socket.assigns.release_with_tracks.result
record = socket.assigns.record
case TracklistPdf.generate(release) do
{:ok, pdf_binary} ->
filename = "#{record.title} - Tracklist.pdf"
filename = "#{release.title} - Tracklist.pdf"
{:noreply,
push_event(socket, "music_library:download", %{
@@ -651,12 +641,11 @@ defmodule MusicLibraryWeb.Components.Release do
def handle_event("print_medium_tracklist", %{"medium-number" => number}, socket)
when release_loaded?(socket.assigns) do
release = socket.assigns.release_with_tracks.result
record = socket.assigns.record
{number, ""} = Integer.parse(number)
case TracklistPdf.generate_medium(release, number) do
{:ok, pdf_binary} ->
filename = "#{record.title} - Disc #{number} - Tracklist.pdf"
filename = "#{release.title} - Disc #{number} - Tracklist.pdf"
{:noreply,
push_event(socket, "music_library:download", %{
@@ -683,7 +672,7 @@ defmodule MusicLibraryWeb.Components.Release do
end
def handle_event("load_release_tracks", _params, socket) do
selected_release_id = socket.assigns.record.selected_release_id
selected_release_id = socket.assigns.release_id
{:noreply,
socket
@@ -753,15 +742,6 @@ defmodule MusicLibraryWeb.Components.Release do
end)
end
@spec scrobble_button_label(MapSet.t()) :: String.t()
def scrobble_button_label(selected_tracks) do
if MapSet.size(selected_tracks) > 0 do
gettext("Scrobble selected tracks")
else
gettext("Scrobble release")
end
end
defp medium_duration(medium) do
medium
|> MusicBrainz.Release.medium_duration()
@@ -776,16 +756,28 @@ defmodule MusicLibraryWeb.Components.Release do
end
end
defp header_subtitle(%{artists: artists}) when is_list(artists) and artists != [] do
@spec header_title(AsyncResult.t() | term()) :: String.t()
defp header_title(%AsyncResult{ok?: true, result: release}), do: release.title
defp header_title(_), do: ""
@spec header_subtitle(AsyncResult.t() | term()) :: String.t() | nil
defp header_subtitle(%AsyncResult{ok?: true, result: release}) do
case release.artists do
[] ->
nil
artists ->
artists
|> Enum.map_join(", ", & &1.name)
|> Enum.map_join(fn artist -> artist.name <> (artist.joinphrase || "") end)
|> String.trim()
|> case do
"" -> nil
names -> names
end
end
end
defp header_subtitle(_record), do: nil
defp header_subtitle(_), do: nil
@spec selected_tracks_summary(MusicBrainz.Release.t(), MapSet.t()) ::
{non_neg_integer(), non_neg_integer(), non_neg_integer()}
@@ -292,13 +292,21 @@ defmodule MusicLibraryWeb.CollectionLive.Show do
<.record_debug_sheet record={@record} embedding_text={@embedding_text} />
<.sheet
:if={@record.selected_release_id}
id="release-with-tracks-sheet"
placement="right"
class="flex min-w-xs flex-col overflow-hidden p-0 sm:min-w-sm"
>
<.live_component
id="release-with-tracks"
sheet_id="release-with-tracks-sheet"
module={MusicLibraryWeb.Components.Release}
record={@record}
release_id={@record.selected_release_id}
show_print?={true}
timezone={@timezone}
/>
</.sheet>
<.live_component
id="record-notes"
@@ -1,7 +1,7 @@
defmodule MusicLibraryWeb.ScrobbleLive.Show do
use MusicLibraryWeb, :live_view
import(MusicLibraryWeb.Components.Release, only: [medium: 1, scrobble_button_label: 1])
import(MusicLibraryWeb.Components.Release, only: [medium: 1])
import MusicLibraryWeb.RecordComponents, only: [country_label: 1]
alias MusicLibrary.ScrobbleActivity
@@ -103,7 +103,11 @@ defmodule MusicLibraryWeb.ScrobbleLive.Show do
else: "scrobble_release"
}
>
<span class="sr-only">{scrobble_button_label(@selected_tracks)}</span>
<span class="sr-only">
{if MapSet.size(@selected_tracks) > 0,
do: gettext("Scrobble selected tracks"),
else: gettext("Scrobble release")}
</span>
<.icon name="hero-play" class="icon" aria-hidden="true" data-slot="icon" />
</.button>
</div>
@@ -117,6 +121,7 @@ defmodule MusicLibraryWeb.ScrobbleLive.Show do
selected_tracks={@selected_tracks}
media_count={MusicBrainz.Release.media_count(@release)}
myself={nil}
show_print?={false}
/>
</.form>
</div>
+2 -1
View File
@@ -666,11 +666,12 @@ msgstr ""
#: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Scrobble release"
msgstr ""
#: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Scrobble selected tracks"
msgstr ""
@@ -12,7 +12,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do
import MusicLibrary.Fixtures.Records
import Phoenix.LiveViewTest,
only: [element: 2, render_async: 1, render_change: 2, render_click: 1]
only: [element: 2, render: 1, render_async: 1, render_change: 2, render_click: 1]
alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures
alias MusicLibrary.Secrets
@@ -288,3 +288,90 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do
end
end
end
defmodule ReleaseComponentHost do
@moduledoc false
use MusicLibraryWeb, :live_view
@impl true
def mount(_params, session, socket) do
{:ok,
assign(socket,
release_id: session["release_id"],
show_print?: session["show_print?"],
timezone: "UTC"
)}
end
@impl true
def render(assigns) do
~H"""
<div>
<.live_component
id="host-release"
module={MusicLibraryWeb.Components.Release}
release_id={@release_id}
show_print?={@show_print?}
sheet_id="host-sheet"
timezone={@timezone}
/>
</div>
"""
end
end
defmodule MusicLibraryWeb.Components.ReleaseTest.ShowPrintTest do
use MusicLibraryWeb.ConnCase, async: false
import Phoenix.LiveViewTest,
only: [render: 1, render_async: 1]
alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures
alias Req.Test
defp stub_musicbrainz_release(_) do
Test.stub(MusicBrainz.API, fn conn ->
case conn.request_path do
"/ws/2/release/" <> _id ->
Test.json(conn, ReleaseFixtures.release_with_media(:marbles))
_ ->
Test.json(conn, %{})
end
end)
:ok
end
describe "show_print? assign" do
setup [:stub_musicbrainz_release]
test "true renders Print tracklist dropdown entries", %{conn: conn} do
{:ok, view, _html} =
Phoenix.LiveViewTest.live_isolated(conn, ReleaseComponentHost,
session: %{
"release_id" => ReleaseFixtures.release_id(:marbles),
"show_print?" => true
}
)
render_async(view)
assert render(view) =~ "Print tracklist"
end
test "false hides Print tracklist dropdown entries", %{conn: conn} do
{:ok, view, _html} =
Phoenix.LiveViewTest.live_isolated(conn, ReleaseComponentHost,
session: %{
"release_id" => ReleaseFixtures.release_id(:marbles),
"show_print?" => false
}
)
render_async(view)
refute render(view) =~ "Print tracklist"
end
end
end