mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-08-28 19:18:25 +00:00
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:
+26
-15
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user