Files
music_library/lib/music_library_web/live/stats_live/index.ex
T
Claudio Ortolina b503662c2e Reduce size of stats live socket
No need to store all artists IDs in the socket, instead it's enough to
intersect them with the scrobbled artist IDs.
2024-12-27 20:27:32 +00:00

156 lines
4.7 KiB
Elixir

defmodule MusicLibraryWeb.StatsLive.Index do
use MusicLibraryWeb, :live_view
import MusicLibraryWeb.StatsLive.DataComponents
alias MusicLibrary.{Artists, Collection, Records, Wishlist}
alias Records.Record
def mount(_params, _session, socket) do
collection_count_by_format = Collection.count_records_by_format()
collection_count_by_type = Collection.count_records_by_type()
collection_count =
Enum.reduce(collection_count_by_format, 0, fn {_, count}, acc -> acc + count end)
wishlist_count = Wishlist.count()
latest_record = Collection.get_latest_record!()
recent_tracks = LastFm.Feed.all_tracks()
recent_release_ids = recent_release_ids(recent_tracks)
collected_release_ids = Collection.collected_release_ids(recent_release_ids)
wishlisted_release_ids = Wishlist.wishlisted_release_ids(recent_release_ids)
all_artist_ids = Artists.get_all_artist_ids()
recent_artist_ids = recent_artist_ids(recent_tracks)
artist_ids = MapSet.intersection(all_artist_ids, recent_artist_ids)
if connected?(socket) do
LastFm.Feed.subscribe()
end
{:ok,
socket
|> stream_configure(:recent_tracks,
dom_id: fn track -> "track-#{track.scrobbled_at_uts}" end
)
|> stream(:recent_tracks, recent_tracks)
|> assign(
page_title: gettext("Stats"),
collection_count_by_format: collection_count_by_format,
collection_count_by_type: collection_count_by_type,
collection_count: collection_count,
wishlist_count: wishlist_count,
latest_record: latest_record,
collected_release_ids: collected_release_ids,
wishlisted_release_ids: wishlisted_release_ids,
artist_ids: artist_ids,
nav_section: :stats
)}
end
def handle_event("refresh_lastfm_feed", _, socket) do
LastFm.Refresh.refresh()
{:noreply, socket}
end
def handle_event("import", %{"id" => musicbrainz_id, "format" => format}, socket) do
case Records.import_from_musicbrainz_release(musicbrainz_id,
format: format,
purchased_at: nil
) do
{:ok, record} ->
{:noreply,
socket
|> put_flash(:info, gettext("Record imported successfully"))
|> push_navigate(to: ~p"/wishlist/#{record.id}")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply,
socket
|> put_flash(
:error,
gettext("Error importing record") <> "," <> inspect(changeset.errors)
)}
{:error, reason} ->
{:noreply,
socket
|> put_flash(:error, gettext("Error importing record") <> "," <> inspect(reason))}
end
end
def handle_info(%{tracks: recent_tracks}, socket) do
recent_release_ids = recent_release_ids(recent_tracks)
collected_release_ids = Collection.collected_release_ids(recent_release_ids)
wishlisted_release_ids = Wishlist.wishlisted_release_ids(recent_release_ids)
all_artist_ids = Artists.get_all_artist_ids()
recent_artist_ids = recent_artist_ids(recent_tracks)
artist_ids = MapSet.intersection(all_artist_ids, recent_artist_ids)
{:noreply,
socket
|> assign(
collected_release_ids: collected_release_ids,
wishlisted_release_ids: wishlisted_release_ids,
artist_ids: artist_ids
)
|> stream(:recent_tracks, recent_tracks, reset: true)}
end
defp recent_release_ids(recent_tracks) do
recent_tracks
|> Enum.map(fn t -> t.album.musicbrainz_id end)
|> Enum.uniq()
|> Enum.reject(fn musicbrainz_id -> musicbrainz_id == "" end)
end
def recent_artist_ids(recent_tracks) do
recent_tracks
|> Enum.map(fn t -> t.artist.musicbrainz_id end)
|> Enum.uniq()
|> Enum.reject(fn musicbrainz_id -> musicbrainz_id == "" end)
|> MapSet.new()
end
# The Tailwind build step requires all needed classes to be explicitly referenced
# in the source code, and not dynamically generated. This implies that one cannot
# (for example) interpolate a number in a class name.
defp stats_class(collection) do
case Enum.count(collection) do
1 -> "grid-cols-1"
2 -> "grid-cols-2"
3 -> "grid-cols-3"
4 -> "grid-cols-4"
5 -> "grid-cols-5"
6 -> "grid-cols-6"
7 -> "grid-cols-7"
8 -> "grid-cols-8"
9 -> "grid-cols-9"
_other -> ""
end
end
defp format_scrobbled_at_uts(uts) do
uts
|> DateTime.from_unix!()
|> DateTime.to_iso8601()
end
defp toggle_actions_menu(track_id) do
JS.toggle(to: "#actions-#{track_id}")
|> JS.toggle_class("pointer-events-none", to: "#scrobble-activity > li")
end
defp close_actions_menu(track_id) do
JS.hide(to: "#actions-#{track_id}")
|> JS.remove_class("pointer-events-none", to: "#scrobble-activity > li")
end
end