mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Merge pull request #1047 from rudycelekli/contrib/gods-026-map-subscription-ownership-20261011-reviewed
fix(maps): stop delivering settled state to detached subscribers
This commit is contained in:
4 files changed
+99
-1
No files matched your search
@@ -36,6 +36,8 @@
|
|||||||
|
|
||||||
- Director timeline seeks cross the dateline along the short longitude arc.
|
- Director timeline seeks cross the dateline along the short longitude arc.
|
||||||
|
|
||||||
|
- Map state updates stop delivering to subscribers removed during the same notification.
|
||||||
|
|
||||||
- Director assets cancelled during the final stream read are discarded with reader cleanup.
|
- Director assets cancelled during the final stream read are discarded with reader cleanup.
|
||||||
|
|
||||||
- Analyst queries preserve useful rows when an upstream timestamp is outside the supported date range.
|
- Analyst queries preserve useful rows when an upstream timestamp is outside the supported date range.
|
||||||
|
|||||||
@@ -5224,6 +5224,10 @@ falls back to OSM on construction failure or two active tile failures, and
|
|||||||
credits follow the source actually displayed. Terrain remains lazy while the
|
credits follow the source actually displayed. Terrain remains lazy while the
|
||||||
photoreal globe is hidden.
|
photoreal globe is hidden.
|
||||||
|
|
||||||
|
Map-stack subscribers hear each settled activation once. A subscriber removed
|
||||||
|
during a delivery, or every subscriber once the controller is destroyed, gets
|
||||||
|
no further callback from it, and a destroyed controller accepts no new ones.
|
||||||
|
|
||||||
Map credentials are passed to each source constructor rather than changing SDK-wide Google or ion defaults.
|
Map credentials are passed to each source constructor rather than changing SDK-wide Google or ion defaults.
|
||||||
|
|
||||||
Bing Aerial and Bing Labels resolve their ion imagery endpoint with the supplied
|
Bing Aerial and Bing Labels resolve their ion imagery endpoint with the supplied
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ export class MapSourceController {
|
|||||||
* @returns {() => void} Unsubscribe.
|
* @returns {() => void} Unsubscribe.
|
||||||
*/
|
*/
|
||||||
subscribe(listener) {
|
subscribe(listener) {
|
||||||
if (typeof listener !== 'function') return () => {};
|
// A destroyed controller never settles again, so it takes no listeners.
|
||||||
|
if (typeof listener !== 'function' || this._destroyed) return () => {};
|
||||||
this._subscribers.add(listener);
|
this._subscribers.add(listener);
|
||||||
return () => {
|
return () => {
|
||||||
this._subscribers.delete(listener);
|
this._subscribers.delete(listener);
|
||||||
@@ -142,6 +143,8 @@ export class MapSourceController {
|
|||||||
if (this._destroyed || !this._subscribers.size) return;
|
if (this._destroyed || !this._subscribers.size) return;
|
||||||
const state = this.getState();
|
const state = this.getState();
|
||||||
for (const listener of [...this._subscribers]) {
|
for (const listener of [...this._subscribers]) {
|
||||||
|
if (this._destroyed) return;
|
||||||
|
if (!this._subscribers.has(listener)) continue;
|
||||||
try {
|
try {
|
||||||
listener(state);
|
listener(state);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { MapSourceController } from './controller.js';
|
||||||
|
function fixture() {
|
||||||
|
return new MapSourceController(
|
||||||
|
{
|
||||||
|
scene: {
|
||||||
|
globe: { show: true },
|
||||||
|
requestRender() {},
|
||||||
|
frameState: {
|
||||||
|
creditDisplay: { addStaticCredit() {}, removeStaticCredit() {} },
|
||||||
|
},
|
||||||
|
primitives: { add() {}, remove() {} },
|
||||||
|
},
|
||||||
|
imageryLayers: { add() {}, remove() {} },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
registry: {
|
||||||
|
defaultId: 'fixture',
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
descriptor: { id: 'fixture', label: 'Fixture', kind: 'imagery' },
|
||||||
|
imagery: async () => ({}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
createImageryLayer: (provider) => ({ provider }),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
test('an unsubscribed owner is skipped in the same map settlement delivery', async () => {
|
||||||
|
const controller = fixture();
|
||||||
|
const calls = [];
|
||||||
|
let detach;
|
||||||
|
controller.subscribe(() => {
|
||||||
|
calls.push('first');
|
||||||
|
detach();
|
||||||
|
});
|
||||||
|
detach = controller.subscribe(() => calls.push('detached'));
|
||||||
|
controller.subscribe(() => calls.push('last'));
|
||||||
|
await controller.setStack('fixture');
|
||||||
|
assert.deepEqual(calls, ['first', 'last']);
|
||||||
|
controller.destroy();
|
||||||
|
});
|
||||||
|
test('destroying the controller during delivery stops remaining listeners', async () => {
|
||||||
|
const controller = fixture();
|
||||||
|
const calls = [];
|
||||||
|
controller.subscribe(() => {
|
||||||
|
calls.push('destroy');
|
||||||
|
controller.destroy();
|
||||||
|
});
|
||||||
|
controller.subscribe(() => calls.push('detached'));
|
||||||
|
await controller.setStack('fixture');
|
||||||
|
assert.deepEqual(calls, ['destroy']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a listener re-added after destroy during delivery is not called', async () => {
|
||||||
|
const controller = fixture();
|
||||||
|
const calls = [];
|
||||||
|
const later = () => calls.push('later');
|
||||||
|
controller.subscribe(() => {
|
||||||
|
calls.push('destroy');
|
||||||
|
controller.destroy();
|
||||||
|
controller.subscribe(later);
|
||||||
|
});
|
||||||
|
controller.subscribe(later);
|
||||||
|
await controller.setStack('fixture');
|
||||||
|
assert.deepEqual(calls, ['destroy']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('delivery order: added during delivery waits, duplicates run once, re-added runs', async () => {
|
||||||
|
const controller = fixture();
|
||||||
|
const calls = [];
|
||||||
|
const twice = () => calls.push('twice');
|
||||||
|
const readded = () => calls.push('readded');
|
||||||
|
let offReadded;
|
||||||
|
controller.subscribe(() => {
|
||||||
|
calls.push('first');
|
||||||
|
controller.subscribe(() => calls.push('added-late'));
|
||||||
|
offReadded();
|
||||||
|
controller.subscribe(readded);
|
||||||
|
});
|
||||||
|
controller.subscribe(twice);
|
||||||
|
controller.subscribe(twice);
|
||||||
|
offReadded = controller.subscribe(readded);
|
||||||
|
await controller.setStack('fixture');
|
||||||
|
assert.deepEqual(calls, ['first', 'twice', 'readded']);
|
||||||
|
controller.destroy();
|
||||||
|
});
|
||||||
Reference in new issue
Block a user