liveview links

This commit is contained in:
Graham McIntire 2025-07-05 13:33:30 -05:00
parent bb82e46822
commit 142900c759
No known key found for this signature in database
12 changed files with 214 additions and 54 deletions

View file

@ -446,9 +446,9 @@ defmodule AprsmeWeb.CoreComponents do
~H"""
<div class="navbar bg-base-200 shadow-lg">
<div class="navbar-start">
<a href="/" class="btn btn-ghost text-xl font-bold text-primary">
<.link navigate="/" class="btn btn-ghost text-xl font-bold text-primary">
aprs.me
</a>
</.link>
</div>
<div class="navbar-center hidden lg:flex">
<.navigation variant={:horizontal} />
@ -751,14 +751,14 @@ defmodule AprsmeWeb.CoreComponents do
~H"""
<%= if @variant == :horizontal do %>
<ul class="menu menu-horizontal px-1">
<li><a href="/" class="link link-hover">Home</a></li>
<li><a href="/api" class="link link-hover">API</a></li>
<li><a href="/about" class="link link-hover">About</a></li>
<li><.link navigate="/" class="text-gray-900 hover:text-gray-700">Home</.link></li>
<li><.link navigate="/api" class="text-gray-900 hover:text-gray-700">API</.link></li>
<li><.link navigate="/about" class="text-gray-900 hover:text-gray-700">About</.link></li>
</ul>
<% else %>
<li><a href="/" class="link link-hover">Home</a></li>
<li><a href="/api" class="link link-hover">API</a></li>
<li><a href="/about" class="link link-hover">About</a></li>
<li><.link navigate="/" class="text-gray-900 hover:text-gray-700">Home</.link></li>
<li><.link navigate="/api" class="text-gray-900 hover:text-gray-700">API</.link></li>
<li><.link navigate="/about" class="text-gray-900 hover:text-gray-700">About</.link></li>
<% end %>
"""
end

View file

@ -4,7 +4,13 @@ defmodule AprsmeWeb.Layouts do
embed_templates "layouts/*"
def body_class(_assigns) do
["bg-base-100 antialiased"]
def body_class(assigns) do
base_classes = ["bg-base-100 antialiased"]
if Map.get(assigns, :map_page, false) do
base_classes ++ ["map-page"]
else
base_classes
end
end
end

View file

@ -1,3 +1,174 @@
<style>
/* Map page specific styles */
body.map-page header {
display: none;
}
body.map-page main {
padding: 0;
max-width: none;
height: 100vh;
overflow: hidden;
}
body.map-page main > div {
max-width: none;
height: 100%;
}
body.map-page .phx-main {
position: relative;
height: 100vh;
overflow: hidden;
}
body.map-page div[data-phx-main="true"] {
height: 100vh;
overflow: hidden;
}
/* Full page map for APRS home page and callsign pages */
body.map-page #aprs-map {
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
height: 100vh !important;
width: 100vw !important;
z-index: 1 !important;
transition: right 0.3s ease-in-out !important;
}
/* Desktop slideover behavior for map */
@media (min-width: 1024px) {
body.map-page #aprs-map.slideover-open {
right: 352px !important;
}
body.map-page #aprs-map.slideover-closed {
right: 0 !important;
}
}
/* Mobile slideover behavior for map */
@media (max-width: 1023px) {
body.map-page #aprs-map {
right: 0 !important;
}
}
/* Slideover toggle button styles */
body.map-page .slideover-toggle {
position: fixed !important;
right: 10px;
top: 10px;
z-index: 1001;
background: white;
border: 2px solid rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 10px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
body.map-page .slideover-toggle:hover {
background: #f8fafc;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
body.map-page .slideover-toggle:active {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
/* Hide toggle on desktop when slideover is open */
@media (min-width: 1024px) {
body.map-page .slideover-toggle.slideover-open {
display: none;
}
}
/* Mobile toggle adjustments */
@media (max-width: 1023px) {
body.map-page .slideover-toggle {
right: 15px;
top: 15px;
padding: 12px;
border-radius: 50%;
width: 48px;
height: 48px;
}
}
/* Slideover panel responsive styles */
body.map-page .slideover-panel {
position: fixed;
top: 0;
right: 0;
height: 100vh;
width: 352px;
background: white;
box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);
z-index: 1000;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow-y: auto;
}
/* Desktop slideover behavior */
@media (min-width: 1024px) {
body.map-page .slideover-panel.slideover-open {
transform: translateX(0);
}
body.map-page .slideover-panel.slideover-closed {
transform: translateX(100%);
}
}
/* Mobile slideover behavior */
@media (max-width: 1023px) {
body.map-page .slideover-panel {
width: 100vw;
max-width: 400px;
box-shadow: -8px 0 32px rgba(0, 0, 0, 0.2);
}
body.map-page .slideover-panel.slideover-open {
transform: translateX(0);
}
body.map-page .slideover-panel.slideover-closed {
transform: translateX(100%);
}
}
/* Mobile panel improvements */
@media (max-width: 1023px) {
body.map-page .slideover-panel {
display: flex;
flex-direction: column;
}
}
/* Improve scrolling on mobile */
@media (max-width: 1023px) {
body.map-page .slideover-panel {
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
body.map-page .slideover-panel .flex-1 {
min-height: 0;
}
}
</style>
<.header />
<main class="min-h-screen bg-base-100">
<div>

View file

@ -9,6 +9,8 @@ defmodule AprsmeWeb.MapLive.CallsignView do
alias AprsmeWeb.MapLive.MapHelpers
alias AprsmeWeb.MapLive.PacketUtils
@layout {AprsmeWeb.Layouts, :map}
@default_center %{lat: 39.0, lng: -98.0}
@default_zoom 4
@default_replay_speed 1.0
@ -61,7 +63,8 @@ defmodule AprsmeWeb.MapLive.CallsignView do
latest_symbol_table_id: "/",
latest_symbol_code: ">",
# Flag to track if latest marker was already pushed
latest_marker_pushed: false
latest_marker_pushed: false,
map_page: true
)
if connected?(socket) do
@ -656,9 +659,11 @@ defmodule AprsmeWeb.MapLive.CallsignView do
<div class="callsign-title">{@callsign}</div>
<div class="nav-links">
<a href="/" class="nav-link"> Back to Map</a>
<a href="/packets" class="nav-link">All Packets</a>
<a href={"/packets/#{String.downcase(@callsign)}"} class="nav-link">{@callsign} Packets</a>
<.link navigate="/" class="nav-link"> Back to Map</.link>
<.link navigate="/packets" class="nav-link">All Packets</.link>
<.link navigate={"/packets/#{String.downcase(@callsign)}"} class="nav-link">
{@callsign} Packets
</.link>
</div>
</div>

View file

@ -12,6 +12,8 @@ defmodule AprsmeWeb.MapLive.Index do
alias AprsmeWeb.MapLive.PacketUtils
alias Phoenix.LiveView.Socket
@layout {AprsmeWeb.Layouts, :map}
@default_center %{lat: 39.8283, lng: -98.5795}
@default_zoom 5
@ -54,7 +56,8 @@ defmodule AprsmeWeb.MapLive.Index do
historical_hours: "1",
packet_age_threshold: one_hour_ago,
slideover_open: true,
deployed_at: deployed_at
deployed_at: deployed_at,
map_page: true
)}
end
@ -774,7 +777,7 @@ defmodule AprsmeWeb.MapLive.Index do
<form phx-change="update_trail_duration" class="relative">
<select
name="trail_duration"
class="w-full px-4 py-3 border border-slate-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm bg-white appearance-none transition-all duration-200 hover:border-slate-400"
class="w-full px-4 py-3 border border-slate-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm bg-white text-gray-900 appearance-none transition-all duration-200 hover:border-slate-400"
>
<option value="1" selected={@trail_duration == "1"}>1 Hour</option>
<option value="6" selected={@trail_duration == "6"}>6 Hours</option>
@ -799,17 +802,6 @@ defmodule AprsmeWeb.MapLive.Index do
</svg>
</div>
</form>
<p class="text-xs text-slate-500 flex items-center space-x-1">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>How long should position trails be displayed</span>
</p>
</div>
<!-- Historical Data -->
@ -828,7 +820,7 @@ defmodule AprsmeWeb.MapLive.Index do
<form phx-change="update_historical_hours" class="relative">
<select
name="historical_hours"
class="w-full px-4 py-3 border border-slate-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm bg-white appearance-none transition-all duration-200 hover:border-slate-400"
class="w-full px-4 py-3 border border-slate-300 rounded-xl shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm bg-white text-gray-900 appearance-none transition-all duration-200 hover:border-slate-400"
>
<option value="1" selected={@historical_hours == "1"}>1 Hour</option>
<option value="3" selected={@historical_hours == "3"}>3 Hours</option>
@ -852,17 +844,6 @@ defmodule AprsmeWeb.MapLive.Index do
</svg>
</div>
</form>
<p class="text-xs text-slate-500 flex items-center space-x-1">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>How many hours of historical packets to load</span>
</p>
</div>
<!-- Navigation -->

View file

@ -199,14 +199,14 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
weather_link_html =
if has_weather_packets?(packet_info.callsign) do
~s( <a href="/weather/#{packet_info.callsign}" class="aprs-info-link">weather charts</a>)
~s( <.link navigate="/weather/#{packet_info.callsign}" class="aprs-info-link">weather charts</.link>)
else
""
end
"""
<div class="aprs-popup">
<div class="aprs-callsign"><strong><a href="/#{packet_info.callsign}">#{packet_info.callsign}</a></strong> <a href="/info/#{packet_info.callsign}" class="aprs-info-link">info</a>#{weather_link_html}</div>
<div class="aprs-callsign"><strong><.link navigate="/#{packet_info.callsign}">#{packet_info.callsign}</.link></strong> <.link navigate="/info/#{packet_info.callsign}" class="aprs-info-link">info</.link>#{weather_link_html}</div>
#{comment_html}
#{timestamp_html}
</div>
@ -234,7 +234,7 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
"""
<div class="aprs-popup" data-timestamp="#{cache_buster}">
<div class="aprs-callsign"><strong><a href="/#{sender}">#{sender}</a></strong> <a href="/info/#{sender}" class="aprs-info-link">info</a> <a href="/weather/#{sender}" class="aprs-info-link">weather charts</a></div>
<div class="aprs-callsign"><strong><.link navigate="/#{sender}">#{sender}</.link></strong> <.link navigate="/info/#{sender}" class="aprs-info-link">info</.link> <.link navigate="/weather/#{sender}" class="aprs-info-link">weather charts</.link></div>
<div class="aprs-comment">Weather Report</div>
#{timestamp_html}
<hr style="margin-top: 4px; margin-bottom: 4px;">

View file

@ -22,8 +22,8 @@ defmodule AprsmeWeb.UserConfirmationInstructionsLive do
</.simple_form>
<p>
<.link href={~p"/users/register"}>Register</.link>
| <.link href={~p"/users/log_in"}>Log in</.link>
<.link navigate={~p"/users/register"}>Register</.link>
| <.link navigate={~p"/users/log_in"}>Log in</.link>
</p>
"""
end

