forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			156 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { VariableUiElement } from "../Base/VariableUIElement"
 | |
| import Svg from "../../Svg"
 | |
| import { UIEventSource } from "../../Logic/UIEventSource"
 | |
| import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"
 | |
| import { BBox } from "../../Logic/BBox"
 | |
| import Loc from "../../Models/Loc"
 | |
| import Hotkeys from "../Base/Hotkeys"
 | |
| import Translations from "../i18n/Translations"
 | |
| import Constants from "../../Models/Constants"
 | |
| 
 | |
| /**
 | |
|  * Displays an icon depending on the state of the geolocation.
 | |
|  * Will set the 'lock' if clicked twice
 | |
|  */
 | |
| export class GeolocationControl extends VariableUiElement {
 | |
|     constructor(
 | |
|         geolocationHandler: GeoLocationHandler,
 | |
|         state: {
 | |
|             locationControl: UIEventSource<Loc>
 | |
|             currentBounds: UIEventSource<BBox>
 | |
|         }
 | |
|     ) {
 | |
|         const lastClick = new UIEventSource<Date>(undefined)
 | |
|         lastClick.addCallbackD((date) => {
 | |
|             geolocationHandler.geolocationState.requestMoment.setData(date)
 | |
|         })
 | |
|         const lastClickWithinThreeSecs = lastClick.map((lastClick) => {
 | |
|             if (lastClick === undefined) {
 | |
|                 return false
 | |
|             }
 | |
|             const timeDiff = (new Date().getTime() - lastClick.getTime()) / 1000
 | |
|             return timeDiff <= 3
 | |
|         })
 | |
|         const lastRequestWithinTimeout = geolocationHandler.geolocationState.requestMoment.map(
 | |
|             (date) => {
 | |
|                 if (date === undefined) {
 | |
|                     return false
 | |
|                 }
 | |
|                 const timeDiff = (new Date().getTime() - date.getTime()) / 1000
 | |
|                 console.log("Timediff", timeDiff)
 | |
|                 return timeDiff <= Constants.zoomToLocationTimeout
 | |
|             }
 | |
|         )
 | |
|         const geolocationState = geolocationHandler?.geolocationState
 | |
|         super(
 | |
|             geolocationState?.permission?.map(
 | |
|                 (permission) => {
 | |
|                     if (permission === "denied") {
 | |
|                         return Svg.location_refused_svg()
 | |
|                     }
 | |
|                     if (geolocationState.isLocked.data) {
 | |
|                         return Svg.location_locked_svg()
 | |
|                     }
 | |
| 
 | |
|                     if (geolocationState.currentGPSLocation.data === undefined) {
 | |
|                         if (permission === "prompt") {
 | |
|                             return Svg.location_empty_svg()
 | |
|                         }
 | |
|                         // Position not yet found, but permission is either requested or granted: we spin to indicate activity
 | |
|                         const icon =
 | |
|                             !geolocationHandler.mapHasMoved.data || lastRequestWithinTimeout.data
 | |
|                                 ? Svg.location_svg()
 | |
|                                 : Svg.location_empty_svg()
 | |
|                         return icon
 | |
|                             .SetClass("cursor-wait")
 | |
|                             .SetStyle("animation: spin 4s linear infinite;")
 | |
|                     }
 | |
| 
 | |
|                     // We have a location, so we show a dot in the center
 | |
| 
 | |
|                     if (lastClickWithinThreeSecs.data) {
 | |
|                         return Svg.location_unlocked_svg()
 | |
|                     }
 | |
| 
 | |
|                     // We have a location, so we show a dot in the center
 | |
|                     return Svg.location_svg()
 | |
|                 },
 | |
|                 [
 | |
|                     geolocationState.currentGPSLocation,
 | |
|                     geolocationState.isLocked,
 | |
|                     geolocationHandler.mapHasMoved,
 | |
|                     lastClickWithinThreeSecs,
 | |
|                     lastRequestWithinTimeout,
 | |
|                 ]
 | |
|             )
 | |
|         )
 | |
| 
 | |
|         async function handleClick() {
 | |
|             if (
 | |
|                 geolocationState.permission.data !== "granted" &&
 | |
|                 geolocationState.currentGPSLocation.data === undefined
 | |
|             ) {
 | |
|                 lastClick.setData(new Date())
 | |
|                 geolocationState.requestMoment.setData(new Date())
 | |
|                 await geolocationState.requestPermission()
 | |
|             }
 | |
| 
 | |
|             if (geolocationState.isLocked.data === true) {
 | |
|                 // Unlock
 | |
|                 geolocationState.isLocked.setData(false)
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             if (geolocationState.currentGPSLocation.data === undefined) {
 | |
|                 // No location is known yet, not much we can do
 | |
|                 lastClick.setData(new Date())
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             // A location _is_ known! Let's move to this location
 | |
|             const currentLocation = geolocationState.currentGPSLocation.data
 | |
|             const inBounds = state.currentBounds.data.contains([
 | |
|                 currentLocation.longitude,
 | |
|                 currentLocation.latitude,
 | |
|             ])
 | |
|             geolocationHandler.MoveMapToCurrentLocation()
 | |
|             if (inBounds) {
 | |
|                 const lc = state.locationControl.data
 | |
|                 state.locationControl.setData({
 | |
|                     ...lc,
 | |
|                     zoom: lc.zoom + 3,
 | |
|                 })
 | |
|             }
 | |
| 
 | |
|             if (lastClickWithinThreeSecs.data) {
 | |
|                 geolocationState.isLocked.setData(true)
 | |
|                 lastClick.setData(undefined)
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             lastClick.setData(new Date())
 | |
|         }
 | |
| 
 | |
|         this.onClick(handleClick)
 | |
|         Hotkeys.RegisterHotkey(
 | |
|             { nomod: "L" },
 | |
|             Translations.t.hotkeyDocumentation.geolocate,
 | |
|             handleClick
 | |
|         )
 | |
| 
 | |
|         lastClick.addCallbackAndRunD((_) => {
 | |
|             window.setTimeout(() => {
 | |
|                 if (lastClickWithinThreeSecs.data) {
 | |
|                     lastClick.ping()
 | |
|                 }
 | |
|             }, 500)
 | |
|         })
 | |
|         geolocationHandler.geolocationState.requestMoment.addCallbackAndRunD((_) => {
 | |
|             window.setTimeout(() => {
 | |
|                 if (lastRequestWithinTimeout.data) {
 | |
|                     geolocationHandler.geolocationState.requestMoment.ping()
 | |
|                 }
 | |
|             }, 500)
 | |
|         })
 | |
|     }
 | |
| }
 |