defmodule MusicLibraryWeb.ReleaseComponent do use MusicLibraryWeb, :live_component use Gettext, backend: MusicLibraryWeb.Gettext alias MusicLibrary.ScrobbleActivity def open(id), do: Fluxon.open_dialog(id) @impl true def mount(socket) do {:ok, socket |> assign(:can_scrobble?, ScrobbleActivity.can_scrobble?()) |> assign(:already_scrobbled, false)} end @impl true def update(%{record: record} = assigns, socket) do {:ok, socket |> assign(assigns) |> assign_async(:release_with_tracks, fn -> with {:ok, release} <- MusicBrainz.get_release(record.selected_release_id) do {:ok, %{release_with_tracks: MusicBrainz.Release.from_api_response(release)}} end end)} end def update(%{already_scrobbled: _already_scrobbled} = assigns, socket) do {:ok, assign(socket, assigns)} end @impl true def render(assigns) do ~H"""
<.sheet :if={@record.selected_release_id} id={@sheet_id} placement="right" class="min-w-xs sm:min-w-sm" >

{gettext("Tracks")}

<.button :if={@can_scrobble?} size="sm" disabled={@already_scrobbled} phx-click="scrobble_release" phx-target={@myself} phx-disable-with={gettext("Scrobbling...")} > {gettext("Scrobble release")} <.button :if={!@can_scrobble?} size="sm" href={LastFm.auth_url()}> {gettext("Connect your Last.fm account")}
<.async_result :let={release_with_tracks} assign={@release_with_tracks}> <:loading> {gettext("Loading release with tracks")} <.loading /> <:failed :let={_failure}>
<.icon name="hero-exclamation-triangle" class="-mt-1 mr-1 h-5 w-5" aria-hidden="true" data-slot="icon" /> {gettext("Error loading tracks")}
1} class="flex justify-between items-center gap-4" >

{medium_title(medium)}

<.button :if={@can_scrobble?} size="sm" disabled={@already_scrobbled} phx-click="scrobble_medium" phx-value-number={medium.number} phx-target={@myself} phx-disable-with={gettext("Scrobbling...")} > {gettext("Scrobble disc")}
  • {track.number || track.position} {track.title} {track.length && format_duration(track.length)}
    {Enum.map_join(track.artists, ", ", fn artist -> artist.name end)}
<.separator />

{medium_duration(medium)}

""" end @impl true def handle_event("scrobble_release", _params, socket) do release_with_tracks_async_result = socket.assigns.release_with_tracks if release_with_tracks = release_with_tracks_async_result && release_with_tracks_async_result.result do case ScrobbleActivity.scrobble_release(release_with_tracks, finished_at: DateTime.utc_now()) do {:ok, _} -> send_update_after(socket.assigns.myself, %{already_scrobbled: false}, 3000) {:noreply, socket |> assign(:already_scrobbled, true) |> put_toast(:info, gettext("Release scrobbled successfully"))} {:error, reason} -> {:noreply, socket |> put_toast( :error, gettext("Error scrobbling release") <> "," <> inspect(reason) )} end else {:noreply, socket |> put_toast(:error, gettext("Error scrobbling release"))} end end def handle_event("scrobble_medium", %{"number" => number}, socket) do release_with_tracks_async_result = socket.assigns.release_with_tracks number = String.to_integer(number) if release_with_tracks = release_with_tracks_async_result && release_with_tracks_async_result.result do case ScrobbleActivity.scrobble_medium(number, release_with_tracks, finished_at: DateTime.utc_now() ) do {:ok, _} -> send_update_after(socket.assigns.myself, %{already_scrobbled: false}, 3000) {:noreply, socket |> assign(:already_scrobbled, true) |> put_toast(:info, gettext("Disc scrobbled successfully"))} {:error, reason} -> {:noreply, socket |> put_toast( :error, gettext("Error scrobbling disc") <> "," <> inspect(reason) )} end else {:noreply, socket |> put_toast(:error, gettext("Error scrobbling disc"))} end end defp medium_duration(medium) do medium |> MusicBrainz.Release.medium_duration() |> format_duration() end defp medium_title(medium) do if medium.title === "" do gettext("Disc %{no}", %{no: medium.number}) else medium.title end end defp format_duration(milliseconds) do milliseconds |> System.convert_time_unit(:millisecond, :second) |> format_seconds() end defp format_seconds(seconds) when seconds <= 59 do "0:#{zero_pad(seconds)}" end defp format_seconds(seconds) do minutes = div(seconds, 60) remaining_seconds = rem(seconds, 60) format_minutes(minutes, remaining_seconds) end defp format_minutes(minutes, seconds) when minutes <= 59 do "#{minutes}:#{zero_pad(seconds)}" end defp format_minutes(minutes, seconds) do hours = div(minutes, 60) remaining_minutes = rem(minutes, 60) format_hours(hours, remaining_minutes, seconds) end defp format_hours(hours, minutes, seconds) do "#{hours}:#{zero_pad(minutes)}:#{zero_pad(seconds)}" end defp zero_pad(integer) do integer |> to_string() |> String.pad_leading(2, "0") end end