Skip to content

Commit

Permalink
Merge pull request #33887 from Expensify/aldo_enable-location-bias-on…
Browse files Browse the repository at this point in the history
…-edit

Move location bias calculation to hook and use it on edit
  • Loading branch information
aldo-expensify authored Jan 3, 2024
2 parents e6616e3 + 9985659 commit 1037c94
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 56 deletions.
52 changes: 52 additions & 0 deletions src/hooks/useLocationBias.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {useMemo} from 'react';

/**
* Construct the rectangular boundary based on user location and waypoints
*/
export default function useLocationBias(allWaypoints: Record<string, {lng?: number; lat?: number}>, userLocation?: {latitude: number; longitude: number}) {
return useMemo(() => {
const hasFilledWaypointCount = Object.values(allWaypoints).some((waypoint) => Object.keys(waypoint).length > 0);
// If there are no filled wayPoints and if user's current location cannot be retrieved,
// it is futile to arrive at a biased location. Let's return
if (!hasFilledWaypointCount && userLocation === undefined) {
return null;
}

// Gather the longitudes and latitudes from filled waypoints.
const longitudes: number[] = Object.values(allWaypoints).reduce((accum: number[], waypoint) => {
if (waypoint?.lng) {
accum.push(waypoint.lng);
}
return accum;
}, []);
const latitudes: number[] = Object.values(allWaypoints).reduce((accum: number[], waypoint) => {
if (waypoint?.lat) {
accum.push(waypoint.lat);
}
return accum;
}, []);

// When no filled waypoints are available but the current location of the user is available,
// let us consider the current user's location to construct a rectangular bound
if (!hasFilledWaypointCount && userLocation !== undefined) {
longitudes.push(userLocation.longitude);
latitudes.push(userLocation.latitude);
}

// Extend the rectangular bound by 0.5 degree (roughly around 25-30 miles in US)
const minLat = Math.min(...latitudes) - 0.5;
const minLng = Math.min(...longitudes) - 0.5;
const maxLat = Math.max(...latitudes) + 0.5;
const maxLng = Math.max(...longitudes) + 0.5;

// Ensuring coordinates do not go out of range.
const south = minLat > -90 ? minLat : -90;
const west = minLng > -180 ? minLng : -180;
const north = maxLat < 90 ? maxLat : 90;
const east = maxLng < 180 ? maxLng : 180;

// Format: rectangle:south,west|north,east
const rectFormat = `rectangle:${south},${west}|${north},${east}`;
return rectFormat;
}, [userLocation, allWaypoints]);
}
19 changes: 17 additions & 2 deletions src/pages/iou/WaypointEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
import ScreenWrapper from '@components/ScreenWrapper';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import useLocationBias from '@hooks/useLocationBias';
import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
Expand Down Expand Up @@ -43,6 +44,15 @@ const propTypes = {
}),
}),

/* Current location coordinates of the user */
userLocation: PropTypes.shape({
/** Latitude of the location */
latitude: PropTypes.number,

/** Longitude of the location */
longitude: PropTypes.number,
}),

