Skip to content

Commit

Permalink
feat: improved permission handling for MapFormField (#1228)
Browse files Browse the repository at this point in the history
  • Loading branch information
PeterBaker0 authored Nov 11, 2024
2 parents 51e005a + fdabfee commit 79d1b43
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 47 deletions.
97 changes: 58 additions & 39 deletions app/src/gui/fields/maps/MapFormField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* Implement MapFormField for entry of data via maps in FAIMS
*/

import React, {useEffect, useState} from 'react';
import React, {useEffect, useRef, useState} from 'react';
import './MapFormField.css';
import MapWrapper from './MapWrapper';

Expand All @@ -29,6 +29,12 @@ import {FieldProps} from 'formik';
import {Alert} from '@mui/material';
import {Capacitor} from '@capacitor/core';
import {APP_NAME} from '../../../buildconfig';
import {useNotification} from '../../../context/popup';

// If no center is available - pass this through
// Sydney CBD
const FALLBACK_CENTER = [151.2093, -33.8688];

export interface MapFieldProps extends FieldProps {
label?: string;
featureType: 'Point' | 'Polygon' | 'LineString';
Expand All @@ -44,52 +50,66 @@ export function MapFormField({
form,
...props
}: MapFieldProps): JSX.Element {
// get previous form state if available
let initialFeatures = {};
if (form.values[field.name]) {
initialFeatures = form.values[field.name];
}
// State

// center location of map - use provided center if any
const [center, setCenter] = useState<number[] | undefined>(props.center);

// and a ref to track if gps location has already been requested
const gpsCenterRequested = useRef<boolean>(false);

// flag set if we find we don't have location permission
const [noPermission, setNoPermission] = useState(false);

const [drawnFeatures, setDrawnFeatures] =
useState<GeoJSONFeatureCollection>(initialFeatures);

// default props.center if not defined
if (!props.center) {
props.center = [0, 0];
}
const [center, setCenter] = useState(props.center);
// Use form value as default field features - otherwise empty {}
const [drawnFeatures, setDrawnFeatures] = useState<GeoJSONFeatureCollection>(
form.values[field.name] ?? {}
);

if (!props.zoom) {
props.zoom = 14;
}
// Default zoom level
const zoom = props.zoom ?? 14;

// default to point if not specified
if (!props.featureType) {
props.featureType = 'Point';
}
const featureType = props.featureType ?? 'Point';

if (!props.label) {
props.label = `Get ${props.featureType}`;
}
// default label
const label = props.label ?? `Get ${props.featureType}`;

const mapCallback = (theFeatures: GeoJSONFeatureCollection) => {
setDrawnFeatures(theFeatures);
form.setFieldValue(field.name, theFeatures, true);
};

// get the current GPS location if don't know the map center
if (center[0] === 0 && center[1] === 0) {
Geolocation.getCurrentPosition()
.then(result => {
setCenter([result.coords.longitude, result.coords.latitude]);
})
.catch(() => {
setNoPermission(true);
});
}
// notification manager
const notify = useNotification();

useEffect(() => {
const getCoords = async () => {
// Always get current position on component mount - this forces a permission
// request
if (!gpsCenterRequested.current) {
// Mark that we've requested already
gpsCenterRequested.current = true;
Geolocation.getCurrentPosition()
.then(result => {
// Only store the center result if we actually need it
if (center === undefined) {
setCenter([result.coords.longitude, result.coords.latitude]);
}
// Since we use a ref to track this running, we can safely run a state
// update here without infinite loop
setNoPermission(false);
})
.catch(() => {
notify.showWarning(
'We were unable to access your current location. Map fields may not work as expected.'
);
setNoPermission(true);
});
}
};
getCoords();
}, []);

let valueText = '';
if (drawnFeatures.features && drawnFeatures.features.length > 0) {
Expand All @@ -114,13 +134,12 @@ export function MapFormField({
<div>
<div>
<MapWrapper
label={
props.label ? props.label : 'Get ' + props.featureType + ' from Map'
}
featureType={props.featureType}
label={label}
featureType={featureType}
features={drawnFeatures}
zoom={props.zoom}
center={center}
zoom={zoom}
center={center ?? FALLBACK_CENTER}
fallbackCenter={center === undefined}
callbackFn={mapCallback}
geoTiff={props.geoTiff}
projection={props.projection}
Expand Down
19 changes: 11 additions & 8 deletions app/src/gui/fields/maps/MapWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ interface MapProps extends ButtonProps {
featureType: 'Point' | 'Polygon' | 'LineString';
zoom: number;
center: Array<number>;
fallbackCenter: boolean;
callbackFn: (features: object) => void;
setNoPermission: (flag: boolean) => void;
}
Expand All @@ -67,6 +68,7 @@ import {AppBar, Dialog, IconButton, Toolbar, Typography} from '@mui/material';
import Feature from 'ol/Feature';
import {Geometry} from 'ol/geom';
import {createCenterControl} from '../../components/map/center-control';
import {useNotification} from '../../../context/popup';

const styles = {
mapContainer: {
Expand All @@ -86,6 +88,9 @@ function MapWrapper(props: MapProps) {
const defaultMapProjection = 'EPSG:3857';
const geoJson = new GeoJSON();

// notifications
const notify = useNotification();

// create state ref that can be accessed in OpenLayers onclick callback function
// https://stackoverflow.com/a/60643670
const mapRef = useRef<Map | undefined>();
Expand Down Expand Up @@ -232,15 +237,13 @@ function MapWrapper(props: MapProps) {
};

const handleClickOpen = () => {
// only show the map if we have a center
if (props.center[0] !== 0 && props.center[1] !== 0) {
setMapOpen(true);
// reset this in case we set it before
props.setNoPermission(false);
} else {
props.setNoPermission(true);
if (props.fallbackCenter) {
notify.showWarning(
'Using default map location - unable to determine current location and no center location configured.'
);
}
// TODO: should do something to inform the user here...
// We always provide a center, so it's always safe to open the map
setMapOpen(true);
};

const refCallback = (element: HTMLElement | null) => {
Expand Down

0 comments on commit 79d1b43

Please sign in to comment.