Improve path page UX: prominent GPS button, share links

- "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
This commit is contained in:
Graham McIntire 2026-04-07 13:52:42 -05:00
parent ed6a378f3e
commit 1b0b55358b
3 changed files with 63 additions and 8 deletions

View file

@ -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

View file

@ -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 = '<span class="text-success">Copied!</span>'
setTimeout(() => { this.el.innerHTML = orig }, 1500)
})
})
}
}
export const LocateMe = {
mounted() {
this.el.addEventListener("click", () => this.locate())

View file

@ -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
<form phx-submit="calculate" phx-change="update_form" class="bg-base-200 rounded-box p-4 mb-6">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 items-end">
<div class="fieldset">
<label class="label text-sm font-medium">Source (callsign or grid)</label>
<label class="label text-sm font-medium">Source</label>
<div class="flex gap-1">
<input
type="text"
name="source"
id="source-input"
value={@source}
placeholder="W5ISP or EM13sf"
placeholder="W5ISP, EM13sf, or lat,lon"
class="input input-bordered flex-1"
required
/>
<button
type="button"
id="locate-me"
phx-hook="LocateMe"
class="btn btn-square btn-sm btn-ghost"
title="Use GPS location"
class="btn btn-sm btn-outline gap-1"
title="Use device GPS"
>
<.icon name="hero-map-pin" class="size-4" />
<.icon name="hero-map-pin" class="size-3.5" /> My Location
</button>
</div>
</div>
@ -896,6 +916,28 @@ defmodule MicrowavepropWeb.PathLive do
</div>
</div>
<% end %>
<%!-- Share links --%>
<div class="flex flex-wrap gap-2 mb-6">
<button
type="button"
phx-hook="CopyLink"
id="copy-link"
data-url={share_url(@result, :fixed)}
class="btn btn-sm btn-outline gap-1"
>
<.icon name="hero-link" class="size-3.5" /> Copy Link
</button>
<button
type="button"
phx-hook="CopyLink"
id="copy-gps-link"
data-url={share_url(@result, :gps)}
class="btn btn-sm btn-outline gap-1"
>
<.icon name="hero-map-pin" class="size-3.5" /> Copy Link (use viewer's GPS)
</button>
</div>
"""
end