defmodule AprsmeWeb.MapLive.Components do @moduledoc """ Reusable function components for the Map LiveView. Extracted to improve maintainability and reduce the main LiveView module size. """ use AprsmeWeb, :html import AprsmeWeb.TimeHelpers, only: [time_ago_in_words: 1] attr :flash, :map, default: %{} attr :slideover_open, :boolean, default: false attr :map_center, :map, required: true attr :map_zoom, :integer, required: true attr :rest, :global def map_container(assigns) do ~H"""
""" end attr :slideover_open, :boolean, required: true attr :loading, :boolean, default: false attr :connection_status, :string, default: "pending" attr :packets, :list, default: [] attr :show_all_packets, :boolean, default: true attr :tracked_callsign, :string, default: "" attr :tracked_callsign_latest_packet, :map, default: nil attr :rest, :global def slideover_panel(assigns) do ~H"""
<.slideover_header {assigns} /> <.slideover_content {assigns} />
""" end defp slideover_header(assigns) do ~H"""

APRS Packets

""" end defp slideover_content(assigns) do ~H"""
<%= if @loading do %> <.loading_indicator /> <% else %> <.packet_controls {assigns} /> <.packet_list {assigns} /> <% end %>
""" end defp loading_indicator(assigns) do ~H"""
Loading packets...
""" end defp packet_controls(assigns) do ~H"""
<.connection_indicator connection_status={@connection_status} /> <.view_toggle show_all_packets={@show_all_packets} /> <.callsign_filter tracked_callsign={@tracked_callsign} tracked_callsign_latest_packet={@tracked_callsign_latest_packet} />
""" end defp connection_indicator(assigns) do ~H"""
<%= case @connection_status do %> <% "connected" -> %> Connected to APRS-IS <% "connecting" -> %> Connecting to APRS-IS... <% "disconnected" -> %> Disconnected from APRS-IS <% _ -> %> Initializing... <% end %>
""" end defp view_toggle(assigns) do ~H"""
""" end defp callsign_filter(assigns) do ~H"""
<%= if @tracked_callsign != "" do %> <% end %>
<%= if @tracked_callsign_latest_packet do %>
Last seen: {time_ago_in_words(get_received_at(@tracked_callsign_latest_packet))}
<% end %>
""" end defp packet_list(assigns) do ~H"""
<.packet_card packet={packet} />
""" end defp packet_card(assigns) do ~H"""
{@packet.sender} <%= if @packet.ssid && @packet.ssid != "0" do %> -{@packet.ssid} <% end %>
<%= if @packet.has_position do %>
📍 {format_coordinates(@packet.lat, @packet.lon)}
<% end %> <%= if @packet.comment do %>
{@packet.comment}
<% end %>
via {@packet.path || "direct"} • {time_ago_in_words(@packet.received_at)}
<%= if @packet.has_position do %> <% end %>
""" end # Helper functions defp format_coordinates(lat, lon) when is_number(lat) and is_number(lon) do "#{Float.round(lat, 4)}, #{Float.round(lon, 4)}" end defp format_coordinates(_, _), do: "Unknown location" # Style component for better organization def map_styles(assigns) do ~H""" """ end # Helper function to get received_at from packet (handles both structs and maps) defp get_received_at(%Aprsme.Packet{} = packet) do packet.received_at end defp get_received_at(packet) when is_map(packet) do # For regular maps, try both atom and string keys Map.get(packet, :received_at) || Map.get(packet, "received_at") end defp get_received_at(_), do: nil end