Replace /scrobble/:release_id with nested route

This commit is contained in:
Claudio Ortolina
2026-04-23 14:39:26 +01:00
parent d8041dc0dd
commit 0dcd6eab83
8 changed files with 137 additions and 606 deletions
@@ -69,7 +69,7 @@ defmodule MusicLibraryWeb.ScrobbleLive.ReleaseGroupShow do
<ul class="mt-6 divide-y divide-zinc-100 dark:divide-slate-300/30"> <ul class="mt-6 divide-y divide-zinc-100 dark:divide-slate-300/30">
<li :for={release <- data.releases}> <li :for={release <- data.releases}>
<.link <.link
navigate={"/scrobble/#{@rg_id}/releases/#{release.id}"} navigate={~p"/scrobble/#{@rg_id}/releases/#{release.id}"}
class="flex items-center gap-x-4 px-4 py-5 transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-700" class="flex items-center gap-x-4 px-4 py-5 transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-700"
> >
<img <img
@@ -0,0 +1,65 @@
defmodule MusicLibraryWeb.ScrobbleLive.ReleaseShow do
@moduledoc false
use MusicLibraryWeb, :live_view
alias MusicLibrary.ScrobbleActivity
@impl true
def render(assigns) do
~H"""
<Layouts.app
flash={@flash}
current_section={@current_section}
socket={@socket}
toasts_sync={assigns[:toasts_sync]}
>
<.alert
:if={not @can_scrobble?}
color="warning"
title={gettext("Last.fm not connected")}
hide_close
>
{gettext(
"You need to connect your Last.fm account to scrobble. Please set up your Last.fm session key in the settings."
)}
</.alert>
<div class="my-4">
<.button variant="ghost" size="sm" navigate={~p"/scrobble/#{@rg_id}"}>
<.icon name="hero-arrow-left" class="icon" aria-hidden="true" data-slot="icon" />
{gettext("Back to releases")}
</.button>
</div>
<.live_component
id="scrobble-release"
sheet_id="scrobble-release"
module={MusicLibraryWeb.Components.Release}
release_id={@release_id}
show_print?={true}
timezone={@timezone}
/>
</Layouts.app>
"""
end
@impl true
def mount(_params, _session, socket) do
{:ok,
assign(socket,
current_section: :scrobble,
can_scrobble?: ScrobbleActivity.can_scrobble?(),
rg_id: nil,
release_id: nil,
page_title: gettext("Scrobble Release")
)}
end
@impl true
def handle_params(%{"rg_id" => rg_id, "release_id" => release_id}, _url, socket) do
{:noreply,
socket
|> assign(:rg_id, rg_id)
|> assign(:release_id, release_id)}
end
end
@@ -1,266 +0,0 @@
defmodule MusicLibraryWeb.ScrobbleLive.Show do
use MusicLibraryWeb, :live_view
import(MusicLibraryWeb.Components.Release, only: [medium: 1])
import MusicLibraryWeb.RecordComponents, only: [country_label: 1]
alias MusicLibrary.ScrobbleActivity
alias MusicLibraryWeb.ErrorMessages
@impl true
def render(assigns) do
~H"""
<Layouts.app
flash={@flash}
current_section={@current_section}
socket={@socket}
toasts_sync={assigns[:toasts_sync]}
>
<div>
<.alert
:if={not @can_scrobble}
color="warning"
title={gettext("Last.fm not connected")}
hide_close
>
{gettext(
"You need to connect your Last.fm account to scrobble. Please set up your Last.fm session key in the settings."
)}
</.alert>
<div class="my-4">
<.button variant="ghost" size="sm" navigate={~p"/scrobble"}>
<.icon name="hero-arrow-left" class="icon" aria-hidden="true" data-slot="icon" />
{gettext("Back to search")}
</.button>
</div>
<div class="mt-4 px-4 md:flex md:gap-x-4">
<div class="drop-shadow-sm md:max-w-152 lg:min-w-152">
<img
src={MusicBrainz.Release.thumb_url(@release)}
alt={"Cover art for #{@release.title}"}
class="w-full rounded-lg drop-shadow-sm"
loading="lazy"
onerror={"this.src = '" <> ~p"/images/cover-not-found.png" <> "';"}
/>
</div>
<div class="grow">
<div class="mt-4 flex items-center justify-between md:mt-0">
<h1 :if={@release.artists != []} class="text-base/6 font-medium text-zinc-700">
{@release.artists |> Enum.map(& &1.name) |> Enum.join(", ")}
</h1>
</div>
<h2 class="mt-1 flex text-lg font-semibold text-wrap text-zinc-700 md:text-2xl dark:text-zinc-300">
{@release.title}
</h2>
<div class="mt-4 md:mt-8">
<dl class="divide-y divide-zinc-100 dark:divide-slate-300/30">
<.dl_row :if={@release.date} label={gettext("Release Date")}>
{@release.date}
</.dl_row>
<.dl_row :if={@release.country} label={gettext("Country")}>
{country_label(@release.country)} {@release.country}
</.dl_row>
<.dl_row :if={@release.barcode} label={gettext("Barcode")}>
<code>{@release.barcode}</code>
</.dl_row>
<.dl_row :if={@release.catalog_number} label={gettext("Catalog Number")}>
<code>{@release.catalog_number}</code>
</.dl_row>
<.dl_row :if={@release.media != []} label={gettext("Media")}>
{ngettext("1 disc", "%{count} discs", MusicBrainz.Release.media_count(@release))}
</.dl_row>
</dl>
</div>
<.form
:if={@release.media != []}
for={@form}
id="scrobble-release-form"
class="mt-6 space-y-4"
phx-change="validate"
phx-auto-recover="recover_form"
>
<input type="hidden" name="release[selected_tracks][]" value="" />
<input type="hidden" name="release[toggle_medium][]" value="" />
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold text-zinc-900 dark:text-zinc-200">
{gettext("Tracks")}
</h3>
<.button
:if={@can_scrobble}
type="button"
variant="soft"
size="sm"
phx-click={
if MapSet.size(@selected_tracks) > 0,
do: "scrobble_selected_tracks",
else: "scrobble_release"
}
>
<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>
<.medium
:for={medium <- @release.media}
can_scrobble?={@can_scrobble}
already_scrobbled={false}
medium={medium}
release_artists={@release.artists}
selected_tracks={@selected_tracks}
media_count={MusicBrainz.Release.media_count(@release)}
myself={nil}
show_print?={false}
/>
</.form>
</div>
</div>
</div>
</Layouts.app>
"""
end
@impl true
def mount(_params, _session, socket) do
{:ok,
assign(socket,
current_section: :scrobble,
release: nil,
can_scrobble: ScrobbleActivity.can_scrobble?(),
page_title: "Scrobble Release",
selected_tracks: MapSet.new(),
form: to_form(%{}, as: :release)
)}
end
@impl true
def handle_params(params, _url, socket) do
apply_action(socket, socket.assigns.live_action, params)
end
defp apply_action(socket, :show, %{"release_id" => release_id}) do
case MusicBrainz.get_release(release_id) do
{:ok, release} ->
release = MusicBrainz.Release.from_api_response(release)
{:noreply,
assign(socket,
release: release,
page_title: "Scrobble: #{release.title}"
)}
{:error, _reason} ->
{:noreply,
socket
|> put_flash(:error, "Failed to fetch release details")
|> push_navigate(to: ~p"/scrobble")}
end
end
@impl true
def handle_event("scrobble_release", _params, socket) do
case ScrobbleActivity.scrobble_release(
socket.assigns.release,
:finished_at,
DateTime.utc_now()
) do
{:ok, _} ->
{:noreply,
socket
|> put_toast(:info, gettext("Release scrobbled successfully"))}
{:error, reason} ->
{:noreply,
socket
|> put_toast(
:error,
gettext("Error scrobbling release") <> ": " <> ErrorMessages.friendly_message(reason)
)}
end
end
def handle_event("scrobble_medium", %{"number" => number}, socket) do
{number, ""} = Integer.parse(number)
case ScrobbleActivity.scrobble_medium(
number,
socket.assigns.release,
:finished_at,
DateTime.utc_now()
) do
{:ok, _} ->
{:noreply,
socket
|> put_toast(:info, gettext("Disc scrobbled successfully"))}
{:error, reason} ->
{:noreply,
socket
|> put_toast(
:error,
gettext("Error scrobbling disc") <> ": " <> ErrorMessages.friendly_message(reason)
)}
end
end
def handle_event("scrobble_selected_tracks", _params, socket) do
release_with_tracks = socket.assigns.release
selected_track_ids = socket.assigns.selected_tracks
if MapSet.size(selected_track_ids) == 0 do
{:noreply, socket |> put_toast(:error, gettext("No tracks selected"))}
else
case ScrobbleActivity.scrobble_tracks(
selected_track_ids,
release_with_tracks,
:finished_at,
DateTime.utc_now()
) do
{:ok, _} ->
{:noreply,
socket
|> assign(:selected_tracks, MapSet.new())
|> put_toast(:info, gettext("Selected tracks scrobbled successfully"))}
{:error, reason} ->
{:noreply,
socket
|> put_toast(
:error,
gettext("Error scrobbling selected tracks") <>
": " <> ErrorMessages.friendly_message(reason)
)}
end
end
end
def handle_event("validate", %{"release" => params}, socket) do
if socket.assigns.release do
new_selected =
MusicLibraryWeb.Components.Release.apply_form_params(
socket.assigns.release,
params,
socket.assigns.selected_tracks
)
{:noreply, assign(socket, :selected_tracks, new_selected)}
else
{:noreply, socket}
end
end
def handle_event("recover_form", params, socket) do
handle_event("validate", params, socket)
end
end
+1
View File
@@ -112,6 +112,7 @@ defmodule MusicLibraryWeb.Router do
live "/scrobble", ScrobbleLive.Index, :index live "/scrobble", ScrobbleLive.Index, :index
live "/scrobble/:rg_id", ScrobbleLive.ReleaseGroupShow, :show live "/scrobble/:rg_id", ScrobbleLive.ReleaseGroupShow, :show
live "/scrobble/:rg_id/releases/:release_id", ScrobbleLive.ReleaseShow, :show
live "/maintenance", MaintenanceLive.Index, :index live "/maintenance", MaintenanceLive.Index, :index
end end
+12 -39
View File
@@ -551,7 +551,6 @@ msgstr ""
msgid "Albums" msgid "Albums"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#: lib/music_library_web/live/stats_live/index.ex #: lib/music_library_web/live/stats_live/index.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tracks" msgid "Tracks"
@@ -594,7 +593,6 @@ msgid "Oban"
msgstr "" msgstr ""
#: lib/music_library_web/components/record_form.ex #: lib/music_library_web/components/record_form.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Release Date" msgid "Release Date"
msgstr "" msgstr ""
@@ -642,14 +640,12 @@ msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Error scrobbling release" msgid "Error scrobbling release"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Release scrobbled successfully" msgid "Release scrobbled successfully"
msgstr "" msgstr ""
@@ -666,42 +662,31 @@ msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Scrobble release" msgid "Scrobble release"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Scrobble selected tracks"
msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Selected tracks scrobbled successfully" msgid "Selected tracks scrobbled successfully"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Error scrobbling selected tracks" msgid "Error scrobbling selected tracks"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No tracks selected" msgid "No tracks selected"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Disc scrobbled successfully" msgid "Disc scrobbled successfully"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Error scrobbling disc" msgid "Error scrobbling disc"
msgstr "" msgstr ""
@@ -1288,7 +1273,6 @@ msgstr ""
#: lib/music_library_web/live/scrobble_live/index.ex #: lib/music_library_web/live/scrobble_live/index.ex
#: lib/music_library_web/live/scrobble_live/release_group_show.ex #: lib/music_library_web/live/scrobble_live/release_group_show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "1 disc" msgid "1 disc"
msgid_plural "%{count} discs" msgid_plural "%{count} discs"
@@ -1300,7 +1284,7 @@ msgstr[1] ""
msgid "Releases for \"%{title}\"" msgid "Releases for \"%{title}\""
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex #: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You need to connect your Last.fm account to scrobble. Please set up your Last.fm session key in the settings." msgid "You need to connect your Last.fm account to scrobble. Please set up your Last.fm session key in the settings."
msgstr "" msgstr ""
@@ -1722,27 +1706,11 @@ msgid "Back"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/release_group_show.ex #: lib/music_library_web/live/scrobble_live/release_group_show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Back to search" msgid "Back to search"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex #: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format
msgid "Barcode"
msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Catalog Number"
msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Country"
msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Last.fm not connected" msgid "Last.fm not connected"
msgstr "" msgstr ""
@@ -1752,11 +1720,6 @@ msgstr ""
msgid "Loading releases..." msgid "Loading releases..."
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Media"
msgstr ""
#: lib/music_library_web/live/scrobble_live/index.ex #: lib/music_library_web/live/scrobble_live/index.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No release groups found for \"%{query}\"" msgid "No release groups found for \"%{query}\""
@@ -2580,3 +2543,13 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Error loading release group" msgid "Error loading release group"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format
msgid "Back to releases"
msgstr ""
#: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format
msgid "Scrobble Release"
msgstr ""
+12 -39
View File
@@ -551,7 +551,6 @@ msgstr ""
msgid "Albums" msgid "Albums"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#: lib/music_library_web/live/stats_live/index.ex #: lib/music_library_web/live/stats_live/index.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tracks" msgid "Tracks"
@@ -594,7 +593,6 @@ msgid "Oban"
msgstr "" msgstr ""
#: lib/music_library_web/components/record_form.ex #: lib/music_library_web/components/record_form.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Release Date" msgid "Release Date"
msgstr "" msgstr ""
@@ -642,14 +640,12 @@ msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Error scrobbling release" msgid "Error scrobbling release"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Release scrobbled successfully" msgid "Release scrobbled successfully"
msgstr "" msgstr ""
@@ -666,42 +662,31 @@ msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/collection_live/show.ex #: lib/music_library_web/live/collection_live/show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Scrobble release" msgid "Scrobble release"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Scrobble selected tracks"
msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Selected tracks scrobbled successfully" msgid "Selected tracks scrobbled successfully"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Error scrobbling selected tracks" msgid "Error scrobbling selected tracks"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No tracks selected" msgid "No tracks selected"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Disc scrobbled successfully" msgid "Disc scrobbled successfully"
msgstr "" msgstr ""
#: lib/music_library_web/components/release.ex #: lib/music_library_web/components/release.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Error scrobbling disc" msgid "Error scrobbling disc"
msgstr "" msgstr ""
@@ -1288,7 +1273,6 @@ msgstr ""
#: lib/music_library_web/live/scrobble_live/index.ex #: lib/music_library_web/live/scrobble_live/index.ex
#: lib/music_library_web/live/scrobble_live/release_group_show.ex #: lib/music_library_web/live/scrobble_live/release_group_show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "1 disc" msgid "1 disc"
msgid_plural "%{count} discs" msgid_plural "%{count} discs"
@@ -1300,7 +1284,7 @@ msgstr[1] ""
msgid "Releases for \"%{title}\"" msgid "Releases for \"%{title}\""
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex #: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You need to connect your Last.fm account to scrobble. Please set up your Last.fm session key in the settings." msgid "You need to connect your Last.fm account to scrobble. Please set up your Last.fm session key in the settings."
msgstr "" msgstr ""
@@ -1722,27 +1706,11 @@ msgid "Back"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/release_group_show.ex #: lib/music_library_web/live/scrobble_live/release_group_show.ex
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Back to search" msgid "Back to search"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex #: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Barcode"
msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Catalog Number"
msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Country"
msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Last.fm not connected" msgid "Last.fm not connected"
msgstr "" msgstr ""
@@ -1752,11 +1720,6 @@ msgstr ""
msgid "Loading releases..." msgid "Loading releases..."
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/show.ex
#, elixir-autogen, elixir-format
msgid "Media"
msgstr ""
#: lib/music_library_web/live/scrobble_live/index.ex #: lib/music_library_web/live/scrobble_live/index.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No release groups found for \"%{query}\"" msgid "No release groups found for \"%{query}\""
@@ -2580,3 +2543,13 @@ msgstr ""
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Error loading release group" msgid "Error loading release group"
msgstr "" msgstr ""
#: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Back to releases"
msgstr ""
#: lib/music_library_web/live/scrobble_live/release_show.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Scrobble Release"
msgstr ""
@@ -0,0 +1,46 @@
defmodule MusicLibraryWeb.ScrobbleLive.ReleaseShowTest do
@moduledoc """
Smoke tests for the scrobble release page. Full scrobble behaviour
(picker, selection bar, handlers) is covered by the Release
LiveComponent tests in `release_test.exs`.
"""
use MusicLibraryWeb.ConnCase
alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures
alias MusicBrainz.Fixtures.ReleaseGroup
alias Req.Test
@rg_id ReleaseGroup.release_group_id(:marbles)
@release_id ReleaseFixtures.release_id(:marbles)
defp stub_mb(_) 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 "ReleaseShow" do
setup [:stub_mb]
test "renders the scrobble UI for the release", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@rg_id}/releases/#{@release_id}")
|> unwrap(&Phoenix.LiveViewTest.render_async/1)
|> assert_has("h2", text: "Marbles")
end
test "back link targets the release-group page", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@rg_id}/releases/#{@release_id}")
|> assert_has("a[href='/scrobble/#{@rg_id}']", text: "Back to releases")
end
end
end
@@ -1,261 +0,0 @@
defmodule MusicLibraryWeb.ScrobbleLive.ShowTest do
use MusicLibraryWeb.ConnCase
# Route /scrobble/:release_id is removed in Phase 5 — skip until then
@moduletag :skip
import Phoenix.LiveViewTest, only: [element: 2, render_change: 2, render_click: 3]
alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures
alias MusicLibrary.Secrets
alias Req.Test
@release_id ReleaseFixtures.release_id(:marbles)
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
defp stub_musicbrainz_release_error(_) do
Test.stub(MusicBrainz.API, fn conn ->
Plug.Conn.send_resp(conn, 404, "Not Found")
end)
:ok
end
defp stub_lastfm_scrobble(_) do
Test.stub(LastFm.API, fn conn ->
Test.json(conn, %{"scrobbles" => %{"@attr" => %{"accepted" => 1}}})
end)
:ok
end
defp stub_lastfm_scrobble_error(_) do
Test.stub(LastFm.API, fn conn ->
Test.json(conn, %{"error" => 11, "message" => "Service temporarily unavailable"})
end)
:ok
end
defp store_lastfm_session_key(_) do
Secrets.store("last_fm_session_key", "test_session_key")
:ok
end
defp first_track_id do
ReleaseFixtures.release_with_media(:marbles)
|> Map.get("media")
|> List.first()
|> Map.get("tracks")
|> List.first()
|> Map.get("id")
end
describe "Show" do
setup [:stub_musicbrainz_release]
test "renders release details", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_has("h2", "Marbles")
|> assert_has("a", "Back to search")
end
test "shows Last.fm not connected alert when no session key", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_has("div", "You need to connect your Last.fm account")
end
test "displays tracks", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_has("h3", "Tracks")
end
end
describe "Show with Last.fm connected" do
setup [:stub_musicbrainz_release, :stub_lastfm_scrobble, :store_lastfm_session_key]
test "does not show Last.fm not connected alert", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> refute_has("[data-part='title']", "Last.fm not connected")
end
test "scrobble full release", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_release", %{})
end)
|> assert_has("#toast-group", "Release scrobbled successfully")
end
test "scrobble single medium", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_medium", %{"number" => "1"})
end)
|> assert_has("#toast-group", "Disc scrobbled successfully")
end
test "scrobble single medium still works with tracks selected elsewhere", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
# Select a track first — AC#3 regression: medium scrobble must not be
# blocked just because something is ticked.
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
render_click(view, "scrobble_medium", %{"number" => "1"})
end)
|> assert_has("#toast-group", "Disc scrobbled successfully")
end
test "toggle track on and off changes button label", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
# Toggle track on — button label changes
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
end)
|> assert_has("button", "Scrobble selected tracks")
# Toggle track off — button label reverts
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => []}})
end)
|> assert_has("button", "Scrobble release")
end
test "toggle medium selects and deselects all tracks in that medium", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
# Toggle medium 1 on — button label changes
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"toggle_medium" => ["1"]}})
end)
|> assert_has("button", "Scrobble selected tracks")
# Toggle medium 1 off — button label reverts
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"toggle_medium" => []}})
end)
|> assert_has("button", "Scrobble release")
end
test "scrobble selected tracks", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
render_click(view, "scrobble_selected_tracks", %{})
end)
|> assert_has("#toast-group", "Selected tracks scrobbled successfully")
end
test "scrobble selected tracks with no selection shows error", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_selected_tracks", %{})
end)
|> assert_has("#toast-group", "No tracks selected")
end
end
describe "Show with Last.fm scrobble error" do
setup [:stub_musicbrainz_release, :stub_lastfm_scrobble_error, :store_lastfm_session_key]
@tag :capture_log
test "scrobble release shows error toast on failure", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_release", %{})
end)
|> assert_has("#toast-group", "Error scrobbling release")
end
@tag :capture_log
test "scrobble medium shows error toast on failure", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_medium", %{"number" => "1"})
end)
|> assert_has("#toast-group", "Error scrobbling disc")
end
@tag :capture_log
test "scrobble selected tracks shows error toast on failure", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
render_click(view, "scrobble_selected_tracks", %{})
end)
|> assert_has("#toast-group", "Error scrobbling selected tracks")
end
end
describe "Show with failed release fetch" do
setup [:stub_musicbrainz_release_error]
test "redirects to scrobble index with error", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_path(~p"/scrobble")
end
end
end