Skip to content

Commit 3141112

Browse files
Resolve conflicts
2 parents 3c175f0 + 3967320 commit 3141112

13 files changed

+204
-88
lines changed

src/components/MoneyReportHeader.tsx

+27-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import {View} from 'react-native';
33
import type {OnyxEntry} from 'react-native-onyx';
44
import {withOnyx} from 'react-native-onyx';
55
import useLocalize from '@hooks/useLocalize';
6+
import useTheme from '@hooks/useTheme';
67
import useThemeStyles from '@hooks/useThemeStyles';
78
import useWindowDimensions from '@hooks/useWindowDimensions';
89
import * as CurrencyUtils from '@libs/CurrencyUtils';
910
import * as HeaderUtils from '@libs/HeaderUtils';
1011
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
1112
import * as ReportUtils from '@libs/ReportUtils';
13+
import * as TransactionUtils from '@libs/TransactionUtils';
14+
import variables from '@styles/variables';
1215
import * as IOU from '@userActions/IOU';
1316
import CONST from '@src/CONST';
1417
import ONYXKEYS from '@src/ONYXKEYS';
@@ -19,8 +22,10 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
1922
import Button from './Button';
2023
import ConfirmModal from './ConfirmModal';
2124
import HeaderWithBackButton from './HeaderWithBackButton';
25+
import Icon from './Icon';
2226
import * as Expensicons from './Icon/Expensicons';
2327
import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar';
28+
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
2429
import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu';
2530
import SettlementButton from './SettlementButton';
2631

