diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d4d2ab1f90a6..2df7dd0b5a76 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -106,6 +106,9 @@ const ONYXKEYS = { /** The NVP with the last payment method used per policy */ NVP_LAST_PAYMENT_METHOD: 'nvp_lastPaymentMethod', + /** This NVP holds to most recent waypoints that a person has used when creating a distance request */ + NVP_RECENT_WAYPOINTS: 'expensify_recentWaypoints', + /** Does this user have push notifications enabled for this device? */ PUSH_NOTIFICATIONS_ENABLED: 'pushNotificationsEnabled', @@ -315,6 +318,7 @@ type OnyxValues = { [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string; [ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: Record; + [ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoints[]; [ONYXKEYS.PUSH_NOTIFICATIONS_ENABLED]: boolean; [ONYXKEYS.PLAID_DATA]: OnyxTypes.PlaidData; [ONYXKEYS.IS_PLAID_DISABLED]: boolean; diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index 204333474849..262f656fcee1 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -61,6 +61,26 @@ const propTypes = { /** Should address search be limited to results in the USA */ isLimitedToUSA: PropTypes.bool, + /** A list of predefined places that can be shown when the user isn't searching for something */ + predefinedPlaces: PropTypes.arrayOf( + PropTypes.shape({ + /** A description of the location (usually the address) */ + description: PropTypes.string, + + /** Data required by the google auto complete plugin to know where to put the markers on the map */ + geometry: PropTypes.shape({ + /** Data about the location */ + location: PropTypes.shape({ + /** Lattitude of the location */ + lat: PropTypes.number, + + /** Longitude of the location */ + lng: PropTypes.number, + }), + }), + }), + ), + /** A map of inputID key names */ renamedInputKeys: PropTypes.shape({ street: PropTypes.string, @@ -102,6 +122,7 @@ const defaultProps = { lng: 'addressLng', }, maxInputLength: undefined, + predefinedPlaces: [], }; // Do not convert to class component! It's been tried before and presents more challenges than it's worth. @@ -122,6 +143,16 @@ function AddressSearch(props) { const saveLocationDetails = (autocompleteData, details) => { const addressComponents = details.address_components; if (!addressComponents) { + // When there are details, but no address_components, this indicates that some predefined options have been passed + // to this component which don't match the usual properties coming from auto-complete. In that case, only a limited + // amount of data massaging needs to happen for what the parent expects to get from this function. + if (_.size(details)) { + props.onPress({ + address: lodashGet(details, 'description', ''), + lat: lodashGet(details, 'geometry.location.lat', 0), + lng: lodashGet(details, 'geometry.location.lng', 0), + }); + } return; } @@ -250,6 +281,7 @@ function AddressSearch(props) { fetchDetails suppressDefaultStyles enablePoweredByContainer={false} + predefinedPlaces={props.predefinedPlaces} ListEmptyComponent={ props.network.isOffline ? null : ( {props.translate('common.noResultsFound')} diff --git a/src/libs/actions/Transaction.js b/src/libs/actions/Transaction.js index 02927bf7d111..1dad0219db1a 100644 --- a/src/libs/actions/Transaction.js +++ b/src/libs/actions/Transaction.js @@ -5,6 +5,12 @@ import ONYXKEYS from '../../ONYXKEYS'; import * as CollectionUtils from '../CollectionUtils'; import * as API from '../API'; +let recentWaypoints = []; +Onyx.connect({ + key: ONYXKEYS.NVP_RECENT_WAYPOINTS, + callback: (val) => (recentWaypoints = val || []), +}); + const allTransactions = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, @@ -87,6 +93,12 @@ function saveWaypoint(transactionID, index, waypoint) { }, }, }); + const recentWaypointAlreadyExists = _.find(recentWaypoints, (recentWaypoint) => recentWaypoint.address === waypoint.address); + if (!recentWaypointAlreadyExists) { + const clonedWaypoints = _.clone(recentWaypoints); + clonedWaypoints.unshift(waypoint); + Onyx.merge(ONYXKEYS.NVP_RECENT_WAYPOINTS, clonedWaypoints.slice(0, 5)); + } } function removeWaypoint(transactionID, currentIndex) { diff --git a/src/pages/iou/WaypointEditor.js b/src/pages/iou/WaypointEditor.js index a3f8f40f5372..c0c621e353cb 100644 --- a/src/pages/iou/WaypointEditor.js +++ b/src/pages/iou/WaypointEditor.js @@ -1,4 +1,5 @@ import React, {useRef} from 'react'; +import _ from 'underscore'; import lodashGet from 'lodash/get'; import {View} from 'react-native'; import PropTypes from 'prop-types'; @@ -53,7 +54,7 @@ const defaultProps = { transaction: {}, }; -function WaypointEditor({transactionID, route: {params: {iouType = '', waypointIndex = ''} = {}} = {}, network, translate, transaction}) { +function WaypointEditor({transactionID, route: {params: {iouType = '', waypointIndex = ''} = {}} = {}, network, translate, transaction, recentWaypoints}) { const textInput = useRef(null); const currentWaypoint = lodashGet(transaction, `comment.waypoints.waypoint${waypointIndex}`, {}); const waypointAddress = lodashGet(currentWaypoint, 'address', ''); @@ -151,6 +152,7 @@ function WaypointEditor({transactionID, route: {params: {iouType = '', waypointI lng: null, state: null, }} + predefinedPlaces={recentWaypoints} /> @@ -169,5 +171,22 @@ export default compose( key: (props) => `${ONYXKEYS.COLLECTION.TRANSACTION}${props.transactionID}`, selector: (transaction) => (transaction ? {transactionID: transaction.transactionID, comment: {waypoints: lodashGet(transaction, 'comment.waypoints')}} : null), }, + + recentWaypoints: { + key: ONYXKEYS.NVP_RECENT_WAYPOINTS, + + // Only grab the most recent 5 waypoints because that's all that is shown in the UI. This also puts them into the format of data + // that the google autocomplete component expects for it's "predefined places" feature. + selector: (waypoints) => + _.map(waypoints ? waypoints.slice(0, 5) : [], (waypoint) => ({ + description: waypoint.address, + geometry: { + location: { + lat: waypoint.lat, + lng: waypoint.lng, + }, + }, + })), + }, }), )(WaypointEditor); diff --git a/src/types/onyx/RecentWaypoints.ts b/src/types/onyx/RecentWaypoints.ts new file mode 100644 index 000000000000..75780ef861e5 --- /dev/null +++ b/src/types/onyx/RecentWaypoints.ts @@ -0,0 +1,12 @@ +type RecentWaypoints = { + /** The full address of the waypoint */ + address: string; + + /** The lattitude of the waypoint */ + lat: number; + + /** The longitude of the waypoint */ + lng: number; +}; + +export default RecentWaypoints; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index e7ab360551c5..8d48eccf1464 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -32,7 +32,6 @@ import ReimbursementAccountDraft from './ReimbursementAccountDraft'; import WalletTransfer from './WalletTransfer'; import ReceiptModal from './ReceiptModal'; import MapboxAccessToken from './MapboxAccessToken'; - import Download from './Download'; import PolicyMember from './PolicyMember'; import Policy from './Policy'; @@ -41,8 +40,8 @@ import ReportAction from './ReportAction'; import ReportActionReactions from './ReportActionReactions'; import SecurityGroup from './SecurityGroup'; import Transaction from './Transaction'; - import Form, {AddDebitCardForm} from './Form'; +import RecentWaypoints from './RecentWaypoints'; export type { Account, @@ -89,4 +88,5 @@ export type { Transaction, Form, AddDebitCardForm, + RecentWaypoints, };