Skip to content

Commit ab5a23e

Browse files
Merge branch 'Expensify:main' into merge-translation-3
2 parents 4117ee1 + 3c4fcce commit ab5a23e

10 files changed

Lines changed: 81 additions & 109 deletions

File tree

src/components/DownloadAppBanner.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import {View} from 'react-native';
3+
import type {LayoutChangeEvent} from 'react-native';
34
import useHasLoggedIntoMobileApp from '@hooks/useHasLoggedIntoMobileApp';
45
import useLocalize from '@hooks/useLocalize';
56
import useThemeStyles from '@hooks/useThemeStyles';
@@ -9,7 +10,11 @@ import ROUTES from '@src/ROUTES';
910
import Button from './Button';
1011
import {ExpensifyMobileApp} from './Icon/Illustrations';
1112

12-
function DownloadAppBanner() {
13+
type DownloadAppBannerProps = {
14+
onLayout?: (e: LayoutChangeEvent) => void;
15+
};
16+
17+
function DownloadAppBanner({onLayout}: DownloadAppBannerProps) {
1318
const styles = useThemeStyles();
1419
const {translate} = useLocalize();
1520
const {hasLoggedIntoMobileApp, isLastMobileAppLoginLoaded} = useHasLoggedIntoMobileApp();
@@ -19,7 +24,10 @@ function DownloadAppBanner() {
1924
}
2025

2126
return (
22-
<View style={[styles.ph2, styles.mb2, styles.stickToBottom]}>
27+
<View
28+
style={[styles.ph2, styles.mb2, styles.stickToBottom, styles.pt2]}
29+
onLayout={onLayout}
30+
>
2331
<BillingBanner
2432
icon={ExpensifyMobileApp}
2533
title={translate('common.getTheApp')}

src/components/MoneyRequestConfirmationList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ function MoneyRequestConfirmationList({
388388
const isMerchantEmpty = useMemo(() => !iouMerchant || isMerchantMissing(transaction), [transaction, iouMerchant]);
389389
const isMerchantRequired = isPolicyExpenseChat && (!isScanRequest || isEditingSplitBill) && shouldShowMerchant;
390390

391-
const isCategoryRequired = !!policy?.requiresCategory;
391+
const isCategoryRequired = !!policy?.requiresCategory && !isTypeInvoice;
392392

393393
useEffect(() => {
394394
if (shouldDisplayFieldError && didConfirmSplit) {

src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {useFocusEffect} from '@react-navigation/native';
1+
import {useFocusEffect, useIsFocused} from '@react-navigation/native';
22
import isEmpty from 'lodash/isEmpty';
33
import React, {memo, useCallback, useMemo, useState} from 'react';
44
import {View} from 'react-native';
@@ -116,6 +116,7 @@ function MoneyRequestReportTransactionList({
116116
const styles = useThemeStyles();
117117
const StyleUtils = useStyleUtils();
118118
const {translate} = useLocalize();
119+
const isFocused = useIsFocused();
119120
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
120121
const {shouldUseNarrowLayout, isSmallScreenWidth, isMediumScreenWidth} = useResponsiveLayout();
121122
const [isModalVisible, setIsModalVisible] = useState(false);
@@ -371,7 +372,7 @@ function MoneyRequestReportTransactionList({
371372
<Animated.Text
372373
style={[styles.textLabelSupporting]}
373374
entering={hasComments ? undefined : FadeIn}
374-
exiting={FadeOut}
375+
exiting={isFocused ? FadeOut : undefined}
375376
>
376377
{hasComments || isLoadingReportActions ? translate('common.comments') : ''}
377378
</Animated.Text>

src/libs/ReportUtils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,12 +1849,11 @@ function pushTransactionViolationsOnyxData(
18491849
const hasDependentTags = hasDependentTagsPolicyUtils(optimisticPolicy, policyTagLists);
18501850

18511851
getAllPolicyReports(policyID).forEach((report) => {
1852-
if (!report?.reportID) {
1852+
const isReportAnInvoice = isInvoiceReport(report);
1853+
if (!report?.reportID || isReportAnInvoice) {
18531854
return;
18541855
}
18551856

1856-
const isReportAnInvoice = isInvoiceReport(report);
1857-
18581857
getReportTransactions(report.reportID).forEach((transaction: Transaction) => {
18591858
const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`] ?? [];
18601859

src/libs/actions/IOU.ts

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,25 +2379,6 @@ function buildOnyxDataForInvoice(invoiceParams: BuildOnyxDataForInvoiceParams):
23792379
return [optimisticData, successData, failureData];
23802380
}
23812381

2382-
const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(
2383-
transactionParams.transaction,
2384-
[],
2385-
policyParams.policy,
2386-
policyParams.policyTagList ?? {},
2387-
policyParams.policyCategories ?? {},
2388-
hasDependentTags(policyParams.policy, policyParams.policyTagList ?? {}),
2389-
true,
2390-
);
2391-
2392-
if (violationsOnyxData) {
2393-
optimisticData.push(violationsOnyxData);
2394-
failureData.push({
2395-
onyxMethod: Onyx.METHOD.SET,
2396-
key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionParams.transaction.transactionID}`,
2397-
value: [],
2398-
});
2399-
}
2400-
24012382
return [optimisticData, successData, failureData];
24022383
}
24032384

@@ -4329,9 +4310,11 @@ function getUpdateMoneyRequestParams(
43294310
}
43304311
const hasModifiedComment = 'comment' in transactionChanges;
43314312

4313+
const isInvoice = isInvoiceReportReportUtils(iouReport);
43324314
if (
43334315
policy &&
43344316
isPaidGroupPolicy(policy) &&
4317+
!isInvoice &&
43354318
updatedTransaction &&
43364319
(hasModifiedTag || hasModifiedCategory || hasModifiedComment || hasModifiedDistanceRate || hasModifiedAmount || hasModifiedCreated)
43374320
) {
@@ -4343,7 +4326,7 @@ function getUpdateMoneyRequestParams(
43434326
policyTagList ?? {},
43444327
policyCategories ?? {},
43454328
hasDependentTags(policy, policyTagList ?? {}),
4346-
isInvoiceReportReportUtils(iouReport),
4329+
isInvoice,
43474330
);
43484331
optimisticData.push(violationsOnyxData);
43494332
failureData.push({

src/pages/TeachersUnite/ImTeacherPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import {useSession} from '@components/OnyxProvider';
2+
import {useSession} from '@components/OnyxListItemProvider';
33
import {isEmailPublicDomain} from '@libs/LoginUtils';
44
import ImTeacherUpdateEmailPage from './ImTeacherUpdateEmailPage';
55
import IntroSchoolPrincipalPage from './IntroSchoolPrincipalPage';

src/pages/iou/request/step/IOURequestStepCurrency.tsx

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,33 @@
11
import React from 'react';
22
import {Keyboard} from 'react-native';
3-
import {withOnyx} from 'react-native-onyx';
4-
import type {OnyxEntry} from 'react-native-onyx';
53
import CurrencySelectionList from '@components/CurrencySelectionList';
64
import type {CurrencyListItem} from '@components/CurrencySelectionList/types';
75
import useLocalize from '@hooks/useLocalize';
8-
import * as CurrencyUtils from '@libs/CurrencyUtils';
6+
import useOnyx from '@hooks/useOnyx';
7+
import {isValidCurrencyCode} from '@libs/CurrencyUtils';
98
import Navigation from '@libs/Navigation/Navigation';
10-
import * as ReportUtils from '@libs/ReportUtils';
9+
import {getTransactionDetails} from '@libs/ReportUtils';
1110
import {appendParam} from '@libs/Url';
12-
import * as IOU from '@userActions/IOU';
11+
import {setMoneyRequestCurrency} from '@userActions/IOU';
1312
import CONST from '@src/CONST';
1413
import ONYXKEYS from '@src/ONYXKEYS';
1514
import type SCREENS from '@src/SCREENS';
16-
import type {Transaction} from '@src/types/onyx';
1715
import StepScreenWrapper from './StepScreenWrapper';
1816
import withFullTransactionOrNotFound from './withFullTransactionOrNotFound';
1917
import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound';
2018

21-
type IOURequestStepCurrencyOnyxProps = {
22-
/** The draft transaction object being modified in Onyx */
23-
draftTransaction: OnyxEntry<Transaction>;
24-
/** List of recently used currencies */
25-
recentlyUsedCurrencies: OnyxEntry<string[]>;
26-
};
27-
28-
type IOURequestStepCurrencyProps = IOURequestStepCurrencyOnyxProps & WithFullTransactionOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_CURRENCY>;
19+
type IOURequestStepCurrencyProps = WithFullTransactionOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_CURRENCY>;
2920

3021
function IOURequestStepCurrency({
3122
route: {
3223
params: {backTo, pageIndex, transactionID, action, currency: selectedCurrency = ''},
3324
},
34-
draftTransaction,
35-
recentlyUsedCurrencies,
3625
}: IOURequestStepCurrencyProps) {
3726
const {translate} = useLocalize();
38-
const {currency: originalCurrency = ''} = ReportUtils.getTransactionDetails(draftTransaction) ?? {};
39-
const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency;
27+
const [draftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {canBeMissing: true});
28+
const [recentlyUsedCurrencies] = useOnyx(ONYXKEYS.RECENTLY_USED_CURRENCIES, {canBeMissing: true});
29+
const {currency: originalCurrency = ''} = getTransactionDetails(draftTransaction) ?? {};
30+
const currency = isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency;
4031

4132
const navigateBack = (selectedCurrencyValue = '') => {
4233
// If the currency selection was done from the confirmation step (eg. + > submit expense > manual > confirm > amount > currency)
@@ -57,7 +48,7 @@ function IOURequestStepCurrency({
5748
const confirmCurrencySelection = (option: CurrencyListItem) => {
5849
Keyboard.dismiss();
5950
if (pageIndex !== CONST.IOU.PAGE_INDEX.CONFIRM) {
60-
IOU.setMoneyRequestCurrency(transactionID, option.currencyCode, action === CONST.IOU.ACTION.EDIT);
51+
setMoneyRequestCurrency(transactionID, option.currencyCode, action === CONST.IOU.ACTION.EDIT);
6152
}
6253

6354
Navigation.setNavigationActionToMicrotaskQueue(() => navigateBack(option.currencyCode));
@@ -90,19 +81,7 @@ function IOURequestStepCurrency({
9081

9182
IOURequestStepCurrency.displayName = 'IOURequestStepCurrency';
9283

93-
const IOURequestStepCurrencyWithOnyx = withOnyx<IOURequestStepCurrencyProps, IOURequestStepCurrencyOnyxProps>({
94-
draftTransaction: {
95-
key: ({route}) => {
96-
const transactionID = route?.params?.transactionID ?? -1;
97-
return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`;
98-
},
99-
},
100-
recentlyUsedCurrencies: {
101-
key: ONYXKEYS.RECENTLY_USED_CURRENCIES,
102-
},
103-
})(IOURequestStepCurrency);
104-
10584
/* eslint-disable rulesdir/no-negated-variables */
106-
const IOURequestStepCurrencyWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepCurrencyWithOnyx);
85+
const IOURequestStepCurrencyWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepCurrency);
10786

10887
export default IOURequestStepCurrencyWithFullTransactionOrNotFound;

src/pages/iou/request/step/IOURequestStepScan/index.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -965,8 +965,19 @@ function IOURequestStepScan({
965965
</>
966966
);
967967

968+
const [containerHeight, setContainerHeight] = useState(0);
969+
const [desktopUploadViewHeight, setDesktopUploadViewHeight] = useState(0);
970+
const [downloadAppBannerHeight, setDownloadAppBannerHeight] = useState(0);
971+
/* We use isMobile() here to explicitly hide DownloadAppBanner component on both mobile web and native apps */
972+
const shouldHideDownloadAppBanner = isMobile() || downloadAppBannerHeight + desktopUploadViewHeight + styles.uploadFileView(isSmallScreenWidth).paddingVertical * 2 > containerHeight;
973+
968974
const desktopUploadView = () => (
969-
<>
975+
<View
976+
style={[styles.alignItemsCenter, styles.justifyContentCenter]}
977+
onLayout={(e) => {
978+
setDesktopUploadViewHeight(e.nativeEvent.layout.height);
979+
}}
980+
>
970981
{PDFValidationComponent}
971982
<ReceiptUpload
972983
width={CONST.RECEIPT.ICON_SIZE}
@@ -1005,7 +1016,7 @@ function IOURequestStepScan({
10051016
/>
10061017
)}
10071018
</AttachmentPicker>
1008-
</>
1019+
</View>
10091020
);
10101021

10111022
return (
@@ -1017,7 +1028,8 @@ function IOURequestStepScan({
10171028
>
10181029
{(isDraggingOverWrapper) => (
10191030
<View
1020-
onLayout={() => {
1031+
onLayout={(event) => {
1032+
setContainerHeight(event.nativeEvent.layout.height);
10211033
if (!onLayout) {
10221034
return;
10231035
}
@@ -1037,8 +1049,7 @@ function IOURequestStepScan({
10371049
dropInnerWrapperStyles={styles.receiptDropInnerWrapper(true)}
10381050
/>
10391051
</DragAndDropConsumer>
1040-
{/* We use isMobile() here to explicitly hide DownloadAppBanner component on both mobile web and native apps */}
1041-
{!isMobile() && <DownloadAppBanner />}
1052+
{!shouldHideDownloadAppBanner && <DownloadAppBanner onLayout={(e) => setDownloadAppBannerHeight(e.nativeEvent.layout.height)} />}
10421053
{ErrorModal}
10431054
{startLocationPermissionFlow && !!receiptFiles.length && (
10441055
<LocationPermissionModal

src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ function IOURequestStepTaxAmountPage({
5656
const textInput = useRef<BaseTextInputRef | null>(null);
5757
const isEditing = action === CONST.IOU.ACTION.EDIT;
5858
const isEditingSplitBill = isEditing && iouType === CONST.IOU.TYPE.SPLIT;
59-
6059
const focusTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
6160

6261
const currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction;

src/pages/workspace/perDiem/WorkspacePerDiemDetailsPage.tsx

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import React, {useState} from 'react';
2-
import {View} from 'react-native';
3-
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
42
import ConfirmModal from '@components/ConfirmModal';
53
import HeaderWithBackButton from '@components/HeaderWithBackButton';
64
import * as Expensicons from '@components/Icon/Expensicons';
@@ -47,8 +45,6 @@ function WorkspacePerDiemDetailsPage({route}: WorkspacePerDiemDetailsPageProps)
4745
const amountValue = selectedSubRate?.rate ? convertToDisplayStringWithoutCurrency(Number(selectedSubRate.rate)) : undefined;
4846
const currencyValue = selectedRate?.currency ? `${selectedRate.currency} - ${getCurrencySymbol(selectedRate.currency)}` : undefined;
4947

50-
const FullPageBlockingView = isEmptyObject(selectedSubRate) ? FullPageOfflineBlockingView : View;
51-
5248
const handleDeletePerDiemRate = () => {
5349
deleteWorkspacePerDiemRates(policyID, customUnit, [
5450
{
@@ -69,6 +65,7 @@ function WorkspacePerDiemDetailsPage({route}: WorkspacePerDiemDetailsPageProps)
6965
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
7066
policyID={policyID}
7167
featureName={CONST.POLICY.MORE_FEATURES.ARE_PER_DIEM_RATES_ENABLED}
68+
shouldBeBlocked={isEmptyObject(selectedSubRate)}
7269
>
7370
<ScreenWrapper
7471
enableEdgeToEdgeBottomSafeAreaPadding
@@ -86,46 +83,41 @@ function WorkspacePerDiemDetailsPage({route}: WorkspacePerDiemDetailsPageProps)
8683
cancelText={translate('common.cancel')}
8784
danger
8885
/>
89-
<FullPageBlockingView
90-
style={!isEmptyObject(selectedSubRate) ? styles.flexGrow1 : []}
86+
<ScrollView
9187
addBottomSafeAreaPadding
88+
contentContainerStyle={styles.flexGrow1}
89+
keyboardShouldPersistTaps="always"
9290
>
93-
<ScrollView
94-
addBottomSafeAreaPadding
95-
contentContainerStyle={styles.flexGrow1}
96-
keyboardShouldPersistTaps="always"
97-
>
98-
<MenuItemWithTopDescription
99-
title={selectedRate?.name}
100-
description={translate('common.destination')}
101-
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_DESTINATION.getRoute(policyID, rateID, subRateID))}
102-
shouldShowRightIcon
103-
/>
104-
<MenuItemWithTopDescription
105-
title={selectedSubRate?.name}
106-
description={translate('common.subrate')}
107-
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_SUBRATE.getRoute(policyID, rateID, subRateID))}
108-
shouldShowRightIcon
109-
/>
110-
<MenuItemWithTopDescription
111-
title={amountValue}
112-
description={translate('workspace.perDiem.amount')}
113-
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_AMOUNT.getRoute(policyID, rateID, subRateID))}
114-
shouldShowRightIcon
115-
/>
116-
<MenuItemWithTopDescription
117-
title={currencyValue}
118-
description={translate('common.currency')}
119-
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_CURRENCY.getRoute(policyID, rateID, subRateID))}
120-
shouldShowRightIcon
121-
/>
122-
<MenuItem
123-
icon={Expensicons.Trashcan}
124-
title={translate('common.delete')}
125-
onPress={() => setDeletePerDiemConfirmModalVisible(true)}
126-
/>
127-
</ScrollView>
128-
</FullPageBlockingView>
91+
<MenuItemWithTopDescription
92+
title={selectedRate?.name}
93+
description={translate('common.destination')}
94+
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_DESTINATION.getRoute(policyID, rateID, subRateID))}
95+
shouldShowRightIcon
96+
/>
97+
<MenuItemWithTopDescription
98+
title={selectedSubRate?.name}
99+
description={translate('common.subrate')}
100+
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_SUBRATE.getRoute(policyID, rateID, subRateID))}
101+
shouldShowRightIcon
102+
/>
103+
<MenuItemWithTopDescription
104+
title={amountValue}
105+
description={translate('workspace.perDiem.amount')}
106+
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_AMOUNT.getRoute(policyID, rateID, subRateID))}
107+
shouldShowRightIcon
108+
/>
109+
<MenuItemWithTopDescription
110+
title={currencyValue}
111+
description={translate('common.currency')}
112+
onPress={() => Navigation.navigate(ROUTES.WORKSPACE_PER_DIEM_EDIT_CURRENCY.getRoute(policyID, rateID, subRateID))}
113+
shouldShowRightIcon
114+
/>
115+
<MenuItem
116+
icon={Expensicons.Trashcan}
117+
title={translate('common.delete')}
118+
onPress={() => setDeletePerDiemConfirmModalVisible(true)}
119+
/>
120+
</ScrollView>
129121
</ScreenWrapper>
130122
</AccessOrNotFoundWrapper>
131123
);

0 commit comments

Comments
 (0)