View file

@ -17,8 +17,8 @@ defmodule AprsmeWeb.UserConfirmationLive do
</.simple_form>
<p class="text-center mt-4">
<.link href={~p"/users/register"}>Register</.link>
| <.link href={~p"/users/log_in"}>Log in</.link>
<.link navigate={~p"/users/register"}>Register</.link>
| <.link navigate={~p"/users/log_in"}>Log in</.link>
</p>
</div>
"""

View file

@ -21,8 +21,8 @@ defmodule AprsmeWeb.UserForgotPasswordLive do
</:actions>
</.simple_form>
<p class="text-center mt-4">
<.link href={~p"/users/register"}>Register</.link>
| <.link href={~p"/users/log_in"}>Log in</.link>
<.link navigate={~p"/users/register"}>Register</.link>
| <.link navigate={~p"/users/log_in"}>Log in</.link>
</p>
</div>
"""

View file

@ -32,7 +32,7 @@ defmodule AprsmeWeb.UserLoginLive do
<:actions :let={f}>
<.input field={{f, :remember_me}} type="checkbox" label="Keep me logged in" />
<.link href={~p"/users/reset_password"} class="text-sm font-semibold">
<.link navigate={~p"/users/reset_password"} class="text-sm font-semibold">
Forgot your password?
</.link>
</:actions>

View file

@ -33,8 +33,8 @@ defmodule AprsmeWeb.UserResetPasswordLive do
</.simple_form>
<p class="text-center mt-4">
<.link href={~p"/users/register"}>Register</.link>
| <.link href={~p"/users/log_in"}>Log in</.link>
<.link navigate={~p"/users/register"}>Register</.link>
| <.link navigate={~p"/users/log_in"}>Log in</.link>
</p>
</div>
"""

View file

@ -39,9 +39,6 @@ defmodule AprsmeWeb.Router do
live "/about", AboutLive, :index
live "/api", ApiDocsLive, :index
live "/info/:callsign", InfoLive.Show, :show
end
live_session :map_pages, layout: {AprsmeWeb.Layouts, :map} do
live "/", MapLive.Index, :index
live "/:callsign", MapLive.CallsignView, :index
end