Merge branch 'feature/callsign-trail-line'
This commit is contained in:
commit
fc3284f079
5 changed files with 401 additions and 5 deletions
|
|
@ -1520,6 +1520,12 @@ let MapAPRSMap = {
|
||||||
|
|
||||||
// Handle clear_all_markers event
|
// Handle clear_all_markers event
|
||||||
self.handleEvent("clear_all_markers", () => {
|
self.handleEvent("clear_all_markers", () => {
|
||||||
|
// Clear trail line if present
|
||||||
|
if (self.trailLine) {
|
||||||
|
self.map?.removeLayer(self.trailLine);
|
||||||
|
self.trailLine = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// Modified to preserve historical trails - only removes non-historical markers
|
// Modified to preserve historical trails - only removes non-historical markers
|
||||||
self.clearAllMarkers();
|
self.clearAllMarkers();
|
||||||
|
|
||||||
|
|
@ -1537,6 +1543,12 @@ let MapAPRSMap = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear trail line if present
|
||||||
|
if (self.trailLine) {
|
||||||
|
self.map.removeLayer(self.trailLine);
|
||||||
|
self.trailLine = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (!L.heatLayer) {
|
if (!L.heatLayer) {
|
||||||
console.error("Leaflet.heat plugin not loaded!");
|
console.error("Leaflet.heat plugin not loaded!");
|
||||||
return;
|
return;
|
||||||
|
|
@ -1595,6 +1607,12 @@ let MapAPRSMap = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear trail line if present
|
||||||
|
if (self.trailLine) {
|
||||||
|
self.map.removeLayer(self.trailLine);
|
||||||
|
self.trailLine = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// Hide heat layer and show marker layer
|
// Hide heat layer and show marker layer
|
||||||
if (self.heatLayer && self.map.hasLayer(self.heatLayer)) {
|
if (self.heatLayer && self.map.hasLayer(self.heatLayer)) {
|
||||||
self.map.removeLayer(self.heatLayer);
|
self.map.removeLayer(self.heatLayer);
|
||||||
|
|
@ -1606,6 +1624,69 @@ let MapAPRSMap = {
|
||||||
console.error("Error in show_markers handler:", error);
|
console.error("Error in show_markers handler:", error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle trail line display for tracked callsigns at low zoom
|
||||||
|
self.handleEvent("show_trail_line", (payload: BaseEventPayload) => {
|
||||||
|
const data = payload as { callsign: string; points: Array<{ lat: number; lng: number; timestamp: string }> };
|
||||||
|
|
||||||
|
if (!self.map) {
|
||||||
|
console.warn("Map not initialized, cannot show trail line");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear existing trail line if present
|
||||||
|
if (self.trailLine) {
|
||||||
|
self.map.removeLayer(self.trailLine);
|
||||||
|
self.trailLine = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear heat map if present (switching from heat map to trail line)
|
||||||
|
if (self.heatLayer) {
|
||||||
|
self.map.removeLayer(self.heatLayer);
|
||||||
|
self.heatLayer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have less than 2 points, don't draw a line
|
||||||
|
if (data.points.length < 2) {
|
||||||
|
console.debug(`Not enough points for trail line: ${data.points.length}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract coordinates for polyline
|
||||||
|
const latlngs = data.points.map(point => [point.lat, point.lng] as [number, number]);
|
||||||
|
|
||||||
|
// Access Leaflet from window
|
||||||
|
const L = (window as any).L;
|
||||||
|
|
||||||
|
// Create polyline
|
||||||
|
self.trailLine = L.polyline(latlngs, {
|
||||||
|
color: '#3B82F6', // blue-500
|
||||||
|
weight: 3,
|
||||||
|
opacity: 0.8,
|
||||||
|
lineCap: 'round',
|
||||||
|
lineJoin: 'round',
|
||||||
|
interactive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add to map
|
||||||
|
self.trailLine.addTo(self.map);
|
||||||
|
|
||||||
|
console.debug(`Trail line added for ${data.callsign} with ${data.points.length} points`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle clearing trail line
|
||||||
|
self.handleEvent("clear_trail_line", (_payload: BaseEventPayload) => {
|
||||||
|
if (!self.map) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove trail line if present
|
||||||
|
if (self.trailLine) {
|
||||||
|
self.map.removeLayer(self.trailLine);
|
||||||
|
self.trailLine = undefined;
|
||||||
|
console.debug("Trail line cleared");
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
sendBoundsToServer() {
|
sendBoundsToServer() {
|
||||||
|
|
|
||||||
1
assets/js/types/map.d.ts
vendored
1
assets/js/types/map.d.ts
vendored
|
|
@ -34,6 +34,7 @@ export interface LiveViewHookContext {
|
||||||
zoomEndHandler?: () => void;
|
zoomEndHandler?: () => void;
|
||||||
rfPathLines?: Array<Polyline | Marker>; // Array of Leaflet polylines and markers for RF path visualization
|
rfPathLines?: Array<Polyline | Marker>; // Array of Leaflet polylines and markers for RF path visualization
|
||||||
trailLayer?: LayerGroup;
|
trailLayer?: LayerGroup;
|
||||||
|
trailLine?: Polyline; // Polyline for callsign trail display at low zoom
|
||||||
markerClusterGroup?: MarkerClusterGroup;
|
markerClusterGroup?: MarkerClusterGroup;
|
||||||
cleanupTimeouts?: ReturnType<typeof setTimeout>[];
|
cleanupTimeouts?: ReturnType<typeof setTimeout>[];
|
||||||
pendingMarkers?: MarkerData[];
|
pendingMarkers?: MarkerData[];
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,22 @@ defmodule AprsmeWeb.MapLive.DisplayManager do
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Handle zoom threshold crossing between heat map and marker modes.
|
Handle zoom threshold crossing between heat map and marker modes.
|
||||||
|
When tracking a callsign at low zoom, show trail line instead of heat map.
|
||||||
"""
|
"""
|
||||||
@spec handle_zoom_threshold_crossing(Socket.t(), integer()) :: Socket.t()
|
@spec handle_zoom_threshold_crossing(Socket.t(), integer()) :: Socket.t()
|
||||||
def handle_zoom_threshold_crossing(socket, zoom) do
|
def handle_zoom_threshold_crossing(socket, zoom) do
|
||||||
|
tracked_callsign = socket.assigns.tracked_callsign || ""
|
||||||
|
|
||||||
if zoom <= @heat_map_max_zoom do
|
if zoom <= @heat_map_max_zoom do
|
||||||
# Switching to heat map
|
# Switching to low zoom mode
|
||||||
socket
|
socket = LiveView.push_event(socket, "clear_all_markers", %{})
|
||||||
|> LiveView.push_event("clear_all_markers", %{})
|
|
||||||
|> send_heat_map_for_current_bounds()
|
# If tracking a callsign, show trail line; otherwise show heat map
|
||||||
|
if tracked_callsign == "" do
|
||||||
|
send_heat_map_for_current_bounds(socket)
|
||||||
|
else
|
||||||
|
send_trail_line_for_tracked_callsign(socket)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
# Switching to markers
|
# Switching to markers
|
||||||
trigger_marker_display(socket)
|
trigger_marker_display(socket)
|
||||||
|
|
@ -74,6 +82,56 @@ defmodule AprsmeWeb.MapLive.DisplayManager do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Send trail line data for tracked callsign.
|
||||||
|
Connects all position points chronologically, regardless of distance.
|
||||||
|
"""
|
||||||
|
@spec send_trail_line_for_tracked_callsign(Socket.t()) :: Socket.t()
|
||||||
|
def send_trail_line_for_tracked_callsign(socket) do
|
||||||
|
callsign = socket.assigns.tracked_callsign
|
||||||
|
threshold = socket.assigns.packet_age_threshold
|
||||||
|
|
||||||
|
packets =
|
||||||
|
socket.assigns.visible_packets
|
||||||
|
|> Map.values()
|
||||||
|
|> Enum.filter(fn packet ->
|
||||||
|
sender = Map.get(packet, :sender) || Map.get(packet, "sender")
|
||||||
|
received_at = Map.get(packet, :received_at) || Map.get(packet, "received_at")
|
||||||
|
|
||||||
|
String.upcase(sender) == String.upcase(callsign) &&
|
||||||
|
DateTime.compare(received_at, threshold) != :lt
|
||||||
|
end)
|
||||||
|
|> Enum.sort_by(
|
||||||
|
fn packet ->
|
||||||
|
received_at = Map.get(packet, :received_at) || Map.get(packet, "received_at")
|
||||||
|
DateTime.to_unix(received_at, :microsecond)
|
||||||
|
end,
|
||||||
|
:asc
|
||||||
|
)
|
||||||
|
|
||||||
|
if Enum.empty?(packets) do
|
||||||
|
socket
|
||||||
|
else
|
||||||
|
points =
|
||||||
|
Enum.map(packets, fn packet ->
|
||||||
|
lat = Map.get(packet, :lat) || Map.get(packet, "lat")
|
||||||
|
lon = Map.get(packet, :lon) || Map.get(packet, "lon")
|
||||||
|
received_at = Map.get(packet, :received_at) || Map.get(packet, "received_at")
|
||||||
|
|
||||||
|
%{
|
||||||
|
lat: Aprsme.EncodingUtils.to_float(lat) || 0.0,
|
||||||
|
lng: Aprsme.EncodingUtils.to_float(lon) || 0.0,
|
||||||
|
timestamp: DateTime.to_iso8601(received_at)
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
LiveView.push_event(socket, "show_trail_line", %{
|
||||||
|
callsign: callsign,
|
||||||
|
points: points
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Send heat map data for filtered packets.
|
Send heat map data for filtered packets.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -510,6 +510,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(tracked_callsign: "", overlay_callsign: "", other_ssids: [])
|
|> assign(tracked_callsign: "", overlay_callsign: "", other_ssids: [])
|
||||||
|
|> push_event("clear_trail_line", %{})
|
||||||
|> update_url_with_current_state()
|
|> update_url_with_current_state()
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
|
|
@ -534,6 +535,14 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
# Trigger cleanup to remove packets that are now outside the new duration
|
# Trigger cleanup to remove packets that are now outside the new duration
|
||||||
send(self(), :cleanup_old_packets)
|
send(self(), :cleanup_old_packets)
|
||||||
|
|
||||||
|
# If tracking a callsign at low zoom, refresh the trail line with new duration
|
||||||
|
socket =
|
||||||
|
if socket.assigns.tracked_callsign != "" and socket.assigns.map_zoom <= 8 do
|
||||||
|
DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||||
|
else
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -1089,8 +1098,15 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
defp update_display_for_batch(socket, []), do: socket
|
defp update_display_for_batch(socket, []), do: socket
|
||||||
|
|
||||||
defp update_display_for_batch(socket, marker_data_list) do
|
defp update_display_for_batch(socket, marker_data_list) do
|
||||||
|
tracked_callsign = socket.assigns.tracked_callsign || ""
|
||||||
|
|
||||||
if socket.assigns.map_zoom <= 8 do
|
if socket.assigns.map_zoom <= 8 do
|
||||||
DisplayManager.send_heat_map_for_current_bounds(socket)
|
# If tracking a callsign, send trail line instead of heat map
|
||||||
|
if tracked_callsign == "" do
|
||||||
|
DisplayManager.send_heat_map_for_current_bounds(socket)
|
||||||
|
else
|
||||||
|
DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
send_marker_batch(socket, marker_data_list)
|
send_marker_batch(socket, marker_data_list)
|
||||||
end
|
end
|
||||||
|
|
@ -1987,6 +2003,15 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
|
|
||||||
socket = HistoricalLoader.start_progressive_historical_loading(socket)
|
socket = HistoricalLoader.start_progressive_historical_loading(socket)
|
||||||
|
|
||||||
|
# If tracking a callsign and at low zoom, send trail line instead of letting
|
||||||
|
# historical loader send heat map
|
||||||
|
socket =
|
||||||
|
if socket.assigns.tracked_callsign != "" and socket.assigns.map_zoom <= 8 do
|
||||||
|
DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||||
|
else
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
# Mark initial historical as completed if this was the initial load
|
# Mark initial historical as completed if this was the initial load
|
||||||
if is_initial_load do
|
if is_initial_load do
|
||||||
assign(socket, initial_historical_completed: true)
|
assign(socket, initial_historical_completed: true)
|
||||||
|
|
|
||||||
231
test/aprsme_web/live/map_live/display_manager_test.exs
Normal file
231
test/aprsme_web/live/map_live/display_manager_test.exs
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
defmodule AprsmeWeb.MapLive.DisplayManagerTest do
|
||||||
|
use Aprsme.DataCase, async: true
|
||||||
|
|
||||||
|
alias AprsmeWeb.MapLive.DisplayManager
|
||||||
|
|
||||||
|
setup do
|
||||||
|
# Create a basic socket mock for testing
|
||||||
|
socket = %Phoenix.LiveView.Socket{
|
||||||
|
assigns: %{
|
||||||
|
visible_packets: %{},
|
||||||
|
tracked_callsign: nil,
|
||||||
|
packet_age_threshold: nil,
|
||||||
|
map_bounds: nil,
|
||||||
|
map_zoom: 10,
|
||||||
|
historical_packets: %{}
|
||||||
|
},
|
||||||
|
private: %{live_temp: %{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, socket: socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper function to assert push events
|
||||||
|
defp assert_push_event(socket, event_name, expected_data) do
|
||||||
|
push_events = get_in(socket.private, [:live_temp, :push_events]) || []
|
||||||
|
|
||||||
|
matching_event = Enum.find(push_events, fn [name, _data] -> name == event_name end)
|
||||||
|
|
||||||
|
assert matching_event != nil, "Expected to find event '#{event_name}' in push_events"
|
||||||
|
|
||||||
|
[_name, data] = matching_event
|
||||||
|
|
||||||
|
# For partial matching, check if all expected keys are present
|
||||||
|
Enum.each(expected_data, fn {key, value} ->
|
||||||
|
assert Map.get(data, key) == value,
|
||||||
|
"Expected #{key} to be #{inspect(value)}, got #{inspect(Map.get(data, key))}"
|
||||||
|
end)
|
||||||
|
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "handle_zoom_threshold_crossing/2" do
|
||||||
|
test "shows trail line for tracked callsign at low zoom", %{socket: socket} do
|
||||||
|
packet1 = %{
|
||||||
|
id: "1",
|
||||||
|
sender: "W5ISP-9",
|
||||||
|
lat: 30.123,
|
||||||
|
lon: -97.456,
|
||||||
|
received_at: ~U[2024-01-01 10:00:00Z]
|
||||||
|
}
|
||||||
|
|
||||||
|
visible_packets = %{"W5ISP-9:1" => packet1}
|
||||||
|
|
||||||
|
socket =
|
||||||
|
Map.put(socket, :assigns, %{
|
||||||
|
socket.assigns
|
||||||
|
| visible_packets: visible_packets,
|
||||||
|
tracked_callsign: "W5ISP-9",
|
||||||
|
packet_age_threshold: ~U[2024-01-01 09:00:00Z],
|
||||||
|
map_zoom: 8
|
||||||
|
})
|
||||||
|
|
||||||
|
# Zoom from 9 to 8 (crossing threshold) - socket already has new zoom
|
||||||
|
result_socket = DisplayManager.handle_zoom_threshold_crossing(socket, 8)
|
||||||
|
|
||||||
|
# Should clear markers and show trail line
|
||||||
|
assert_push_event(result_socket, "clear_all_markers", %{})
|
||||||
|
assert_push_event(result_socket, "show_trail_line", %{callsign: "W5ISP-9"})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "shows heat map when no tracked callsign at low zoom", %{socket: socket} do
|
||||||
|
socket =
|
||||||
|
Map.put(socket, :assigns, %{
|
||||||
|
socket.assigns
|
||||||
|
| visible_packets: %{},
|
||||||
|
tracked_callsign: "",
|
||||||
|
historical_packets: %{},
|
||||||
|
map_bounds: %{north: 90, south: -90, east: 180, west: -180},
|
||||||
|
map_zoom: 8
|
||||||
|
})
|
||||||
|
|
||||||
|
# Zoom from 9 to 8 (crossing threshold) - socket already has new zoom
|
||||||
|
result_socket = DisplayManager.handle_zoom_threshold_crossing(socket, 8)
|
||||||
|
|
||||||
|
# Should clear markers and show heat map
|
||||||
|
assert_push_event(result_socket, "clear_all_markers", %{})
|
||||||
|
assert_push_event(result_socket, "show_heat_map", %{heat_points: []})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "shows markers for tracked callsign at high zoom", %{socket: socket} do
|
||||||
|
packet1 = %{
|
||||||
|
id: "1",
|
||||||
|
sender: "W5ISP-9",
|
||||||
|
lat: 30.123,
|
||||||
|
lon: -97.456,
|
||||||
|
received_at: ~U[2024-01-01 10:00:00Z]
|
||||||
|
}
|
||||||
|
|
||||||
|
visible_packets = %{"W5ISP-9:1" => packet1}
|
||||||
|
|
||||||
|
socket =
|
||||||
|
Map.put(socket, :assigns, %{
|
||||||
|
socket.assigns
|
||||||
|
| visible_packets: visible_packets,
|
||||||
|
tracked_callsign: "W5ISP-9",
|
||||||
|
packet_age_threshold: ~U[2024-01-01 09:00:00Z],
|
||||||
|
map_zoom: 9
|
||||||
|
})
|
||||||
|
|
||||||
|
# Zoom from 8 to 9 (crossing threshold) - socket already has new zoom
|
||||||
|
result_socket = DisplayManager.handle_zoom_threshold_crossing(socket, 9)
|
||||||
|
|
||||||
|
# Should show markers
|
||||||
|
assert_push_event(result_socket, "show_markers", %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "send_trail_line_for_tracked_callsign/1" do
|
||||||
|
test "pushes trail line event with sorted points", %{socket: socket} do
|
||||||
|
packet1 = %{
|
||||||
|
id: "1",
|
||||||
|
sender: "W5ISP-9",
|
||||||
|
lat: 30.123,
|
||||||
|
lon: -97.456,
|
||||||
|
received_at: ~U[2024-01-01 10:00:00Z]
|
||||||
|
}
|
||||||
|
|
||||||
|
packet2 = %{
|
||||||
|
id: "2",
|
||||||
|
sender: "W5ISP-9",
|
||||||
|
lat: 30.124,
|
||||||
|
lon: -97.457,
|
||||||
|
received_at: ~U[2024-01-01 10:05:00Z]
|
||||||
|
}
|
||||||
|
|
||||||
|
packet3 = %{
|
||||||
|
id: "3",
|
||||||
|
sender: "W5ISP-9",
|
||||||
|
lat: 30.125,
|
||||||
|
lon: -97.458,
|
||||||
|
received_at: ~U[2024-01-01 10:10:00Z]
|
||||||
|
}
|
||||||
|
|
||||||
|
visible_packets = %{"W5ISP-9:1" => packet1, "W5ISP-9:2" => packet2, "W5ISP-9:3" => packet3}
|
||||||
|
|
||||||
|
socket =
|
||||||
|
Map.put(socket, :assigns, %{
|
||||||
|
socket.assigns
|
||||||
|
| visible_packets: visible_packets,
|
||||||
|
tracked_callsign: "W5ISP-9",
|
||||||
|
packet_age_threshold: ~U[2024-01-01 09:00:00Z]
|
||||||
|
})
|
||||||
|
|
||||||
|
result_socket = DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||||
|
|
||||||
|
push_events = get_in(result_socket.private, [:live_temp, :push_events]) || []
|
||||||
|
assert length(push_events) == 1
|
||||||
|
|
||||||
|
[[event_name, event_data]] = push_events
|
||||||
|
assert event_name == "show_trail_line"
|
||||||
|
|
||||||
|
assert event_data == %{
|
||||||
|
callsign: "W5ISP-9",
|
||||||
|
points: [
|
||||||
|
%{lat: 30.123, lng: -97.456, timestamp: "2024-01-01T10:00:00Z"},
|
||||||
|
%{lat: 30.124, lng: -97.457, timestamp: "2024-01-01T10:05:00Z"},
|
||||||
|
%{lat: 30.125, lng: -97.458, timestamp: "2024-01-01T10:10:00Z"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "filters by packet age threshold", %{socket: socket} do
|
||||||
|
packet1 = %{
|
||||||
|
id: "1",
|
||||||
|
sender: "W5ISP-9",
|
||||||
|
lat: 30.123,
|
||||||
|
lon: -97.456,
|
||||||
|
received_at: ~U[2024-01-01 09:00:00Z]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Too old
|
||||||
|
packet2 = %{
|
||||||
|
id: "2",
|
||||||
|
sender: "W5ISP-9",
|
||||||
|
lat: 30.124,
|
||||||
|
lon: -97.457,
|
||||||
|
received_at: ~U[2024-01-01 10:05:00Z]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Within threshold
|
||||||
|
|
||||||
|
visible_packets = %{"W5ISP-9:1" => packet1, "W5ISP-9:2" => packet2}
|
||||||
|
|
||||||
|
socket =
|
||||||
|
Map.put(socket, :assigns, %{
|
||||||
|
socket.assigns
|
||||||
|
| visible_packets: visible_packets,
|
||||||
|
tracked_callsign: "W5ISP-9",
|
||||||
|
packet_age_threshold: ~U[2024-01-01 10:00:00Z]
|
||||||
|
})
|
||||||
|
|
||||||
|
result_socket = DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||||
|
|
||||||
|
push_events = get_in(result_socket.private, [:live_temp, :push_events]) || []
|
||||||
|
assert length(push_events) == 1
|
||||||
|
|
||||||
|
[[event_name, event_data]] = push_events
|
||||||
|
assert event_name == "show_trail_line"
|
||||||
|
|
||||||
|
assert event_data == %{
|
||||||
|
callsign: "W5ISP-9",
|
||||||
|
points: [%{lat: 30.124, lng: -97.457, timestamp: "2024-01-01T10:05:00Z"}]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns socket unchanged when no packets", %{socket: socket} do
|
||||||
|
socket =
|
||||||
|
Map.put(socket, :assigns, %{
|
||||||
|
socket.assigns
|
||||||
|
| visible_packets: %{},
|
||||||
|
tracked_callsign: "W5ISP-9",
|
||||||
|
packet_age_threshold: ~U[2024-01-01 10:00:00Z]
|
||||||
|
})
|
||||||
|
|
||||||
|
result_socket = DisplayManager.send_trail_line_for_tracked_callsign(socket)
|
||||||
|
|
||||||
|
push_events = get_in(result_socket.private, [:live_temp, :push_events]) || []
|
||||||
|
assert push_events == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue