Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When map is panned, geolocation stops centering #568

Merged
merged 2 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 71 additions & 15 deletions src/components/map/ConnectGeolocation.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,48 @@
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useGeolocation } from 'react-use'

import {
geolocationCentering,
geolocationError,
geolocationFollowing,
geolocationLoading,
geolocationReceived,
GeolocationState,
geolocationTracking,
} from '../../redux/geolocationSlice'
import { distanceInMeters } from '../../utils/mapDistance'

export const isGeolocationOpen = (geolocationState) =>
geolocationState !== GeolocationState.INITIAL &&
geolocationState !== GeolocationState.DENIED

const MIN_TRACKING_ZOOM = 16

export const ConnectGeolocation = () => {
const { googleMap, getGoogleMaps } = useSelector((state) => state.map)
const maps = getGoogleMaps()
const dispatch = useDispatch()
const [isMapMoving, setIsMapMoving] = useState(false)

useEffect(() => {
if (!googleMap) {
return
}

const handleDragStart = () => setIsMapMoving(true)
const handleDragEnd = () => setIsMapMoving(false)

const dragStartListener = googleMap.addListener(
'dragstart',
handleDragStart,
)
const dragEndListener = googleMap.addListener('dragend', handleDragEnd)

return () => {
dragStartListener.remove()
dragEndListener.remove()
}
}, [googleMap])

/*
note: if GeolocationState.INITIAL or GeolocationState.DENIED then this component does not render
Expand All @@ -25,6 +54,8 @@ export const ConnectGeolocation = () => {

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

useEffect(() => {
Expand All @@ -34,34 +65,59 @@ export const ConnectGeolocation = () => {
break

case GeolocationState.LOADING:
case GeolocationState.CENTERING:
case GeolocationState.TRACKING:
case GeolocationState.FIRST_LOCATION:
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(
geolocationReceived({
latitude: geolocation.latitude,
longitude: geolocation.longitude,
accuracy: geolocation.accuracy,
geolocationState,
}),
} 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:
// Do nothing for these states
// Should not happen
// @see
break

default:
console.warn(`Unhandled geolocation state: ${geolocationState}`)
}
}, [geolocationState, geolocation, dispatch])
}, [geolocation.loading, Math.round(geolocation.timestamp / 5000), dispatch]) //eslint-disable-line

return null
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { rgba } from 'polished'
import { useDispatch, useSelector } from 'react-redux'
import { css } from 'styled-components'
import styled from 'styled-components/macro'

import { MIN_GEOLOCATION_ZOOM } from '../../constants/map'
import {
geolocationCentering,
GeolocationState,
} from '../../redux/geolocationSlice'

const Heading = styled.div`
position: absolute;
top: 0;
Expand All @@ -21,7 +28,6 @@ const Heading = styled.div`
`

const Pin = styled.div`
cursor: pointer;
position: absolute;
z-index: 1;

Expand All @@ -35,6 +41,7 @@ const Pin = styled.div`
`

const GeolocationWrapper = styled.div`
${({ isClickable }) => isClickable && 'cursor: pointer;'};
position: relative;
z-index: 3;

Expand All @@ -51,8 +58,9 @@ const GeolocationWrapper = styled.div`
border-radius: 50%;
}

${({ heading }) =>
!heading &&
${({ hasHeading, isPulsing }) =>
!hasHeading &&
isPulsing &&
css`
&::before {
z-index: 1;
Expand Down Expand Up @@ -95,11 +103,34 @@ const GeolocationWrapper = styled.div`
}
`

const Geolocation = ({ heading, onClick, ...props }) => (
<GeolocationWrapper {...props}>
<Pin onClick={onClick} />
{heading && <Heading heading={heading} />}
</GeolocationWrapper>
)
const GeolocationDot = () => {
const { googleMap } = useSelector((state) => state.map)
const { geolocation, geolocationState } = useSelector(
(state) => state.geolocation,
)
const dispatch = useDispatch()

const handleClick = () => {
if (geolocationState === GeolocationState.DOT_ON) {
dispatch(geolocationCentering(geolocation))
googleMap.panTo({ lat: geolocation.latitude, lng: geolocation.longitude })
if (googleMap.getZoom() < MIN_GEOLOCATION_ZOOM) {
googleMap.setZoom(MIN_GEOLOCATION_ZOOM)
}
}
}

export default Geolocation
return (
<GeolocationWrapper
onClick={handleClick}
hasHeading={!!geolocation?.heading}
isPulsing={geolocationState !== GeolocationState.DOT_ON}
isClickable={geolocationState === GeolocationState.DOT_ON}
>
<Pin />
{geolocation?.heading && <Heading heading={geolocation.heading} />}
</GeolocationWrapper>
)
}

export default GeolocationDot
21 changes: 3 additions & 18 deletions src/components/map/MapPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import { useDispatch, useSelector } from 'react-redux'
import { toast } from 'react-toastify'
import styled from 'styled-components/macro'

import {
MIN_GEOLOCATION_ZOOM,
VISIBLE_CLUSTER_ZOOM_LIMIT,
} from '../../constants/map'
import { VISIBLE_CLUSTER_ZOOM_LIMIT } from '../../constants/map'
import { fetchFilterCounts } from '../../redux/filterSlice'
import { updatePosition } from '../../redux/locationSlice'
import { setGoogle } from '../../redux/mapSlice'
Expand All @@ -22,7 +19,7 @@ import LoadingIndicator from '../ui/LoadingIndicator'
import CloseStreetView from './CloseStreetView'
import Cluster from './Cluster'
import { ConnectGeolocation, isGeolocationOpen } from './ConnectGeolocation'
import Geolocation from './Geolocation'
import GeolocationDot from './GeolocationDot'
import Location from './Location'
import PanoramaHandler from './PanoramaHandler'
import {
Expand Down Expand Up @@ -249,16 +246,6 @@ const MapPage = ({ isDesktop }) => {
}
}

const handleGeolocationClick = () => {
googleMap?.panTo({
lat: geolocation.latitude,
lng: geolocation.longitude,
})
if (currentZoom < MIN_GEOLOCATION_ZOOM) {
googleMap?.setZoom(MIN_GEOLOCATION_ZOOM)
}
}

const handleLocationClick = (location) => {
if (!isAddingLocation && !isEditingLocation) {
history.push(`/locations/${location.id}`)
Expand Down Expand Up @@ -392,11 +379,9 @@ const MapPage = ({ isDesktop }) => {
yesIWantToUseGoogleMapApiInternals
>
{geolocation && !geolocation.loading && !geolocation.error && (
<Geolocation
onClick={handleGeolocationClick}
<GeolocationDot
lat={geolocation.latitude}
lng={geolocation.longitude}
heading={geolocation.heading}
/>
)}
{place && currentZoom >= VISIBLE_CLUSTER_ZOOM_LIMIT && (
Expand Down
30 changes: 23 additions & 7 deletions src/components/map/TrackLocationButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { toast } from 'react-toastify'
import { keyframes } from 'styled-components'
import styled from 'styled-components/macro'

import { MIN_GEOLOCATION_ZOOM } from '../../constants/map'
import {
disableGeolocation,
geolocationCentering,
GeolocationState,
requestGeolocation,
} from '../../redux/geolocationSlice'
Expand All @@ -25,7 +27,11 @@ const SpinningLoader = styled(LoaderAlt)`
`

const getTrackLocationColor = (geolocationState) =>
geolocationState === GeolocationState.TRACKING ? 'blue' : 'tertiaryText'
geolocationState === GeolocationState.CENTERING ||
geolocationState === GeolocationState.TRACKING ||
geolocationState === GeolocationState.DOT_ON
? 'blue'
: 'tertiaryText'

const getCursorStyle = (geolocationState) => {
if (geolocationState === GeolocationState.DENIED) {
Expand All @@ -42,6 +48,8 @@ const TrackLocationIcon = ({ geolocationState, ...props }) => {
return <CurrentLocation opacity="0.5" {...props} />
} else if (geolocationState === GeolocationState.LOADING) {
return <SpinningLoader {...props} />
} else if (geolocationState === GeolocationState.DOT_ON) {
return <CurrentLocation opacity="0.8" {...props} />
} else {
return <CurrentLocation {...props} />
}
Expand Down Expand Up @@ -82,14 +90,16 @@ const TrackLocationIconButton = styled(IconButton).attrs((props) => ({

const TrackLocationButton = ({ isIcon }) => {
const dispatch = useDispatch()
const geolocationState = useSelector(
(state) => state.geolocation.geolocationState,
const { geolocation, geolocationState } = useSelector(
(state) => state.geolocation,
)

const TrackLocationBtn = isIcon
? TrackLocationIconButton
: TrackLocationPrependButton

const { googleMap } = useSelector((state) => state.map)

return (
<TrackLocationBtn
geolocationState={geolocationState}
Expand All @@ -98,12 +108,18 @@ const TrackLocationButton = ({ isIcon }) => {
toast.info(
'Permission to use your location was denied. To enable geolocation, please allow location sharing in your browser settings and refresh the page.',
)
} else if (geolocationState === GeolocationState.DOT_ON) {
dispatch(geolocationCentering(geolocation))
googleMap.panTo({
lat: geolocation.latitude,
lng: geolocation.longitude,
})
if (googleMap.getZoom() < MIN_GEOLOCATION_ZOOM) {
googleMap.setZoom(MIN_GEOLOCATION_ZOOM)
}
} else if (geolocationState === GeolocationState.INITIAL) {
dispatch(requestGeolocation())
} else if (
geolocationState === GeolocationState.TRACKING ||
geolocationState === GeolocationState.FIRST_LOCATION
) {
} else {
dispatch(disableGeolocation())
}
event.stopPropagation()
Expand Down
9 changes: 7 additions & 2 deletions src/components/mobile/LocationPositionNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@ const LocationPositionNav = () => {
const handleCancel = () => {
if (storedPosition && googleMap) {
const currentCenter = googleMap.getCenter().toJSON()
const distance = distanceInMeters(currentCenter, storedPosition)
const distanceMeters = distanceInMeters(
currentCenter.lat,
currentCenter.lng,
storedPosition.lat,
storedPosition.lng,
)

googleMap.setCenter(storedPosition)

if (distance > 1) {
if (distanceMeters > 1) {
// Wait half a second to let the user observe the edited position being undone
setTimeout(
() => history.push(`/locations/${locationId}/edit/details`),
Expand Down
Loading
Loading