From 1b0b55358bbbe12ae1025aedd795209eb041a6b2 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 7 Apr 2026 13:52:42 -0500 Subject: [PATCH] Improve path page UX: prominent GPS button, share links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "My Location" button with text label (was just an icon) - Two share buttons after results: - "Copy Link" — fixed coordinates, exact result - "Copy Link (use viewer's GPS)" — source=gps, dynamic per viewer - CopyLink JS hook copies full URL to clipboard with "Copied!" feedback --- assets/js/app.js | 4 +- assets/js/locate_me_hook.js | 13 ++++++ lib/microwaveprop_web/live/path_live.ex | 54 ++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 09bee7f5..96c78e6b 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -35,7 +35,7 @@ import {ContactMap} from "./contact_map_hook" import {ContactsMap} from "./contacts_map_hook" import {ElevationProfile} from "./elevation_profile_hook" import {WeatherMap} from "./weather_map_hook" -import {LocateMe} from "./locate_me_hook" +import {LocateMe, CopyLink} from "./locate_me_hook" const UtcClock = { mounted() { @@ -57,7 +57,7 @@ const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute const liveSocket = new LiveSocket("/live", Socket, { longPollFallbackMs: 2500, params: initLiveStash({_csrf_token: csrfToken}), - hooks: {...colocatedHooks, PropagationMap, UtcClock, ContactMap, ContactsMap, ElevationProfile, WeatherMap, LocateMe}, + hooks: {...colocatedHooks, PropagationMap, UtcClock, ContactMap, ContactsMap, ElevationProfile, WeatherMap, LocateMe, CopyLink}, }) // Show progress bar on live navigation and form submits diff --git a/assets/js/locate_me_hook.js b/assets/js/locate_me_hook.js index 93e40df4..07ce5d95 100644 --- a/assets/js/locate_me_hook.js +++ b/assets/js/locate_me_hook.js @@ -1,3 +1,16 @@ +export const CopyLink = { + mounted() { + this.el.addEventListener("click", () => { + const url = window.location.origin + this.el.dataset.url + navigator.clipboard.writeText(url).then(() => { + const orig = this.el.innerHTML + this.el.innerHTML = 'Copied!' + setTimeout(() => { this.el.innerHTML = orig }, 1500) + }) + }) + } +} + export const LocateMe = { mounted() { this.el.addEventListener("click", () => this.locate()) diff --git a/lib/microwaveprop_web/live/path_live.ex b/lib/microwaveprop_web/live/path_live.ex index c9f3a26f..4ae311a3 100644 --- a/lib/microwaveprop_web/live/path_live.ex +++ b/lib/microwaveprop_web/live/path_live.ex @@ -470,6 +470,27 @@ defmodule MicrowavepropWeb.PathLive do defp format_utc_hour(%DateTime{} = dt), do: Calendar.strftime(dt, "%H:%M") defp format_utc_hour(_), do: "?" + defp share_url(result, mode) do + source = + case mode do + :gps -> "gps" + :fixed -> "#{Float.round(result.source.lat, 4)},#{Float.round(result.source.lon, 4)}" + end + + params = %{ + "source" => source, + "destination" => result.destination.label, + "band" => to_string(result.band_mhz), + "src_height_ft" => to_string(result.station_params.src_height_ft), + "dst_height_ft" => to_string(result.station_params.dst_height_ft), + "tx_power_dbm" => to_string(result.station_params.tx_power_dbm), + "src_gain_dbi" => to_string(result.station_params.src_gain_dbi), + "dst_gain_dbi" => to_string(result.station_params.dst_gain_dbi) + } + + "/path?" <> URI.encode_query(params) + end + defp format_mw(mw) when mw >= 1000, do: "#{format_number(mw / 1000)} W" defp format_mw(mw) when mw >= 1, do: "#{format_number(mw)} mW" defp format_mw(mw), do: "#{format_number(mw * 1000)} uW" @@ -488,25 +509,24 @@ defmodule MicrowavepropWeb.PathLive do
- +
@@ -896,6 +916,28 @@ defmodule MicrowavepropWeb.PathLive do
<% end %> + + <%!-- Share links --%> +
+ + +
""" end