From c5e1951f517e7c7ac10ccd13b2b71e61d2dbe253 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sun, 8 Nov 2026 19:57:28 -0800 Subject: [PATCH] fix(cockpit): one place lookup that loads several packs reads as one load, not a LOAD COMPLETE between packs --- src/data/placeBelow.js | 26 ++++++++++++++++++++++++-- src/data/placePacksLoading.test.mjs | 14 ++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/data/placeBelow.js b/src/data/placeBelow.js index 2cd5cf854..e8456973a 100644 --- a/src/data/placeBelow.js +++ b/src/data/placeBelow.js @@ -50,6 +50,11 @@ const PACKS = { /** The packs on their way now, and those whose last load failed. */ const loadingPacks = new Set(); const failedPacks = new Set(); +// One lookup loads its packs one after another (countries and seas, then +// divisions, then places): the status holds them as one load until the +// lookup ends, rather than ending and starting a batch between packs. +let lookupsInFlight = 0; +let lookupLoaded = false; const loadingListeners = new Set(); function notifyLoading() { @@ -74,6 +79,7 @@ export function reportedPlacePackLoad(name, load) { return async () => { // A retry is a fresh try: its batch ends on what this load does. failedPacks.delete(name); + if (lookupsInFlight > 0) lookupLoaded = true; loadingPacks.add(name); notifyLoading(); try { @@ -97,7 +103,10 @@ export function reportedPlacePackLoad(name, load) { * @returns {{loading: boolean, failed: boolean}} */ export function placePacksStatus() { - return { loading: loadingPacks.size > 0, failed: failedPacks.size > 0 }; + return { + loading: loadingPacks.size > 0 || (lookupLoaded && lookupsInFlight > 0), + failed: failedPacks.size > 0, + }; } /** @@ -324,7 +333,20 @@ async function orNull(lookup) { * simplification) or it is invalid. Rejects only when the country and * marine packs fail. */ -export async function placeBelow( +export async function placeBelow(latitude, longitude, options) { + lookupsInFlight += 1; + try { + return await findPlaceBelow(latitude, longitude, options); + } finally { + lookupsInFlight -= 1; + if (lookupsInFlight === 0 && lookupLoaded) { + lookupLoaded = false; + notifyLoading(); + } + } +} + +async function findPlaceBelow( latitude, longitude, { nearKm = null, detail = DETAIL_LOOKUPS } = {}, diff --git a/src/data/placePacksLoading.test.mjs b/src/data/placePacksLoading.test.mjs index 03a9d9a52..6aa3f69d3 100644 --- a/src/data/placePacksLoading.test.mjs +++ b/src/data/placePacksLoading.test.mjs @@ -41,3 +41,17 @@ test('a pack that would not load stays failed until it loads', async () => { assert.equal(await retry, 1); assert.deepEqual(place.placePacksStatus(), { loading: false, failed: false }); }); + +test('one lookup that loads several packs reads as one load', async () => { + const place = await import('./placeBelow.js?one-lookup'); + const heard = []; + const stop = place.onPlacePacksLoadingChange(() => { + const { loading } = place.placePacksStatus(); + if (heard.at(-1) !== loading) heard.push(loading); + }); + // On land with a radius: countries and seas, then divisions, then places. + const found = await place.placeBelow(48.86, 2.35, { nearKm: 50 }); + assert.equal(found.near, 'Paris'); + assert.deepEqual(heard, [true, false], 'no gap between the packs'); + stop(); +});