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:
@@ -83,4 +83,13 @@ defmodule MusicLibrary.RecordsTest do
|
||||
assert expected == Records.get_record!(expected.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "broadcast_index_changed/0 and subscribe_to_index/0" do
|
||||
test "broadcasts :records_index_changed to subscribers" do
|
||||
Records.subscribe_to_index()
|
||||
Records.broadcast_index_changed()
|
||||
|
||||
assert_received :records_index_changed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroupTest do
|
||||
import MusicBrainz.Fixtures.ReleaseGroup
|
||||
import MusicLibrary.Fixtures.Records
|
||||
|
||||
alias MusicLibrary.Records
|
||||
alias MusicLibrary.Records.Record
|
||||
alias MusicLibrary.Worker.FetchArtistInfo
|
||||
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
|
||||
@@ -93,5 +94,37 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroupTest do
|
||||
"purchased_at" => DateTime.to_iso8601(DateTime.utc_now())
|
||||
})
|
||||
end
|
||||
|
||||
test "broadcasts index_changed after successful import" do
|
||||
release_group_data = release_group(:marbles)
|
||||
release_group_id = release_group_id(:marbles)
|
||||
release_group_releases_data = release_group_releases(:marbles)
|
||||
|
||||
cover_data = marbles_cover_data()
|
||||
|
||||
Req.Test.stub(MusicBrainz.API, fn conn ->
|
||||
case conn.path_info do
|
||||
[_ws, _version, "release-group", ^release_group_id] ->
|
||||
Req.Test.json(conn, release_group_data)
|
||||
|
||||
[_ws, _version, "release"] ->
|
||||
Req.Test.json(conn, release_group_releases_data)
|
||||
|
||||
[_release_group, ^release_group_id, "front"] ->
|
||||
Plug.Conn.send_resp(conn, 200, cover_data)
|
||||
end
|
||||
end)
|
||||
|
||||
Records.subscribe_to_index()
|
||||
|
||||
assert :ok =
|
||||
perform_job(ImportFromMusicbrainzReleaseGroup, %{
|
||||
"release_group_id" => release_group_id,
|
||||
"format" => "cd",
|
||||
"purchased_at" => DateTime.to_iso8601(DateTime.utc_now())
|
||||
})
|
||||
|
||||
assert_received :records_index_changed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,6 +5,7 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseTest do
|
||||
import MusicBrainz.Fixtures.ReleaseGroup
|
||||
import MusicLibrary.Fixtures.Records
|
||||
|
||||
alias MusicLibrary.Records
|
||||
alias MusicLibrary.Records.Record
|
||||
alias MusicLibrary.Worker.ImportFromMusicbrainzRelease
|
||||
|
||||
@@ -63,5 +64,44 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseTest do
|
||||
"selected_release_id" => "nonexistent-release-id"
|
||||
})
|
||||
end
|
||||
|
||||
test "broadcasts index_changed after successful import" do
|
||||
release_data = release(:marbles)
|
||||
release_id = release_id(:marbles)
|
||||
|
||||
release_group_data = release_group(:marbles)
|
||||
release_group_id = release_group_id(:marbles)
|
||||
release_group_releases_data = release_group_releases(:marbles)
|
||||
|
||||
cover_data = marbles_cover_data()
|
||||
|
||||
Req.Test.stub(MusicBrainz.API, fn conn ->
|
||||
case conn.path_info do
|
||||
[_ws, _version, "release-group", ^release_group_id] ->
|
||||
Req.Test.json(conn, release_group_data)
|
||||
|
||||
[_ws, _version, "release", ^release_id] ->
|
||||
Req.Test.json(conn, release_data)
|
||||
|
||||
[_ws, _version, "release"] ->
|
||||
Req.Test.json(conn, release_group_releases_data)
|
||||
|
||||
[_release_group, ^release_group_id, "front"] ->
|
||||
Plug.Conn.send_resp(conn, 200, cover_data)
|
||||
end
|
||||
end)
|
||||
|
||||
Records.subscribe_to_index()
|
||||
|
||||
assert :ok =
|
||||
perform_job(ImportFromMusicbrainzRelease, %{
|
||||
"release_id" => release_id,
|
||||
"format" => "cd",
|
||||
"purchased_at" => DateTime.to_iso8601(DateTime.utc_now()),
|
||||
"selected_release_id" => release_id
|
||||
})
|
||||
|
||||
assert_received :records_index_changed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user