Fix stale PubSub subscriptions on record navigation

Subscription management was only done in mount/3, meaning
navigating to a different record via handle_params/3 left the
old subscription active. A background update for the old record
would then overwrite the currently displayed record.

Fix: manage subscriptions in handle_params/3 (unsubscribe old,
subscribe new) via shared RecordActions.manage_subscription/2,
and guard handle_info({:update,...}) against mismatched IDs.
This commit is contained in:
Claudio Ortolina
2026-05-07 10:14:10 +01:00
parent 365195ff75
commit 5f240fe573
4 changed files with 41 additions and 15 deletions
+8
View File
@@ -110,6 +110,14 @@ defmodule MusicLibrary.Records do
Phoenix.PubSub.subscribe(MusicLibrary.PubSub, "records:#{record_id}")
end
@doc """
Unsubscribes the calling process from updates for a given record.
"""
@spec unsubscribe(String.t()) :: :ok | {:error, term()}
def unsubscribe(record_id) do
Phoenix.PubSub.unsubscribe(MusicLibrary.PubSub, "records:#{record_id}")
end
@spec notify_update(Record.t()) :: :ok | {:error, term()}
def notify_update(record) do
Phoenix.PubSub.broadcast(
@@ -348,11 +348,7 @@ defmodule MusicLibraryWeb.CollectionLive.Show do
end
@impl true
def mount(%{"id" => record_id}, _session, socket) do
if connected?(socket) do
Records.subscribe(record_id)
end
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:current_section, :collection)
@@ -362,6 +358,8 @@ defmodule MusicLibraryWeb.CollectionLive.Show do
@impl true
def handle_params(%{"id" => id}, _, socket) do
RecordActions.manage_subscription(socket, id)
record = Records.get_record!(id)
last_listened_track = ListeningStats.get_last_listened_track(record)
play_count = ListeningStats.play_count(record)
@@ -474,10 +472,14 @@ defmodule MusicLibraryWeb.CollectionLive.Show do
@impl true
def handle_info({:update, record}, socket) do
if record.id == socket.assigns.record.id do
{:noreply,
socket
|> RecordActions.handle_record_updated(record)
|> assign_similar_records()}
else
{:noreply, socket}
end
end
defp page_title(action, record) do
@@ -284,13 +284,9 @@ defmodule MusicLibraryWeb.WishlistLive.Show do
end
@impl true
def mount(%{"id" => record_id}, _session, socket) do
def mount(_params, _session, socket) do
current_date = DateTime.utc_now() |> DateTime.to_date()
if connected?(socket) do
Records.subscribe(record_id)
end
{:ok,
socket
|> assign(current_section: :wishlist)
@@ -299,6 +295,8 @@ defmodule MusicLibraryWeb.WishlistLive.Show do
@impl true
def handle_params(%{"id" => id}, _, socket) do
RecordActions.manage_subscription(socket, id)
record = Records.get_record!(id)
online_store_templates = OnlineStoreTemplates.list_enabled_templates()
@@ -367,7 +365,11 @@ defmodule MusicLibraryWeb.WishlistLive.Show do
@impl true
def handle_info({:update, record}, socket) do
if record.id == socket.assigns.record.id do
{:noreply, RecordActions.handle_record_updated(socket, record)}
else
{:noreply, socket}
end
end
defp page_title(action, record) do
@@ -97,6 +97,20 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActions do
end
end
@doc """
Manages PubSub subscriptions when navigating between records.
Unsubscribes from the previous record (if any) and subscribes to the new one,
but only when the socket is connected.
"""
def manage_subscription(socket, new_id) do
if Phoenix.LiveView.connected?(socket) do
if socket.assigns[:record], do: Records.unsubscribe(socket.assigns.record.id)
Records.subscribe(new_id)
end
:ok
end
def handle_chats_changed(socket) do
{:noreply,
assign(socket, :chat_count, Chats.count_chats(:record, socket.assigns.record.musicbrainz_id))}