@@ -71,6 +76,7 @@ function MoneyReportHeader({
7176
onBackButtonPress,
7277
}: MoneyReportHeaderProps) {
7378
const styles = useThemeStyles();
79+
const theme = useTheme();
7480
const [isDeleteRequestModalVisible, setIsDeleteRequestModalVisible] = useState(false);
7581
const {translate} = useLocalize();
7682
const {windowWidth} = useWindowDimensions();
@@ -98,6 +104,9 @@ function MoneyReportHeader({
98104
const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport);
99105
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false);
100106

107+
const transactionIDs = TransactionUtils.getAllReportTransactions(moneyRequestReport?.reportID).map((transaction) => transaction.transactionID);
108+
const allHavePendingRTERViolation = TransactionUtils.allHavePendingRTERViolation(transactionIDs);
109+
101110
const cancelPayment = useCallback(() => {
102111
if (!chatReport) {
103112
return;
@@ -112,12 +121,12 @@ function MoneyReportHeader({
112121

113122
const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport);
114123

115-
const shouldShowSettlementButton = !ReportUtils.isInvoiceReport(moneyRequestReport) && (shouldShowPayButton || shouldShowApproveButton);
124+
const shouldShowSettlementButton = !ReportUtils.isInvoiceReport(moneyRequestReport) && (shouldShowPayButton || shouldShowApproveButton) && !allHavePendingRTERViolation;
116125

117-
const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0;
126+
const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0 && !allHavePendingRTERViolation;
118127
const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport);
119128
const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE;
120-
const shouldShowNextStep = !ReportUtils.isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length;
129+
const shouldShowNextStep = !ReportUtils.isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length && !allHavePendingRTERViolation;
121130
const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep;
122131
const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport);
123132
const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport.currency);
@@ -203,7 +212,7 @@ function MoneyReportHeader({
203212
shouldShowBackButton={shouldUseNarrowLayout}
204213
onBackButtonPress={onBackButtonPress}
205214
// Shows border if no buttons or next steps are showing below the header
206-
shouldShowBorderBottom={!(shouldShowAnyButton && shouldUseNarrowLayout) && !(shouldShowNextStep && !shouldUseNarrowLayout)}
215+
shouldShowBorderBottom={!(shouldShowAnyButton && shouldUseNarrowLayout) && !(shouldShowNextStep && !shouldUseNarrowLayout) && !allHavePendingRTERViolation}
207216
shouldShowThreeDotsButton
208217
threeDotsMenuItems={threeDotsMenuItems}
209218
threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(windowWidth)}
@@ -241,6 +250,20 @@ function MoneyReportHeader({
241250
</View>
242251
)}
243252
</HeaderWithBackButton>
253+
{allHavePendingRTERViolation && (
254+
<MoneyRequestHeaderStatusBar
255+
title={
256+
<Icon
257+
src={Expensicons.Hourglass}
258+
height={variables.iconSizeSmall}
259+
width={variables.iconSizeSmall}
260+
fill={theme.icon}
261+
/>
262+
}
263+
description={translate('iou.pendingMatchWithCreditCardDescription')}
264+
shouldShowBorderBottom
265+
/>
266+
)}
244267
<View style={isMoreContentShown ? [styles.dFlex, styles.flexColumn, styles.borderBottom] : []}>
245268
{shouldShowSettlementButton && shouldUseNarrowLayout && (
246269
<View style={[styles.ph5, styles.pb2]}>

src/components/MoneyRequestHeader.tsx

+50-39
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import type {ReactNode} from 'react';
12
import React, {useCallback, useEffect, useState} from 'react';
23
import {View} from 'react-native';
34
import {withOnyx} from 'react-native-onyx';
4-
import type {OnyxEntry} from 'react-native-onyx';
5+
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
56
import useLocalize from '@hooks/useLocalize';
67
import useTheme from '@hooks/useTheme';
78
import useThemeStyles from '@hooks/useThemeStyles';
@@ -16,12 +17,14 @@ import * as IOU from '@userActions/IOU';
1617
import CONST from '@src/CONST';
1718
import ONYXKEYS from '@src/ONYXKEYS';
1819
import ROUTES from '@src/ROUTES';
19-
import type {Policy, Report, ReportAction, ReportActions, Session, Transaction} from '@src/types/onyx';
20+
import type {Policy, Report, ReportAction, ReportActions, Session, Transaction, TransactionViolations} from '@src/types/onyx';
2021
import type {OriginalMessageIOU} from '@src/types/onyx/OriginalMessage';
22+
import type IconAsset from '@src/types/utils/IconAsset';
2123
import ConfirmModal from './ConfirmModal';
2224
import HeaderWithBackButton from './HeaderWithBackButton';
2325
import Icon from './Icon';
2426
import * as Expensicons from './Icon/Expensicons';
27+
import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar';
2528
import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar';
2629
import ProcessMoneyRequestHoldMenu from './ProcessMoneyRequestHoldMenu';
2730

@@ -35,6 +38,9 @@ type MoneyRequestHeaderOnyxProps = {
3538
/** All the data for the transaction */
3639
transaction: OnyxEntry<Transaction>;
3740

41+
/** The violations of the transaction */
42+
transactionViolations: OnyxCollection<TransactionViolations>;
43+
3844
/** All report actions */
3945
// eslint-disable-next-line react/no-unused-prop-types
4046
parentReportActions: OnyxEntry<ReportActions>;
@@ -65,6 +71,7 @@ function MoneyRequestHeader({
6571
parentReport,
6672
report,
6773
parentReportAction,
74+
transactionViolations,
6875
transaction,
6976
shownHoldUseExplanation = false,
7077
policy,
@@ -101,7 +108,6 @@ function MoneyRequestHeader({
101108
}, [parentReport?.reportID, parentReportAction, setIsDeleteModalVisible]);
102109

103110
const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
104-
const isPending = TransactionUtils.isExpensifyCardTransaction(transaction) && TransactionUtils.isPending(transaction);
105111

106112
const isDeletedParentAction = ReportActionsUtils.isDeletedAction(parentReportAction);
107113
const canHoldOrUnholdRequest = !isSettled && !isApproved && !isDeletedParentAction;
@@ -120,6 +126,33 @@ function MoneyRequestHeader({
120126
}
121127
};
122128

129+
const getStatusIcon: (src: IconAsset) => ReactNode = (src) => (
130+
<Icon
131+
src={src}
132+
height={variables.iconSizeSmall}
133+
width={variables.iconSizeSmall}
134+
fill={theme.icon}
135+
/>
136+
);
137+
138+
const getStatusBarProps: () => MoneyRequestHeaderStatusBarProps | undefined = () => {
139+
if (isOnHold) {
140+
return {title: translate('iou.hold'), description: translate('iou.expenseOnHold'), danger: true, shouldShowBorderBottom: true};
141+
}
142+
143+
if (TransactionUtils.isExpensifyCardTransaction(transaction) && TransactionUtils.isPending(transaction)) {
144+
return {title: getStatusIcon(Expensicons.CreditCardHourglass), description: translate('iou.transactionPendingDescription'), shouldShowBorderBottom: true};
145+
}
146+
if (isScanning) {
147+
return {title: getStatusIcon(Expensicons.ReceiptScan), description: translate('iou.receiptScanInProgressDescription'), shouldShowBorderBottom: true};
148+
}
149+
if (TransactionUtils.hasPendingRTERViolation(TransactionUtils.getTransactionViolations(transaction?.transactionID ?? '', transactionViolations))) {
150+
return {title: getStatusIcon(Expensicons.Hourglass), description: translate('iou.pendingMatchWithCreditCardDescription'), shouldShowBorderBottom: true};
151+
}
152+
};
153+
154+
const statusBarProps = getStatusBarProps();
155+
123156
useEffect(() => {
124157
if (canDeleteRequest) {
125158
return;
@@ -184,7 +217,7 @@ function MoneyRequestHeader({
184217
<>
185218
<View style={[styles.pl0]}>
186219
<HeaderWithBackButton
187-
shouldShowBorderBottom={!isScanning && !isPending && !isOnHold}
220+
shouldShowBorderBottom={!statusBarProps && !isOnHold}
188221
shouldShowReportAvatarWithDisplay
189222
shouldEnableDetailPageNavigation
190223
shouldShowPinButton={false}
@@ -199,40 +232,12 @@ function MoneyRequestHeader({
199232
shouldShowBackButton={shouldUseNarrowLayout}
200233
onBackButtonPress={onBackButtonPress}
201234
/>
202-
{isPending && (
203-
<MoneyRequestHeaderStatusBar
204-
title={
205-
<Icon
206-
src={Expensicons.CreditCardHourglass}
207-
height={variables.iconSizeSmall}
208-
width={variables.iconSizeSmall}
209-
fill={theme.icon}
210-
/>
211-
}
212-
description={translate('iou.transactionPendingDescription')}
213-
shouldShowBorderBottom={!isScanning}
214-
/>
215-
)}
216-
{isScanning && (
235+
{statusBarProps && (
217236
<MoneyRequestHeaderStatusBar
218-
title={
219-
<Icon
220-
src={Expensicons.ReceiptScan}
221-
height={variables.iconSizeSmall}
222-
width={variables.iconSizeSmall}
223-
fill={theme.icon}
224-
/>
225-
}
226-
description={translate('iou.receiptScanInProgressDescription')}
227-
shouldShowBorderBottom
228-
/>
229-
)}
230-
{isOnHold && (
231-
<MoneyRequestHeaderStatusBar
232-
title={translate('iou.hold')}
233-
description={translate('iou.expenseOnHold')}
234-
shouldShowBorderBottom
235-
danger
237+
title={statusBarProps.title}
238+
description={statusBarProps.description}
239+
danger={statusBarProps.danger}
240+
shouldShowBorderBottom={statusBarProps.shouldShowBorderBottom}
236241
/>
237242
)}
238243
</View>
@@ -259,7 +264,7 @@ function MoneyRequestHeader({
259264

260265
MoneyRequestHeader.displayName = 'MoneyRequestHeader';
261266

262-
const MoneyRequestHeaderWithTransaction = withOnyx<MoneyRequestHeaderProps, Pick<MoneyRequestHeaderOnyxProps, 'transaction' | 'shownHoldUseExplanation'>>({
267+
const MoneyRequestHeaderWithTransaction = withOnyx<MoneyRequestHeaderProps, Pick<MoneyRequestHeaderOnyxProps, 'transactionViolations' | 'transaction' | 'shownHoldUseExplanation'>>({
263268
transaction: {
264269
key: ({report, parentReportActions}) => {
265270
const parentReportAction = (report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : {}) as ReportAction & OriginalMessageIOU;
@@ -270,9 +275,15 @@ const MoneyRequestHeaderWithTransaction = withOnyx<MoneyRequestHeaderProps, Pick
270275
key: ONYXKEYS.NVP_HOLD_USE_EXPLAINED,
271276
initWithStoredValues: true,
272277
},
278+
transactionViolations: {
279+
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
280+
},
273281
})(MoneyRequestHeader);
274282

275-
export default withOnyx<Omit<MoneyRequestHeaderProps, 'transaction' | 'shownHoldUseExplanation'>, Omit<MoneyRequestHeaderOnyxProps, 'transaction' | 'shownHoldUseExplanation'>>({
283+
export default withOnyx<
284+
Omit<MoneyRequestHeaderProps, 'transactionViolations' | 'transaction' | 'shownHoldUseExplanation'>,
285+
Omit<MoneyRequestHeaderOnyxProps, 'transactionViolations' | 'transaction' | 'shownHoldUseExplanation'>
286+
>({
276287
session: {
277288
key: ONYXKEYS.SESSION,
278289
},

src/components/MoneyRequestHeaderStatusBar.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ function MoneyRequestHeaderStatusBar({title, description, shouldShowBorderBottom
5757
MoneyRequestHeaderStatusBar.displayName = 'MoneyRequestHeaderStatusBar';
5858

5959
export default MoneyRequestHeaderStatusBar;
60+
61+
export type {MoneyRequestHeaderStatusBarProps};

src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx

+20-17
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import CONST from '@src/CONST';
3636
import type {IOUMessage} from '@src/types/onyx/OriginalMessage';
3737
import type {EmptyObject} from '@src/types/utils/EmptyObject';
3838
import {isEmptyObject} from '@src/types/utils/EmptyObject';
39-
import type {MoneyRequestPreviewProps} from './types';
39+
import type {MoneyRequestPreviewProps, PendingMessageProps} from './types';
4040

4141
function MoneyRequestPreviewContent({
4242
iouReport,
@@ -84,7 +84,6 @@ function MoneyRequestPreviewContent({
8484
const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH});
8585
const hasReceipt = TransactionUtils.hasReceipt(transaction);
8686
const isScanning = hasReceipt && TransactionUtils.isReceiptBeingScanned(transaction);
87-
const isPending = TransactionUtils.isPending(transaction);
8887
const isOnHold = TransactionUtils.isOnHold(transaction);
8988
const isSettlementOrApprovalPartial = Boolean(iouReport?.pendingFields?.partial);
9089
const isPartialHold = isSettlementOrApprovalPartial && isOnHold;
@@ -184,6 +183,21 @@ function MoneyRequestPreviewContent({
184183
return message;
185184
};
186185

186+
const getPendingMessageProps: () => PendingMessageProps = () => {
187+
if (isScanning) {
188+
return {shouldShow: true, messageIcon: ReceiptScan, messageDescription: translate('iou.receiptScanInProgress')};
189+
}
190+
if (TransactionUtils.isPending(transaction)) {
191+
return {shouldShow: true, messageIcon: Expensicons.CreditCardHourglass, messageDescription: translate('iou.transactionPending')};
192+
}
193+
if (TransactionUtils.hasPendingUI(transaction, TransactionUtils.getTransactionViolations(transaction?.transactionID ?? '', transactionViolations))) {
194+
return {shouldShow: true, messageIcon: Expensicons.Hourglass, messageDescription: translate('iou.pendingMatchWithCreditCard')};
195+
}
196+
return {shouldShow: false};
197+
};
198+
199+
const pendingMessageProps = getPendingMessageProps();
200+
187201
const getDisplayAmountText = (): string => {
188202
if (isScanning) {
189203
return translate('iou.receiptScanning');
@@ -312,26 +326,15 @@ function MoneyRequestPreviewContent({
312326
</Text>
313327
)}
314328
</View>
315-
{isScanning && (
316-
<View style={[styles.flexRow, styles.alignItemsCenter, styles.mt2]}>
317-
<Icon
318-
src={ReceiptScan}
319-
height={variables.iconSizeExtraSmall}
320-
width={variables.iconSizeExtraSmall}
321-
fill={theme.textSupporting}
322-
/>
323-
<Text style={[styles.textMicroSupporting, styles.ml1, styles.amountSplitPadding]}>{translate('iou.receiptScanInProgress')}</Text>
324-
</View>
325-
)}
326-
{isPending && (
329+
{pendingMessageProps.shouldShow && (
327330
<View style={[styles.flexRow, styles.alignItemsCenter, styles.mt2]}>
328331
<Icon
329-
src={Expensicons.CreditCardHourglass}
332+
src={pendingMessageProps.messageIcon}
330333
height={variables.iconSizeExtraSmall}
331334
width={variables.iconSizeExtraSmall}
332-
fill={theme.textSupporting}
335+
fill={theme.icon}
333336
/>
334-
<Text style={[styles.textMicroSupporting, styles.ml1, styles.amountSplitPadding]}>{translate('iou.transactionPending')}</Text>
337+
<Text style={[styles.textMicroSupporting, styles.ml1, styles.amountSplitPadding]}>{pendingMessageProps.messageDescription}</Text>
335338
</View>
336339
)}
337340
</View>

src/components/ReportActionItem/MoneyRequestPreview/types.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native';
22
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
33
import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu';
44
import type * as OnyxTypes from '@src/types/onyx';
5+
import type IconAsset from '@src/types/utils/IconAsset';
56

67
type MoneyRequestPreviewOnyxProps = {
78
/** All of the personal details for everyone */
@@ -71,4 +72,19 @@ type MoneyRequestPreviewProps = MoneyRequestPreviewOnyxProps & {
7172
isWhisper?: boolean;
7273
};
7374

74-
export type {MoneyRequestPreviewProps, MoneyRequestPreviewOnyxProps};
75+
type NoPendingProps = {shouldShow: false};
76+
77+
type PendingProps = {
78+
/** Whether to show the pending message or not */
79+
shouldShow: true;
80+
81+
/** The icon to be displayed if a request is pending */
82+
messageIcon: IconAsset;
83+
84+
/** The description to be displayed if a request is pending */
85+
messageDescription: string;
86+
};
87+
88+
type PendingMessageProps = PendingProps | NoPendingProps;
89+
90+
export type {MoneyRequestPreviewProps, MoneyRequestPreviewOnyxProps, PendingMessageProps};

0 commit comments

Comments
 (0)