Skip to content

Commit

Permalink
Merge pull request #26836 from paultsimura/fix-26537
Browse files Browse the repository at this point in the history
fix: BUG: [distance] request confirmation offline doesn't show TBD
  • Loading branch information
neil-marcellini authored Sep 18, 2023
2 parents 2905e6d + 38014f7 commit e23c181
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 44 deletions.
17 changes: 4 additions & 13 deletions src/components/ConfirmedRoute.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, {useEffect} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';

import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
import lodashIsNil from 'lodash/isNil';
import _ from 'underscore';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
Expand All @@ -13,9 +13,8 @@ import * as Expensicons from './Icon/Expensicons';
import theme from '../styles/themes/default';
import styles from '../styles/styles';
import transactionPropTypes from './transactionPropTypes';
import BlockingView from './BlockingViews/BlockingView';
import PendingMapView from './MapView/PendingMapView';
import useNetwork from '../hooks/useNetwork';
import useLocalize from '../hooks/useLocalize';
import DistanceMapView from './DistanceMapView';

const propTypes = {
Expand Down Expand Up @@ -44,7 +43,7 @@ const getWaypointMarkers = (waypoints) => {
const lastWaypointIndex = numberOfWaypoints - 1;
return _.filter(
_.map(waypoints, (waypoint, key) => {
if (!waypoint || waypoint.lng === undefined || waypoint.lat === undefined) {
if (!waypoint || lodashIsNil(waypoint.lat) || lodashIsNil(waypoint.lng)) {
return;
}

Expand Down Expand Up @@ -76,7 +75,6 @@ const getWaypointMarkers = (waypoints) => {

function ConfirmedRoute({mapboxAccessToken, transaction}) {
const {isOffline} = useNetwork();
const {translate} = useLocalize();
const {route0: route} = transaction.routes || {};
const waypoints = lodashGet(transaction, 'comment.waypoints', {});
const coordinates = lodashGet(route, 'geometry.coordinates', []);
Expand Down Expand Up @@ -104,14 +102,7 @@ function ConfirmedRoute({mapboxAccessToken, transaction}) {
styleURL={CONST.MAPBOX.STYLE_URL}
/>
) : (
<View style={[styles.mapPendingView]}>
<BlockingView
icon={Expensicons.EmptyStateRoutePending}
title={translate('distance.mapPending.title')}
subtitle={isOffline ? translate('distance.mapPending.subtitle') : translate('distance.mapPending.onlineSubtitle')}
shouldShowLink={false}
/>
</View>
<PendingMapView />
)}
</>
);
Expand Down
24 changes: 10 additions & 14 deletions src/components/DistanceRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React, {useEffect, useMemo, useState, useRef} from 'react';
import {ScrollView, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
import lodashHas from 'lodash/has';
import lodashIsNull from 'lodash/isNull';
import lodashIsNil from 'lodash/isNil';
import PropTypes from 'prop-types';
import _ from 'underscore';

Expand Down Expand Up @@ -32,7 +31,7 @@ import Button from './Button';
import DistanceMapView from './DistanceMapView';
import LinearGradient from './LinearGradient';
import * as Expensicons from './Icon/Expensicons';
import BlockingView from './BlockingViews/BlockingView';
import PendingMapView from './MapView/PendingMapView';
import DotIndicatorMessage from './DotIndicatorMessage';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
import {iouPropTypes} from '../pages/iou/propTypes';
Expand Down Expand Up @@ -109,16 +108,17 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
const lastWaypointIndex = numberOfWaypoints - 1;
const isLoadingRoute = lodashGet(transaction, 'comment.isLoading', false);
const hasRouteError = !!lodashGet(transaction, 'errorFields.route');
const doesRouteExist = lodashHas(transaction, 'routes.route0.geometry.coordinates');
const hasRoute = TransactionUtils.hasRoute(transaction);
const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints);
const previousValidatedWaypoints = usePrevious(validatedWaypoints);
const haveValidatedWaypointsChanged = !_.isEqual(previousValidatedWaypoints, validatedWaypoints);
const shouldFetchRoute = (!doesRouteExist || haveValidatedWaypointsChanged) && !isLoadingRoute && _.size(validatedWaypoints) > 1;
const isRouteAbsentWithoutErrors = !hasRoute && !hasRouteError;
const shouldFetchRoute = (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && _.size(validatedWaypoints) > 1;
const waypointMarkers = useMemo(
() =>
_.filter(
_.map(waypoints, (waypoint, key) => {
if (!waypoint || !lodashHas(waypoint, 'lat') || !lodashHas(waypoint, 'lng') || lodashIsNull(waypoint.lat) || lodashIsNull(waypoint.lng)) {
if (!waypoint || lodashIsNil(waypoint.lat) || lodashIsNil(waypoint.lng)) {
return;
}

Expand Down Expand Up @@ -284,14 +284,10 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
overlayStyle={styles.m4}
/>
) : (
<View style={[styles.mapPendingView]}>
<BlockingView
icon={Expensicons.EmptyStateRoutePending}
title={translate('distance.mapPending.title')}
subtitle={isOffline ? translate('distance.mapPending.subtitle') : translate('distance.mapPending.onlineSubtitle')}
shouldShowLink={false}
/>
</View>
<PendingMapView
title={translate('distance.mapPending.title')}
subtitle={isOffline ? translate('distance.mapPending.subtitle') : translate('distance.mapPending.onlineSubtitle')}
/>
)}
</View>
<Button
Expand Down
10 changes: 9 additions & 1 deletion src/components/MapView/MapViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ type DirectionProps = {
coordinates: Array<[number, number]>;
};

type PendingMapViewProps = {
/** Title message below the icon */
title?: string;

/** Subtitle message below the title */
subtitle?: string;
};

// Initial state of the map
type InitialState = {
// Coordinate on which to center the map
Expand Down Expand Up @@ -55,4 +63,4 @@ type MapViewHandle = {
fitBounds: (ne: [number, number], sw: [number, number], paddingConfig?: number | number[], animationDuration?: number) => void;
};

export type {DirectionStyle, WayPoint, MapViewProps, DirectionProps, MapViewHandle};
export type {DirectionStyle, WayPoint, MapViewProps, DirectionProps, PendingMapViewProps, MapViewHandle};
36 changes: 36 additions & 0 deletions src/components/MapView/PendingMapView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import {View} from 'react-native';
import _ from 'lodash';
import variables from '../../styles/variables';
import styles from '../../styles/styles';
import Icon from '../Icon';
import {PendingMapViewProps} from './MapViewTypes';
import BlockingView from '../BlockingViews/BlockingView';
import * as Expensicons from '../Icon/Expensicons';

function PendingMapView({title = '', subtitle = ''}: PendingMapViewProps) {
const hasTextContent = !_.isEmpty(title) || !_.isEmpty(subtitle);

return (
<View style={[styles.mapPendingView]}>
{hasTextContent ? (
<BlockingView
icon={Expensicons.EmptyStateRoutePending}
title={title}
subtitle={subtitle}
shouldShowLink={false}
/>
) : (
<View style={[styles.flex1, styles.alignItemsCenter, styles.justifyContentCenter, styles.ph10]}>
<Icon
src={Expensicons.EmptyStateRoutePending}
width={variables.iconSizeUltraLarge}
height={variables.iconSizeUltraLarge}
/>
</View>
)}
</View>
);
}

export default PendingMapView;
21 changes: 13 additions & 8 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import ConfirmedRoute from './ConfirmedRoute';
import transactionPropTypes from './transactionPropTypes';
import DistanceRequestUtils from '../libs/DistanceRequestUtils';
import * as IOU from '../libs/actions/IOU';
import * as TransactionUtils from '../libs/TransactionUtils';

const propTypes = {
/** Callback to inform parent modal of success */
Expand Down Expand Up @@ -201,10 +202,14 @@ function MoneyRequestConfirmationList(props) {
const tagListName = lodashGet(props.policyTags, [tagListKey, 'name'], '');
const canUseTags = Permissions.canUseTags(props.betas);

const formattedAmount = CurrencyUtils.convertToDisplayString(
shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount,
props.isDistanceRequest ? currency : props.iouCurrencyCode,
);
const hasRoute = TransactionUtils.hasRoute(transaction);
const isDistanceRequestWithoutRoute = props.isDistanceRequest && !hasRoute;
const formattedAmount = isDistanceRequestWithoutRoute
? translate('common.tbd')
: CurrencyUtils.convertToDisplayString(
shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount,
props.isDistanceRequest ? currency : props.iouCurrencyCode,
);

useEffect(() => {
if (!shouldCalculateDistanceAmount) {
Expand Down Expand Up @@ -232,7 +237,7 @@ function MoneyRequestConfirmationList(props) {

const splitOrRequestOptions = useMemo(() => {
let text;
if (props.receiptPath) {
if (props.receiptPath || isDistanceRequestWithoutRoute) {
text = translate('iou.request');
} else {
const translationKey = props.hasMultipleParticipants ? 'iou.splitAmount' : 'iou.requestAmount';
Expand All @@ -244,7 +249,7 @@ function MoneyRequestConfirmationList(props) {
value: props.hasMultipleParticipants ? CONST.IOU.MONEY_REQUEST_TYPE.SPLIT : CONST.IOU.MONEY_REQUEST_TYPE.REQUEST,
},
];
}, [props.hasMultipleParticipants, props.receiptPath, translate, formattedAmount]);
}, [props.hasMultipleParticipants, props.receiptPath, translate, formattedAmount, isDistanceRequestWithoutRoute]);

const selectedParticipants = useMemo(() => _.filter(props.selectedParticipants, (participant) => participant.selected), [props.selectedParticipants]);
const payeePersonalDetails = useMemo(() => props.payeePersonalDetails || props.currentUserPersonalDetails, [props.payeePersonalDetails, props.currentUserPersonalDetails]);
Expand Down Expand Up @@ -323,9 +328,9 @@ function MoneyRequestConfirmationList(props) {
if (!props.isDistanceRequest) {
return;
}
const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(distance, unit, rate, currency, translate);
const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate);
IOU.setMoneyRequestMerchant(distanceMerchant);
}, [distance, unit, rate, currency, translate, props.isDistanceRequest]);
}, [hasRoute, distance, unit, rate, currency, translate, props.isDistanceRequest]);

/**
* @param {Object} option
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export default {
kilometers: 'kilometers',
recent: 'Recent',
all: 'All',
tbd: 'TBD',
},
anonymousReportFooter: {
logoTagline: 'Join the discussion.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ export default {
kilometers: 'kilómetros',
recent: 'Reciente',
all: 'Todo',
tbd: 'Por determinar',
},
anonymousReportFooter: {
logoTagline: 'Únete a la discusión.',
Expand Down
25 changes: 19 additions & 6 deletions src/libs/DistanceRequestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,34 @@ function convertDistanceUnit(distanceInMeters, unit) {
*
* @param {Number} distanceInMeters Distance traveled
* @param {'mi' | 'km'} unit Unit that should be used to display the distance
* @returns {String} The distance in requested units, rounded to 2 decimals
*/
const getRoundedDistanceInUnits = (distanceInMeters, unit) => {
const convertedDistance = convertDistanceUnit(distanceInMeters, unit);
return convertedDistance.toFixed(2);
};

/**
*
* @param {boolean} hasRoute Whether the route exists for the distance request
* @param {Number} distanceInMeters Distance traveled
* @param {'mi' | 'km'} unit Unit that should be used to display the distance
* @param {Number} rate Expensable amount allowed per unit
* @param {String} currency The currency associated with the rate
* @param {Function} translate Translate function
* @returns {String} A string that describes the distance travled and the rate used for expense calculation
* @returns {String} A string that describes the distance traveled and the rate used for expense calculation
*/
const getDistanceMerchant = (distanceInMeters, unit, rate, currency, translate) => {
const convertedDistance = convertDistanceUnit(distanceInMeters, unit);
const getDistanceMerchant = (hasRoute, distanceInMeters, unit, rate, currency, translate) => {
const distanceInUnits = hasRoute ? getRoundedDistanceInUnits(distanceInMeters, unit) : translate('common.tbd');

const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers');
const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer');
const roundedDistance = convertedDistance.toFixed(2);
const unitString = roundedDistance === 1 ? singularDistanceUnit : distanceUnit;
const unitString = distanceInUnits === 1 ? singularDistanceUnit : distanceUnit;

const ratePerUnit = rate * 0.01;
const currencySymbol = CurrencyUtils.getCurrencySymbol(currency) || `${currency} `;

return `${roundedDistance} ${unitString} @ ${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`;
return `${distanceInUnits} ${unitString} @ ${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`;
};

/**
Expand Down
11 changes: 11 additions & 0 deletions src/libs/TransactionUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,16 @@ function hasMissingSmartscanFields(transaction) {
return hasReceipt(transaction) && !isDistanceRequest(transaction) && !isReceiptBeingScanned(transaction) && areRequiredFieldsEmpty(transaction);
}

/**
* Check if the transaction has a defined route
*
* @param {Object} transaction
* @returns {Boolean}
*/
function hasRoute(transaction) {
return !!lodashGet(transaction, 'routes.route0.geometry.coordinates');
}

/**
* Get the transactions related to a report preview with receipts
* Get the details linked to the IOU reportAction
Expand Down Expand Up @@ -382,6 +392,7 @@ export {
getLinkedTransaction,
getAllReportTransactions,
hasReceipt,
hasRoute,
isReceiptBeingScanned,
getValidWaypoints,
isDistanceRequest,
Expand Down
5 changes: 3 additions & 2 deletions src/pages/iou/steps/MoneyRequestConfirmPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import reportPropTypes from '../../reportPropTypes';
import personalDetailsPropType from '../../personalDetailsPropType';
import * as FileUtils from '../../../libs/fileDownload/FileUtils';
import * as Policy from '../../../libs/actions/Policy';
import useNetwork from '../../../hooks/useNetwork';
import useWindowDimensions from '../../../hooks/useWindowDimensions';
import * as StyleUtils from '../../../styles/StyleUtils';
import {iouPropTypes, iouDefaultProps} from '../propTypes';
Expand Down Expand Up @@ -59,6 +60,7 @@ const defaultProps = {
};

function MoneyRequestConfirmPage(props) {
const {isOffline} = useNetwork();
const {windowHeight} = useWindowDimensions();
const prevMoneyRequestId = useRef(props.iou.id);
const iouType = useRef(lodashGet(props.route, 'params.iouType', ''));
Expand All @@ -81,8 +83,7 @@ function MoneyRequestConfirmPage(props) {
if (typeof props.iou.billable !== 'boolean') {
IOU.setMoneyRequestBillable(lodashGet(props.policy, 'defaultBillable', false));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [isOffline, participants, props.iou.billable, props.policy]);

useEffect(() => {
// ID in Onyx could change by initiating a new request in a separate browser tab or completing a request
Expand Down
1 change: 1 addition & 0 deletions src/styles/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default {
iconSizeXLarge: 28,
iconSizeExtraLarge: 40,
iconSizeSuperLarge: 60,
iconSizeUltraLarge: 120,
emojiSize: 20,
emojiLineHeight: 28,
iouAmountTextSize: 40,
Expand Down

0 comments on commit e23c181

Please sign in to comment.