style: format layer loading feedback helpers

This commit is contained in:
Sameh Khamis committed 2026-09-12 22:41:08 -07:00
1 parent a41f3ec3cd
commit dec9dd288c
3 files changed
+245 -246

No files matched your search

+51 -157
View File
@@ -10,47 +10,27 @@
"src/data/localGeojsonCore.js",
"src/data/localGeojsonLod.js"
],
"external": [
"cesium"
]
"external": ["cesium"]
},
"application": {
"exports": [
"./application"
],
"modules": [
"src/app/application.js"
],
"exports": ["./application"],
"modules": ["src/app/application.js"],
"external": []
},
"viewer": {
"exports": [
"./application/viewer"
],
"modules": [
"src/app/viewer.js"
],
"external": [
"cesium"
]
"exports": ["./application/viewer"],
"modules": ["src/app/viewer.js"],
"external": ["cesium"]
},
"vite-build": {
"runtime": "node",
"exports": [
"./build/vite"
],
"modules": [
"build/vite.js"
],
"external": [
"vite-plugin-cesium"
]
"exports": ["./build/vite"],
"modules": ["build/vite.js"],
"external": ["vite-plugin-cesium"]
},
"live-providers": {
"runtime": "node",
"exports": [
"./server/providers/live"
],
"exports": ["./server/providers/live"],
"modules": [
"server/providers/live.js",
"server/providers/common/http.js",
@@ -65,24 +45,16 @@
"src/data/aisStreamAdapter.js",
"src/data/aisWatchdog.js"
],
"external": [
"ws"
]
"external": ["ws"]
},
"adsb-lol-source": {
"exports": [
"./sources/adsb-lol"
],
"modules": [
"src/data/adsbLolFallback.js"
],
"exports": ["./sources/adsb-lol"],
"modules": ["src/data/adsbLolFallback.js"],
"external": []
},
"place-providers": {
"runtime": "node",
"exports": [
"./server/providers/places"
],
"exports": ["./server/providers/places"],
"modules": [
"server/providers/places.js",
"server/providers/places/google.js",
@@ -97,19 +69,13 @@
"external": []
},
"place-payloads": {
"exports": [
"./sources/places"
],
"modules": [
"src/data/placeProviderPayloads.js"
],
"exports": ["./sources/places"],
"modules": ["src/data/placeProviderPayloads.js"],
"external": []
},
"space-providers": {
"runtime": "node",
"exports": [
"./server/providers/space"
],
"exports": ["./server/providers/space"],
"modules": [
"server/providers/space.js",
"server/providers/space/celestrak.js",
@@ -120,19 +86,13 @@
"external": []
},
"space-requests": {
"exports": [
"./sources/space"
],
"modules": [
"src/data/spaceProviderRequests.js"
],
"exports": ["./sources/space"],
"modules": ["src/data/spaceProviderRequests.js"],
"external": []
},
"terrain-provider": {
"runtime": "node",
"exports": [
"./server/providers/terrain"
],
"exports": ["./server/providers/terrain"],
"modules": [
"server/providers/terrain.js",
"src/data/terrainHeightsProxy.js"
@@ -140,50 +100,30 @@
"external": []
},
"terrain-source": {
"exports": [
"./sources/terrain"
],
"modules": [
"src/data/terrainHeightsProxy.js"
],
"exports": ["./sources/terrain"],
"modules": ["src/data/terrainHeightsProxy.js"],
"external": []
},
"traffic-provider": {
"runtime": "node",
"exports": [
"./server/providers/traffic"
],
"modules": [
"server/providers/traffic.js",
"src/data/tomtomTiles.js"
],
"exports": ["./server/providers/traffic"],
"modules": ["server/providers/traffic.js", "src/data/tomtomTiles.js"],
"external": []
},
"traffic-source": {
"exports": [
"./sources/traffic"
],
"modules": [
"src/data/tomtomTiles.js"
],
"exports": ["./sources/traffic"],
"modules": ["src/data/tomtomTiles.js"],
"external": []
},
"firms-provider": {
"runtime": "node",
"exports": [
"./server/providers/firms"
],
"modules": [
"server/providers/firms.js",
"src/data/firmsCsv.js"
],
"exports": ["./server/providers/firms"],
"modules": ["server/providers/firms.js", "src/data/firmsCsv.js"],
"external": []
},
"gbfs-provider": {
"runtime": "node",
"exports": [
"./server/providers/gbfs"
],
"exports": ["./server/providers/gbfs"],
"modules": [
"server/providers/gbfs.js",
"server/providers/common/http.js",
@@ -192,19 +132,13 @@
"external": []
},
"gbfs-source": {
"exports": [
"./sources/gbfs"
],
"modules": [
"src/data/gbfsSource.js"
],
"exports": ["./sources/gbfs"],
"modules": ["src/data/gbfsSource.js"],
"external": []
},
"cctv-provider": {
"runtime": "node",
"exports": [
"./server/providers/cctv"
],
"exports": ["./server/providers/cctv"],
"modules": [
"server/providers/cctv.js",
"server/providers/cctv/catalog.js",
@@ -221,9 +155,7 @@
},
"radio-provider": {
"runtime": "node",
"exports": [
"./server/providers/radio"
],
"exports": ["./server/providers/radio"],
"modules": [
"server/providers/radio.js",
"server/providers/radio/catalog.js",
@@ -237,9 +169,7 @@
},
"overpass-provider": {
"runtime": "node",
"exports": [
"./server/providers/overpass"
],
"exports": ["./server/providers/overpass"],
"modules": [
"server/providers/common/geo.js",
"server/providers/common/http.js",
@@ -258,9 +188,7 @@
},
"military-installations-provider": {
"runtime": "node",
"exports": [
"./server/providers/military-installations"
],
"exports": ["./server/providers/military-installations"],
"modules": [
"server/providers/common/http.js",
"server/providers/common/query.js",
@@ -277,9 +205,7 @@
},
"regional-provider": {
"runtime": "node",
"exports": [
"./server/providers/regional"
],
"exports": ["./server/providers/regional"],
"modules": [
"server/providers/common/http.js",
"server/providers/common/query.js",
@@ -298,9 +224,7 @@
},
"openai-provider": {
"runtime": "node",
"exports": [
"./server/providers/openai"
],
"exports": ["./server/providers/openai"],
"modules": [
"server/providers/common/rate-limit.js",
"server/providers/common/request.js",
@@ -320,9 +244,7 @@
},
"standalone-key-setup": {
"runtime": "node",
"exports": [
"./server/standalone/key-setup"
],
"exports": ["./server/standalone/key-setup"],
"modules": [
"scripts/pinokio-environment.mjs",
"server/providers/common/source-root.js",
@@ -333,9 +255,7 @@
"external": []
},
"place-search": {
"exports": [
"./search"
],
"exports": ["./search"],
"modules": [
"src/search/index.js",
"src/search/placeSearch.js",
@@ -345,27 +265,17 @@
"external": []
},
"panel-disclosure": {
"exports": [
"./ui/panels"
],
"modules": [
"src/ui/panelDisclosure.js"
],
"exports": ["./ui/panels"],
"modules": ["src/ui/panelDisclosure.js"],
"external": []
},
"surface-keyboard": {
"exports": [
"./ui/surfaces"
],
"modules": [
"src/ui/surfaceKeyboard.js"
],
"exports": ["./ui/surfaces"],
"modules": ["src/ui/surfaceKeyboard.js"],
"external": []
},
"panel-rail-layout": {
"exports": [
"./ui/layout"
],
"exports": ["./ui/layout"],
"modules": [
"src/ui/panelRails.js",
"src/ui/leftPanelRail.js",
@@ -377,9 +287,7 @@
"external": []
},
"visual-input": {
"exports": [
"./ui/input"
],
"exports": ["./ui/input"],
"modules": [
"src/ui/visualInput.js",
"src/ui/applicationShortcuts.js",
@@ -388,19 +296,12 @@
"external": []
},
"display-controls": {
"exports": [
"./ui/display"
],
"modules": [
"src/ui/displayControls.js"
],
"exports": ["./ui/display"],
"modules": ["src/ui/displayControls.js"],
"external": []
},
"visual-effects": {
"exports": [
"./ui/effects",
"./ui/effects/bloom"
],
"exports": ["./ui/effects", "./ui/effects/bloom"],
"modules": [
"src/ui/effects.js",
"src/ui/visualEffects.js",
@@ -413,14 +314,10 @@
"src/styles/noir.js",
"src/styles/snow.js"
],
"external": [
"cesium"
]
"external": ["cesium"]
},
"map-source-controls": {
"exports": [
"./ui/maps"
],
"exports": ["./ui/maps"],
"modules": [
"src/ui/mapSource.js",
"src/ui/mapSourceControls.js",
@@ -430,10 +327,7 @@
"external": []
},
"layer-panel": {
"exports": [
"./ui/layers",
"./ui/layers/feedback"
],
"exports": ["./ui/layers", "./ui/layers/feedback"],
"modules": [
"src/ui/layers.js",
"src/ui/layerPanel.js",
+6 -3
View File
@@ -5,14 +5,17 @@ export function installationFeedback(stats = {}, now = Date.now()) {
timeout: 'Overpass timed out',
query_failed: 'Overpass could not complete the query',
};
const reason = reasons[stats.failureReason] || 'Overpass temporarily unavailable';
if (stats.loading) return stats.retrying ? 'Retrying mapped sites…' : 'Fetching mapped sites…';
const reason =
reasons[stats.failureReason] || 'Overpass temporarily unavailable';
if (stats.loading)
return stats.retrying ? 'Retrying mapped sites…' : 'Fetching mapped sites…';
if (stats.retryAt > 0) {
const seconds = Math.max(0, Math.ceil((stats.retryAt - now) / 1000));
return `${reason} — ${seconds ? `retrying in ${seconds}s` : 'retry pending'}`;
}
if (stats.status === 'unavailable') return reason;
if (stats.status === 'zoom-in') return 'Zoom in to search mapped installations';
if (stats.status === 'zoom-in')
return 'Zoom in to search mapped installations';
if (stats.stale) return 'Showing cached mapped sites';
if (stats.status === 'idle') return 'Mapped sites not loaded';
return 'Mapped sites loaded';
+188 -86
View File
@@ -16,21 +16,34 @@ function finiteCount(value) {
/** Normalize one manager layer into a small loading-feedback record. */
export function normalizeLayerLoading(layer = {}) {
const stats = layer.stats || {};
const lifecycleState = String(layer.lifecycleState || (layer.enabled ? 'enabled' : 'disabled'));
const lifecycleState = String(
layer.lifecycleState || (layer.enabled ? 'enabled' : 'disabled'),
);
const status = String(stats.status || '').toLowerCase();
const disabling = lifecycleState === 'disabling';
const loading = lifecycleState === 'enabling' || disabling || stats.loading === true || stats.refreshing === true;
const loading =
lifecycleState === 'enabling' ||
disabling ||
stats.loading === true ||
stats.refreshing === true;
const count = finiteCount(stats.count);
const stoppingInstallations = layer.id === 'military-installations' && disabling;
const stoppingInstallations =
layer.id === 'military-installations' && 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.
const guidance = GUIDANCE_STATUSES.includes(status);
const error = stoppingInstallations ? null
: (!guidance && stats.error) || stats.lastError || stats.managerRefreshError || null;
const unavailable = !stoppingInstallations && (stats.unavailable === true
|| stats.available === false
|| ['unavailable', 'offline', 'down', 'error'].includes(status));
const error = stoppingInstallations
? null
: (!guidance && stats.error) ||
stats.lastError ||
stats.managerRefreshError ||
null;
const unavailable =
!stoppingInstallations &&
(stats.unavailable === true ||
stats.available === false ||
['unavailable', 'offline', 'down', 'error'].includes(status));
const keyRequired = stats.keyRequired === true || stats.missingKey === true;
const degraded = stats.degraded === true || Boolean(error);
const accepted = Boolean(stats.lastUpdate) || count > 0;
@@ -39,7 +52,8 @@ export function normalizeLayerLoading(layer = {}) {
label: String(layer.name || layer.id || 'Layer'),
loading,
disabling,
refresh: loading && layer.enabled && (stats.refreshing === true || accepted),
refresh:
loading && layer.enabled && (stats.refreshing === true || accepted),
lifecycleState,
count,
accepted,
@@ -47,18 +61,27 @@ export function normalizeLayerLoading(layer = {}) {
unavailable,
keyRequired,
degraded,
installationRetry: layer.id === 'military-installations' && layer.enabled && !disabling
? { retryAt: Number(stats.retryAt) || 0, retrying: stats.retrying === true,
failureReason: stats.failureReason, loading, status: stats.status }
: null,
installationRetry:
layer.id === 'military-installations' && layer.enabled && !disabling
? {
retryAt: Number(stats.retryAt) || 0,
retrying: stats.retrying === true,
failureReason: stats.failureReason,
loading,
status: stats.status,
}
: null,
};
}
function terminalFromParticipantStats(summary, participantIds) {
if (!participantIds?.length) return null;
const participants = new Set(participantIds);
return summary.records.some((record) => participants.has(record.id)
&& (record.error || record.unavailable || record.keyRequired))
return summary.records.some(
(record) =>
participants.has(record.id) &&
(record.error || record.unavailable || record.keyRequired),
)
? 'error'
: null;
}
@@ -67,13 +90,17 @@ function terminalFromParticipantStats(summary, participantIds) {
export function aggregateLayerLoading(layers = []) {
const records = layers.map(normalizeLayerLoading);
const active = records.filter((record) => record.loading);
const disabling = active.length > 0 && active.every((record) => record.disabling);
const disabling =
active.length > 0 && active.every((record) => record.disabling);
return {
records,
active,
activeIds: active.map((record) => record.id),
disabling,
refresh: !disabling && active.length > 0 && active.every((record) => record.refresh),
refresh:
!disabling &&
active.length > 0 &&
active.every((record) => record.refresh),
};
}
@@ -92,11 +119,11 @@ export function createLoadingFeedbackState() {
}
/** Create a top-center status notice, optionally persistent until explicitly cleared. */
export function createGlobalStatusNotice(message, nowMs = 0, {
state = 'error',
detail = '',
persistent = false,
} = {}) {
export function createGlobalStatusNotice(
message,
nowMs = 0,
{ state = 'error', detail = '', persistent = false } = {},
) {
const label = String(message || '').trim();
if (!label) return null;
return {
@@ -117,9 +144,11 @@ export function presentGlobalStatusNotice(notice, nowMs = 0) {
const now = Number.isFinite(nowMs) ? nowMs : 0;
if (!notice?.label) return null;
if (!notice.persistent && !Number.isFinite(notice.hideAt)) {
notice.hideAt = now + (Number.isFinite(notice.dwellMs)
? notice.dwellMs
: LOADING_FAILURE_DWELL_MS);
notice.hideAt =
now +
(Number.isFinite(notice.dwellMs)
? notice.dwellMs
: LOADING_FAILURE_DWELL_MS);
}
if (Number.isFinite(notice.hideAt) && now >= notice.hideAt) return null;
return {
@@ -130,10 +159,16 @@ export function presentGlobalStatusNotice(notice, nowMs = 0) {
}
/** Whether deferred notice work still owns the current presentation epoch. */
export function canPresentDeferredStatusNotice(expectedGeneration, currentGeneration, disposed = false) {
return !disposed
&& Number.isSafeInteger(expectedGeneration)
&& expectedGeneration === currentGeneration;
export function canPresentDeferredStatusNotice(
expectedGeneration,
currentGeneration,
disposed = false,
) {
return (
!disposed &&
Number.isSafeInteger(expectedGeneration) &&
expectedGeneration === currentGeneration
);
}
/**
@@ -141,9 +176,19 @@ export function canPresentDeferredStatusNotice(expectedGeneration, currentGenera
* hide a terminal manager failure. Failure dwell starts when the manager
* reports it, so it must remain the highest-priority presentation while live.
*/
export function presentGlobalLoadingStatus(notice, loadingState, summary, nowMs = 0) {
const loadingPresentation = presentLoadingFeedback(loadingState, summary, nowMs);
if (['error', 'retry'].includes(loadingPresentation?.state)) return loadingPresentation;
export function presentGlobalLoadingStatus(
notice,
loadingState,
summary,
nowMs = 0,
) {
const loadingPresentation = presentLoadingFeedback(
loadingState,
summary,
nowMs,
);
if (['error', 'retry'].includes(loadingPresentation?.state))
return loadingPresentation;
return presentGlobalStatusNotice(notice, nowMs) || loadingPresentation;
}
@@ -162,11 +207,11 @@ export function createTrafficSyncFeedbackState() {
* Reduce one sampled Street Traffic status without extending completion on
* every animation-loop poll. Coverage describes accepted data, not work.
*/
export function reduceTrafficSyncFeedback(previous, {
enabled = false,
stats = {},
forceShow = false,
} = {}, nowMs = 0) {
export function reduceTrafficSyncFeedback(
previous,
{ enabled = false, stats = {}, forceShow = false } = {},
nowMs = 0,
) {
const state = previous || createTrafficSyncFeedbackState();
const now = Number.isFinite(nowMs) ? nowMs : 0;
if (!enabled) return createTrafficSyncFeedbackState();
@@ -174,10 +219,13 @@ export function reduceTrafficSyncFeedback(previous, {
const hasProgress = Number.isFinite(stats.phaseProgressPct);
const progressPct = hasProgress
? Math.max(0, Math.min(100, Math.round(stats.phaseProgressPct)))
: (stats.loading ? 1 : 100);
const busy = stats.loading === true
|| stats.worldJumping === true
|| (hasProgress && (progressPct < 100 || (stats.prewarmQueueDepth ?? 0) > 0));
: stats.loading
? 1
: 100;
const busy =
stats.loading === true ||
stats.worldJumping === true ||
(hasProgress && (progressPct < 100 || (stats.prewarmQueueDepth ?? 0) > 0));
const label = String(stats.phaseLabel || stats.loadingLabel || '').trim();
if (busy) {
@@ -193,13 +241,13 @@ export function reduceTrafficSyncFeedback(previous, {
};
}
const existingConfirmation = state.confirmationUntil > now
? state.confirmationUntil
: 0;
const confirmationUntil = existingConfirmation || (state.busy || forceShow
? now + TRAFFIC_SYNC_CONFIRM_MS
: 0);
const visible = confirmationUntil > now && progressPct >= 100 && Boolean(label);
const existingConfirmation =
state.confirmationUntil > now ? state.confirmationUntil : 0;
const confirmationUntil =
existingConfirmation ||
(state.busy || forceShow ? now + TRAFFIC_SYNC_CONFIRM_MS : 0);
const visible =
confirmationUntil > now && progressPct >= 100 && Boolean(label);
return {
busy: false,
visible,
@@ -217,7 +265,8 @@ export function reduceTrafficSyncFeedback(previous, {
function terminalFromEvent(event) {
const type = String(event?.type || '');
if (type === 'visibility-failed' || type === 'refresh-failed' || event?.error) return 'error';
if (type === 'visibility-failed' || type === 'refresh-failed' || event?.error)
return 'error';
if (type === 'visibility-cancelled' || event?.cancelled) return 'cancelled';
if (type === 'visibility' || type === 'refresh') return 'complete';
return null;
@@ -238,7 +287,9 @@ export function reduceLoadingFeedback(previous, summary, nowMs, event = null) {
const beginning = state.phase !== 'loading';
const startedAt = beginning ? now : state.startedAt;
const priorParticipants = beginning ? [] : state.activeIds;
const activeIds = [...new Set([...priorParticipants, ...summary.activeIds])];
const activeIds = [
...new Set([...priorParticipants, ...summary.activeIds]),
];
const eventLayerId = String(event?.layerId || '');
const eventParticipates = eventLayerId && activeIds.includes(eventLayerId);
const batchOutcome = mergeTerminalOutcome(
@@ -257,27 +308,41 @@ export function reduceLoadingFeedback(previous, summary, nowMs, event = null) {
activeIds,
batchOutcome,
terminal: null,
operation: summary.disabling ? 'disabling' : summary.refresh ? 'refresh' : 'loading',
failedEventIds: [...new Set([
...(beginning ? [] : state.failedEventIds || []),
...(eventParticipates && terminalFromEvent(event) === 'error' ? [eventLayerId] : []),
])],
operation: summary.disabling
? 'disabling'
: summary.refresh
? 'refresh'
: 'loading',
failedEventIds: [
...new Set([
...(beginning ? [] : state.failedEventIds || []),
...(eventParticipates && terminalFromEvent(event) === 'error'
? [eventLayerId]
: []),
]),
],
};
}
if (state.phase === 'loading') {
const eventLayerId = String(event?.layerId || '');
const eventParticipates = eventLayerId && state.activeIds.includes(eventLayerId);
const terminal = mergeTerminalOutcome(
const eventParticipates =
eventLayerId && state.activeIds.includes(eventLayerId);
const terminal =
mergeTerminalOutcome(
state.batchOutcome,
terminalFromParticipantStats(summary, state.activeIds),
),
eventParticipates ? terminalFromEvent(event) : null,
) || 'complete';
mergeTerminalOutcome(
state.batchOutcome,
terminalFromParticipantStats(summary, state.activeIds),
),
eventParticipates ? terminalFromEvent(event) : null,
) || 'complete';
const wasVisible = state.visible || now >= state.showAt;
if (!wasVisible && terminal === 'complete') return createLoadingFeedbackState();
const dwell = terminal === 'error' ? LOADING_FAILURE_DWELL_MS : LOADING_TERMINAL_DWELL_MS;
if (!wasVisible && terminal === 'complete')
return createLoadingFeedbackState();
const dwell =
terminal === 'error'
? LOADING_FAILURE_DWELL_MS
: LOADING_TERMINAL_DWELL_MS;
return {
...state,
phase: 'terminal',
@@ -285,10 +350,14 @@ export function reduceLoadingFeedback(previous, summary, nowMs, event = null) {
hideAt: now + dwell,
batchOutcome: terminal,
terminal,
failedEventIds: [...new Set([
...(state.failedEventIds || []),
...(eventParticipates && terminalFromEvent(event) === 'error' ? [eventLayerId] : []),
])],
failedEventIds: [
...new Set([
...(state.failedEventIds || []),
...(eventParticipates && terminalFromEvent(event) === 'error'
? [eventLayerId]
: []),
]),
],
};
}
@@ -298,11 +367,17 @@ 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 site = summary.records.find(record => record.installationRetry?.retryAt > 0);
const otherFailure = summary.records.some(record => record.id !== 'military-installations'
&& (state?.activeIds || []).includes(record.id)
&& (record.error || record.unavailable || record.keyRequired))
|| (state?.failedEventIds || []).some(id => id !== 'military-installations');
const site = summary.records.find(
(record) => record.installationRetry?.retryAt > 0,
);
const otherFailure =
summary.records.some(
(record) =>
record.id !== 'military-installations' &&
(state?.activeIds || []).includes(record.id) &&
(record.error || record.unavailable || record.keyRequired),
) ||
(state?.failedEventIds || []).some((id) => id !== 'military-installations');
// Keep the actual retry visible between attempts, without hiding another
// participant's failure or pretending that a scheduled retry is fetching.
if (site && !summary.active.length && !otherFailure) {
@@ -312,28 +387,55 @@ export function presentLoadingFeedback(state, summary, nowMs) {
}
if (!state?.visible) return null;
if (state.phase === 'terminal') {
const labels = { complete: 'LOAD COMPLETE', cancelled: 'LOAD CANCELLED', error: 'LOAD FAILED' };
const label = state.operation === 'disabling' && state.terminal === 'complete'
? 'LIVE DATA OFF'
: state.terminal === 'complete' && state.activeIds?.length === 1 && state.activeIds[0] === 'military-installations'
? 'MAPPED SITES LOADED' : labels[state.terminal] || 'LOAD COMPLETE';
const labels = {
complete: 'LOAD COMPLETE',
cancelled: 'LOAD CANCELLED',
error: 'LOAD FAILED',
};
const label =
state.operation === 'disabling' && state.terminal === 'complete'
? 'LIVE DATA OFF'
: state.terminal === 'complete' &&
state.activeIds?.length === 1 &&
state.activeIds[0] === 'military-installations'
? 'MAPPED SITES LOADED'
: labels[state.terminal] || 'LOAD COMPLETE';
return { state: state.terminal, label, detail: '' };
}
const active = summary.active;
if (active.length === 1 && active[0].installationRetry && !summary.disabling) {
return { state: 'loading', label: active[0].installationRetry.retrying
? 'RETRYING MAPPED SITES' : 'FETCHING MAPPED SITES', detail: 'OpenStreetMap · Overpass' };
if (
active.length === 1 &&
active[0].installationRetry &&
!summary.disabling
) {
return {
state: 'loading',
label: active[0].installationRetry.retrying
? 'RETRYING MAPPED SITES'
: 'FETCHING MAPPED SITES',
detail: 'OpenStreetMap · Overpass',
};
}
const elapsed = Math.max(0, nowMs - state.startedAt);
const label = summary.disabling
? 'TURNING OFF LIVE DATA'
: summary.refresh ? 'REFRESHING LIVE DATA' : 'LOADING LIVE DATA';
const names = active.slice(0, 2).map((record) => record.label).join(' · ');
: summary.refresh
? 'REFRESHING LIVE DATA'
: 'LOADING LIVE DATA';
const names = active
.slice(0, 2)
.map((record) => record.label)
.join(' · ');
const suffix = active.length > 2 ? ` +${active.length - 2}` : '';
return {
state: elapsed >= LOADING_LONG_THRESHOLD_MS
? 'long'
: summary.disabling ? 'disabling' : summary.refresh ? 'refresh' : 'loading',
state:
elapsed >= LOADING_LONG_THRESHOLD_MS
? 'long'
: summary.disabling
? 'disabling'
: summary.refresh
? 'refresh'
: 'loading',
label,
detail: `${names}${suffix}`,
};