mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
Street Level: without a Mapillary key, light a keyless source instead of opening on KEY REQUIRED
This commit is contained in:
1 parent
a33bc379cb
commit
579acf5081
4 files changed
+86
-3
No files matched your search
@@ -126,8 +126,17 @@ export function createStreetLevelLayer({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// One source at a time: the first provider that defaults on.
|
/** `def`, or the first keyless provider not refused when `def` has no key. */
|
||||||
const startsOn = defs.find((def) => def.defaultOn !== false);
|
function keylessInstead(def, refused = () => false) {
|
||||||
|
if (def.keyReady?.() !== false) return def;
|
||||||
|
return defs.find((d) => !d.requiresKeyId && !refused(d.id)) || def;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One source at a time: the first provider that defaults on. Without its
|
||||||
|
// key, the first keyless source instead, so a fresh keyless install opens
|
||||||
|
// on coverage rather than KEY REQUIRED.
|
||||||
|
const preferred = defs.find((def) => def.defaultOn !== false);
|
||||||
|
const startsOn = preferred && keylessInstead(preferred);
|
||||||
for (const def of defs) {
|
for (const def of defs) {
|
||||||
const entry = {
|
const entry = {
|
||||||
def,
|
def,
|
||||||
@@ -585,7 +594,15 @@ export function createStreetLevelLayer({
|
|||||||
providerIds: state.providers.keys(),
|
providerIds: state.providers.keys(),
|
||||||
filter: state.filter,
|
filter: state.filter,
|
||||||
});
|
});
|
||||||
const lit = litByParams(decoded.providers);
|
// Links and stored state omit Mapillary when it is on (the codec
|
||||||
|
// default), so a keyless install would restore a source it cannot draw.
|
||||||
|
const asked = litByParams(decoded.providers);
|
||||||
|
const lit =
|
||||||
|
asked &&
|
||||||
|
keylessInstead(
|
||||||
|
state.providers.get(asked).def,
|
||||||
|
(id) => decoded.providers.get(id) === false,
|
||||||
|
).id;
|
||||||
const before = litIds();
|
const before = litIds();
|
||||||
for (const [id, entry] of state.providers)
|
for (const [id, entry] of state.providers)
|
||||||
if (id !== lit) switchProvider(entry, false);
|
if (id !== lit) switchProvider(entry, false);
|
||||||
|
|||||||
@@ -1127,3 +1127,64 @@ test('the panel says why a lit Street View draws nothing off Google 3D', async (
|
|||||||
const view = presentStreetLevelPanel(layer.getUIState());
|
const view = presentStreetLevelPanel(layer.getUIState());
|
||||||
assert.match(view.meta, /Google Street View needs the Google 3D map/);
|
assert.match(view.meta, /Google Street View needs the Google 3D map/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('without a Mapillary key the first keyless source is lit instead', async (t) => {
|
||||||
|
const kartaViewSource = {
|
||||||
|
getTile: async () => ({ lines: [] }),
|
||||||
|
nearestPhotos: async () => [],
|
||||||
|
getPhoto: async () => {
|
||||||
|
throw new Error('KartaView has no such photo');
|
||||||
|
},
|
||||||
|
getSequencePhotos: async () => ({ photos: [], hasMore: false }),
|
||||||
|
probeImage: async () => true,
|
||||||
|
};
|
||||||
|
const lit = (hasToken) => {
|
||||||
|
const layer = createStreetLevelLayer({
|
||||||
|
providers: [
|
||||||
|
createMapillaryProvider({
|
||||||
|
source: fakeMapillarySource({ hasToken: () => hasToken }),
|
||||||
|
photoViewer: fakePhotoViewer(),
|
||||||
|
}),
|
||||||
|
createKartaViewProvider({ source: kartaViewSource }),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
t.after(() => layer.destroy());
|
||||||
|
return layer
|
||||||
|
.getUIState()
|
||||||
|
.providers.filter((provider) => provider.on)
|
||||||
|
.map((provider) => provider.id);
|
||||||
|
};
|
||||||
|
assert.deepEqual(lit(false), ['kartaview']);
|
||||||
|
assert.deepEqual(lit(true), ['mapillary']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('restoring the default "Mapillary on" without its key keeps a keyless source', (t) => {
|
||||||
|
const layer = createStreetLevelLayer({
|
||||||
|
providers: [
|
||||||
|
createMapillaryProvider({
|
||||||
|
source: fakeMapillarySource({ hasToken: () => false }),
|
||||||
|
photoViewer: fakePhotoViewer(),
|
||||||
|
}),
|
||||||
|
createKartaViewProvider({
|
||||||
|
source: {
|
||||||
|
getTile: async () => ({ lines: [] }),
|
||||||
|
nearestPhotos: async () => [],
|
||||||
|
getPhoto: async () => null,
|
||||||
|
getSequencePhotos: async () => ({ photos: [], hasMore: false }),
|
||||||
|
probeImage: async () => true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
t.after(() => layer.destroy());
|
||||||
|
const litIds = () =>
|
||||||
|
layer
|
||||||
|
.getUIState()
|
||||||
|
.providers.filter((provider) => provider.on)
|
||||||
|
.map((provider) => provider.id);
|
||||||
|
layer.setParams({ mapillary: true, providerSwitches: '' });
|
||||||
|
assert.deepEqual(litIds(), ['kartaview']);
|
||||||
|
// A link that switches KartaView off keeps its word.
|
||||||
|
layer.setParams({ mapillary: true, providerSwitches: 'kartaview-0' });
|
||||||
|
assert.deepEqual(litIds(), ['mapillary']);
|
||||||
|
});
|
||||||
@@ -74,6 +74,9 @@ export function createMapillaryProvider({ source, photoViewer = null }) {
|
|||||||
name: MAPILLARY_NAME,
|
name: MAPILLARY_NAME,
|
||||||
label: MAPILLARY_LABEL,
|
label: MAPILLARY_LABEL,
|
||||||
requiresKeyId: MAPILLARY_KEY_ID,
|
requiresKeyId: MAPILLARY_KEY_ID,
|
||||||
|
/** A browser token or a token-adding Graph proxy; else KEY REQUIRED. */
|
||||||
|
keyReady: () =>
|
||||||
|
source.hasToken?.() !== false || Boolean(source.graphEndpoint),
|
||||||
pickPrefix: PICK_PREFIX.root,
|
pickPrefix: PICK_PREFIX.root,
|
||||||
colors: COLORS,
|
colors: COLORS,
|
||||||
credit: Object.freeze({ html: MAPILLARY_CREDIT_HTML }),
|
credit: Object.freeze({ html: MAPILLARY_CREDIT_HTML }),
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export function filtersOf(provider) {
|
|||||||
* @property {boolean} [groundClick] No coverage: a ground click at street zoom opens its nearest image.
|
* @property {boolean} [groundClick] No coverage: a ground click at street zoom opens its nearest image.
|
||||||
* @property {string} [requiresMapStack] Usable only on this map stack.
|
* @property {string} [requiresMapStack] Usable only on this map stack.
|
||||||
* @property {boolean} [defaultOn] Default true; the first that defaults on is lit.
|
* @property {boolean} [defaultOn] Default true; the first that defaults on is lit.
|
||||||
|
* @property {() => boolean} [keyReady] False when its key is missing now; the
|
||||||
|
* first keyless provider is lit instead of a default that cannot draw.
|
||||||
* @property {{kind: 'view-cone', color: string}} [marker] Default: GEV's pin.
|
* @property {{kind: 'view-cone', color: string}} [marker] Default: GEV's pin.
|
||||||
* @property {Array<'pano'|'since'>} [filters] Filters it honours (default both).
|
* @property {Array<'pano'|'since'>} [filters] Filters it honours (default both).
|
||||||
* @property {{noun: string, action: string}} [coverageLabel] How the panel counts its lines and says what a click does (default: sequences, "click a line for its photos").
|
* @property {{noun: string, action: string}} [coverageLabel] How the panel counts its lines and says what a click does (default: sequences, "click a line for its photos").
|
||||||
|
|||||||
Reference in new issue
Block a user