diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index d9e4ef2c0f6e..c2ee21eaa0cb 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -111,6 +111,9 @@ const propTypes = { /** Information about the network */ network: networkPropTypes.isRequired, + /** Location bias for querying search results. */ + locationBias: PropTypes.string, + ...withLocalizePropTypes, }; @@ -138,6 +141,7 @@ const defaultProps = { maxInputLength: undefined, predefinedPlaces: [], resultTypes: 'address', + locationBias: undefined, }; function AddressSearch({ @@ -162,6 +166,7 @@ function AddressSearch({ shouldSaveDraft, translate, value, + locationBias, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -179,11 +184,11 @@ function AddressSearch({ language: preferredLocale, types: resultTypes, components: isLimitedToUSA ? 'country:us' : undefined, + ...(locationBias && {locationbias: locationBias}), }), - [preferredLocale, resultTypes, isLimitedToUSA], + [preferredLocale, resultTypes, isLimitedToUSA, locationBias], ); const shouldShowCurrentLocationButton = canUseCurrentLocation && searchValue.trim().length === 0 && isFocused; - const saveLocationDetails = (autocompleteData, details) => { const addressComponents = details.address_components; if (!addressComponents) { diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.js b/src/pages/iou/request/step/IOURequestStepWaypoint.js index dc5b9f7d6275..3c361acd3233 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.js +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.js @@ -1,5 +1,6 @@ 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'; @@ -38,6 +39,15 @@ const propTypes = { /** The optimistic transaction for this request */ transaction: transactionPropTypes, + /* Current location coordinates of the user */ + userLocation: PropTypes.shape({ + /** Latitude of the location */ + latitude: PropTypes.number, + + /** Longitude of the location */ + longitude: PropTypes.number, + }), + /** Recent waypoints that the user has selected */ recentWaypoints: PropTypes.arrayOf( PropTypes.shape({ @@ -65,6 +75,7 @@ const propTypes = { const defaultProps = { recentWaypoints: [], transaction: {}, + userLocation: undefined, }; function IOURequestStepWaypoint({ @@ -73,6 +84,7 @@ function IOURequestStepWaypoint({ params: {iouType, pageIndex, reportID, transactionID}, }, transaction, + userLocation, }) { const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); @@ -100,6 +112,58 @@ 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 waypointAddress = lodashGet(currentWaypoint, 'address', ''); // Hide the menu when there is only start and finish waypoint const shouldShowThreeDotsButton = waypointCount > 2; @@ -219,6 +283,7 @@ function IOURequestStepWaypoint({ (textInput.current = e)} @@ -257,6 +322,9 @@ export default compose( withWritableReportOrNotFound, withFullTransactionOrNotFound, withOnyx({ + userLocation: { + key: ONYXKEYS.USER_LOCATION, + }, recentWaypoints: { key: ONYXKEYS.NVP_RECENT_WAYPOINTS,