2022-12-22 04:13:52 +01:00
|
|
|
import { VariableUiElement } from "../Base/VariableUIElement"
|
|
|
|
import Svg from "../../Svg"
|
2023-12-12 17:08:59 +01:00
|
|
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
2022-12-22 04:13:52 +01:00
|
|
|
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"
|
2022-12-24 03:44:21 +01:00
|
|
|
import Hotkeys from "../Base/Hotkeys"
|
2022-12-28 00:37:48 +01:00
|
|
|
import Translations from "../i18n/Translations"
|
2023-02-09 03:12:21 +01:00
|
|
|
import Constants from "../../Models/Constants"
|
2023-03-24 19:21:15 +01:00
|
|
|
import { MapProperties } from "../../Models/MapProperties"
|
2022-12-22 04:13:52 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Displays an icon depending on the state of the geolocation.
|
|
|
|
* Will set the 'lock' if clicked twice
|
|
|
|
*/
|
|
|
|
export class GeolocationControl extends VariableUiElement {
|
2023-12-12 17:08:59 +01:00
|
|
|
public readonly lastClick = new UIEventSource<Date>(undefined)
|
|
|
|
private readonly _geolocationHandler: GeoLocationHandler
|
|
|
|
private readonly _mapProperties: MapProperties
|
|
|
|
private readonly _lastClickWithinThreeSecs: Store<boolean>
|
|
|
|
constructor(
|
|
|
|
geolocationHandler: GeoLocationHandler,
|
|
|
|
state: MapProperties,
|
|
|
|
lastGeolocationRequestByUser: UIEventSource<Date> = undefined
|
|
|
|
) {
|
|
|
|
const lastClick = lastGeolocationRequestByUser ?? new UIEventSource<Date>(undefined)
|
2023-02-09 03:12:21 +01:00
|
|
|
lastClick.addCallbackD((date) => {
|
|
|
|
geolocationHandler.geolocationState.requestMoment.setData(date)
|
|
|
|
})
|
2022-12-22 04:13:52 +01:00
|
|
|
const lastClickWithinThreeSecs = lastClick.map((lastClick) => {
|
|
|
|
if (lastClick === undefined) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
const timeDiff = (new Date().getTime() - lastClick.getTime()) / 1000
|
|
|
|
return timeDiff <= 3
|
|
|
|
})
|
2023-02-09 03:12:21 +01:00
|
|
|
const lastRequestWithinTimeout = geolocationHandler.geolocationState.requestMoment.map(
|
|
|
|
(date) => {
|
|
|
|
if (date === undefined) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
const timeDiff = (new Date().getTime() - date.getTime()) / 1000
|
|
|
|
return timeDiff <= Constants.zoomToLocationTimeout
|
|
|
|
}
|
|
|
|
)
|
2023-02-07 02:11:17 +01:00
|
|
|
const geolocationState = geolocationHandler?.geolocationState
|
2022-12-22 04:13:52 +01:00
|
|
|
super(
|
2023-02-07 02:11:17 +01:00
|
|
|
geolocationState?.permission?.map(
|
2022-12-22 04:13:52 +01:00
|
|
|
(permission) => {
|
|
|
|
if (permission === "denied") {
|
|
|
|
return Svg.location_refused_svg()
|
|
|
|
}
|
2023-03-24 19:21:15 +01:00
|
|
|
if (!geolocationState.allowMoving.data) {
|
2022-12-22 04:13:52 +01:00
|
|
|
return Svg.location_locked_svg()
|
|
|
|
}
|
|
|
|
|
2022-12-23 15:52:22 +01:00
|
|
|
if (geolocationState.currentGPSLocation.data === undefined) {
|
|
|
|
if (permission === "prompt") {
|
|
|
|
return Svg.location_empty_svg()
|
|
|
|
}
|
2022-12-22 04:13:52 +01:00
|
|
|
// Position not yet found, but permission is either requested or granted: we spin to indicate activity
|
2023-02-09 03:12:21 +01:00
|
|
|
const icon =
|
|
|
|
!geolocationHandler.mapHasMoved.data || lastRequestWithinTimeout.data
|
|
|
|
? Svg.location_svg()
|
|
|
|
: Svg.location_empty_svg()
|
2022-12-22 04:13:52 +01:00
|
|
|
return icon
|
|
|
|
.SetClass("cursor-wait")
|
|
|
|
.SetStyle("animation: spin 4s linear infinite;")
|
|
|
|
}
|
|
|
|
|
2022-12-23 15:52:22 +01:00
|
|
|
// We have a location, so we show a dot in the center
|
|
|
|
|
2022-12-28 00:37:48 +01:00
|
|
|
if (lastClickWithinThreeSecs.data) {
|
2022-12-22 04:13:52 +01:00
|
|
|
return Svg.location_unlocked_svg()
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have a location, so we show a dot in the center
|
|
|
|
return Svg.location_svg()
|
|
|
|
},
|
|
|
|
[
|
|
|
|
geolocationState.currentGPSLocation,
|
2023-03-24 19:21:15 +01:00
|
|
|
geolocationState.allowMoving,
|
2022-12-22 04:13:52 +01:00
|
|
|
geolocationHandler.mapHasMoved,
|
|
|
|
lastClickWithinThreeSecs,
|
2023-02-09 03:12:21 +01:00
|
|
|
lastRequestWithinTimeout,
|
2022-12-22 04:13:52 +01:00
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2023-12-12 17:08:59 +01:00
|
|
|
this._geolocationHandler = geolocationHandler
|
|
|
|
this._mapProperties = state
|
2022-12-22 04:13:52 +01:00
|
|
|
|
2023-12-12 17:08:59 +01:00
|
|
|
this.lastClick = lastClick
|
|
|
|
this._lastClickWithinThreeSecs = lastClickWithinThreeSecs
|
2022-12-24 03:44:21 +01:00
|
|
|
|
2023-12-12 17:08:59 +01:00
|
|
|
this.onClick(() => this.handleClick())
|
|
|
|
Hotkeys.RegisterHotkey({ nomod: "L" }, Translations.t.hotkeyDocumentation.geolocate, () =>
|
|
|
|
this.handleClick()
|
2022-12-24 03:44:21 +01:00
|
|
|
)
|
2022-12-22 04:13:52 +01:00
|
|
|
|
|
|
|
lastClick.addCallbackAndRunD((_) => {
|
|
|
|
window.setTimeout(() => {
|
|
|
|
if (lastClickWithinThreeSecs.data) {
|
|
|
|
lastClick.ping()
|
|
|
|
}
|
|
|
|
}, 500)
|
|
|
|
})
|
2023-02-09 03:12:21 +01:00
|
|
|
geolocationHandler.geolocationState.requestMoment.addCallbackAndRunD((_) => {
|
|
|
|
window.setTimeout(() => {
|
|
|
|
if (lastRequestWithinTimeout.data) {
|
|
|
|
geolocationHandler.geolocationState.requestMoment.ping()
|
|
|
|
}
|
|
|
|
}, 500)
|
|
|
|
})
|
2022-12-22 04:13:52 +01:00
|
|
|
}
|
2023-12-12 17:08:59 +01:00
|
|
|
|
|
|
|
public async handleClick() {
|
|
|
|
const geolocationHandler = this._geolocationHandler
|
|
|
|
const geolocationState = this._geolocationHandler.geolocationState
|
|
|
|
const lastClick = this.lastClick
|
|
|
|
const state = this._mapProperties
|
|
|
|
if (
|
|
|
|
geolocationState.permission.data !== "granted" &&
|
|
|
|
geolocationState.currentGPSLocation.data === undefined
|
|
|
|
) {
|
|
|
|
lastClick.setData(new Date())
|
|
|
|
geolocationState.requestMoment.setData(new Date())
|
|
|
|
await geolocationState.requestPermission()
|
|
|
|
}
|
|
|
|
|
|
|
|
if (geolocationState.allowMoving.data === false) {
|
|
|
|
// Unlock
|
|
|
|
geolocationState.allowMoving.setData(true)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// A location _is_ known! Let's move to this location
|
|
|
|
const currentLocation = geolocationState.currentGPSLocation.data
|
|
|
|
if (currentLocation === undefined) {
|
|
|
|
// No location is known yet, not much we can do
|
|
|
|
lastClick.setData(new Date())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const inBounds = state.bounds.data.contains([
|
|
|
|
currentLocation.longitude,
|
|
|
|
currentLocation.latitude,
|
|
|
|
])
|
|
|
|
geolocationHandler.MoveMapToCurrentLocation()
|
|
|
|
if (inBounds) {
|
|
|
|
state.zoom.update((z) => z + 3)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._lastClickWithinThreeSecs.data) {
|
|
|
|
geolocationState.allowMoving.setData(false)
|
|
|
|
lastClick.setData(undefined)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
lastClick.setData(new Date())
|
|
|
|
}
|
2022-12-22 04:13:52 +01:00
|
|
|
}
|