forked from MapComplete/MapComplete
		
	Zoom to geolocation automatically if within 60 seconds, fix reading the previous map location from local storage if not initialized, fix #724"
This commit is contained in:
		
							parent
							
								
									0cb306e542
								
							
						
					
					
						commit
						49f26687e3
					
				
					 3 changed files with 72 additions and 38 deletions
				
			
		|  | @ -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<boolean>; | ||||
| 
 | ||||
|     /** | ||||
|      * 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<boolean>; | ||||
| 
 | ||||
|  | @ -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<Date>; | ||||
| 
 | ||||
|     /** | ||||
|      * 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<Coordinates>(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,7 +202,6 @@ 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(); | ||||
|  | @ -240,7 +262,7 @@ export default class GeoLocationHandler extends VariableUiElement { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if(typeof navigator === "undefined"){ | ||||
|         if (typeof navigator === "undefined") { | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|  | @ -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) | ||||
|  |  | |||
|  | @ -44,28 +44,33 @@ export default class ElementsState extends FeatureSwitchState { | |||
|     constructor(layoutToUse: LayoutConfig) { | ||||
|         super(layoutToUse); | ||||
|          | ||||
|          | ||||
|             function localStorageSynced(key: string, deflt: number, docs: string ): UIEventSource<number>{ | ||||
|                 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); | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue