diff --git a/assets/js/hooks/info_map.js b/assets/js/hooks/info_map.js
index 9fdfba9..c40ea31 100644
--- a/assets/js/hooks/info_map.js
+++ b/assets/js/hooks/info_map.js
@@ -65,6 +65,12 @@ export const InfoMap = {
return;
}
+ // Hide loading spinner
+ const loadingEl = this.el.querySelector(`#${this.el.id}-loading`);
+ if (loadingEl) {
+ loadingEl.style.display = 'none';
+ }
+
// Initialize the map
try {
this.map = L.map(this.el, {
diff --git a/lib/aprsme_web/components/info_map_component.ex b/lib/aprsme_web/components/info_map_component.ex
index 15f9406..8b61f05 100644
--- a/lib/aprsme_web/components/info_map_component.ex
+++ b/lib/aprsme_web/components/info_map_component.ex
@@ -28,7 +28,7 @@ defmodule AprsmeWeb.Components.InfoMapComponent do
data-callsign={@callsign}
data-symbol-html={@symbol_html}
>
-
diff --git a/lib/aprsme_web/live/info_live/show.ex b/lib/aprsme_web/live/info_live/show.ex
index e52c52b..03ff870 100644
--- a/lib/aprsme_web/live/info_live/show.ex
+++ b/lib/aprsme_web/live/info_live/show.ex
@@ -67,27 +67,55 @@ defmodule AprsmeWeb.InfoLive.Show do
"InfoLive received packet update for #{socket.assigns.callsign}: #{inspect(Map.get(incoming_packet, "raw_packet"))}"
)
- # Refresh all data when new packet arrives
- packet = get_latest_packet(socket.assigns.callsign)
- packet = if packet, do: SharedPacketHandler.enrich_with_device_info(packet)
- # Get locale from socket assigns
- locale = Map.get(socket.assigns, :locale, "en")
- neighbors = get_neighbors(packet, socket.assigns.callsign, locale)
- has_weather_packets = PacketUtils.has_weather_packets?(socket.assigns.callsign)
- other_ssids = get_other_ssids(socket.assigns.callsign)
- heard_by_stations = get_heard_by_stations(socket.assigns.callsign, locale)
- stations_heard_by = get_stations_heard_by(socket.assigns.callsign, locale)
+ # Get the new packet data
+ new_packet = get_latest_packet(socket.assigns.callsign)
+ new_packet = if new_packet, do: SharedPacketHandler.enrich_with_device_info(new_packet)
- socket =
- socket
- |> assign(:packet, packet)
- |> assign(:neighbors, neighbors)
- |> assign(:has_weather_packets, has_weather_packets)
- |> assign(:other_ssids, other_ssids)
- |> assign(:heard_by_stations, heard_by_stations)
- |> assign(:stations_heard_by, stations_heard_by)
+ current_packet = socket.assigns.packet
- {:noreply, socket}
+ # Check if this is a position update by comparing location and other key fields
+ position_changed = position_changed?(current_packet, new_packet)
+
+ if position_changed do
+ # Only update position-related data if position changed
+ # Get locale from socket assigns
+ locale = Map.get(socket.assigns, :locale, "en")
+ neighbors = get_neighbors(new_packet, socket.assigns.callsign, locale)
+
+ socket =
+ socket
+ |> assign(:packet, new_packet)
+ |> assign(:neighbors, neighbors)
+
+ {:noreply, socket}
+ else
+ # Just update the packet data (for timestamp, comment, etc.) without affecting neighbors
+ socket = assign(socket, :packet, new_packet)
+
+ {:noreply, socket}
+ end
+ end
+
+ defp position_changed?(nil, _new_packet), do: true
+ defp position_changed?(_current_packet, nil), do: false
+
+ defp position_changed?(current_packet, new_packet) do
+ # Compare lat/lon to see if position actually changed
+ current_lat = to_float(current_packet.lat)
+ current_lon = to_float(current_packet.lon)
+ new_lat = to_float(new_packet.lat)
+ new_lon = to_float(new_packet.lon)
+
+ # Consider position changed if coordinates differ by more than ~100 meters (0.001 degrees)
+ lat_diff = abs(current_lat - new_lat)
+ lon_diff = abs(current_lon - new_lon)
+
+ lat_diff > 0.001 or lon_diff > 0.001
+ end
+
+ # Expose for testing
+ if Mix.env() == :test do
+ def position_changed_for_test(current, new), do: position_changed?(current, new)
end
defp get_latest_packet(callsign) do
diff --git a/test/aprsme_web/live/info_live/position_change_test.exs b/test/aprsme_web/live/info_live/position_change_test.exs
new file mode 100644
index 0000000..c8b7db8
--- /dev/null
+++ b/test/aprsme_web/live/info_live/position_change_test.exs
@@ -0,0 +1,47 @@
+defmodule AprsmeWeb.InfoLive.PositionChangeTest do
+ @moduledoc """
+ Tests for position change detection in InfoLive
+ """
+ use ExUnit.Case, async: true
+
+ alias AprsmeWeb.InfoLive.Show
+
+ describe "position_changed?/2" do
+ test "returns true when current packet is nil" do
+ new_packet = %{lat: 40.7128, lon: -74.0060}
+ assert Show.position_changed_for_test(nil, new_packet)
+ end
+
+ test "returns false when new packet is nil" do
+ current_packet = %{lat: 40.7128, lon: -74.0060}
+ refute Show.position_changed_for_test(current_packet, nil)
+ end
+
+ test "returns true when position changed significantly" do
+ current_packet = %{lat: 40.7128, lon: -74.0060}
+ # ~0.8km difference
+ new_packet = %{lat: 40.7200, lon: -74.0060}
+ assert Show.position_changed_for_test(current_packet, new_packet)
+ end
+
+ test "returns false when position changed minimally" do
+ current_packet = %{lat: 40.7128, lon: -74.0060}
+ # ~10m difference
+ new_packet = %{lat: 40.7129, lon: -74.0061}
+ refute Show.position_changed_for_test(current_packet, new_packet)
+ end
+
+ test "returns true when longitude changed significantly" do
+ current_packet = %{lat: 40.7128, lon: -74.0060}
+ # longitude changed
+ new_packet = %{lat: 40.7128, lon: -74.0200}
+ assert Show.position_changed_for_test(current_packet, new_packet)
+ end
+
+ test "handles Decimal values" do
+ current_packet = %{lat: Decimal.new("40.7128"), lon: Decimal.new("-74.0060")}
+ new_packet = %{lat: Decimal.new("40.7200"), lon: Decimal.new("-74.0060")}
+ assert Show.position_changed_for_test(current_packet, new_packet)
+ end
+ end
+end