Skip to content
This repository has been archived by the owner on Feb 1, 2024. It is now read-only.

Commit

Permalink
Set map view on facility contextually
Browse files Browse the repository at this point in the history
When the user has arrived at the map by visiting a URL which contains an
OAR ID, set the map view to center on the facility when the facility
data returns. Otherwise, selecting a facility will not re-center the
viewport.

If the OAR ID encoded in a URL returns an error, switch off the setView
behavior to ensure that the next manually selected facility does not
incite a setView call.

If a facility which is off-screen is selected (as may happen when
selecting a facility from the list sidebar), center the map view on the
facility when its data returns.
  • Loading branch information
Kelly Innes committed Aug 28, 2019
1 parent 962ee4c commit 50f4092
Showing 1 changed file with 117 additions and 3 deletions.
120 changes: 117 additions & 3 deletions src/app/src/components/VectorTileFacilitiesMap.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from 'react';
import { bool, func, number, shape, string } from 'prop-types';
import { arrayOf, bool, func, number, shape, string } from 'prop-types';
import { connect } from 'react-redux';
import { Map as ReactLeafletMap, ZoomControl } from 'react-leaflet';
import ReactLeafletGoogleLayer from 'react-leaflet-google-layer';
Expand All @@ -9,6 +9,9 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
import { toast } from 'react-toastify';
import noop from 'lodash/noop';
import get from 'lodash/get';
import head from 'lodash/head';
import last from 'lodash/last';
import delay from 'lodash/delay';

import Button from './Button';
import VectorTileFacilitiesLayer from './VectorTileFacilitiesLayer';
Expand All @@ -17,9 +20,12 @@ import { COUNTRY_CODES } from '../util/constants';

import { makeFacilityDetailLink } from '../util/util';

import { facilityDetailsPropType } from '../util/propTypes';

import {
initialCenter,
initialZoom,
detailsZoomLevel,
GOOGLE_CLIENT_SIDE_API_KEY,
} from '../util/constants.facilitiesMap';

Expand All @@ -35,9 +41,95 @@ const mapComponentStyles = Object.freeze({
}),
});

function useUpdateLeafletMapImperatively(resetButtonClickCount) {
function useUpdateLeafletMapImperatively(
resetButtonClickCount,
{ oarID, data, error, fetching },
) {
const mapRef = useRef(null);

// Set the map view on a facility location if the user has arrived
// directly from a URL containing a valid OAR ID
const [
shouldSetViewOnReceivingData,
setShouldSetViewOnReceivingData,
] = useState(!!oarID);

useEffect(() => {
if (shouldSetViewOnReceivingData) {
if (data) {
const leafletMap = get(mapRef, 'current.leafletElement', null);

const facilityLocation = get(
data,
'geometry.coordinates',
null,
);

if (leafletMap && facilityLocation) {
leafletMap.setView(
{
lng: head(facilityLocation),
lat: last(facilityLocation),
},
detailsZoomLevel,
);
}

setShouldSetViewOnReceivingData(false);
} else if (error) {
setShouldSetViewOnReceivingData(false);
}
}
}, [
shouldSetViewOnReceivingData,
setShouldSetViewOnReceivingData,
data,
error,
]);

// Set the map view on the facility location if it is not within the
// current viewport bbox
const [appIsGettingFacilityData, setAppIsGettingFacilityData] = useState(fetching);

useEffect(() => {
if (shouldSetViewOnReceivingData) {
noop();
} else if (fetching && !appIsGettingFacilityData) {
setAppIsGettingFacilityData(true);
} else if (!fetching && appIsGettingFacilityData && data) {
const leafletMap = get(mapRef, 'current.leafletElement', null);
const facilityLocation = get(data, 'geometry.coordinates', null);

delay(
() => {
if (leafletMap && facilityLocation) {
const facilityLatLng = {
lng: head(facilityLocation),
lat: last(facilityLocation),
};

const mapBoundsContainsFacility = leafletMap
.getBounds()
.contains(facilityLatLng);

if (!mapBoundsContainsFacility) {
leafletMap.setView(facilityLatLng);
}
}

setAppIsGettingFacilityData(false);
},
0,
);
}
}, [
fetching,
appIsGettingFacilityData,
setAppIsGettingFacilityData,
data,
shouldSetViewOnReceivingData,
]);

// Reset the map state when the reset button is clicked
const [
currentResetButtonClickCount,
Expand Down Expand Up @@ -71,8 +163,16 @@ function VectorTileFacilitiesMap({
match: {
params: { oarID },
},
facilityDetailsData,
errorFetchingFacilityDetailsData,
fetchingDetailsData,
}) {
const mapRef = useUpdateLeafletMapImperatively(resetButtonClickCount);
const mapRef = useUpdateLeafletMapImperatively(resetButtonClickCount, {
oarID,
data: facilityDetailsData,
fetching: fetchingDetailsData,
error: errorFetchingFacilityDetailsData,
});

if (!clientInfoFetched) {
return null;
Expand Down Expand Up @@ -122,6 +222,11 @@ function VectorTileFacilitiesMap({
);
}

VectorTileFacilitiesMap.defaultProps = {
facilityDetailsData: null,
errorFetchingFacilityDetailsData: null,
};

VectorTileFacilitiesMap.propTypes = {
resetButtonClickCount: number.isRequired,
clientInfoFetched: bool.isRequired,
Expand All @@ -132,18 +237,27 @@ VectorTileFacilitiesMap.propTypes = {
oarID: string,
}),
}).isRequired,
facilityDetailsData: facilityDetailsPropType,
errorFetchingFacilityDetailsData: arrayOf(string),
fetchingDetailsData: bool.isRequired,
};

function mapStateToProps({
ui: {
facilitiesSidebarTabSearch: { resetButtonClickCount },
},
clientInfo: { fetched, countryCode },
facilities: {
singleFacility: { data, error, fetching },
},
}) {
return {
resetButtonClickCount,
clientInfoFetched: fetched,
countryCode: countryCode || COUNTRY_CODES.default,
facilityDetailsData: data,
errorFetchingFacilityDetailsData: error,
fetchingDetailsData: fetching,
};
}

Expand Down

0 comments on commit 50f4092

Please sign in to comment.