feat: Implement direct station URLs with persistent latest packet display
- Add support for station-specific URLs (e.g., /w5isp) as path parameters - Always display the most recent packet for tracked stations, regardless of trail/historical duration - Fetch and store tracked callsign's latest packet on mount - Update packet filtering to preserve tracked callsign's packet even if outside time threshold - Keep tracked callsign's latest packet updated with real-time arrivals - Display tracked callsign's packet immediately when map loads - Remove obsolete RedirectController in favor of direct LiveView routing This ensures users viewing a specific station always see its most recent position, even if it falls outside the selected historical timeframe. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f60840c0e0
commit
5c69604c32
4 changed files with 53 additions and 14 deletions
|
|
@ -1,7 +0,0 @@
|
||||||
defmodule AprsmeWeb.RedirectController do
|
|
||||||
use AprsmeWeb, :controller
|
|
||||||
|
|
||||||
def callsign_to_map(conn, %{"callsign" => callsign}) do
|
|
||||||
redirect(conn, to: "/?call=#{callsign}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -62,8 +62,8 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
# Setup additional subscriptions if connected
|
# Setup additional subscriptions if connected
|
||||||
socket = setup_additional_subscriptions(socket)
|
socket = setup_additional_subscriptions(socket)
|
||||||
|
|
||||||
# Handle callsign tracking
|
# Handle callsign tracking - check path params first, then query params
|
||||||
tracked_callsign = Map.get(params, "call", "")
|
tracked_callsign = Map.get(params, "callsign", Map.get(params, "call", ""))
|
||||||
|
|
||||||
{final_map_center, final_map_zoom} =
|
{final_map_center, final_map_zoom} =
|
||||||
Navigation.handle_callsign_tracking(
|
Navigation.handle_callsign_tracking(
|
||||||
|
|
@ -169,6 +169,14 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
historical_hours = Map.get(socket.assigns, :historical_hours, "1")
|
historical_hours = Map.get(socket.assigns, :historical_hours, "1")
|
||||||
packet_age_threshold = Map.get(socket.assigns, :packet_age_threshold, one_hour_ago)
|
packet_age_threshold = Map.get(socket.assigns, :packet_age_threshold, one_hour_ago)
|
||||||
|
|
||||||
|
# If tracking a specific callsign, fetch their latest packet
|
||||||
|
tracked_callsign_latest_packet =
|
||||||
|
if tracked_callsign == "" do
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
CachedQueries.get_latest_packet_for_callsign_cached(tracked_callsign)
|
||||||
|
end
|
||||||
|
|
||||||
assign(socket,
|
assign(socket,
|
||||||
map_ready: false,
|
map_ready: false,
|
||||||
map_bounds: initial_bounds,
|
map_bounds: initial_bounds,
|
||||||
|
|
@ -178,6 +186,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
packet_state: PacketManager.init_packet_state(),
|
packet_state: PacketManager.init_packet_state(),
|
||||||
overlay_callsign: "",
|
overlay_callsign: "",
|
||||||
tracked_callsign: tracked_callsign,
|
tracked_callsign: tracked_callsign,
|
||||||
|
tracked_callsign_latest_packet: tracked_callsign_latest_packet,
|
||||||
trail_duration: trail_duration,
|
trail_duration: trail_duration,
|
||||||
historical_hours: historical_hours,
|
historical_hours: historical_hours,
|
||||||
packet_age_threshold: packet_age_threshold,
|
packet_age_threshold: packet_age_threshold,
|
||||||
|
|
@ -270,12 +279,14 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
|
|
||||||
# Filter by time and bounds
|
# Filter by time and bounds
|
||||||
filtered_packets =
|
filtered_packets =
|
||||||
filter_packets_by_time_and_bounds(
|
filter_packets_by_time_and_bounds_with_tracked(
|
||||||
Map.new(current_packets, fn packet ->
|
Map.new(current_packets, fn packet ->
|
||||||
{get_callsign_key(packet), packet}
|
{get_callsign_key(packet), packet}
|
||||||
end),
|
end),
|
||||||
socket.assigns.map_bounds,
|
socket.assigns.map_bounds,
|
||||||
socket.assigns.packet_age_threshold
|
socket.assigns.packet_age_threshold,
|
||||||
|
socket.assigns.tracked_callsign,
|
||||||
|
socket.assigns.tracked_callsign_latest_packet
|
||||||
)
|
)
|
||||||
|
|
||||||
visible_packets_list = DataBuilder.build_packet_data_list_from_map(filtered_packets, false, socket)
|
visible_packets_list = DataBuilder.build_packet_data_list_from_map(filtered_packets, false, socket)
|
||||||
|
|
@ -312,6 +323,21 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
Navigation.zoom_to_current_location(socket)
|
Navigation.zoom_to_current_location(socket)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If we're tracking a callsign and have its latest packet, display it immediately
|
||||||
|
socket =
|
||||||
|
if socket.assigns.tracked_callsign != "" and socket.assigns.tracked_callsign_latest_packet do
|
||||||
|
packet = socket.assigns.tracked_callsign_latest_packet
|
||||||
|
packet_data = DataBuilder.build_packet_data(packet)
|
||||||
|
|
||||||
|
push_event(socket, "add_historical_packets_batch", %{
|
||||||
|
packets: [packet_data],
|
||||||
|
batch: 0,
|
||||||
|
is_final: false
|
||||||
|
})
|
||||||
|
else
|
||||||
|
socket
|
||||||
|
end
|
||||||
|
|
||||||
# Wait for JavaScript to send the actual map bounds before loading historical packets
|
# Wait for JavaScript to send the actual map bounds before loading historical packets
|
||||||
# The calculated bounds might be too small/inaccurate
|
# The calculated bounds might be too small/inaccurate
|
||||||
Logger.debug("Map ready - waiting for JavaScript to send actual bounds before loading historical packets")
|
Logger.debug("Map ready - waiting for JavaScript to send actual bounds before loading historical packets")
|
||||||
|
|
@ -836,6 +862,8 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
packet_sender = Map.get(packet, :sender, Map.get(packet, "sender", ""))
|
packet_sender = Map.get(packet, :sender, Map.get(packet, "sender", ""))
|
||||||
|
|
||||||
if String.upcase(packet_sender) == String.upcase(socket.assigns.tracked_callsign) do
|
if String.upcase(packet_sender) == String.upcase(socket.assigns.tracked_callsign) do
|
||||||
|
# Update the tracked callsign's latest packet
|
||||||
|
socket = assign(socket, :tracked_callsign_latest_packet, packet)
|
||||||
process_packet_for_display(packet, socket)
|
process_packet_for_display(packet, socket)
|
||||||
else
|
else
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
|
|
@ -1554,6 +1582,25 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
|> Map.new()
|
|> Map.new()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec filter_packets_by_time_and_bounds_with_tracked(map(), map(), DateTime.t(), String.t(), map() | nil) :: map()
|
||||||
|
defp filter_packets_by_time_and_bounds_with_tracked(
|
||||||
|
packets,
|
||||||
|
bounds,
|
||||||
|
time_threshold,
|
||||||
|
tracked_callsign,
|
||||||
|
tracked_latest_packet
|
||||||
|
) do
|
||||||
|
filtered = filter_packets_by_time_and_bounds(packets, bounds, time_threshold)
|
||||||
|
|
||||||
|
# Always include the tracked callsign's latest packet if we have one
|
||||||
|
if tracked_callsign != "" and tracked_latest_packet do
|
||||||
|
key = get_callsign_key(tracked_latest_packet)
|
||||||
|
Map.put(filtered, key, tracked_latest_packet)
|
||||||
|
else
|
||||||
|
filtered
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Helper functions for marker operations
|
# Helper functions for marker operations
|
||||||
|
|
||||||
@spec remove_markers_batch(Socket.t(), list()) :: Socket.t()
|
@spec remove_markers_batch(Socket.t(), list()) :: Socket.t()
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ defmodule AprsmeWeb.MapLive.Navigation do
|
||||||
|
|
||||||
def handle_callsign_search(callsign, socket) do
|
def handle_callsign_search(callsign, socket) do
|
||||||
if Utils.valid_callsign?(callsign) do
|
if Utils.valid_callsign?(callsign) do
|
||||||
|
# Navigate to the new URL structure with the callsign as a path param
|
||||||
{:noreply, LiveView.push_navigate(socket, to: "/#{callsign}")}
|
{:noreply, LiveView.push_navigate(socket, to: "/#{callsign}")}
|
||||||
else
|
else
|
||||||
{:noreply, LiveView.put_flash(socket, :error, "Invalid callsign format")}
|
{:noreply, LiveView.put_flash(socket, :error, "Invalid callsign format")}
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,8 @@ defmodule AprsmeWeb.Router do
|
||||||
live "/api", ApiDocsLive, :index
|
live "/api", ApiDocsLive, :index
|
||||||
live "/info/:callsign", InfoLive.Show, :show
|
live "/info/:callsign", InfoLive.Show, :show
|
||||||
live "/", MapLive.Index, :index
|
live "/", MapLive.Index, :index
|
||||||
|
live "/:callsign", MapLive.Index, :index
|
||||||
end
|
end
|
||||||
|
|
||||||
# Redirect old callsign map URLs to new format
|
|
||||||
get "/:callsign", RedirectController, :callsign_to_map
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# API v1 routes
|
# API v1 routes
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue