From 065ca4bf7cd3acbfcf6aade29ced6e2d7036107e Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Mon, 4 Nov 2024 12:22:23 +0000 Subject: [PATCH] Make stats feed update automatically --- lib/last_fm/feed.ex | 7 +++++++ lib/last_fm/refresh.ex | 19 ++++++++++++++++++- lib/last_fm/supervisor.ex | 1 + .../live/stats_live/index.ex | 15 +++++++++++++++ priv/gettext/default.pot | 2 +- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/last_fm/feed.ex b/lib/last_fm/feed.ex index 3f0cedc8..3378c01a 100644 --- a/lib/last_fm/feed.ex +++ b/lib/last_fm/feed.ex @@ -14,11 +14,18 @@ defmodule LastFm.Feed do def update(tracks) do data = Enum.map(tracks, fn t -> {t.scrobbled_at_uts, t} end) + :ets.delete_all_objects(__MODULE__) :ets.insert(__MODULE__, data) + + Phoenix.PubSub.broadcast(LastFm.PubSub, "feed:update", %{tracks: data}) end def all do :ets.tab2list(__MODULE__) |> Enum.reverse() end + + def subscribe do + Phoenix.PubSub.subscribe(LastFm.PubSub, "feed:update") + end end diff --git a/lib/last_fm/refresh.ex b/lib/last_fm/refresh.ex index 7c0afd54..1eef6c1b 100644 --- a/lib/last_fm/refresh.ex +++ b/lib/last_fm/refresh.ex @@ -5,12 +5,13 @@ defmodule LastFm.Refresh do alias LastFm.Feed - @refresh_interval System.convert_time_unit(30, :second, :millisecond) + @refresh_interval System.convert_time_unit(10, :second, :millisecond) def start_link(config) do GenServer.start_link(__MODULE__, config, name: __MODULE__) end + @impl true def init(config) do if enabled?(config) do {:ok, config, {:continue, :refresh}} @@ -19,6 +20,7 @@ defmodule LastFm.Refresh do end end + @impl true def handle_continue(:refresh, config) do case config.api.get_recent_tracks(config.user, config.api_key) do {:ok, tracks} -> @@ -33,6 +35,21 @@ defmodule LastFm.Refresh do end end + @impl true + def handle_info(:refresh, config) do + case config.api.get_recent_tracks(config.user, config.api_key) do + {:ok, tracks} -> + Feed.update(tracks) + Process.send_after(self(), :refresh, @refresh_interval) + {:noreply, config} + + {:error, _reason} -> + # TODO: think about failure scenario - error is logged at the API level + Process.send_after(self(), :refresh, @refresh_interval) + {:noreply, config} + end + end + defp enabled?(config) do config.api && config.api_key end diff --git a/lib/last_fm/supervisor.ex b/lib/last_fm/supervisor.ex index 2e155746..ec3f5c88 100644 --- a/lib/last_fm/supervisor.ex +++ b/lib/last_fm/supervisor.ex @@ -10,6 +10,7 @@ defmodule LastFm.Supervisor do :ok = LastFm.Feed.create_table!() children = [ + {Phoenix.PubSub, name: LastFm.PubSub}, {LastFm.Refresh, %{api: api(), user: "cloud8421", api_key: api_key()}} ] diff --git a/lib/music_library_web/live/stats_live/index.ex b/lib/music_library_web/live/stats_live/index.ex index 16e11002..86d9cfb3 100644 --- a/lib/music_library_web/live/stats_live/index.ex +++ b/lib/music_library_web/live/stats_live/index.ex @@ -20,6 +20,10 @@ defmodule MusicLibraryWeb.StatsLive.Index do recent_tracks = LastFm.Feed.all() + if connected?(socket) do + LastFm.Feed.subscribe() + end + {:ok, socket |> assign( @@ -34,6 +38,17 @@ defmodule MusicLibraryWeb.StatsLive.Index do )} end + def handle_info(%{tracks: tracks}, socket) do + socket = + if socket.assigns.recent_tracks !== tracks do + assign(socket, :recent_tracks, tracks) + else + socket + end + + {:noreply, socket} + 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. diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index a49769ef..3c1ca97b 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -269,7 +269,7 @@ msgid "Something went wrong!" msgstr "" #: lib/music_library_web/components/layouts/app.html.heex:7 -#: lib/music_library_web/live/stats_live/index.ex:26 +#: lib/music_library_web/live/stats_live/index.ex:30 #, elixir-autogen, elixir-format msgid "Stats" msgstr ""