feat: add mapped ALPR cameras with bounded source loading

Adapt the ALPR camera contribution to reusable layer components, retaining source attribution, bounded queries, selection and voice controls.

Co-authored-by: Bilawal Sidhu <[email protected]>
This commit is contained in:
Tom-NeverwinterandBilawal Sidhu authored and Sameh Khamis committed 2026-09-13 17:21:42 -07:00
1 parent ba7fda74d7
commit 4374c0fa60
28 files changed
+2358 -100

No files matched your search

+43 -1
View File
@@ -28,7 +28,7 @@ export function normalizeLayerLoading(layer = {}) {
stats.refreshing === true;
const count = finiteCount(stats.count);
const stoppingInstallations =
layer.id === 'military-installations' && disabling;
['military-installations', 'alpr-cameras'].includes(layer.id) && disabling;
// Guidance statuses ask the user to act (zoom in, run a search). They are
// normal operation, never a batch failure — mirrors layerFeedState's carve-out
// so a prompt stored alongside the status cannot turn the chip red.
@@ -61,6 +61,14 @@ export function normalizeLayerLoading(layer = {}) {
unavailable,
keyRequired,
degraded,
cameraRetry:
layer.id === 'alpr-cameras' && layer.enabled && !disabling
? {
retryAt: Number(stats.retryAt) || 0,
retrying: stats.retrying === true,
error,
}
: null,
installationRetry:
layer.id === 'military-installations' && layer.enabled && !disabling
? {
@@ -367,6 +375,30 @@ export function reduceLoadingFeedback(previous, summary, nowMs, event = null) {
/** Build the user-facing status copy for the current loading state. */
export function presentLoadingFeedback(state, summary, nowMs) {
const camera = summary.records.find(
(record) => record.cameraRetry?.retryAt > 0,
);
const otherCameraFailure =
summary.records.some(
(record) =>
record.id !== 'alpr-cameras' &&
(state?.activeIds || []).includes(record.id) &&
(record.error || record.unavailable || record.keyRequired),
) || (state?.failedEventIds || []).some((id) => id !== 'alpr-cameras');
if (camera && !summary.active.length && !otherCameraFailure) {
const seconds = Math.max(
0,
Math.ceil((camera.cameraRetry.retryAt - Date.now()) / 1000),
);
return {
state: 'retry',
label: (
camera.cameraRetry.error || 'Overpass temporarily unavailable'
).toUpperCase(),
detail: `ALPR cameras · ${seconds ? `retrying in ${seconds}s` : 'retry pending'}`,
};
}
const site = summary.records.find(
(record) => record.installationRetry?.retryAt > 0,
);
@@ -403,6 +435,16 @@ export function presentLoadingFeedback(state, summary, nowMs) {
return { state: state.terminal, label, detail: '' };
}
const active = summary.active;
if (active.length === 1 && active[0].cameraRetry && !summary.disabling) {
return {
state: 'loading',
label: active[0].cameraRetry.retrying
? 'RETRYING ALPR CAMERAS'
: 'FETCHING ALPR CAMERAS',
detail: 'OpenStreetMap · Overpass',
};
}
if (
active.length === 1 &&
active[0].installationRetry &&