diff --git a/lib/microwaveprop_web/live/weather_map_live.ex b/lib/microwaveprop_web/live/weather_map_live.ex index ffbe9f6c..8db5644c 100644 --- a/lib/microwaveprop_web/live/weather_map_live.ex +++ b/lib/microwaveprop_web/live/weather_map_live.ex @@ -232,6 +232,14 @@ defmodule MicrowavepropWeb.WeatherMapLive do end end + # `/map`'s PropagationMap hook pushes `map_bounds` on moveend/zoomend. + # When a user navigates /map → /weather, an in-flight bounds push from + # the old hook can land on the new WeatherMapLive (LV reuses sockets + # across live navigation). WeatherMapLive doesn't need bounds — the + # /weather/cells HTTP endpoint computes them client-side — so swallow + # the event instead of crashing the LV process. + def handle_event("map_bounds", _params, socket), do: {:noreply, socket} + def handle_event("point_detail", %{"lat" => lat, "lon" => lon}, socket) do detail = if socket.assigns.valid_time do diff --git a/test/microwaveprop_web/live/weather_map_live_test.exs b/test/microwaveprop_web/live/weather_map_live_test.exs index be1eb5b5..95b79d8a 100644 --- a/test/microwaveprop_web/live/weather_map_live_test.exs +++ b/test/microwaveprop_web/live/weather_map_live_test.exs @@ -319,4 +319,26 @@ defmodule MicrowavepropWeb.WeatherMapLiveTest do assert html =~ ~s(data-selected-layer=) end end + + describe "stale client events" do + test "ignores `map_bounds` from a stale browser tab without crashing", %{conn: conn} do + # A user can navigate /map → /weather while the propagation map's + # JS hook still has a queued moveend → pushEvent("map_bounds", …) + # in flight. The WeatherMapLive doesn't need viewport bounds (it + # fetches /weather/cells over HTTP using the *client* viewport), + # but it MUST tolerate the rogue event instead of crashing the + # LV process. + {:ok, lv, _html} = live(conn, ~p"/weather") + + bounds = %{ + "south" => 32.57, + "north" => 33.62, + "west" => -97.37, + "east" => -95.76 + } + + html = render_hook(lv, "map_bounds", bounds) + assert is_binary(html) + end + end end