From 49f26687e3465a631436f50658fdc75931466eb1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 9 Apr 2022 19:29:51 +0200 Subject: [PATCH] Zoom to geolocation automatically if within 60 seconds, fix reading the previous map location from local storage if not initialized, fix #724" --- Logic/Actors/GeoLocationHandler.ts | 54 +++++++++++++++++++++--------- Logic/State/ElementsState.ts | 49 +++++++++++++++------------ Models/Constants.ts | 7 ++++ 3 files changed, 72 insertions(+), 38 deletions(-) diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index 8eba88e44..52f420f20 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -6,6 +6,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; import {QueryParameters} from "../Web/QueryParameters"; import FeatureSource from "../FeatureSource/FeatureSource"; import {BBox} from "../BBox"; +import Constants from "../../Models/Constants"; export interface GeoLocationPointProperties { id: "gps", @@ -25,13 +26,11 @@ export default class GeoLocationHandler extends VariableUiElement { /** * Wether or not the geolocation is active, aka the user requested the current location - * @private */ private readonly _isActive: UIEventSource; /** * Wether or not the geolocation is locked, aka the user requested the current location and wants the crosshair to follow the user - * @private */ private readonly _isLocked: UIEventSource; @@ -54,9 +53,8 @@ export default class GeoLocationHandler extends VariableUiElement { /** * The date when the user requested the geolocation. If we have a location, it'll autozoom to it the first 30 secs - * @private */ - private _lastUserRequest: Date; + private _lastUserRequest: UIEventSource; /** * A small flag on localstorage. If the user previously granted the geolocation, it will be set. @@ -80,6 +78,8 @@ export default class GeoLocationHandler extends VariableUiElement { ) { const currentGPSLocation = new UIEventSource(undefined, "GPS-coordinate") const leafletMap = state.leafletMap + const initedAt = new Date() + let autozoomDone = false; const hasLocation = currentGPSLocation.map( (location) => location !== undefined ); @@ -97,13 +97,30 @@ export default class GeoLocationHandler extends VariableUiElement { const timeDiff = (new Date().getTime() - lastClick.getTime()) / 1000 return timeDiff <= 3 }) + + const latLonGiven = QueryParameters.wasInitialized("lat") && QueryParameters.wasInitialized("lon") + const willFocus = lastClick.map(lastUserRequest => { + const timeDiffInited = (new Date().getTime() - initedAt.getTime()) / 1000 + console.log("TimeDiff with initedAtt is ", timeDiffInited) + if (!latLonGiven && !autozoomDone && timeDiffInited < Constants.zoomToLocationTimeout) { + return true + } + if (lastUserRequest === undefined) { + return false; + } + const timeDiff = (new Date().getTime() - lastUserRequest.getTime()) / 1000 + console.log("TimeDiff with lastClick is ", timeDiff) + return timeDiff <= Constants.zoomToLocationTimeout + }) + lastClick.addCallbackAndRunD(_ => { window.setTimeout(() => { - if (lastClickWithinThreeSecs.data) { + if (lastClickWithinThreeSecs.data || willFocus.data) { lastClick.ping() } }, 500) }) + super( hasLocation.map( (hasLocationData) => { @@ -116,7 +133,8 @@ export default class GeoLocationHandler extends VariableUiElement { } if (!hasLocationData) { // Position not yet found but we are active: we spin to indicate activity - const icon = Svg.location_empty_svg() + // If will focus is active too, we indicate this differently + const icon = willFocus.data ? Svg.location_svg() : Svg.location_empty_svg() icon.SetStyle("animation: spin 4s linear infinite;") return icon; } @@ -130,7 +148,7 @@ export default class GeoLocationHandler extends VariableUiElement { // We have a location, so we show a dot in the center return Svg.location_svg(); }, - [isActive, isLocked, permission, lastClickWithinThreeSecs] + [isActive, isLocked, permission, lastClickWithinThreeSecs, willFocus] ) ); this.SetClass("mapcontrol") @@ -142,6 +160,7 @@ export default class GeoLocationHandler extends VariableUiElement { this._leafletMap = leafletMap; this._layoutToUse = state.layoutToUse; this._hasLocation = hasLocation; + this._lastUserRequest = lastClick const self = this; const currentPointer = this._isActive.map( @@ -183,8 +202,7 @@ export default class GeoLocationHandler extends VariableUiElement { self.init(true, true); }); - const latLonGiven = QueryParameters.wasInitialized("lat") && QueryParameters.wasInitialized("lon") - + const doAutoZoomToLocation = !latLonGiven && state.featureSwitchGeolocation.data && state.selectedElement.data !== undefined this.init(false, doAutoZoomToLocation); @@ -221,8 +239,12 @@ export default class GeoLocationHandler extends VariableUiElement { self.currentLocation?.features?.setData([{feature, freshness: new Date()}]) const timeSinceRequest = - (new Date().getTime() - (self._lastUserRequest?.getTime() ?? 0)) / 1000; - if (timeSinceRequest < 30) { + (new Date().getTime() - (self._lastUserRequest.data?.getTime() ?? 0)) / 1000; + + if (willFocus.data) { + console.log("Zooming to user location: willFocus is set") + willFocus.setData(false) + autozoomDone = true; self.MoveToCurrentLocation(16); } else if (self._isLocked.data) { self.MoveToCurrentLocation(); @@ -239,8 +261,8 @@ export default class GeoLocationHandler extends VariableUiElement { self.MoveToCurrentLocation(16); return; } - - if(typeof navigator === "undefined"){ + + if (typeof navigator === "undefined") { return } @@ -271,7 +293,7 @@ export default class GeoLocationHandler extends VariableUiElement { /** * Moves to the currently loaded location. - * + * * // Should move to any location * let resultingLocation = undefined * let resultingzoom = 1 @@ -321,7 +343,7 @@ export default class GeoLocationHandler extends VariableUiElement { */ private MoveToCurrentLocation(targetZoom?: number) { const location = this._currentGPSLocation.data; - this._lastUserRequest = undefined; + this._lastUserRequest.setData(undefined); if ( this._currentGPSLocation.data.latitude === 0 && @@ -356,7 +378,7 @@ export default class GeoLocationHandler extends VariableUiElement { private StartGeolocating(zoomToGPS = true) { const self = this; - this._lastUserRequest = zoomToGPS ? new Date() : new Date(0); + this._lastUserRequest.setData(zoomToGPS ? new Date() : new Date(0)) if (self._permission.data === "denied") { self._previousLocationGrant.setData(""); self._isActive.setData(false) diff --git a/Logic/State/ElementsState.ts b/Logic/State/ElementsState.ts index c7fc1dbbf..f92891358 100644 --- a/Logic/State/ElementsState.ts +++ b/Logic/State/ElementsState.ts @@ -43,29 +43,34 @@ export default class ElementsState extends FeatureSwitchState { constructor(layoutToUse: LayoutConfig) { super(layoutToUse); + + + function localStorageSynced(key: string, deflt: number, docs: string ): UIEventSource{ + const localStorage = LocalStorageSource.Get(key) + const previousValue = localStorage.data + const src = UIEventSource.asFloat( + QueryParameters.GetQueryParameter( + key, + "" + deflt, + docs + ).syncWith(localStorage) + ); + + if(src.data === deflt){ + const prev = Number(previousValue) + if(!isNaN(prev)){ + src.setData(prev) + } + } + + return src; + } // -- Location control initialization - const zoom = UIEventSource.asFloat( - QueryParameters.GetQueryParameter( - "z", - "" + (layoutToUse?.startZoom ?? 1), - "The initial/current zoom level" - ).syncWith(LocalStorageSource.Get("zoom")) - ); - const lat = UIEventSource.asFloat( - QueryParameters.GetQueryParameter( - "lat", - "" + (layoutToUse?.startLat ?? 0), - "The initial/current latitude" - ).syncWith(LocalStorageSource.Get("lat")) - ); - const lon = UIEventSource.asFloat( - QueryParameters.GetQueryParameter( - "lon", - "" + (layoutToUse?.startLon ?? 0), - "The initial/current longitude of the app" - ).syncWith(LocalStorageSource.Get("lon")) - ); + const zoom = localStorageSynced("z",(layoutToUse?.startZoom ?? 1),"The initial/current zoom level") + const lat = localStorageSynced("lat",(layoutToUse?.startLat ?? 0),"The initial/current latitude") + const lon = localStorageSynced("lon",(layoutToUse?.startLon ?? 0),"The initial/current longitude of the app") + this.locationControl.setData({ zoom: Utils.asFloat(zoom.data), @@ -73,7 +78,7 @@ export default class ElementsState extends FeatureSwitchState { lon: Utils.asFloat(lon.data), }) this.locationControl.addCallback((latlonz) => { - // Sync th location controls + // Sync the location controls zoom.setData(latlonz.zoom); lat.setData(latlonz.lat); lon.setData(latlonz.lon); diff --git a/Models/Constants.ts b/Models/Constants.ts index 117f31cd0..09053fea3 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -62,6 +62,13 @@ export default class Constants { */ static distanceToChangeObjectBins = [25, 50, 100, 500, 1000, 5000, Number.MAX_VALUE] static themeOrder = ["personal", "cyclofix", "waste" , "etymology", "food","cafes_and_pubs", "playgrounds", "hailhydrant", "toilets", "aed", "bookcases"]; + /** + * Upon initialization, the GPS will search the location. + * If the location is found within the given timout, it'll automatically fly to it. + * + * In seconds + */ + static zoomToLocationTimeout = 60; private static isRetina(): boolean { if (Utils.runningFromConsole) {