Toggle between recently scrobbled albums and tracks

This commit is contained in:
Claudio Ortolina
2025-03-14 10:30:20 +00:00
parent 6c9a504c5e
commit 84870f87ad
3 changed files with 215 additions and 14 deletions
+32 -4
View File
@@ -19,10 +19,13 @@ defmodule MusicLibraryWeb.StatsLive.Index do
|> stream_configure(:recent_tracks,
dom_id: fn track -> "track-#{track.scrobbled_at_uts}" end
)
|> stream(:recent_tracks, recent_tracks)
|> stream_configure(:recent_albums,
dom_id: fn album -> "album-#{album.scrobbled_at_uts}" end
)
|> assign_counts()
|> assign_scrobble_activity(recent_tracks)
|> assign(
scrobble_activity_mode: :tracks,
latest_record: latest_record,
page_title: gettext("Stats"),
nav_section: :stats
@@ -60,11 +63,20 @@ defmodule MusicLibraryWeb.StatsLive.Index do
end
end
def handle_info(%{tracks: recent_tracks}, socket) do
def handle_event("set_scrobble_activity_mode", %{"mode" => mode}, socket)
when mode in ["tracks", "albums"] do
recent_tracks = LastFm.Feed.all_tracks()
{:noreply,
socket
|> assign_scrobble_activity(recent_tracks)
|> stream(:recent_tracks, recent_tracks, reset: true)}
|> assign(scrobble_activity_mode: String.to_existing_atom(mode))}
end
def handle_info(%{tracks: recent_tracks}, socket) do
{:noreply,
socket
|> assign_scrobble_activity(recent_tracks)}
end
defp assign_counts(socket) do
@@ -95,7 +107,23 @@ defmodule MusicLibraryWeb.StatsLive.Index do
recent_artist_ids = recent_artist_ids(recent_tracks)
artist_ids = MapSet.intersection(all_artist_ids, recent_artist_ids)
assign(socket,
recent_albums =
recent_tracks
|> Enum.uniq_by(fn t -> t.album end)
|> Enum.map(fn t ->
%{
scrobbled_at_uts: t.scrobbled_at_uts,
scrobbled_at_label: t.scrobbled_at_label,
metadata: t.album,
artist: t.artist,
cover_url: t.cover_url
}
end)
socket
|> stream(:recent_tracks, recent_tracks, reset: true)
|> stream(:recent_albums, recent_albums, reset: true)
|> assign(
collected_releases: collected_releases,
wishlisted_releases: wishlisted_releases,
artist_ids: artist_ids
@@ -83,10 +83,169 @@
</button>
</div>
<%!-- TODO: replace with OSS version --%>
<span class="mt-4 isolate inline-flex rounded-md shadow-xs">
<button
phx-click={JS.push("set_scrobble_activity_mode", value: %{"mode" => :tracks})}
class={[
"relative inline-flex items-center rounded-l-md",
"px-3 py-2 text-xs sm:text-sm font-semibold",
"ring-1 ring-inset focus:z-10",
"bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 ring-zinc-300",
@scrobble_activity_mode == :tracks && "!bg-zinc-100 dark:!bg-zinc-700"
]}
>
{gettext("Tracks")}
</button>
<button
phx-click={JS.push("set_scrobble_activity_mode", value: %{"mode" => :albums})}
class={[
"relative -ml-px inline-flex items-center rounded-r-md",
"px-3 py-2 text-xs sm:text-sm font-semibold",
"ring-1 ring-inset focus:z-10",
"bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 ring-zinc-300",
@scrobble_activity_mode == :albums && "!bg-zinc-100 dark:!bg-zinc-700"
]}
>
{gettext("Albums")}
</button>
</span>
<ul
id="scrobble-activity"
:if={@scrobble_activity_mode == :albums}
id="scrobble-activity-albums"
role="list"
class="mt-5 rounded-md px-4 py-5 shadow-sm"
class="mt-4 rounded-md px-4 py-5 shadow-sm"
phx-update="stream"
>
<li :for={{id, album} <- @streams.recent_albums} id={id} class="group">
<div class="relative pb-8 group-last:pb-0">
<span
class="group-last:hidden absolute left-6 top-6 -ml-px h-full w-0.5 bg-zinc-200"
aria-hidden="true"
>
</span>
<div class="relative flex space-x-3 items-center justify-between">
<div class="flex min-w-0 justify-between space-x-4 items-center">
<img
class="h-12 w-12 rounded-md shadow-sm"
src={album.cover_url}
alt={album.metadata.title}
/>
<div>
<p
:if={album.artist.musicbrainz_id not in @artist_ids}
class="font-semibold text-sm block text-zinc-500 dark:text-zinc-400"
>
{album.artist.name}
</p>
<.link
:if={album.artist.musicbrainz_id in @artist_ids}
class="font-semibold text-sm block text-zinc-700 hover:text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-300"
navigate={~p"/artists/#{album.artist.musicbrainz_id}"}
>
{album.artist.name}
</.link>
<p class="font-semibold text-sm md:text-base text-zinc-700 dark:text-zinc-300">
{album.metadata.title}
</p>
<time
datetime={format_scrobbled_at_uts(album.scrobbled_at_uts)}
class="whitespace-nowrap text-right text-xs sm:text-sm text-zinc-500 dark:text-zinc-400"
>
{album.scrobbled_at_label}
</time>
</div>
</div>
<div class="flex gap-1 flex-col text-right">
<.badge
:if={album.metadata.musicbrainz_id == ""}
color={:gray}
text={gettext("No MB ID")}
/>
<.link
:if={
record_id = tracked_record?(@collected_releases, album.metadata.musicbrainz_id)
}
navigate={~p"/collection/#{record_id}"}
>
<.badge color={:green} text={gettext("Collected")} />
</.link>
<.link
:if={
record_id = tracked_record?(@wishlisted_releases, album.metadata.musicbrainz_id)
}
navigate={~p"/wishlist/#{record_id}"}
>
<.badge color={:yellow} text={gettext("Wishlisted")} />
</.link>
</div>
<div
:if={
album.metadata.musicbrainz_id !== "" and
!tracked_record?(
@collected_releases ++ @wishlisted_releases,
album.metadata.musicbrainz_id
)
}
class="relative flex-none"
>
<button
type="button"
class="text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-300"
aria-expanded="false"
aria-haspopup="true"
phx-click={toggle_actions_menu(album.scrobbled_at_uts)}
phx-click-away={close_actions_menu(album.scrobbled_at_uts)}
>
<span class="sr-only">{gettext("Choose which format to import")}</span>
<.icon name="hero-star" class="-mt-1 h-5 w-5" aria-hidden="true" data-slot="icon" />
</button>
<!--
Dropdown menu, show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<.focus_wrap
id={"actions-#{album.scrobbled_at_uts}"}
class={[
"hidden pointer-events-auto absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white dark:bg-zinc-800 py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-hidden"
]}
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu-0-button"
>
<.link
:for={format <- Records.Record.formats()}
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
role="menuitem"
tabindex="0"
id={"actions-#{album.scrobbled_at_uts}-#{format}-import"}
phx-click={
JS.push("import",
value: %{id: album.metadata.musicbrainz_id, format: format},
page_loading: true
)
}
>
{format_label(format)}
</.link>
</.focus_wrap>
</div>
</div>
</div>
</li>
</ul>
<ul
:if={@scrobble_activity_mode == :tracks}
id="scrobble-activity-tracks"
role="list"
class="mt-4 rounded-md px-4 py-5 shadow-sm"
phx-update="stream"
>
<li :for={{id, track} <- @streams.recent_tracks} id={id} class="group">
+22 -8
View File
@@ -61,8 +61,8 @@ msgstr ""
#: lib/music_library_web/live/collection_live/index.ex:133
#: lib/music_library_web/live/collection_live/index.ex:140
#: lib/music_library_web/live/stats_live/index.ex:53
#: lib/music_library_web/live/stats_live/index.ex:59
#: lib/music_library_web/live/stats_live/index.ex:56
#: lib/music_library_web/live/stats_live/index.ex:62
#: lib/music_library_web/live/wishlist_live/index.ex:120
#: lib/music_library_web/live/wishlist_live/index.ex:127
#, elixir-autogen, elixir-format
@@ -176,7 +176,7 @@ msgid "Purchased on"
msgstr ""
#: lib/music_library_web/live/collection_live/index.ex:125
#: lib/music_library_web/live/stats_live/index.ex:45
#: lib/music_library_web/live/stats_live/index.ex:48
#: lib/music_library_web/live/wishlist_live/index.ex:112
#, elixir-autogen, elixir-format
msgid "Record imported successfully"
@@ -226,7 +226,7 @@ msgid "Something went wrong!"
msgstr ""
#: lib/music_library_web/components/layouts/app.html.heex:14
#: lib/music_library_web/live/stats_live/index.ex:27
#: lib/music_library_web/live/stats_live/index.ex:30
#, elixir-autogen, elixir-format
msgid "Stats"
msgstr ""
@@ -317,19 +317,22 @@ msgid "Scrobble activity"
msgstr ""
#: lib/music_library_web/components/add_record_component.ex:92
#: lib/music_library_web/live/stats_live/index.html.heex:169
#: lib/music_library_web/live/stats_live/index.html.heex:201
#: lib/music_library_web/live/stats_live/index.html.heex:328
#, elixir-autogen, elixir-format
msgid "Choose which format to import"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex:293
#: lib/music_library_web/live/stats_live/index.html.heex:141
#: lib/music_library_web/live/stats_live/index.html.heex:171
#: lib/music_library_web/live/stats_live/index.html.heex:300
#, elixir-autogen, elixir-format
msgid "Collected"
msgstr ""
#: lib/music_library_web/components/barcode_scanner_component.ex:290
#: lib/music_library_web/live/stats_live/index.html.heex:147
#: lib/music_library_web/live/stats_live/index.html.heex:179
#: lib/music_library_web/live/stats_live/index.html.heex:306
#, elixir-autogen, elixir-format
msgid "Wishlisted"
msgstr ""
@@ -373,7 +376,8 @@ msgstr ""
msgid "Error refreshing cover"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex:135
#: lib/music_library_web/live/stats_live/index.html.heex:163
#: lib/music_library_web/live/stats_live/index.html.heex:294
#, elixir-autogen, elixir-format
msgid "No MB ID"
msgstr ""
@@ -691,3 +695,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Search for a record"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex:109
#, elixir-autogen, elixir-format
msgid "Albums"
msgstr ""
#: lib/music_library_web/live/stats_live/index.html.heex:97
#, elixir-autogen, elixir-format
msgid "Tracks"
msgstr ""