Skip to content

Commit

Permalink
Try to fix geolocation permission issues (#625)
Browse files Browse the repository at this point in the history
* Remove an unnecessary REQUESTED state
* Replace useGeolocation with our own implementation
  • Loading branch information
wbazant authored Dec 10, 2024
1 parent 9ed1b97 commit f437eff
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 70 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"react-select": "^4.3.0",
"react-split-pane": "^0.1.92",
"react-toastify": "^8.1.0",
"react-use": "^17.2.1",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.6",
"react-window-infinite-loader": "^1.0.7",
Expand Down
171 changes: 107 additions & 64 deletions src/components/map/ConnectGeolocation.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useEffect, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useGeolocation } from 'react-use'

import {
geolocationCentering,
geolocationError,
geolocationFollowing,
geolocationLoading,
GeolocationState,
geolocationTracking,
} from '../../redux/geolocationSlice'
Expand All @@ -18,6 +16,72 @@ export const isGeolocationOpen = (geolocationState) =>

const MIN_TRACKING_ZOOM = 16

const useGeolocation = () => {
const [state, setState] = useState({
loading: true,
heading: null,
latitude: null,
longitude: null,
error: null,
})

const watchId = useRef(null)

useEffect(() => {
if (!navigator.geolocation) {
setState((s) => ({
...s,
loading: false,
error: {
code: 0,
message: 'Geolocation not supported',
},
}))
return
}

const onSuccess = (position) => {
const { heading, latitude, longitude } = position.coords

setState({
loading: false,
heading,
latitude,
longitude,
error: null,
})
}

const onError = (error) => {
setState((s) => ({
...s,
loading: false,
error,
}))
}

const options = {
enableHighAccuracy: true,
maximumAge: 5000,
timeout: 60000,
}

watchId.current = navigator.geolocation.watchPosition(
onSuccess,
onError,
options,
)

return () => {
if (watchId.current) {
navigator.geolocation.clearWatch(watchId.current)
}
}
}, [])

return state
}

export const ConnectGeolocation = () => {
const { googleMap, getGoogleMaps } = useSelector((state) => state.map)
const maps = getGoogleMaps ? getGoogleMaps() : null
Expand Down Expand Up @@ -52,69 +116,48 @@ export const ConnectGeolocation = () => {
(state) => state.geolocation.geolocationState,
)

const geolocation = useGeolocation({
enableHighAccuracy: true,
maximumAge: 5000,
timeout: 60000,
})
const geolocation = useGeolocation()

useEffect(() => {
switch (geolocationState) {
case GeolocationState.REQUESTED:
dispatch(geolocationLoading())
break

case GeolocationState.LOADING:
case GeolocationState.CENTERING:
case GeolocationState.TRACKING:
case GeolocationState.DOT_ON:
if (geolocation.loading) {
// Still loading, do nothing
} else if (geolocation.error) {
dispatch(geolocationError(geolocation.error))
} else if (!geolocation.latitude || !geolocation.longitude) {
dispatch(geolocationError({ message: 'Unknown error' }))
} else if (isMapMoving) {
// Do nothing
} else {
const newPosition = new maps.LatLng(
geolocation.latitude,
geolocation.longitude,
)
const currentCenter = googleMap.getCenter()
const distanceFromCenter = distanceInMeters(
currentCenter.lat(),
currentCenter.lng(),
geolocation.latitude,
geolocation.longitude,
)

if (
geolocationState === GeolocationState.LOADING &&
distanceFromCenter > 1
) {
dispatch(geolocationCentering(geolocation))
googleMap.setZoom(Math.max(googleMap.getZoom(), MIN_TRACKING_ZOOM))
googleMap.panTo(newPosition)
} else if (
geolocationState === GeolocationState.TRACKING &&
distanceFromCenter > 1
) {
dispatch(geolocationCentering(geolocation))
googleMap.panTo(newPosition)
} else if (geolocationState === GeolocationState.DOT_ON) {
dispatch(geolocationFollowing(geolocation))
} else {
dispatch(geolocationTracking(geolocation))
}
}
break
case GeolocationState.INITIAL:
case GeolocationState.DENIED:
// Should not happen
break

default:
if (geolocation.loading) {
// Still loading, do nothing
} else if (geolocation.error) {
dispatch(geolocationError(geolocation.error))
} else if (!geolocation.latitude || !geolocation.longitude) {
dispatch(geolocationError({ message: 'Unknown error' }))
} else if (isMapMoving) {
// Do nothing
} else {
const newPosition = new maps.LatLng(
geolocation.latitude,
geolocation.longitude,
)
const currentCenter = googleMap.getCenter()
const distanceFromCenter = distanceInMeters(
currentCenter.lat(),
currentCenter.lng(),
geolocation.latitude,
geolocation.longitude,
)

if (
geolocationState === GeolocationState.LOADING &&
distanceFromCenter > 1
) {
dispatch(geolocationCentering(geolocation))
googleMap.setZoom(Math.max(googleMap.getZoom(), MIN_TRACKING_ZOOM))
googleMap.panTo(newPosition)
} else if (
geolocationState === GeolocationState.TRACKING &&
distanceFromCenter > 1
) {
dispatch(geolocationCentering(geolocation))
googleMap.panTo(newPosition)
} else if (geolocationState === GeolocationState.DOT_ON) {
dispatch(geolocationFollowing(geolocation))
} else {
dispatch(geolocationTracking(geolocation))
}
}
}, [geolocation.loading, Math.round(geolocation.timestamp / 5000), dispatch]) //eslint-disable-line

Expand Down
6 changes: 1 addition & 5 deletions src/redux/geolocationSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { updateLastMapView } from './viewportSlice'

export const GeolocationState = {
INITIAL: 'INITIAL',
REQUESTED: 'REQUESTED',
DENIED: 'DENIED',
LOADING: 'LOADING',
FIRST_LOCATION: 'FIRST_LOCATION',
Expand All @@ -23,7 +22,7 @@ export const geolocationSlice = createSlice({
},
reducers: {
requestGeolocation: (state) => {
state.geolocationState = GeolocationState.REQUESTED
state.geolocationState = GeolocationState.LOADING
},
geolocationDenied: (state) => {
state.geolocationState = GeolocationState.DENIED
Expand All @@ -32,9 +31,6 @@ export const geolocationSlice = createSlice({
state.geolocationState = GeolocationState.INITIAL
state.geolocation = null
},
geolocationLoading: (state) => {
state.geolocationState = GeolocationState.LOADING
},
geolocationCentering: (state, action) => {
state.geolocationState = GeolocationState.CENTERING
state.geolocation = action.payload
Expand Down

0 comments on commit f437eff

Please sign in to comment.