Skip to content

Commit

Permalink
Merge pull request #28710 from Expensify/youssef_startSplitBill
Browse files Browse the repository at this point in the history
Add `Scan` to `Split Bill` and implement IOU action startSplitBill
  • Loading branch information
youssef-lr authored Oct 10, 2023
2 parents 14ef765 + 993f0ce commit aff4623
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 45 deletions.
75 changes: 50 additions & 25 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ const propTypes = {
/** File path of the receipt */
receiptPath: PropTypes.string,

/** File source of the receipt */
receiptSource: PropTypes.string,
/** File name of the receipt */
receiptFilename: PropTypes.string,

/** List styles for OptionsSelector */
listStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
Expand All @@ -141,6 +141,9 @@ const propTypes = {
/** Whether the money request is a distance request */
isDistanceRequest: PropTypes.bool,

/** Whether the receipt associated with this report is being scanned */
isScanning: PropTypes.bool,

/** A flag for verifying that the current report is a sub-report of a workspace chat */
isPolicyExpenseChat: PropTypes.bool,

Expand Down Expand Up @@ -171,14 +174,15 @@ const defaultProps = {
reportID: '',
...withCurrentUserPersonalDetailsDefaultProps,
receiptPath: '',
receiptSource: '',
receiptFilename: '',
listStyles: [],
policyCategories: {},
policyTags: {},
transactionID: '',
transaction: {},
mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'},
isDistanceRequest: false,
isScanning: false,
isPolicyExpenseChat: false,
};

Expand All @@ -188,9 +192,6 @@ function MoneyRequestConfirmationList(props) {
const {onSendMoney, onConfirm, onSelectParticipant, transaction} = props;
const {translate, toLocaleDigit} = useLocalize();

// A flag and a toggler for showing the rest of the form fields
const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false);
const shouldShowAllFields = props.isDistanceRequest || shouldExpandFields;
const isTypeRequest = props.iouType === CONST.IOU.MONEY_REQUEST_TYPE.REQUEST;

const {unit, rate, currency} = props.mileageRate;
Expand All @@ -201,6 +202,14 @@ function MoneyRequestConfirmationList(props) {
const shouldShowCategories =
props.isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && (props.iouCategory || OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories)));

// A flag for showing SmartScan fields: date, merchant, and amount, only when we don't have a receiptPath (e.g. manual request)
// or in the split details page which is ReadOnly
const shouldShowSmartScanFields = (!props.receiptPath || props.isReadOnly) && !props.isScanning;

// A flag and a toggler for showing the rest of the form fields
const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false);
const shouldShowAllFields = props.isDistanceRequest || shouldExpandFields || !shouldShowSmartScanFields;

// Fetches the first tag list of the policy
const policyTag = PolicyUtils.getTag(props.policyTags);
const policyTagList = lodashGet(policyTag, 'tags', {});
Expand Down Expand Up @@ -245,7 +254,10 @@ function MoneyRequestConfirmationList(props) {
const getParticipantsWithAmount = useCallback(
(participantsList) => {
const iouAmount = IOUUtils.calculateAmount(participantsList.length, props.iouAmount, props.iouCurrencyCode);
return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode));
return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(
participantsList,
props.iouAmount > 0 ? CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode) : '',
);
},
[props.iouAmount, props.iouCurrencyCode],
);
Expand All @@ -254,7 +266,9 @@ function MoneyRequestConfirmationList(props) {

const splitOrRequestOptions = useMemo(() => {
let text;
if (props.receiptPath || isDistanceRequestWithoutRoute) {
if (props.receiptPath && props.hasMultipleParticipants && props.iouAmount === 0) {
text = translate('iou.split');
} else if (props.receiptPath || isDistanceRequestWithoutRoute) {
text = translate('iou.request');
} else {
const translationKey = props.hasMultipleParticipants ? 'iou.splitAmount' : 'iou.requestAmount';
Expand All @@ -266,7 +280,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, isDistanceRequestWithoutRoute]);
}, [props.hasMultipleParticipants, props.iouAmount, 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 All @@ -290,7 +304,7 @@ function MoneyRequestConfirmationList(props) {
const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, props.iouAmount, props.iouCurrencyCode, true);
const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(
payeePersonalDetails,
CurrencyUtils.convertToDisplayString(myIOUAmount, props.iouCurrencyCode),
props.iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, props.iouCurrencyCode) : '',
);

sections.push(
Expand Down Expand Up @@ -459,6 +473,9 @@ function MoneyRequestConfirmationList(props) {
);
}, [confirm, props.bankAccountRoute, props.iouCurrencyCode, props.iouType, props.isReadOnly, props.policyID, selectedParticipants, splitOrRequestOptions, translate, formError]);

const {image: receiptImage, thumbnail: receiptThumbnail} =
props.receiptPath && props.receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptFilename) : {};

