Reduce memo thrash on map update

This commit is contained in:
Jack Kingsman
2026-03-15 18:07:30 -07:00
parent 226dc4f59e
commit c76f230c9f
35 changed files with 90 additions and 50 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
frontend/prebuilt/** -diff

View File

@@ -1,2 +0,0 @@
import{r as d,D as j,j as e,U as y,m as N}from"./index-DSM_ctlA.js";import{M as w,T as _}from"./leaflet-Bn7rjtY9.js";import{C as k,P as C}from"./Popup-CqM5D37r.js";import{u as M}from"./hooks-Bk-tBmjl.js";const i={recent:"#06b6d4",today:"#2563eb",stale:"#f59e0b",old:"#64748b"},R="#0f172a",p="#f8fafc";function E(a){if(a==null)return i.old;const r=Date.now()/1e3-a,n=3600,l=86400;return r<n?i.recent:r<l?i.today:r<3*l?i.stale:i.old}function v({contacts:a,focusedContact:t}){const r=M(),[n,l]=d.useState(!1);return d.useEffect(()=>{if(t&&t.lat!=null&&t.lon!=null){r.setView([t.lat,t.lon],12),l(!0);return}if(n)return;const c=()=>{if(a.length===0){r.setView([20,0],2),l(!0);return}if(a.length===1){r.setView([a[0].lat,a[0].lon],10),l(!0);return}const u=a.map(m=>[m.lat,m.lon]);r.fitBounds(u,{padding:[50,50],maxZoom:12}),l(!0)};"geolocation"in navigator?navigator.geolocation.getCurrentPosition(u=>{r.setView([u.coords.latitude,u.coords.longitude],8),l(!0)},()=>{c()},{timeout:5e3,maximumAge:3e5}):c()},[r,a,n,t]),null}function V({contacts:a,focusedKey:t}){const r=Date.now()/1e3-604800,n=d.useMemo(()=>a.filter(s=>j(s.lat,s.lon)&&(s.public_key===t||s.last_seen!=null&&s.last_seen>r)),[a,t,r]),l=d.useMemo(()=>t&&n.find(s=>s.public_key===t)||null,[t,n]),c=l!=null&&(l.last_seen==null||l.last_seen<=r),u=d.useRef({}),m=d.useCallback((s,o)=>{u.current[s]=o},[]);return d.useEffect(()=>{if(l&&u.current[l.public_key]){const s=setTimeout(()=>{var o;(o=u.current[l.public_key])==null||o.openPopup()},100);return()=>clearTimeout(s)}},[l]),e.jsxs("div",{className:"flex flex-col h-full",children:[e.jsxs("div",{className:"px-4 py-2 bg-muted/50 text-xs text-muted-foreground flex items-center justify-between",children:[e.jsxs("span",{children:["Showing ",n.length," contact",n.length!==1?"s":""," heard in the last 7 days",c?" plus the focused contact":""]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.recent},"aria-hidden":"true"})," ","<1h"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.today},"aria-hidden":"true"})," ","<1d"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.stale},"aria-hidden":"true"})," ","<3d"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.old},"aria-hidden":"true"})," ","older"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full border-2",style:{borderColor:p,backgroundColor:i.today},"aria-hidden":"true"})," ","repeater"]})]})]}),e.jsx("div",{className:"flex-1 relative",style:{zIndex:0},role:"img","aria-label":"Map showing mesh node locations",children:e.jsxs(w,{center:[20,0],zoom:2,className:"h-full w-full",style:{background:"#1a1a2e"},children:[e.jsx(_,{attribution:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',url:"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"}),e.jsx(v,{contacts:n,focusedContact:l}),n.map(s=>{const o=s.type===y,x=E(s.last_seen),f=s.name||s.public_key.slice(0,12),h=s.last_seen!=null?N(s.last_seen):"Never heard by this server",g=o?10:7;return e.jsx(d.Fragment,{children:e.jsx(k,{ref:b=>m(s.public_key,b),center:[s.lat,s.lon],radius:g,pathOptions:{color:o?p:R,fillColor:x,fillOpacity:.9,weight:o?3:2},children:e.jsx(C,{children:e.jsxs("div",{className:"text-sm",children:[e.jsxs("div",{className:"font-medium flex items-center gap-1",children:[o&&e.jsx("span",{title:"Repeater","aria-hidden":"true",children:"🛜"}),f]}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:["Last heard: ",h]}),e.jsxs("div",{className:"text-xs text-gray-400 mt-1 font-mono",children:[s.lat.toFixed(5),", ",s.lon.toFixed(5)]})]})})},s.public_key)},s.public_key)})]})})]})}export{V as MapView};
//# sourceMappingURL=MapView-7UG06zXw.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import{r as u,D as j,j as e,U as y,m as N}from"./index-DhrMLy6_.js";import{M as w,T as _}from"./leaflet-lJm4twjh.js";import{C as k,P as C}from"./Popup-DkSClDwo.js";import{u as M}from"./hooks-DfhmuKzA.js";const i={recent:"#06b6d4",today:"#2563eb",stale:"#f59e0b",old:"#64748b"},R="#0f172a",p="#f8fafc";function E(a){if(a==null)return i.old;const l=Date.now()/1e3-a,n=3600,t=86400;return l<n?i.recent:l<t?i.today:l<3*t?i.stale:i.old}function v({contacts:a,focusedContact:r}){const l=M(),[n,t]=u.useState(!1);return u.useEffect(()=>{if(r&&r.lat!=null&&r.lon!=null){l.setView([r.lat,r.lon],12),t(!0);return}if(n)return;const c=()=>{if(a.length===0){l.setView([20,0],2),t(!0);return}if(a.length===1){l.setView([a[0].lat,a[0].lon],10),t(!0);return}const d=a.map(m=>[m.lat,m.lon]);l.fitBounds(d,{padding:[50,50],maxZoom:12}),t(!0)};"geolocation"in navigator?navigator.geolocation.getCurrentPosition(d=>{l.setView([d.coords.latitude,d.coords.longitude],8),t(!0)},()=>{c()},{timeout:5e3,maximumAge:3e5}):c()},[l,a,n,r]),null}function V({contacts:a,focusedKey:r}){const[l]=u.useState(()=>Date.now()/1e3-604800),n=u.useMemo(()=>a.filter(s=>j(s.lat,s.lon)&&(s.public_key===r||s.last_seen!=null&&s.last_seen>l)),[a,r,l]),t=u.useMemo(()=>r&&n.find(s=>s.public_key===r)||null,[r,n]),c=t!=null&&(t.last_seen==null||t.last_seen<=l),d=u.useRef({}),m=u.useCallback((s,o)=>{d.current[s]=o},[]);return u.useEffect(()=>{if(t&&d.current[t.public_key]){const s=setTimeout(()=>{var o;(o=d.current[t.public_key])==null||o.openPopup()},100);return()=>clearTimeout(s)}},[t]),e.jsxs("div",{className:"flex flex-col h-full",children:[e.jsxs("div",{className:"px-4 py-2 bg-muted/50 text-xs text-muted-foreground flex items-center justify-between",children:[e.jsxs("span",{children:["Showing ",n.length," contact",n.length!==1?"s":""," heard in the last 7 days",c?" plus the focused contact":""]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.recent},"aria-hidden":"true"})," ","<1h"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.today},"aria-hidden":"true"})," ","<1d"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.stale},"aria-hidden":"true"})," ","<3d"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full",style:{backgroundColor:i.old},"aria-hidden":"true"})," ","older"]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx("span",{className:"w-3 h-3 rounded-full border-2",style:{borderColor:p,backgroundColor:i.today},"aria-hidden":"true"})," ","repeater"]})]})]}),e.jsx("div",{className:"flex-1 relative",style:{zIndex:0},role:"img","aria-label":"Map showing mesh node locations",children:e.jsxs(w,{center:[20,0],zoom:2,className:"h-full w-full",style:{background:"#1a1a2e"},children:[e.jsx(_,{attribution:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',url:"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"}),e.jsx(v,{contacts:n,focusedContact:t}),n.map(s=>{const o=s.type===y,x=E(s.last_seen),f=s.name||s.public_key.slice(0,12),h=s.last_seen!=null?N(s.last_seen):"Never heard by this server",g=o?10:7;return e.jsx(u.Fragment,{children:e.jsx(k,{ref:b=>m(s.public_key,b),center:[s.lat,s.lon],radius:g,pathOptions:{color:o?p:R,fillColor:x,fillOpacity:.9,weight:o?3:2},children:e.jsx(C,{children:e.jsxs("div",{className:"text-sm",children:[e.jsxs("div",{className:"font-medium flex items-center gap-1",children:[o&&e.jsx("span",{title:"Repeater","aria-hidden":"true",children:"🛜"}),f]}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:["Last heard: ",h]}),e.jsxs("div",{className:"text-xs text-gray-400 mt-1 font-mono",children:[s.lat.toFixed(5),", ",s.lon.toFixed(5)]})]})})},s.public_key)},s.public_key)})]})})]})}export{V as MapView};
//# sourceMappingURL=MapView-BW3wM0G8.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
import{j as l}from"./index-DSM_ctlA.js";import{d as m,l as u,a as f,e as d,M as x,T as y}from"./leaflet-Bn7rjtY9.js";import{C as p,P as c}from"./Popup-CqM5D37r.js";const g=m(function({positions:n,...t},o){const s=new u.Polyline(n,t);return f(s,d(o,{overlayContainer:s}))},function(n,t,o){t.positions!==o.positions&&n.setLatLngs(t.positions)});function C({neighbors:i,radioLat:n,radioLon:t,radioName:o}){const s=i.filter(e=>e.lat!=null&&e.lon!=null),r=n!=null&&t!=null&&!(n===0&&t===0);if(s.length===0&&!r)return null;const h=r?[n,t]:[s[0].lat,s[0].lon];return l.jsx("div",{className:"min-h-48 flex-1 rounded border border-border overflow-hidden",role:"img","aria-label":"Map showing repeater neighbor locations",children:l.jsxs(x,{center:h,zoom:10,className:"h-full w-full",style:{background:"#1a1a2e"},children:[l.jsx(y,{attribution:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',url:"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"}),r&&s.map((e,a)=>l.jsx(g,{positions:[[n,t],[e.lat,e.lon]],pathOptions:{color:"#3b82f6",weight:1.5,opacity:.5,dashArray:"6 4"}},`line-${a}`)),r&&l.jsx(p,{center:[n,t],radius:8,pathOptions:{color:"#1d4ed8",fillColor:"#3b82f6",fillOpacity:1,weight:2},children:l.jsx(c,{children:l.jsx("span",{className:"text-sm font-medium",children:o||"Our Radio"})})}),s.map((e,a)=>l.jsx(p,{center:[e.lat,e.lon],radius:6,pathOptions:{color:"#000",fillColor:e.snr>=6?"#22c55e":e.snr>=0?"#eab308":"#ef4444",fillOpacity:.8,weight:1},children:l.jsx(c,{children:l.jsx("span",{className:"text-sm",children:e.name||e.pubkey_prefix})})},a))]})})}export{C as NeighborsMiniMap};
//# sourceMappingURL=NeighborsMiniMap-C3Ju-R0X.js.map
import{j as l}from"./index-DhrMLy6_.js";import{d as m,l as u,a as f,e as d,M as x,T as y}from"./leaflet-lJm4twjh.js";import{C as p,P as c}from"./Popup-DkSClDwo.js";const g=m(function({positions:n,...t},o){const s=new u.Polyline(n,t);return f(s,d(o,{overlayContainer:s}))},function(n,t,o){t.positions!==o.positions&&n.setLatLngs(t.positions)});function C({neighbors:i,radioLat:n,radioLon:t,radioName:o}){const s=i.filter(e=>e.lat!=null&&e.lon!=null),r=n!=null&&t!=null&&!(n===0&&t===0);if(s.length===0&&!r)return null;const h=r?[n,t]:[s[0].lat,s[0].lon];return l.jsx("div",{className:"min-h-48 flex-1 rounded border border-border overflow-hidden",role:"img","aria-label":"Map showing repeater neighbor locations",children:l.jsxs(x,{center:h,zoom:10,className:"h-full w-full",style:{background:"#1a1a2e"},children:[l.jsx(y,{attribution:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',url:"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"}),r&&s.map((e,a)=>l.jsx(g,{positions:[[n,t],[e.lat,e.lon]],pathOptions:{color:"#3b82f6",weight:1.5,opacity:.5,dashArray:"6 4"}},`line-${a}`)),r&&l.jsx(p,{center:[n,t],radius:8,pathOptions:{color:"#1d4ed8",fillColor:"#3b82f6",fillOpacity:1,weight:2},children:l.jsx(c,{children:l.jsx("span",{className:"text-sm font-medium",children:o||"Our Radio"})})}),s.map((e,a)=>l.jsx(p,{center:[e.lat,e.lon],radius:6,pathOptions:{color:"#000",fillColor:e.snr>=6?"#22c55e":e.snr>=0?"#eab308":"#ef4444",fillOpacity:.8,weight:1},children:l.jsx(c,{children:l.jsx("span",{className:"text-sm",children:e.name||e.pubkey_prefix})})},a))]})})}export{C as NeighborsMiniMap};
//# sourceMappingURL=NeighborsMiniMap-BfUArOef.js.map

View File

@@ -1,4 +1,4 @@
import{r as x,D as l,j as i}from"./index-DSM_ctlA.js";import{c as O,l as b,a as y,e as w,b as C,M as L,T as M,L as R}from"./leaflet-Bn7rjtY9.js";import{u as T}from"./hooks-Bk-tBmjl.js";const h=O(function({position:n,...t},o){const r=new b.Marker(n,t);return y(r,w(o,{overlayContainer:r}))},function(n,t,o){t.position!==o.position&&n.setLatLng(t.position),t.icon!=null&&t.icon!==o.icon&&n.setIcon(t.icon),t.zIndexOffset!=null&&t.zIndexOffset!==o.zIndexOffset&&n.setZIndexOffset(t.zIndexOffset),t.opacity!=null&&t.opacity!==o.opacity&&n.setOpacity(t.opacity),n.dragging!=null&&t.draggable!==o.draggable&&(t.draggable===!0?n.dragging.enable():n.dragging.disable())}),p=C(function(n,t){const o=new b.Tooltip(n,t.overlayContainer);return y(o,t)},function(n,t,{position:o},r){x.useEffect(function(){const s=t.overlayContainer;if(s==null)return;const{instance:u}=n,f=a=>{a.tooltip===u&&(o!=null&&u.setLatLng(o),u.update(),r(!0))},c=a=>{a.tooltip===u&&r(!1)};return s.on({tooltipopen:f,tooltipclose:c}),s.bindTooltip(u),function(){s.off({tooltipopen:f,tooltipclose:c}),s._map!=null&&s.unbindTooltip()}},[n,t,r,o])}),m=["#f97316","#eab308","#22c55e","#06b6d4","#ec4899","#f43f5e","#a855f7","#64748b"],E="#3b82f6",S="#8b5cf6";function g(e,n){return R.divIcon({className:"",iconSize:[24,24],iconAnchor:[12,12],html:`<div style="
import{r as x,D as l,j as i}from"./index-DhrMLy6_.js";import{c as O,l as b,a as y,e as w,b as C,M as L,T as M,L as R}from"./leaflet-lJm4twjh.js";import{u as T}from"./hooks-DfhmuKzA.js";const h=O(function({position:n,...t},o){const r=new b.Marker(n,t);return y(r,w(o,{overlayContainer:r}))},function(n,t,o){t.position!==o.position&&n.setLatLng(t.position),t.icon!=null&&t.icon!==o.icon&&n.setIcon(t.icon),t.zIndexOffset!=null&&t.zIndexOffset!==o.zIndexOffset&&n.setZIndexOffset(t.zIndexOffset),t.opacity!=null&&t.opacity!==o.opacity&&n.setOpacity(t.opacity),n.dragging!=null&&t.draggable!==o.draggable&&(t.draggable===!0?n.dragging.enable():n.dragging.disable())}),p=C(function(n,t){const o=new b.Tooltip(n,t.overlayContainer);return y(o,t)},function(n,t,{position:o},r){x.useEffect(function(){const s=t.overlayContainer;if(s==null)return;const{instance:u}=n,f=a=>{a.tooltip===u&&(o!=null&&u.setLatLng(o),u.update(),r(!0))},c=a=>{a.tooltip===u&&r(!1)};return s.on({tooltipopen:f,tooltipclose:c}),s.bindTooltip(u),function(){s.off({tooltipopen:f,tooltipclose:c}),s._map!=null&&s.unbindTooltip()}},[n,t,r,o])}),m=["#f97316","#eab308","#22c55e","#06b6d4","#ec4899","#f43f5e","#a855f7","#64748b"],E="#3b82f6",S="#8b5cf6";function g(e,n){return R.divIcon({className:"",iconSize:[24,24],iconAnchor:[12,12],html:`<div style="
width:24px;height:24px;border-radius:50%;
background:${n};color:#fff;
display:flex;align-items:center;justify-content:center;
@@ -6,4 +6,4 @@ import{r as x,D as l,j as i}from"./index-DSM_ctlA.js";import{c as O,l as b,a as
border:2px solid rgba(255,255,255,0.8);
box-shadow:0 1px 4px rgba(0,0,0,0.4);
">${e}</div>`})}function z(e){return m[e%m.length]}function N(e){const n=[];l(e.sender.lat,e.sender.lon)&&n.push([e.sender.lat,e.sender.lon]);for(const t of e.hops)for(const o of t.matches)l(o.lat,o.lon)&&n.push([o.lat,o.lon]);return l(e.receiver.lat,e.receiver.lon)&&n.push([e.receiver.lat,e.receiver.lon]),n}function I({points:e}){const n=T(),t=x.useRef(!1);return x.useEffect(()=>{t.current||e.length===0||(t.current=!0,e.length===1?n.setView(e[0],12):n.fitBounds(e,{padding:[30,30],maxZoom:14}))},[n,e]),null}function k({resolved:e,senderInfo:n}){const t=N(e),o=t.length>0;let r=2,d=0;l(e.sender.lat,e.sender.lon)&&d++,l(e.receiver.lat,e.receiver.lon)&&d++;for(const f of e.hops)f.matches.length===0?r++:(r+=f.matches.length,d+=f.matches.filter(c=>l(c.lat,c.lon)).length);const s=o&&d<r;if(!o)return i.jsx("div",{className:"h-14 rounded border border-border bg-muted/30 flex items-center justify-center text-sm text-muted-foreground",children:"No nodes in this route have GPS coordinates"});const u=t[0];return i.jsxs("div",{children:[i.jsx("div",{className:"rounded border border-border overflow-hidden",role:"img","aria-label":"Map showing message route between nodes",style:{height:220},children:i.jsxs(L,{center:u,zoom:10,className:"h-full w-full",style:{background:"#1a1a2e"},children:[i.jsx(M,{attribution:'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',url:"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"}),i.jsx(I,{points:t}),l(e.sender.lat,e.sender.lon)&&i.jsx(h,{position:[e.sender.lat,e.sender.lon],icon:g("S",E),children:i.jsx(p,{direction:"top",offset:[0,-14],children:n.name||"Sender"})}),e.hops.map((f,c)=>f.matches.filter(a=>l(a.lat,a.lon)).map((a,j)=>i.jsx(h,{position:[a.lat,a.lon],icon:g(String(c+1),z(c)),children:i.jsx(p,{direction:"top",offset:[0,-14],children:a.name||a.public_key.slice(0,12)})},`hop-${c}-${j}`))),l(e.receiver.lat,e.receiver.lon)&&i.jsx(h,{position:[e.receiver.lat,e.receiver.lon],icon:g("R",S),children:i.jsx(p,{direction:"top",offset:[0,-14],children:e.receiver.name||"Receiver"})})]})}),s&&i.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"Some nodes in this route have no GPS and are not shown"})]})}export{k as PathRouteMap};
//# sourceMappingURL=PathRouteMap-DdylhAp-.js.map
//# sourceMappingURL=PathRouteMap-Cg3mrjz1.js.map

View File

@@ -1,2 +1,2 @@
import{d,l as f,a as s,e as C,b as m}from"./leaflet-Bn7rjtY9.js";import{r as P}from"./index-DSM_ctlA.js";function v(o,n,e){n.center!==e.center&&o.setLatLng(n.center),n.radius!=null&&n.radius!==e.radius&&o.setRadius(n.radius)}const b=d(function({center:n,children:e,...r},p){const t=new f.CircleMarker(n,r);return s(t,C(p,{overlayContainer:t}))},v),k=m(function(n,e){const r=new f.Popup(n,e.overlayContainer);return s(r,e)},function(n,e,{position:r},p){P.useEffect(function(){const{instance:a}=n;function i(u){u.popup===a&&(a.update(),p(!0))}function c(u){u.popup===a&&p(!1)}return e.map.on({popupopen:i,popupclose:c}),e.overlayContainer==null?(r!=null&&a.setLatLng(r),a.openOn(e.map)):e.overlayContainer.bindPopup(a),function(){var l;e.map.off({popupopen:i,popupclose:c}),(l=e.overlayContainer)==null||l.unbindPopup(),e.map.removeLayer(a)}},[n,e,p,r])});export{b as C,k as P};
//# sourceMappingURL=Popup-CqM5D37r.js.map
import{d,l as f,a as s,e as C,b as m}from"./leaflet-lJm4twjh.js";import{r as P}from"./index-DhrMLy6_.js";function v(o,n,e){n.center!==e.center&&o.setLatLng(n.center),n.radius!=null&&n.radius!==e.radius&&o.setRadius(n.radius)}const b=d(function({center:n,children:e,...r},p){const t=new f.CircleMarker(n,r);return s(t,C(p,{overlayContainer:t}))},v),k=m(function(n,e){const r=new f.Popup(n,e.overlayContainer);return s(r,e)},function(n,e,{position:r},p){P.useEffect(function(){const{instance:a}=n;function i(u){u.popup===a&&(a.update(),p(!0))}function c(u){u.popup===a&&p(!1)}return e.map.on({popupopen:i,popupclose:c}),e.overlayContainer==null?(r!=null&&a.setLatLng(r),a.openOn(e.map)):e.overlayContainer.bindPopup(a),function(){var l;e.map.off({popupopen:i,popupclose:c}),(l=e.overlayContainer)==null||l.unbindPopup(),e.map.removeLayer(a)}},[n,e,p,r])});export{b as C,k as P};
//# sourceMappingURL=Popup-DkSClDwo.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"Popup-CqM5D37r.js","sources":["../../node_modules/@react-leaflet/core/lib/circle.js","../../node_modules/react-leaflet/lib/CircleMarker.js","../../node_modules/react-leaflet/lib/Popup.js"],"sourcesContent":["export function updateCircle(layer, props, prevProps) {\n if (props.center !== prevProps.center) {\n layer.setLatLng(props.center);\n }\n if (props.radius != null && props.radius !== prevProps.radius) {\n layer.setRadius(props.radius);\n }\n}\n","import { createElementObject, createPathComponent, extendContext, updateCircle } from '@react-leaflet/core';\nimport { CircleMarker as LeafletCircleMarker } from 'leaflet';\nexport const CircleMarker = createPathComponent(function createCircleMarker({ center , children: _c , ...options }, ctx) {\n const marker = new LeafletCircleMarker(center, options);\n return createElementObject(marker, extendContext(ctx, {\n overlayContainer: marker\n }));\n}, updateCircle);\n","import { createElementObject, createOverlayComponent } from '@react-leaflet/core';\nimport { Popup as LeafletPopup } from 'leaflet';\nimport { useEffect } from 'react';\nexport const Popup = createOverlayComponent(function createPopup(props, context) {\n const popup = new LeafletPopup(props, context.overlayContainer);\n return createElementObject(popup, context);\n}, function usePopupLifecycle(element, context, { position }, setOpen) {\n useEffect(function addPopup() {\n const { instance } = element;\n function onPopupOpen(event) {\n if (event.popup === instance) {\n instance.update();\n setOpen(true);\n }\n }\n function onPopupClose(event) {\n if (event.popup === instance) {\n setOpen(false);\n }\n }\n context.map.on({\n popupopen: onPopupOpen,\n popupclose: onPopupClose\n });\n if (context.overlayContainer == null) {\n // Attach to a Map\n if (position != null) {\n instance.setLatLng(position);\n }\n instance.openOn(context.map);\n } else {\n // Attach to container component\n context.overlayContainer.bindPopup(instance);\n }\n return function removePopup() {\n context.map.off({\n popupopen: onPopupOpen,\n popupclose: onPopupClose\n });\n context.overlayContainer?.unbindPopup();\n context.map.removeLayer(instance);\n };\n }, [\n element,\n context,\n setOpen,\n position\n ]);\n});\n"],"names":["updateCircle","layer","props","prevProps","CircleMarker","createPathComponent","center","_c","options","ctx","marker","LeafletCircleMarker","createElementObject","extendContext","Popup","createOverlayComponent","context","popup","LeafletPopup","element","position","setOpen","useEffect","instance","onPopupOpen","event","onPopupClose","_a"],"mappings":"yGAAO,SAASA,EAAaC,EAAOC,EAAOC,EAAW,CAC9CD,EAAM,SAAWC,EAAU,QAC3BF,EAAM,UAAUC,EAAM,MAAM,EAE5BA,EAAM,QAAU,MAAQA,EAAM,SAAWC,EAAU,QACnDF,EAAM,UAAUC,EAAM,MAAM,CAEpC,CCLY,MAACE,EAAeC,EAAoB,SAA4B,CAAE,OAAAC,EAAS,SAAUC,EAAK,GAAGC,CAAO,EAAIC,EAAK,CACrH,MAAMC,EAAS,IAAIC,eAAoBL,EAAQE,CAAO,EACtD,OAAOI,EAAoBF,EAAQG,EAAcJ,EAAK,CAClD,iBAAkBC,CAC1B,CAAK,CAAC,CACN,EAAGV,CAAY,ECJFc,EAAQC,EAAuB,SAAqBb,EAAOc,EAAS,CAC7E,MAAMC,EAAQ,IAAIC,EAAAA,MAAahB,EAAOc,EAAQ,gBAAgB,EAC9D,OAAOJ,EAAoBK,EAAOD,CAAO,CAC7C,EAAG,SAA2BG,EAASH,EAAS,CAAE,SAAAI,CAAQ,EAAKC,EAAS,CACpEC,EAAAA,UAAU,UAAoB,CAC1B,KAAM,CAAE,SAAAC,CAAQ,EAAMJ,EACtB,SAASK,EAAYC,EAAO,CACpBA,EAAM,QAAUF,IAChBA,EAAS,OAAM,EACfF,EAAQ,EAAI,EAEpB,CACA,SAASK,EAAaD,EAAO,CACrBA,EAAM,QAAUF,GAChBF,EAAQ,EAAK,CAErB,CACA,OAAAL,EAAQ,IAAI,GAAG,CACX,UAAWQ,EACX,WAAYE,CACxB,CAAS,EACGV,EAAQ,kBAAoB,MAExBI,GAAY,MACZG,EAAS,UAAUH,CAAQ,EAE/BG,EAAS,OAAOP,EAAQ,GAAG,GAG3BA,EAAQ,iBAAiB,UAAUO,CAAQ,EAExC,UAAuB,OAC1BP,EAAQ,IAAI,IAAI,CACZ,UAAWQ,EACX,WAAYE,CAC5B,CAAa,GACDC,EAAAX,EAAQ,mBAAR,MAAAW,EAA0B,cAC1BX,EAAQ,IAAI,YAAYO,CAAQ,CACpC,CACJ,EAAG,CACCJ,EACAH,EACAK,EACAD,CACR,CAAK,CACL,CAAC","x_google_ignoreList":[0,1,2]}
{"version":3,"file":"Popup-DkSClDwo.js","sources":["../../node_modules/@react-leaflet/core/lib/circle.js","../../node_modules/react-leaflet/lib/CircleMarker.js","../../node_modules/react-leaflet/lib/Popup.js"],"sourcesContent":["export function updateCircle(layer, props, prevProps) {\n if (props.center !== prevProps.center) {\n layer.setLatLng(props.center);\n }\n if (props.radius != null && props.radius !== prevProps.radius) {\n layer.setRadius(props.radius);\n }\n}\n","import { createElementObject, createPathComponent, extendContext, updateCircle } from '@react-leaflet/core';\nimport { CircleMarker as LeafletCircleMarker } from 'leaflet';\nexport const CircleMarker = createPathComponent(function createCircleMarker({ center , children: _c , ...options }, ctx) {\n const marker = new LeafletCircleMarker(center, options);\n return createElementObject(marker, extendContext(ctx, {\n overlayContainer: marker\n }));\n}, updateCircle);\n","import { createElementObject, createOverlayComponent } from '@react-leaflet/core';\nimport { Popup as LeafletPopup } from 'leaflet';\nimport { useEffect } from 'react';\nexport const Popup = createOverlayComponent(function createPopup(props, context) {\n const popup = new LeafletPopup(props, context.overlayContainer);\n return createElementObject(popup, context);\n}, function usePopupLifecycle(element, context, { position }, setOpen) {\n useEffect(function addPopup() {\n const { instance } = element;\n function onPopupOpen(event) {\n if (event.popup === instance) {\n instance.update();\n setOpen(true);\n }\n }\n function onPopupClose(event) {\n if (event.popup === instance) {\n setOpen(false);\n }\n }\n context.map.on({\n popupopen: onPopupOpen,\n popupclose: onPopupClose\n });\n if (context.overlayContainer == null) {\n // Attach to a Map\n if (position != null) {\n instance.setLatLng(position);\n }\n instance.openOn(context.map);\n } else {\n // Attach to container component\n context.overlayContainer.bindPopup(instance);\n }\n return function removePopup() {\n context.map.off({\n popupopen: onPopupOpen,\n popupclose: onPopupClose\n });\n context.overlayContainer?.unbindPopup();\n context.map.removeLayer(instance);\n };\n }, [\n element,\n context,\n setOpen,\n position\n ]);\n});\n"],"names":["updateCircle","layer","props","prevProps","CircleMarker","createPathComponent","center","_c","options","ctx","marker","LeafletCircleMarker","createElementObject","extendContext","Popup","createOverlayComponent","context","popup","LeafletPopup","element","position","setOpen","useEffect","instance","onPopupOpen","event","onPopupClose","_a"],"mappings":"yGAAO,SAASA,EAAaC,EAAOC,EAAOC,EAAW,CAC9CD,EAAM,SAAWC,EAAU,QAC3BF,EAAM,UAAUC,EAAM,MAAM,EAE5BA,EAAM,QAAU,MAAQA,EAAM,SAAWC,EAAU,QACnDF,EAAM,UAAUC,EAAM,MAAM,CAEpC,CCLY,MAACE,EAAeC,EAAoB,SAA4B,CAAE,OAAAC,EAAS,SAAUC,EAAK,GAAGC,CAAO,EAAIC,EAAK,CACrH,MAAMC,EAAS,IAAIC,eAAoBL,EAAQE,CAAO,EACtD,OAAOI,EAAoBF,EAAQG,EAAcJ,EAAK,CAClD,iBAAkBC,CAC1B,CAAK,CAAC,CACN,EAAGV,CAAY,ECJFc,EAAQC,EAAuB,SAAqBb,EAAOc,EAAS,CAC7E,MAAMC,EAAQ,IAAIC,EAAAA,MAAahB,EAAOc,EAAQ,gBAAgB,EAC9D,OAAOJ,EAAoBK,EAAOD,CAAO,CAC7C,EAAG,SAA2BG,EAASH,EAAS,CAAE,SAAAI,CAAQ,EAAKC,EAAS,CACpEC,EAAAA,UAAU,UAAoB,CAC1B,KAAM,CAAE,SAAAC,CAAQ,EAAMJ,EACtB,SAASK,EAAYC,EAAO,CACpBA,EAAM,QAAUF,IAChBA,EAAS,OAAM,EACfF,EAAQ,EAAI,EAEpB,CACA,SAASK,EAAaD,EAAO,CACrBA,EAAM,QAAUF,GAChBF,EAAQ,EAAK,CAErB,CACA,OAAAL,EAAQ,IAAI,GAAG,CACX,UAAWQ,EACX,WAAYE,CACxB,CAAS,EACGV,EAAQ,kBAAoB,MAExBI,GAAY,MACZG,EAAS,UAAUH,CAAQ,EAE/BG,EAAS,OAAOP,EAAQ,GAAG,GAG3BA,EAAQ,iBAAiB,UAAUO,CAAQ,EAExC,UAAuB,OAC1BP,EAAQ,IAAI,IAAI,CACZ,UAAWQ,EACX,WAAYE,CAC5B,CAAa,GACDC,EAAAX,EAAQ,mBAAR,MAAAW,EAA0B,cAC1BX,EAAQ,IAAI,YAAYO,CAAQ,CACpC,CACJ,EAAG,CACCJ,EACAH,EACAK,EACAD,CACR,CAAK,CACL,CAAC","x_google_ignoreList":[0,1,2]}

View File

@@ -1,2 +0,0 @@
import"./index-DSM_ctlA.js";import{u as t}from"./leaflet-Bn7rjtY9.js";function r(){return t().map}export{r as u};
//# sourceMappingURL=hooks-Bk-tBmjl.js.map

View File

@@ -0,0 +1,2 @@
import"./index-DhrMLy6_.js";import{u as t}from"./leaflet-lJm4twjh.js";function r(){return t().map}export{r as u};
//# sourceMappingURL=hooks-DfhmuKzA.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"hooks-Bk-tBmjl.js","sources":["../../node_modules/react-leaflet/lib/hooks.js"],"sourcesContent":["import { useLeafletContext } from '@react-leaflet/core';\nimport { useEffect } from 'react';\nexport function useMap() {\n return useLeafletContext().map;\n}\nexport function useMapEvent(type, handler) {\n const map = useMap();\n useEffect(function addMapEventHandler() {\n // @ts-ignore event type\n map.on(type, handler);\n return function removeMapEventHandler() {\n // @ts-ignore event type\n map.off(type, handler);\n };\n }, [\n map,\n type,\n handler\n ]);\n return map;\n}\nexport function useMapEvents(handlers) {\n const map = useMap();\n useEffect(function addMapEventHandlers() {\n map.on(handlers);\n return function removeMapEventHandlers() {\n map.off(handlers);\n };\n }, [\n map,\n handlers\n ]);\n return map;\n}\n"],"names":["useMap","useLeafletContext"],"mappings":"sEAEO,SAASA,GAAS,CACrB,OAAOC,EAAiB,EAAG,GAC/B","x_google_ignoreList":[0]}
{"version":3,"file":"hooks-DfhmuKzA.js","sources":["../../node_modules/react-leaflet/lib/hooks.js"],"sourcesContent":["import { useLeafletContext } from '@react-leaflet/core';\nimport { useEffect } from 'react';\nexport function useMap() {\n return useLeafletContext().map;\n}\nexport function useMapEvent(type, handler) {\n const map = useMap();\n useEffect(function addMapEventHandler() {\n // @ts-ignore event type\n map.on(type, handler);\n return function removeMapEventHandler() {\n // @ts-ignore event type\n map.off(type, handler);\n };\n }, [\n map,\n type,\n handler\n ]);\n return map;\n}\nexport function useMapEvents(handlers) {\n const map = useMap();\n useEffect(function addMapEventHandlers() {\n map.on(handlers);\n return function removeMapEventHandlers() {\n map.off(handlers);\n };\n }, [\n map,\n handlers\n ]);\n return map;\n}\n"],"names":["useMap","useLeafletContext"],"mappings":"sEAEO,SAASA,GAAS,CACrB,OAAOC,EAAiB,EAAG,GAC/B","x_google_ignoreList":[0]}

View File

@@ -1,2 +1,2 @@
import{r as s,j as l,Q as f,l as u}from"./index-DSM_ctlA.js";var N=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],h=N.reduce((a,r)=>{const t=f(`Primitive.${r}`),o=s.forwardRef((i,e)=>{const{asChild:p,...n}=i,m=p?t:r;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),l.jsx(m,{...n,ref:e})});return o.displayName=`Primitive.${r}`,{...a,[r]:o}},{}),x="Separator",c="horizontal",w=["horizontal","vertical"],d=s.forwardRef((a,r)=>{const{decorative:t,orientation:o=c,...i}=a,e=S(o)?o:c,n=t?{role:"none"}:{"aria-orientation":e==="vertical"?e:void 0,role:"separator"};return l.jsx(h.div,{"data-orientation":e,...n,...i,ref:r})});d.displayName=x;function S(a){return w.includes(a)}var v=d;const O=s.forwardRef(({className:a,orientation:r="horizontal",decorative:t=!0,...o},i)=>l.jsx(v,{ref:i,decorative:t,orientation:r,className:u("shrink-0 bg-border",r==="horizontal"?"h-[1px] w-full":"h-full w-[1px]",a),...o}));O.displayName=v.displayName;export{O as S};
//# sourceMappingURL=separator-B1Gufat6.js.map
import{r as s,j as l,Q as f,l as u}from"./index-DhrMLy6_.js";var N=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],h=N.reduce((a,r)=>{const t=f(`Primitive.${r}`),o=s.forwardRef((i,e)=>{const{asChild:p,...n}=i,m=p?t:r;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),l.jsx(m,{...n,ref:e})});return o.displayName=`Primitive.${r}`,{...a,[r]:o}},{}),x="Separator",c="horizontal",w=["horizontal","vertical"],d=s.forwardRef((a,r)=>{const{decorative:t,orientation:o=c,...i}=a,e=S(o)?o:c,n=t?{role:"none"}:{"aria-orientation":e==="vertical"?e:void 0,role:"separator"};return l.jsx(h.div,{"data-orientation":e,...n,...i,ref:r})});d.displayName=x;function S(a){return w.includes(a)}var v=d;const O=s.forwardRef(({className:a,orientation:r="horizontal",decorative:t=!0,...o},i)=>l.jsx(v,{ref:i,decorative:t,orientation:r,className:u("shrink-0 bg-border",r==="horizontal"?"h-[1px] w-full":"h-full w-[1px]",a),...o}));O.displayName=v.displayName;export{O as S};
//# sourceMappingURL=separator-DS1eMu86.js.map

View File

@@ -50,7 +50,7 @@
undecryptedCount: fetchJsonOrThrow('/api/packets/undecrypted/count'),
};
</script>
<script type="module" crossorigin src="/assets/index-DSM_ctlA.js"></script>
<script type="module" crossorigin src="/assets/index-DhrMLy6_.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Kkb-Bio9.css">
</head>
<body>

View File

@@ -104,7 +104,7 @@ function MapBoundsHandler({
}
export function MapView({ contacts, focusedKey }: MapViewProps) {
const sevenDaysAgo = Date.now() / 1000 - 7 * 24 * 60 * 60;
const [sevenDaysAgo] = useState(() => Date.now() / 1000 - 7 * 24 * 60 * 60);
// Filter to contacts with GPS coordinates, heard within the last 7 days.
// Always include the focused contact so "view on map" links work for older nodes.

View File

@@ -52,4 +52,43 @@ describe('MapView', () => {
).toBeInTheDocument();
expect(screen.getByText('Last heard: Never heard by this server')).toBeInTheDocument();
});
it('keeps the 7-day cutoff stable for the lifetime of the mounted map', () => {
vi.useFakeTimers();
try {
vi.setSystemTime(new Date('2026-03-15T12:00:00Z'));
const contact: Contact = {
public_key: 'bb'.repeat(32),
name: 'Almost Stale',
type: 1,
flags: 0,
last_path: null,
last_path_len: -1,
out_path_hash_mode: -1,
route_override_path: null,
route_override_len: null,
route_override_hash_mode: null,
last_advert: null,
lat: 41,
lon: -73,
last_seen: Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60 + 60,
on_radio: false,
last_contacted: null,
last_read_at: null,
first_seen: null,
};
const { rerender } = render(<MapView contacts={[contact]} focusedKey={null} />);
expect(screen.getByText(/showing 1 contact heard in the last 7 days/i)).toBeInTheDocument();
vi.advanceTimersByTime(2 * 60 * 1000);
rerender(<MapView contacts={[contact]} focusedKey={null} />);
expect(screen.getByText(/showing 1 contact heard in the last 7 days/i)).toBeInTheDocument();
} finally {
vi.useRealTimers();
}
});
});