ML-168: broadcast index_changed event after background import

Import workers now broadcast :records_index_changed on
"records:index_changed" after successful import via
Records.broadcast_index_changed/0.

CollectionLive.Index and WishlistLive.Index subscribe to the
topic in mount/3 and reload their record streams on receipt,
with a live_action guard to skip reloads when the grid is
hidden behind a modal (:import, :barcode_scan).

IndexActions.handle_index_changed/1 refreshes total_entries
before reloading to keep the pagination bar accurate.
This commit is contained in:
Claudio Ortolina
2026-05-14 16:53:28 +01:00
parent d878364423
commit a59dd22a18
13 changed files with 252 additions and 16 deletions
@@ -12,6 +12,7 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
alias MusicLibrary.Assets.{Image, Transform}
alias MusicLibrary.Records.Record
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
alias Phoenix.LiveViewTest
alias Req.Test
# make it a multiple of 4 for easier calculations
@@ -93,6 +94,37 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
end
end
describe "PubSub index_changed" do
test "reloads stream when live_action is :index", %{conn: conn} do
{:ok, view, _html} = LiveViewTest.live(conn, ~p"/collection")
html_before = LiveViewTest.render(view)
# Create a new record behind the scenes (simulating completed background import)
_new_record = record()
# Send the index_changed message directly
send(view.pid, :records_index_changed)
# The view should now include the new record
html_after = LiveViewTest.render(view)
assert html_after != html_before
end
test "ignores message when live_action is :import (guard clause)", %{conn: conn} do
{:ok, view, _html} = LiveViewTest.live(conn, ~p"/collection/import")
html_before = LiveViewTest.render(view)
send(view.pid, :records_index_changed)
html_after = LiveViewTest.render(view)
# Should be identical — the message is a no-op when grid is behind modal
assert html_after == html_before
end
end
describe "Search and pagination" do
setup [:fill_collection]
@@ -9,6 +9,7 @@ defmodule MusicLibraryWeb.WishlistLive.IndexTest do
alias MusicLibrary.Records.Record
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
alias Phoenix.LiveViewTest
alias Req.Test
defp fill_wishlist(_) do
@@ -16,6 +17,37 @@ defmodule MusicLibraryWeb.WishlistLive.IndexTest do
%{wishlist: records}
end
describe "PubSub index_changed" do
test "reloads stream when live_action is :index", %{conn: conn} do
{:ok, view, _html} = LiveViewTest.live(conn, ~p"/wishlist")
html_before = LiveViewTest.render(view)
# Create a new wishlist record behind the scenes (simulating completed background import)
_new_record = record(%{purchased_at: nil})
# Send the index_changed message directly
send(view.pid, :records_index_changed)
# The view should now include the new record
html_after = LiveViewTest.render(view)
assert html_after != html_before
end
test "ignores message when live_action is :import (guard clause)", %{conn: conn} do
{:ok, view, _html} = LiveViewTest.live(conn, ~p"/wishlist/import")
html_before = LiveViewTest.render(view)
send(view.pid, :records_index_changed)
html_after = LiveViewTest.render(view)
# Should be identical — the message is a no-op when grid is behind modal
assert html_after == html_before
end
end
describe "Wishlist" do
setup [:fill_wishlist]