return (
<OptionsSelector
sections={optionSelectorSections}
Expand All @@ -483,12 +500,17 @@ function MoneyRequestConfirmationList(props) {
<ConfirmedRoute transactionID={props.transactionID} />
</View>
)}
{!_.isEmpty(props.receiptPath) ? (
{(receiptImage || receiptThumbnail) && (
<Image
style={styles.moneyRequestImage}
source={{uri: ReceiptUtils.getThumbnailAndImageURIs(props.receiptPath, props.receiptSource).image}}
source={{uri: receiptThumbnail || receiptImage}}
// AuthToken is required when retrieving the image from the server
// but we don't need it to load the blob:// or file:// image when starting a money request / split bill
// So if we have a thumbnail, it means we're retrieving the image from the server
isAuthTokenRequired={!_.isEmpty(receiptThumbnail)}
/>
) : (
)}
{shouldShowSmartScanFields && (
<MenuItemWithTopDescription
shouldShowRightIcon={!props.isReadOnly && !props.isDistanceRequest}
title={formattedAmount}
Expand Down Expand Up @@ -527,16 +549,18 @@ function MoneyRequestConfirmationList(props) {
)}
{shouldShowAllFields && (
<>
<MenuItemWithTopDescription
shouldShowRightIcon={!props.isReadOnly && isTypeRequest}
title={props.iouCreated || format(new Date(), CONST.DATE.FNS_FORMAT_STRING)}
description={translate('common.date')}
style={[styles.moneyRequestMenuItem]}
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(props.iouType, props.reportID))}
disabled={didConfirm || props.isReadOnly || !isTypeRequest}
/>
{props.isDistanceRequest ? (
{shouldShowSmartScanFields && (
<MenuItemWithTopDescription
shouldShowRightIcon={!props.isReadOnly && isTypeRequest}
title={props.iouCreated || format(new Date(), CONST.DATE.FNS_FORMAT_STRING)}
description={translate('common.date')}
style={[styles.moneyRequestMenuItem]}
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_DATE.getRoute(props.iouType, props.reportID))}
disabled={didConfirm || props.isReadOnly}
/>
)}
{props.isDistanceRequest && (
<MenuItemWithTopDescription
shouldShowRightIcon={!props.isReadOnly && isTypeRequest}
title={props.iouMerchant}
Expand All @@ -546,15 +570,16 @@ function MoneyRequestConfirmationList(props) {
onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_DISTANCE.getRoute(props.iouType, props.reportID))}
disabled={didConfirm || props.isReadOnly || !isTypeRequest}
/>
) : (
)}
{shouldShowSmartScanFields && (
<MenuItemWithTopDescription
shouldShowRightIcon={!props.isReadOnly && isTypeRequest}
title={props.iouMerchant}
description={translate('common.merchant')}
style={[styles.moneyRequestMenuItem]}
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_MERCHANT.getRoute(props.iouType, props.reportID))}
disabled={didConfirm || props.isReadOnly || !isTypeRequest}
disabled={didConfirm || props.isReadOnly}
/>
)}
{shouldShowCategories && (
Expand Down
2 changes: 1 addition & 1 deletion src/components/MoneyRequestHeaderStatusBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function MoneyRequestHeaderStatusBar() {
const {translate} = useLocalize();

return (
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.flexGrow1, styles.overflowHidden, styles.ph5, styles.pb3, styles.borderBottom, styles.w100]}>
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.overflowHidden, styles.ph5, styles.pb3, styles.borderBottom, styles.w100]}>
<View style={[styles.moneyRequestHeaderStatusBarBadge]}>
<Text style={[styles.textStrong, styles.textLabel]}>{translate('iou.receiptStatusTitle')}</Text>
</View>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ReportActionItem/MoneyRequestPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ function MoneyRequestPreview(props) {
)}
{shouldShowDescription && <Text style={[styles.colorMuted]}>{description}</Text>}
</View>
{props.isBillSplit && !_.isEmpty(participantAccountIDs) && (
{props.isBillSplit && !_.isEmpty(participantAccountIDs) && requestAmount > 0 && (
<Text style={[styles.textLabel, styles.colorMuted, styles.ml1, styles.amountSplitPadding]}>
{props.translate('iou.amountEach', {
amount: CurrencyUtils.convertToDisplayString(
Expand Down
Loading

0 comments on commit aff4623

Please sign in to comment.