recentWaypoints: PropTypes.arrayOf(
PropTypes.shape({
/** The name of the location */
Expand Down Expand Up @@ -74,9 +84,10 @@ const defaultProps = {
route: {},
recentWaypoints: [],
transaction: {},
userLocation: undefined,
};

function WaypointEditor({route: {params: {iouType = '', transactionID = '', waypointIndex = '', threadReportID = 0}} = {}, transaction, recentWaypoints}) {
function WaypointEditor({route: {params: {iouType = '', transactionID = '', waypointIndex = '', threadReportID = 0}} = {}, transaction, recentWaypoints, userLocation}) {
const styles = useThemeStyles();
const {windowWidth} = useWindowDimensions();
const [isDeleteStopModalOpen, setIsDeleteStopModalOpen] = useState(false);
Expand All @@ -91,7 +102,7 @@ function WaypointEditor({route: {params: {iouType = '', transactionID = '', wayp

const waypointCount = _.size(allWaypoints);
const filledWaypointCount = _.size(_.filter(allWaypoints, (waypoint) => !_.isEmpty(waypoint)));

const locationBias = useLocationBias(allWaypoints, userLocation);
const waypointDescriptionKey = useMemo(() => {
switch (parsedWaypointIndex) {
case 0:
Expand Down Expand Up @@ -221,6 +232,7 @@ function WaypointEditor({route: {params: {iouType = '', transactionID = '', wayp
>
<InputWrapper
InputComponent={AddressSearch}
locationBias={locationBias}
canUseCurrentLocation
inputID={`waypoint${waypointIndex}`}
ref={(e) => (textInput.current = e)}
Expand Down Expand Up @@ -254,6 +266,9 @@ WaypointEditor.displayName = 'WaypointEditor';
WaypointEditor.propTypes = propTypes;
WaypointEditor.defaultProps = defaultProps;
export default withOnyx({
userLocation: {
key: ONYXKEYS.USER_LOCATION,
},
transaction: {
key: ({route}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(route, 'params.transactionID')}`,
},
Expand Down
56 changes: 2 additions & 54 deletions src/pages/iou/request/step/IOURequestStepWaypoint.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {useNavigation} from '@react-navigation/native';
import lodashGet from 'lodash/get';
import lodashIsNil from 'lodash/isNil';
import PropTypes from 'prop-types';
import React, {useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
Expand All @@ -16,6 +15,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
import ScreenWrapper from '@components/ScreenWrapper';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import useLocationBias from '@hooks/useLocationBias';
import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
Expand Down Expand Up @@ -97,7 +97,6 @@ function IOURequestStepWaypoint({
const parsedWaypointIndex = parseInt(pageIndex, 10);
const allWaypoints = lodashGet(transaction, 'comment.waypoints', {});
const currentWaypoint = lodashGet(allWaypoints, `waypoint${pageIndex}`, {});

const waypointCount = _.size(allWaypoints);
const filledWaypointCount = _.size(_.filter(allWaypoints, (waypoint) => !_.isEmpty(waypoint)));

Expand All @@ -112,58 +111,7 @@ function IOURequestStepWaypoint({
}
}, [parsedWaypointIndex, waypointCount]);

// Construct the rectangular boundary based on user location and waypoints
const locationBias = useMemo(() => {
// If there are no filled wayPoints and if user's current location cannot be retrieved,
// it is futile to arrive at a biased location. Let's return
if (filledWaypointCount === 0 && _.isEmpty(userLocation)) {
return null;
}

// Gather the longitudes and latitudes from filled waypoints.
const longitudes = _.filter(
_.map(allWaypoints, (waypoint) => {
if (!waypoint || lodashIsNil(waypoint.lng)) {
return;
}
return waypoint.lng;
}),
(lng) => lng,
);
const latitudes = _.filter(
_.map(allWaypoints, (waypoint) => {
if (!waypoint || lodashIsNil(waypoint.lat)) {
return;
}
return waypoint.lat;
}),
(lat) => lat,
);

// When no filled waypoints are available but the current location of the user is available,
// let us consider the current user's location to construct a rectangular bound
if (filledWaypointCount === 0 && !_.isEmpty(userLocation)) {
longitudes.push(userLocation.longitude);
latitudes.push(userLocation.latitude);
}

// Extend the rectangular bound by 0.5 degree (roughly around 25-30 miles in US)
const minLat = Math.min(...latitudes) - 0.5;
const minLng = Math.min(...longitudes) - 0.5;
const maxLat = Math.max(...latitudes) + 0.5;
const maxLng = Math.max(...longitudes) + 0.5;

// Ensuring coordinates do not go out of range.
const south = minLat > -90 ? minLat : -90;
const west = minLng > -180 ? minLng : -180;
const north = maxLat < 90 ? maxLat : 90;
const east = maxLng < 180 ? maxLng : 180;

// Format: rectangle:south,west|north,east
const rectFormat = `rectangle:${south},${west}|${north},${east}`;
return rectFormat;
}, [userLocation, filledWaypointCount, allWaypoints]);

const locationBias = useLocationBias(allWaypoints, userLocation);
const waypointAddress = lodashGet(currentWaypoint, 'address', '');
// Hide the menu when there is only start and finish waypoint
const shouldShowThreeDotsButton = waypointCount > 2;
Expand Down

0 comments on commit 1037c94

Please sign in to comment.