Fix View Route on Map — use sessionStorage instead of URL params

Hop hashes are 1-2 byte truncated values that don't work in URL params.
Now passes raw hops via sessionStorage; map page reads them after nodes
load and resolves via prefix match against full public keys.
This commit is contained in:
you
2026-03-18 22:43:00 +00:00
parent d03bdeef10
commit f694022ba3
2 changed files with 48 additions and 17 deletions
+26 -15
View File
@@ -88,45 +88,56 @@
onWS(wsHandler);
loadNodes().then(() => {
// Check for highlight route param (from packet detail)
const hashQuery = location.hash.split('?')[1];
if (hashQuery) {
const params = new URLSearchParams(hashQuery);
const highlight = params.get('highlight');
if (highlight) drawPacketRoute(highlight.split(','));
// Check for route from packet detail (via sessionStorage)
const routeHopsJson = sessionStorage.getItem('map-route-hops');
if (routeHopsJson) {
sessionStorage.removeItem('map-route-hops');
try {
const hopKeys = JSON.parse(routeHopsJson);
drawPacketRoute(hopKeys);
} catch {}
}
});
}
function drawPacketRoute(hopKeys) {
// Resolve hop keys to positions
// Resolve hop short hashes to node positions via prefix match
const positions = [];
for (const hop of hopKeys) {
const hopLower = hop.toLowerCase();
const node = nodes.find(n =>
n.public_key.toLowerCase().startsWith(hop.toLowerCase())
n.public_key.toLowerCase().startsWith(hopLower)
);
if (node && node.lat != null && node.lon != null && !(node.lat === 0 && node.lon === 0)) {
positions.push({ lat: node.lat, lon: node.lon, name: node.name || hop });
}
}
if (positions.length < 2) return;
if (positions.length < 1) return;
// Draw route polyline
// Even a single node is worth showing (zoom to it)
const coords = positions.map(p => [p.lat, p.lon]);
const routeLine = L.polyline(coords, {
color: '#f59e0b', weight: 3, opacity: 0.8, dashArray: '8 4'
}).addTo(markerLayer);
if (positions.length >= 2) {
// Draw route polyline
L.polyline(coords, {
color: '#f59e0b', weight: 3, opacity: 0.8, dashArray: '8 4'
}).addTo(markerLayer);
}
// Add numbered markers at each hop
positions.forEach((p, i) => {
L.circleMarker([p.lat, p.lon], {
radius: 8, fillColor: i === 0 ? '#22c55e' : i === positions.length - 1 ? '#ef4444' : '#f59e0b',
radius: 10, fillColor: i === 0 ? '#22c55e' : i === positions.length - 1 ? '#ef4444' : '#f59e0b',
fillOpacity: 0.9, color: '#fff', weight: 2
}).addTo(markerLayer).bindTooltip(`${i + 1}. ${p.name}`, { permanent: true, direction: 'top', className: 'route-tooltip' });
});
// Fit map to route
map.fitBounds(L.latLngBounds(coords).pad(0.2));
if (coords.length >= 2) {
map.fitBounds(L.latLngBounds(coords).pad(0.3));
} else {
map.setView(coords[0], 13);
}
}
async function loadNodes() {
+22 -2
View File
@@ -445,7 +445,7 @@
<dt>Timestamp</dt><dd>${pkt.timestamp}</dd>
<dt>Path</dt><dd>${pathHops.length ? renderPath(pathHops) : '—'}</dd>
</dl>
${pathHops.length ? `<a class="detail-map-link" href="#/map?highlight=${encodeURIComponent(pathHops.join(','))}&packet=${pkt.hash || pkt.id}" onclick="event.stopPropagation()">🗺️ View route on map</a>` : ''}
${pathHops.length ? `<button class="detail-map-link" id="viewRouteBtn">🗺️ View route on map</button>` : ''}
${hasRawHex ? `<div class="hex-legend">${buildHexLegend(ranges)}</div>
<div class="hex-dump">${createColoredHexDump(pkt.raw_hex, ranges)}</div>` : ''}
@@ -459,7 +459,6 @@
const replayBtn = panel.querySelector('.replay-live-btn');
if (replayBtn) {
replayBtn.addEventListener('click', () => {
// Store packet in sessionStorage for the live page to pick up
const livePkt = {
id: pkt.id, hash: pkt.hash,
_ts: new Date(pkt.timestamp).getTime(),
@@ -470,6 +469,27 @@
window.location.hash = '#/live';
});
}
// Wire up view route on map button
const routeBtn = document.getElementById('viewRouteBtn');
if (routeBtn && pathHops.length) {
routeBtn.addEventListener('click', async () => {
try {
const resp = await fetch('/api/resolve-hops?hops=' + encodeURIComponent(pathHops.join(',')));
const data = await resp.json();
// Build array of {hop, name, pubkey} with resolved full pubkeys
const resolvedHops = pathHops.map(h => {
const name = data.resolved[h];
// Find full pubkey from name if possible
return name || h;
});
sessionStorage.setItem('map-route-hops', JSON.stringify(pathHops));
window.location.hash = '#/map?route=1';
} catch {
window.location.hash = '#/map';
}
});
}
}
function escapeHtml(s) {