From d42e15c15970cd50b3b50114b812e147a6e2bfbf Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 25 Jul 2025 14:18:22 -0500 Subject: [PATCH 1/2] Add real-time last update display to map sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a "Last Update" section below the navigation that shows: - Relative time ("less than a minute ago") - Exact UTC timestamp ("2025-07-25 19:04 UTC") Updates automatically whenever LiveView receives packet updates or map bounds changes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lib/aprsme_web/live/map_live/index.ex | 26 ++++++++++++++++++- .../live/map_live/packet_processor.ex | 4 +++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/aprsme_web/live/map_live/index.ex b/lib/aprsme_web/live/map_live/index.ex index d17c62f..937f182 100644 --- a/lib/aprsme_web/live/map_live/index.ex +++ b/lib/aprsme_web/live/map_live/index.ex @@ -199,7 +199,9 @@ defmodule AprsmeWeb.MapLive.Index do # Overlay controls overlay_callsign: "", # Slideover state - will be set based on screen size - slideover_open: true + slideover_open: true, + # Track when last update occurred for display + last_update_at: DateTime.utc_now() ) end @@ -1295,6 +1297,27 @@ defmodule AprsmeWeb.MapLive.Index do /> + +
+
+ + + + {gettext("Last Update")} +
+
+
{time_ago_in_words(@last_update_at)}
+
+ {Calendar.strftime(@last_update_at, "%Y-%m-%d %H:%M UTC")} +
+
+
+
@@ -1618,6 +1641,7 @@ defmodule AprsmeWeb.MapLive.Index do socket |> assign(map_bounds: map_bounds, packet_state: updated_packet_state) |> assign(needs_initial_historical_load: false) + |> assign(last_update_at: DateTime.utc_now()) # Load historical packets for the new bounds (now socket.assigns.map_bounds is correct) Logger.debug("Starting progressive historical loading for new bounds") diff --git a/lib/aprsme_web/live/map_live/packet_processor.ex b/lib/aprsme_web/live/map_live/packet_processor.ex index 8441add..3c7f407 100644 --- a/lib/aprsme_web/live/map_live/packet_processor.ex +++ b/lib/aprsme_web/live/map_live/packet_processor.ex @@ -38,6 +38,10 @@ defmodule AprsmeWeb.MapLive.PacketProcessor do # Handle packet visibility logic socket = handle_packet_visibility(packet, lat, lon, callsign_key, socket) + + # Update last update timestamp for display purposes + socket = assign(socket, :last_update_at, DateTime.utc_now()) + {:noreply, socket} end From 7b8b417673574877b4d1302fabd5ede4c04a434a Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 25 Jul 2025 14:26:58 -0500 Subject: [PATCH 2/2] Address PR feedback: Add defensive handling and documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add nil handling for @last_update_at in template with fallback message - Add documentation comments explaining timestamp update logic - Improve code clarity based on review feedback Addresses suggestions from automated PR review. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lib/aprsme_web/live/map_live/index.ex | 16 +++++++++++----- lib/aprsme_web/live/map_live/packet_processor.ex | 3 ++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/aprsme_web/live/map_live/index.ex b/lib/aprsme_web/live/map_live/index.ex index 937f182..369e9a0 100644 --- a/lib/aprsme_web/live/map_live/index.ex +++ b/lib/aprsme_web/live/map_live/index.ex @@ -200,7 +200,8 @@ defmodule AprsmeWeb.MapLive.Index do overlay_callsign: "", # Slideover state - will be set based on screen size slideover_open: true, - # Track when last update occurred for display + # Track when last update occurred for real-time display in map sidebar + # Updated when packets are processed or map bounds change last_update_at: DateTime.utc_now() ) end @@ -1311,10 +1312,14 @@ defmodule AprsmeWeb.MapLive.Index do {gettext("Last Update")}
-
{time_ago_in_words(@last_update_at)}
-
- {Calendar.strftime(@last_update_at, "%Y-%m-%d %H:%M UTC")} -
+ <%= if @last_update_at do %> +
{time_ago_in_words(@last_update_at)}
+
+ {Calendar.strftime(@last_update_at, "%Y-%m-%d %H:%M UTC")} +
+ <% else %> +
No updates yet
+ <% end %>
@@ -1641,6 +1646,7 @@ defmodule AprsmeWeb.MapLive.Index do socket |> assign(map_bounds: map_bounds, packet_state: updated_packet_state) |> assign(needs_initial_historical_load: false) + # Update last update timestamp when map bounds change - this triggers data refresh |> assign(last_update_at: DateTime.utc_now()) # Load historical packets for the new bounds (now socket.assigns.map_bounds is correct) diff --git a/lib/aprsme_web/live/map_live/packet_processor.ex b/lib/aprsme_web/live/map_live/packet_processor.ex index 3c7f407..7fddaf9 100644 --- a/lib/aprsme_web/live/map_live/packet_processor.ex +++ b/lib/aprsme_web/live/map_live/packet_processor.ex @@ -39,7 +39,8 @@ defmodule AprsmeWeb.MapLive.PacketProcessor do # Handle packet visibility logic socket = handle_packet_visibility(packet, lat, lon, callsign_key, socket) - # Update last update timestamp for display purposes + # Update last update timestamp for real-time display in map sidebar + # This timestamp is shown to users to indicate when data was last refreshed socket = assign(socket, :last_update_at, DateTime.utc_now()) {:noreply, socket}