fix(bt-locate): stabilize first-load map and release v2.21.1

This commit is contained in:
Smittix
2026-02-20 00:49:08 +00:00
parent a407c7708d
commit 9ec316fbe2
5 changed files with 107 additions and 18 deletions
+9
View File
@@ -2,6 +2,15 @@
All notable changes to iNTERCEPT will be documented in this file.
## [2.21.1] - 2026-02-20
### Fixed
- BT Locate map first-load rendering race that could cause blank/late map initialization
- BT Locate mode switch timing so Leaflet invalidation runs after panel visibility settles
- BT Locate trail restore startup latency by batching historical GPS point rendering
---
## [2.21.0] - 2026-02-20
### Added
+12 -3
View File
@@ -7,10 +7,19 @@ import os
import sys
# Application version
VERSION = "2.21.0"
# Changelog - latest release notes (shown on welcome screen)
VERSION = "2.21.1"
# Changelog - latest release notes (shown on welcome screen)
CHANGELOG = [
{
"version": "2.21.1",
"date": "February 2026",
"highlights": [
"BT Locate map first-load fix with render stabilization retries during initial mode open",
"BT Locate trail restore optimization for faster startup when historical GPS points exist",
"BT Locate mode-switch map invalidation timing fix to prevent delayed/blank map render",
]
},
{
"version": "2.21.0",
"date": "February 2026",
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "intercept"
version = "2.21.0"
version = "2.21.1"
description = "Signal Intelligence Platform - Pager/433MHz/ADS-B/Satellite/WiFi/Bluetooth"
readme = "README.md"
requires-python = ">=3.9"
+79 -14
View File
@@ -37,6 +37,7 @@ const BtLocate = (function() {
let smoothingEnabled = true;
let lastRenderedDetectionKey = null;
let pendingHeatSync = false;
let mapStabilizeTimer = null;
const MAX_HEAT_POINTS = 1200;
const MAX_TRAIL_POINTS = 1200;
@@ -44,6 +45,8 @@ const BtLocate = (function() {
const OUTLIER_HARD_JUMP_METERS = 2000;
const OUTLIER_SOFT_JUMP_METERS = 450;
const OUTLIER_MAX_SPEED_MPS = 50;
const MAP_STABILIZE_INTERVAL_MS = 150;
const MAP_STABILIZE_ATTEMPTS = 28;
const OVERLAY_STORAGE_KEYS = {
heatmap: 'btLocateHeatmapEnabled',
movement: 'btLocateMovementEnabled',
@@ -99,6 +102,7 @@ const BtLocate = (function() {
Settings.createTileLayer().addTo(map);
}
flushPendingHeatSync();
scheduleMapStabilization(10);
}, 150);
}
checkStatus();
@@ -111,17 +115,25 @@ const BtLocate = (function() {
map = L.map('btLocateMap', {
center: [0, 0],
zoom: 2,
zoomControl: true,
});
// Use tile provider from user settings
if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
Settings.createTileLayer().addTo(map);
Settings.registerMap(map);
} else {
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
maxZoom: 19,
attribution: '© OSM © CARTO'
}).addTo(map);
zoomControl: true,
});
let tileLayer = null;
// Use tile provider from user settings
if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
tileLayer = Settings.createTileLayer();
tileLayer.addTo(map);
Settings.registerMap(map);
} else {
tileLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
maxZoom: 19,
attribution: '© OSM © CARTO'
});
tileLayer.addTo(map);
}
if (tileLayer && typeof tileLayer.on === 'function') {
tileLayer.on('load', () => {
scheduleMapStabilization(8);
});
}
ensureHeatLayer();
syncMovementLayer();
@@ -133,6 +145,7 @@ const BtLocate = (function() {
safeInvalidateMap();
flushPendingHeatSync();
}, 100);
scheduleMapStabilization();
}
// Init RSSI chart canvas
@@ -524,6 +537,8 @@ const BtLocate = (function() {
const lon = Number(point.lon);
if (!isFinite(lat) || !isFinite(lon)) return false;
if (!shouldAcceptMapPoint(point, lat, lon)) return false;
const suppressFollow = options.suppressFollow === true;
const bulkLoad = options.bulkLoad === true;
const trailPoint = normalizeTrailPoint(point, lat, lon);
const band = (trailPoint.proximity_band || 'FAR').toLowerCase();
@@ -563,13 +578,17 @@ const BtLocate = (function() {
if (heatPoints.length > MAX_HEAT_POINTS) {
heatPoints.splice(0, heatPoints.length - MAX_HEAT_POINTS);
}
if (bulkLoad) {
pendingHeatSync = true;
return true;
}
syncHeatLayer();
if (!isMapRenderable()) {
safeInvalidateMap();
}
const canFollowMap = isMapRenderable();
if (autoFollowEnabled && !options.suppressFollow && canFollowMap) {
if (autoFollowEnabled && !suppressFollow && canFollowMap) {
if (!gpsLocked) {
gpsLocked = true;
map.setView([lat, lon], Math.max(map.getZoom(), 16));
@@ -645,8 +664,13 @@ const BtLocate = (function() {
const gpsTrail = Array.isArray(trail.gps_trail) ? trail.gps_trail : [];
const allTrail = Array.isArray(trail.trail) ? trail.trail : [];
const recentGpsTrail = gpsTrail.slice(-MAX_TRAIL_POINTS);
gpsTrail.forEach(p => addMapMarker(p, { suppressFollow: true }));
recentGpsTrail.forEach(p => addMapMarker(p, {
suppressFollow: true,
bulkLoad: true,
}));
syncHeatLayer();
if (allTrail.length > 0) {
rssiHistory = allTrail.map(p => p.rssi).filter(v => typeof v === 'number' && isFinite(v)).slice(-MAX_RSSI_POINTS);
@@ -659,7 +683,7 @@ const BtLocate = (function() {
drawRssiChart();
}
updateStats(allTrail.length, gpsTrail.length);
updateStats(allTrail.length, recentGpsTrail.length);
if (trailPoints.length > 0 && map) {
const latestGps = trailPoints[trailPoints.length - 1];
@@ -675,6 +699,7 @@ const BtLocate = (function() {
syncStrongestMarker();
updateConfidenceLayer();
updateMovementStats();
scheduleMapStabilization(12);
})
.catch(() => {});
}
@@ -908,6 +933,45 @@ const BtLocate = (function() {
return true;
}
function stopMapStabilization() {
if (mapStabilizeTimer) {
clearInterval(mapStabilizeTimer);
mapStabilizeTimer = null;
}
}
function scheduleMapStabilization(attempts = MAP_STABILIZE_ATTEMPTS) {
if (!map) return;
stopMapStabilization();
let remaining = Math.max(1, Number(attempts) || MAP_STABILIZE_ATTEMPTS);
const tick = () => {
if (!map) {
stopMapStabilization();
return;
}
if (safeInvalidateMap()) {
flushPendingHeatSync();
syncMovementLayer();
syncStrongestMarker();
updateConfidenceLayer();
if (isMapRenderable()) {
stopMapStabilization();
return;
}
}
remaining -= 1;
if (remaining <= 0) {
stopMapStabilization();
}
};
tick();
if (map && !mapStabilizeTimer && !isMapRenderable()) {
mapStabilizeTimer = setInterval(tick, MAP_STABILIZE_INTERVAL_MS);
}
}
function flushPendingHeatSync() {
if (!pendingHeatSync) return;
syncHeatLayer();
@@ -1566,6 +1630,7 @@ const BtLocate = (function() {
syncStrongestMarker();
updateConfidenceLayer();
}
scheduleMapStabilization(8);
}
return {
+6
View File
@@ -4242,6 +4242,12 @@
SubGhz.init();
} else if (mode === 'bt_locate') {
BtLocate.init();
setTimeout(() => {
if (typeof BtLocate !== 'undefined' && BtLocate.invalidateMap) BtLocate.invalidateMap();
}, 100);
setTimeout(() => {
if (typeof BtLocate !== 'undefined' && BtLocate.invalidateMap) BtLocate.invalidateMap();
}, 320);
} else if (mode === 'spaceweather') {
SpaceWeather.init();
}