fix(import): enforce geometry budgets and queued navigation

This commit is contained in:
Mazeyar Moeini Feizabadi committed 2026-10-14 01:02:50 +02:00
1 parent 7694c02b91
commit a5b21b5d0f
7 files changed
+238 -30

No files matched your search

+6 -5
View File
@@ -212,17 +212,18 @@ export function initGeoFileImport({
*/
function importFiles(files) {
const list = [...(files || [])];
const run = queue.then(() => importBatch(list));
if (destroyed || !list.length) return Promise.resolve([]);
// Capture the user's intent when the files are chosen, including time
// spent waiting for an earlier batch. Later navigation must still win.
const generation = beginAutoFly();
const run = queue.then(() => importBatch(list, generation));
queue = run.catch(() => {});
return run;
}
async function importBatch(list) {
async function importBatch(list, generation) {
const outcomes = [];
if (destroyed || !list.length) return outcomes;
// Stamped now, re-asserted before the fly: whatever navigation happens
// while the files load (a drag, a search, Cockpit) supersedes this one.
const generation = beginAutoFly();
for (const file of list) {
if (destroyed) break;
const fileName = String(file?.name || 'file');
+36
View File
@@ -529,6 +529,42 @@ test('a slow import does not fly once something newer took the camera', async ()
assert.equal(flown.length, 0);
});
test('a queued import cannot fly over navigation that happened while it waited', async () => {
let release;
const gate = new Promise((resolve) => {
release = resolve;
});
let generation = 0;
const flown = [];
const nav = {
beginDeferredLocationNavigation: () => ++generation,
reassertDeferredLocationNavigation: (stamp) => stamp === generation,
};
const t = setup({
navigation: nav,
flyToSource: (v, ds) => flown.push(ds),
load: async (f) => {
if (f.name === 'first.geojson') await gate;
return {
dataSource: {},
name: f.name,
format: 'geojson',
entityCount: 1,
revoke() {},
};
},
});
const first = t.tool.importFiles([file('first.geojson')]);
await new Promise((resolve) => setImmediate(resolve));
const queued = t.tool.importFiles([file('queued.geojson')]);
generation++; // A newer user camera action while the second batch is waiting.
release();
const results = await Promise.all([first, queued]);
assert.ok(results.every(([outcome]) => outcome.ok));
assert.equal(t.tool.list().length, 2);
assert.equal(flown.length, 0);
});
test('a refused deferred stamp (Cockpit) imports without flying', async () => {
const flown = [];
const nav = navigationDouble();