mirror of
https://github.com/smittix/intercept.git
synced 2026-06-08 14:11:54 -07:00
fix(bt-locate): stabilize first-load map and release v2.21.1
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user