fix(street-level): own the framing flight and the nearest-photo lookup

From the IC8 review of #768, each with tests that fail before:

- P1: closing a photo, switching the layer off or destroying it stops the
  1.6 s framing flight toward that photo. The flight is claimed with a
  token before flyToBoundingSphere and released by its complete/cancel
  callbacks, so cancelFraming() calls camera.cancelFlight() only while
  the flight is still Street Level's: a newer navigation flight (a search
  result, a scene) is never cancelled. Turning FOLLOW on also stops it, so
  the tween cannot overwrite the follow view.
- P2: an older nearest-photo lookup can no longer replace a newer choice.
  The core owns one AbortController per lookup, aborted by a newer lookup,
  a direct open or cone click, closing the viewer, disable and destroy;
  the signal reaches source.nearestImages through the provider, and an
  aborted lookup never surfaces as an error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-06 15:16:57 +01:00
1 parent c2f1b54d00
commit 09fc10756c
8 files changed
+371 -27

No files matched your search

+34 -2
View File
@@ -70,11 +70,26 @@ export function createCameraFollow({ state, parts }) {
requestRender();
}
/**
* The framing flight this module started, while it is still the camera's
* current flight. Cesium cancels it (calling `cancel`) when any other
* flight starts, so a stale token never cancels someone else's flight.
* @type {object|null}
*/
let framing = null;
/** Frame the current image from a short distance behind it. */
function lookAtPosition() {
const { position, bearing, altitude } = state.street;
if (!position || !state.viewer) return;
const ground = groundHeightAt(position.lon, position.lat, altitude);
// Owned before the call: starting it cancels the previous flight (ours
// included) synchronously, and a zero-length one completes at once.
const flight = {};
framing = flight;
const release = () => {
if (framing === flight) framing = null;
};
state.viewer.camera.flyToBoundingSphere(
new Cesium.BoundingSphere(
Cesium.Cartesian3.fromDegrees(position.lon, position.lat, ground + 2),
@@ -87,15 +102,32 @@ export function createCameraFollow({ state, parts }) {
140,
),
duration: 1.6,
complete: release,
cancel: release,
},
);
}
/**
* Stop the framing flight if it is still in the air (the image closed or
* the layer went off). A flight another feature started since is left be.
*/
function cancelFraming() {
if (!framing) return;
// Give up ownership first: cancelFlight delivers `cancel` synchronously.
framing = null;
state.viewer?.camera?.cancelFlight?.();
}
function setFollow(enabled) {
state.street.follow = enabled === true && state.street.followAvailable;
if (state.street.follow) followCamera();
// The framing tween would keep overwriting the follow view until it lands.
if (state.street.follow) {
cancelFraming();
followCamera();
}
state.notify?.();
}
return { followCamera, lookAtPosition, setFollow };
return { followCamera, lookAtPosition, cancelFraming, setFollow };
}