From 09a1942fd86e4ec470873c41413b098afa1168df Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 1 Oct 2024 16:24:51 +0800 Subject: [PATCH 001/174] fix the wrong calculation --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 874056cac4a0..05e85701abbb 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6448,7 +6448,7 @@ function getReportFromHoldRequestsOnyxData( chatReport.reportID, chatReport.policyID ?? iouReport?.policyID ?? '', recipient.accountID ?? 1, - holdTransactions.reduce((acc, transaction) => acc + transaction.amount, 0) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), + ((iouReport?.total ?? 0) - ((iouReport?.unheldTotal ?? 0) + (iouReport?.nonReimbursableTotal ?? 0))) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), getCurrency(firstHoldTransaction), false, newParentReportActionID, From 2b5a1525508c12e7c195c90c42ea9c8675a89642 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 1 Oct 2024 21:44:49 +0800 Subject: [PATCH 002/174] use the current iou when creating the new report --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 05e85701abbb..4ec22844af2d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6449,7 +6449,7 @@ function getReportFromHoldRequestsOnyxData( chatReport.policyID ?? iouReport?.policyID ?? '', recipient.accountID ?? 1, ((iouReport?.total ?? 0) - ((iouReport?.unheldTotal ?? 0) + (iouReport?.nonReimbursableTotal ?? 0))) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), - getCurrency(firstHoldTransaction), + iouReport?.currency ?? '', false, newParentReportActionID, ); From 6196f9c0a253417c7f626f6ec8bbd7e8af1b8b85 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 1 Oct 2024 21:51:03 +0800 Subject: [PATCH 003/174] remove unused import --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 4ec22844af2d..95a4683fcc90 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -52,7 +52,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as SessionUtils from '@libs/SessionUtils'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import {getCurrency, getTransaction} from '@libs/TransactionUtils'; +import {getTransaction} from '@libs/TransactionUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; From 1ccf57b2abac893a93e7f60d4e9438b3705d693a Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 15 Oct 2024 23:57:57 +0530 Subject: [PATCH 004/174] fix: The Date options for Report Fields are ambiguous and should be updated. Signed-off-by: krishna2323 --- src/libs/WorkspaceReportFieldUtils.ts | 2 +- src/pages/workspace/reportFields/CreateReportFieldsPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/WorkspaceReportFieldUtils.ts b/src/libs/WorkspaceReportFieldUtils.ts index b7d93b8dee3a..503c1d440d69 100644 --- a/src/libs/WorkspaceReportFieldUtils.ts +++ b/src/libs/WorkspaceReportFieldUtils.ts @@ -80,7 +80,7 @@ function getReportFieldInitialValue(reportField: PolicyReportField | null): stri } if (reportField.type === CONST.REPORT_FIELD_TYPES.DATE) { - return Localize.translateLocal('common.currentDate'); + return Localize.translateLocal('common.initialValue'); } return reportField.value ?? reportField.defaultValue; diff --git a/src/pages/workspace/reportFields/CreateReportFieldsPage.tsx b/src/pages/workspace/reportFields/CreateReportFieldsPage.tsx index 8dbd90c9e929..31bd5883e431 100644 --- a/src/pages/workspace/reportFields/CreateReportFieldsPage.tsx +++ b/src/pages/workspace/reportFields/CreateReportFieldsPage.tsx @@ -171,7 +171,7 @@ function CreateReportFieldsPage({ {inputValues[INPUT_IDS.TYPE] === CONST.REPORT_FIELD_TYPES.DATE && ( Date: Wed, 16 Oct 2024 19:25:02 +0800 Subject: [PATCH 005/174] fix total calculation --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 82847d121fd7..47f56790b46a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6496,7 +6496,7 @@ function getReportFromHoldRequestsOnyxData( chatReport.reportID, chatReport.policyID ?? iouReport?.policyID ?? '', recipient.accountID ?? 1, - ((iouReport?.total ?? 0) - ((iouReport?.unheldTotal ?? 0) + (iouReport?.nonReimbursableTotal ?? 0))) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), + ((iouReport?.total ?? 0) - (iouReport?.unheldTotal ?? 0)) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), iouReport?.currency ?? '', false, newParentReportActionID, From 7c223b95f3744d9531e11f8ae3d30d24cbb8209b Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 18 Oct 2024 18:24:56 +0800 Subject: [PATCH 006/174] get the reimbursable amount when the type is pay --- src/components/MoneyReportHeader.tsx | 2 +- .../ReportActionItem/ReportPreview.tsx | 24 +++++++------- src/libs/ReportUtils.ts | 31 +++++++++++++------ src/pages/home/ReportScreen.tsx | 1 + src/types/onyx/Report.ts | 3 ++ 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index caa50abfca46..b88658a32c1c 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -158,7 +158,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea shouldShowExportIntegrationButton; const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport?.currency); - const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, policy); + const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, policy, shouldShowPayButton); const isAnyTransactionOnHold = ReportUtils.hasHeldExpenses(moneyRequestReport?.reportID); const displayedAmount = isAnyTransactionOnHold && canAllowSettlement ? nonHeldAmount : formattedAmount; const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 411f6be7252c..91c27467d73e 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -100,6 +100,9 @@ function ReportPreview({ const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); + const [invoiceReceiverPolicy] = useOnyx( + `${ONYXKEYS.COLLECTION.POLICY}${chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.policyID : -1}`, + ); const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -121,13 +124,19 @@ function ReportPreview({ const [isPaidAnimationRunning, setIsPaidAnimationRunning] = useState(false); const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [requestType, setRequestType] = useState(); - const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy); - const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(iouReport?.reportID ?? ''); const [paymentType, setPaymentType] = useState(); - const [invoiceReceiverPolicy] = useOnyx( - `${ONYXKEYS.COLLECTION.POLICY}${chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.policyID : -1}`, + + const getCanIOUBePaid = useCallback( + (onlyShowPayElsewhere = false) => IOU.canIOUBePaid(iouReport, chatReport, policy, allTransactions, onlyShowPayElsewhere), + [iouReport, chatReport, policy, allTransactions], ); + const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]); + const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]); + const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere; + const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy, shouldShowPayButton); + const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(iouReport?.reportID ?? ''); + const managerID = iouReport?.managerID ?? action.childManagerAccountID ?? 0; const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport); @@ -317,14 +326,7 @@ function ReportPreview({ ]); const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); - const getCanIOUBePaid = useCallback( - (onlyShowPayElsewhere = false) => IOU.canIOUBePaid(iouReport, chatReport, policy, allTransactions, onlyShowPayElsewhere), - [iouReport, chatReport, policy, allTransactions], - ); - const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]); - const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]); - const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere; const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(iouReport, policy), [iouReport, policy]); const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4bae619d928e..126612b34901 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -7677,26 +7677,37 @@ function hasUpdatedTotal(report: OnyxInputOrEntry, policy: OnyxInputOrEn /** * Return held and full amount formatted with used currency */ -function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry): string[] { +function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry, isReimbursableOnly: boolean): string[] { const reportTransactions = reportsTransactions[iouReport?.reportID ?? ''] ?? []; const hasPendingTransaction = reportTransactions.some((transaction) => !!transaction.pendingAction); // if the report is an expense report, the total amount should be negated const coefficient = isExpenseReport(iouReport) ? -1 : 1; + let total = iouReport?.total ?? 0; + if (isReimbursableOnly) { + total -= iouReport?.nonReimbursableTotal ?? 0; + } + if (hasUpdatedTotal(iouReport, policy) && hasPendingTransaction) { - const unheldTotal = reportTransactions.reduce((currentVal, transaction) => currentVal + (!TransactionUtils.isOnHold(transaction) ? transaction.amount : 0), 0); + const unheldNonReimbursableTotal = reportTransactions.reduce( + (currentVal, transaction) => currentVal + (!TransactionUtils.isOnHold(transaction) && !transaction.reimbursable ? transaction.amount : 0), + 0, + ); + let unheldTotal = reportTransactions.reduce((currentVal, transaction) => currentVal + (!TransactionUtils.isOnHold(transaction) ? transaction.amount : 0), 0); + + if (isReimbursableOnly) { + unheldTotal -= unheldNonReimbursableTotal; + } - return [ - CurrencyUtils.convertToDisplayString(unheldTotal * coefficient, iouReport?.currency), - CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * coefficient, iouReport?.currency), - ]; + return [CurrencyUtils.convertToDisplayString(unheldTotal * coefficient, iouReport?.currency), CurrencyUtils.convertToDisplayString(total * coefficient, iouReport?.currency)]; } - return [ - CurrencyUtils.convertToDisplayString((iouReport?.unheldTotal ?? 0) * coefficient, iouReport?.currency), - CurrencyUtils.convertToDisplayString((iouReport?.total ?? 0) * coefficient, iouReport?.currency), - ]; + let unheldTotal = iouReport?.unheldTotal ?? 0; + if (isReimbursableOnly) { + unheldTotal -= iouReport?.unheldNonReimbursableTotal ?? 0; + } + return [CurrencyUtils.convertToDisplayString(unheldTotal * coefficient, iouReport?.currency), CurrencyUtils.convertToDisplayString(total * coefficient, iouReport?.currency)]; } /** diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 4a87d51e3c82..2cc229c4fda5 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -193,6 +193,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro ownerAccountID: reportOnyx.ownerAccountID, currency: reportOnyx.currency, unheldTotal: reportOnyx.unheldTotal, + unheldNonReimbursableTotal: reportOnyx.unheldNonReimbursableTotal, participants: reportOnyx.participants, isWaitingOnBankAccount: reportOnyx.isWaitingOnBankAccount, iouReportID: reportOnyx.iouReportID, diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 43c82cfdc227..2eedb57f4f13 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -218,6 +218,9 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< /** For expense reports, this is the total amount requested */ unheldTotal?: number; + /** Total amount of unheld and non-reimbursable transactions in an expense report */ + unheldNonReimbursableTotal?: number; + /** For expense reports, this is the currency of the expense */ currency?: string; From 903d17d0a942dc4eed3d32fd1dde0c1183a9414d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 22 Oct 2024 11:33:56 +0800 Subject: [PATCH 007/174] rename --- src/libs/ReportUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f90fd469c617..9c766a972a65 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -7679,7 +7679,7 @@ function hasUpdatedTotal(report: OnyxInputOrEntry, policy: OnyxInputOrEn /** * Return held and full amount formatted with used currency */ -function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry, isReimbursableOnly: boolean): string[] { +function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry, shouldExcludeNonReimbursables: boolean): string[] { const reportTransactions = reportsTransactions[iouReport?.reportID ?? ''] ?? []; const hasPendingTransaction = reportTransactions.some((transaction) => !!transaction.pendingAction); @@ -7687,7 +7687,7 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry const coefficient = isExpenseReport(iouReport) ? -1 : 1; let total = iouReport?.total ?? 0; - if (isReimbursableOnly) { + if (shouldExcludeNonReimbursables) { total -= iouReport?.nonReimbursableTotal ?? 0; } @@ -7698,7 +7698,7 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry ); let unheldTotal = reportTransactions.reduce((currentVal, transaction) => currentVal + (!TransactionUtils.isOnHold(transaction) ? transaction.amount : 0), 0); - if (isReimbursableOnly) { + if (shouldExcludeNonReimbursables) { unheldTotal -= unheldNonReimbursableTotal; } @@ -7706,7 +7706,7 @@ function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry } let unheldTotal = iouReport?.unheldTotal ?? 0; - if (isReimbursableOnly) { + if (shouldExcludeNonReimbursables) { unheldTotal -= iouReport?.unheldNonReimbursableTotal ?? 0; } return [CurrencyUtils.convertToDisplayString(unheldTotal * coefficient, iouReport?.currency), CurrencyUtils.convertToDisplayString(total * coefficient, iouReport?.currency)]; From 997de55caf5614183ec9d93691017aa5bac02698 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 22 Oct 2024 15:50:39 +0800 Subject: [PATCH 008/174] update unheldTotal and unheldReimburableTotal optimistically --- src/components/MoneyReportHeader.tsx | 2 +- .../ReportActionItem/ReportPreview.tsx | 2 +- src/libs/ReportUtils.ts | 29 ++---- src/libs/actions/IOU.ts | 93 +++++++++++++++++-- 4 files changed, 92 insertions(+), 34 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 6bf2e0843922..ee74ac1ed58a 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -167,7 +167,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea shouldShowExportIntegrationButton; const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport?.currency); - const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, policy, shouldShowPayButton); + const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, shouldShowPayButton); const isAnyTransactionOnHold = ReportUtils.hasHeldExpenses(moneyRequestReport?.reportID); const displayedAmount = isAnyTransactionOnHold && canAllowSettlement ? nonHeldAmount : formattedAmount; const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 9452f19cd45b..b54507c4869d 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -135,7 +135,7 @@ function ReportPreview({ const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]); const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]); const shouldShowPayButton = isPaidAnimationRunning || canIOUBePaid || onlyShowPayElsewhere; - const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, policy, shouldShowPayButton); + const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(iouReport, shouldShowPayButton); const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(iouReport?.reportID ?? ''); const managerID = iouReport?.managerID ?? action.childManagerAccountID ?? 0; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9c766a972a65..d835c05e0c7d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -155,7 +155,9 @@ type OptimisticExpenseReport = Pick< | 'stateNum' | 'statusNum' | 'total' + | 'unheldTotal' | 'nonReimbursableTotal' + | 'unheldNonReimbursableTotal' | 'parentReportID' | 'lastVisibleActionCreated' | 'parentReportActionID' @@ -4579,7 +4581,9 @@ function buildOptimisticExpenseReport( stateNum, statusNum, total: storedTotal, + unheldTotal: storedTotal, nonReimbursableTotal: reimbursable ? 0 : storedTotal, + unheldNonReimbursableTotal: reimbursable ? 0 : storedTotal, participants: { [payeeAccountID]: { notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, @@ -7679,36 +7683,17 @@ function hasUpdatedTotal(report: OnyxInputOrEntry, policy: OnyxInputOrEn /** * Return held and full amount formatted with used currency */ -function getNonHeldAndFullAmount(iouReport: OnyxEntry, policy: OnyxEntry, shouldExcludeNonReimbursables: boolean): string[] { - const reportTransactions = reportsTransactions[iouReport?.reportID ?? ''] ?? []; - const hasPendingTransaction = reportTransactions.some((transaction) => !!transaction.pendingAction); - +function getNonHeldAndFullAmount(iouReport: OnyxEntry, shouldExcludeNonReimbursables: boolean): string[] { // if the report is an expense report, the total amount should be negated const coefficient = isExpenseReport(iouReport) ? -1 : 1; let total = iouReport?.total ?? 0; - if (shouldExcludeNonReimbursables) { - total -= iouReport?.nonReimbursableTotal ?? 0; - } - - if (hasUpdatedTotal(iouReport, policy) && hasPendingTransaction) { - const unheldNonReimbursableTotal = reportTransactions.reduce( - (currentVal, transaction) => currentVal + (!TransactionUtils.isOnHold(transaction) && !transaction.reimbursable ? transaction.amount : 0), - 0, - ); - let unheldTotal = reportTransactions.reduce((currentVal, transaction) => currentVal + (!TransactionUtils.isOnHold(transaction) ? transaction.amount : 0), 0); - - if (shouldExcludeNonReimbursables) { - unheldTotal -= unheldNonReimbursableTotal; - } - - return [CurrencyUtils.convertToDisplayString(unheldTotal * coefficient, iouReport?.currency), CurrencyUtils.convertToDisplayString(total * coefficient, iouReport?.currency)]; - } - let unheldTotal = iouReport?.unheldTotal ?? 0; if (shouldExcludeNonReimbursables) { + total -= iouReport?.nonReimbursableTotal ?? 0; unheldTotal -= iouReport?.unheldNonReimbursableTotal ?? 0; } + return [CurrencyUtils.convertToDisplayString(unheldTotal * coefficient, iouReport?.currency), CurrencyUtils.convertToDisplayString(total * coefficient, iouReport?.currency)]; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ae527da83e07..b4263ce0f620 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2075,9 +2075,15 @@ function getMoneyRequestInformation( : ReportUtils.buildOptimisticIOUReport(payeeAccountID, payerAccountID, amount, chatReport.reportID, currency); } else if (isPolicyExpenseChat) { iouReport = {...iouReport}; - if (iouReport?.currency === currency && typeof iouReport.total === 'number') { - // Because of the Expense reports are stored as negative values, we subtract the total from the amount - iouReport.total -= amount; + // Because of the Expense reports are stored as negative values, we subtract the total from the amount + if (iouReport?.currency === currency) { + if (typeof iouReport.total === 'number') { + iouReport.total -= amount; + } + + if (typeof iouReport.unheldTotal === 'number') { + iouReport.unheldTotal -= amount; + } } } else { iouReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, payeeAccountID, amount, currency); @@ -2301,10 +2307,17 @@ function getTrackExpenseInformation( iouReport = ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID ?? '-1', payeeAccountID, amount, currency, false); } else { iouReport = {...iouReport}; - if (iouReport?.currency === currency && typeof iouReport.total === 'number' && typeof iouReport.nonReimbursableTotal === 'number') { - // Because of the Expense reports are stored as negative values, we subtract the total from the amount - iouReport.total -= amount; - iouReport.nonReimbursableTotal -= amount; + // Because of the Expense reports are stored as negative values, we subtract the total from the amount + if (iouReport?.currency === currency) { + if (typeof iouReport.total === 'number' && typeof iouReport.nonReimbursableTotal === 'number') { + iouReport.total -= amount; + iouReport.nonReimbursableTotal -= amount; + } + + if (typeof iouReport.unheldTotal === 'number' && typeof iouReport.unheldNonReimbursableTotal === 'number') { + iouReport.unheldTotal -= amount; + iouReport.unheldNonReimbursableTotal -= amount; + } } } } @@ -2505,6 +2518,7 @@ function getUpdateMoneyRequestParams( // Step 2: Get all the collections being updated const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const isTransactionOnHold = TransactionUtils.isOnHold(transaction); const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); @@ -2624,6 +2638,14 @@ function getUpdateMoneyRequestParams( if (!transaction?.reimbursable && typeof updatedMoneyRequestReport.nonReimbursableTotal === 'number') { updatedMoneyRequestReport.nonReimbursableTotal -= diff; } + if (!isTransactionOnHold) { + if (typeof updatedMoneyRequestReport.unheldTotal === 'number') { + updatedMoneyRequestReport.unheldTotal -= diff; + } + if (!transaction?.reimbursable && typeof updatedMoneyRequestReport.unheldNonReimbursableTotal === 'number') { + updatedMoneyRequestReport.unheldNonReimbursableTotal -= diff; + } + } } else { updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID ?? -1, diff, TransactionUtils.getCurrency(transaction), false, true); } @@ -4224,9 +4246,15 @@ function createSplitsAndOnyxData( ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport.reportID, oneOnOneChatReport.policyID ?? '-1', currentUserAccountID, splitAmount, currency) : ReportUtils.buildOptimisticIOUReport(currentUserAccountID, accountID, splitAmount, oneOnOneChatReport.reportID, currency); } else if (isOwnPolicyExpenseChat) { - if (typeof oneOnOneIOUReport?.total === 'number') { - // Because of the Expense reports are stored as negative values, we subtract the total from the amount - oneOnOneIOUReport.total -= splitAmount; + // Because of the Expense reports are stored as negative values, we subtract the total from the amount + if (oneOnOneIOUReport?.currency === currency) { + if (typeof oneOnOneIOUReport.total === 'number') { + oneOnOneIOUReport.total -= splitAmount; + } + + if (typeof oneOnOneIOUReport.unheldTotal === 'number') { + oneOnOneIOUReport.unheldTotal -= splitAmount; + } } } else { oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport, currentUserAccountID, splitAmount, currency); @@ -5624,6 +5652,7 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const reportPreviewAction = getReportPreviewAction(iouReport?.chatReportID ?? '-1', iouReport?.reportID ?? '-1')!; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const isTransactionOnHold = TransactionUtils.isOnHold(transaction); const transactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; const transactionThreadID = reportAction.childReportID; let transactionThread = null; @@ -5679,6 +5708,16 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT if (!transaction?.reimbursable && typeof updatedIOUReport.nonReimbursableTotal === 'number') { updatedIOUReport.nonReimbursableTotal += amountDiff; } + + if (!isTransactionOnHold) { + if (typeof updatedIOUReport.unheldTotal === 'number') { + updatedIOUReport.unheldTotal += amountDiff; + } + + if (!transaction?.reimbursable && typeof updatedIOUReport.unheldNonReimbursableTotal === 'number') { + updatedIOUReport.unheldNonReimbursableTotal += amountDiff; + } + } } } else { updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, reportAction.actorAccountID ?? -1, TransactionUtils.getAmount(transaction, false), currency, true); @@ -7954,6 +7993,8 @@ function putOnHold(transactionID: string, comment: string, reportID: string, sea const transactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] ?? []; const updatedViolations = [...transactionViolations, newViolation]; const parentReportActionOptimistic = ReportUtils.getOptimisticDataForParentReportAction(reportID, createdReportActionComment.created, CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const iouReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`]; const optimisticData: OnyxUpdate[] = [ { @@ -7981,6 +8022,21 @@ function putOnHold(transactionID: string, comment: string, reportID: string, sea }, ]; + if (iouReport && iouReport.currency === transaction?.currency) { + const isExpenseReport = ReportUtils.isExpenseReport(iouReport); + const coefficient = isExpenseReport ? -1 : 1; + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: { + unheldTotal: (iouReport.unheldTotal ?? 0) - TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient, + unheldNonReimbursableTotal: !transaction?.reimbursable + ? (iouReport.unheldNonReimbursableTotal ?? 0) - TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient + : iouReport.unheldNonReimbursableTotal, + }, + }); + } + parentReportActionOptimistic.forEach((parentActionData) => { if (!parentActionData) { return; @@ -8058,6 +8114,8 @@ function putOnHold(transactionID: string, comment: string, reportID: string, sea function unholdRequest(transactionID: string, reportID: string, searchHash?: number) { const createdReportAction = ReportUtils.buildOptimisticUnHoldReportAction(); const transactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; + const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const iouReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`]; const optimisticData: OnyxUpdate[] = [ { @@ -8084,6 +8142,21 @@ function unholdRequest(transactionID: string, reportID: string, searchHash?: num }, ]; + if (iouReport && iouReport.currency === transaction?.currency) { + const isExpenseReport = ReportUtils.isExpenseReport(iouReport); + const coefficient = isExpenseReport ? -1 : 1; + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: { + unheldTotal: (iouReport.unheldTotal ?? 0) + TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient, + unheldNonReimbursableTotal: !transaction?.reimbursable + ? (iouReport.unheldNonReimbursableTotal ?? 0) + TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient + : iouReport.unheldNonReimbursableTotal, + }, + }); + } + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, From 6e1f6e41d154ef365ab0fca7e9d9b0710dbdf8e5 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 23 Oct 2024 11:15:11 +0800 Subject: [PATCH 009/174] update comment --- src/types/onyx/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 2eedb57f4f13..1e937e8c9093 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -218,7 +218,7 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback< /** For expense reports, this is the total amount requested */ unheldTotal?: number; - /** Total amount of unheld and non-reimbursable transactions in an expense report */ + /** Total amount of unheld non-reimbursable transactions in an expense report */ unheldNonReimbursableTotal?: number; /** For expense reports, this is the currency of the expense */ From 077a7d5f61d578f00318b5e963935936c97e7373 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 23 Oct 2024 11:16:11 +0800 Subject: [PATCH 010/174] calc the amount once --- src/libs/actions/IOU.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index edd31a9ac8aa..49496b3087bd 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -8024,14 +8024,13 @@ function putOnHold(transactionID: string, comment: string, reportID: string, sea if (iouReport && iouReport.currency === transaction?.currency) { const isExpenseReport = ReportUtils.isExpenseReport(iouReport); const coefficient = isExpenseReport ? -1 : 1; + const transactionAmount = TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient; optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, value: { - unheldTotal: (iouReport.unheldTotal ?? 0) - TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient, - unheldNonReimbursableTotal: !transaction?.reimbursable - ? (iouReport.unheldNonReimbursableTotal ?? 0) - TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient - : iouReport.unheldNonReimbursableTotal, + unheldTotal: (iouReport.unheldTotal ?? 0) - transactionAmount, + unheldNonReimbursableTotal: !transaction?.reimbursable ? (iouReport.unheldNonReimbursableTotal ?? 0) - transactionAmount : iouReport.unheldNonReimbursableTotal, }, }); } @@ -8144,14 +8143,13 @@ function unholdRequest(transactionID: string, reportID: string, searchHash?: num if (iouReport && iouReport.currency === transaction?.currency) { const isExpenseReport = ReportUtils.isExpenseReport(iouReport); const coefficient = isExpenseReport ? -1 : 1; + const transactionAmount = TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient; optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, value: { - unheldTotal: (iouReport.unheldTotal ?? 0) + TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient, - unheldNonReimbursableTotal: !transaction?.reimbursable - ? (iouReport.unheldNonReimbursableTotal ?? 0) + TransactionUtils.getAmount(transaction, isExpenseReport) * coefficient - : iouReport.unheldNonReimbursableTotal, + unheldTotal: (iouReport.unheldTotal ?? 0) + transactionAmount, + unheldNonReimbursableTotal: !transaction?.reimbursable ? (iouReport.unheldNonReimbursableTotal ?? 0) + transactionAmount : iouReport.unheldNonReimbursableTotal, }, }); } From d7fddd16e266a0f2e6d8f4376a2abf62b2db6ef8 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 23 Oct 2024 15:27:37 +0800 Subject: [PATCH 011/174] store the correct non reimbursable amount --- src/libs/ReportUtils.ts | 7 ++++--- src/libs/actions/IOU.ts | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index dcb2a45bfb97..5b529f662b35 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4557,11 +4557,12 @@ function buildOptimisticExpenseReport( payeeAccountID: number, total: number, currency: string, - reimbursable = true, + nonReimbursableTotal: number = 0, parentReportActionID?: string, ): OptimisticExpenseReport { // The amount for Expense reports are stored as negative value in the database const storedTotal = total * -1; + const storedNonReimbursableTotal = nonReimbursableTotal * -1; const policyName = getPolicyName(ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`]); const formattedTotal = CurrencyUtils.convertToDisplayString(storedTotal, currency); const policy = getPolicy(policyID); @@ -4581,8 +4582,8 @@ function buildOptimisticExpenseReport( statusNum, total: storedTotal, unheldTotal: storedTotal, - nonReimbursableTotal: reimbursable ? 0 : storedTotal, - unheldNonReimbursableTotal: reimbursable ? 0 : storedTotal, + nonReimbursableTotal: storedNonReimbursableTotal, + unheldNonReimbursableTotal: storedNonReimbursableTotal, participants: { [payeeAccountID]: { notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 49496b3087bd..5a8fbc8c7976 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2304,7 +2304,7 @@ function getTrackExpenseInformation( shouldCreateNewMoneyRequestReport = ReportUtils.shouldCreateNewMoneyRequestReport(iouReport, chatReport); if (!iouReport || shouldCreateNewMoneyRequestReport) { - iouReport = ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID ?? '-1', payeeAccountID, amount, currency, false); + iouReport = ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID ?? '-1', payeeAccountID, amount, currency, 0); } else { iouReport = {...iouReport}; // Because of the Expense reports are stored as negative values, we subtract the total from the amount @@ -6549,13 +6549,14 @@ function getReportFromHoldRequestsOnyxData( const firstHoldTransaction = holdTransactions.at(0); const newParentReportActionID = rand64(); + const coefficient = ReportUtils.isExpenseReport(iouReport) ? -1 : 1; const optimisticExpenseReport = ReportUtils.buildOptimisticExpenseReport( chatReport.reportID, chatReport.policyID ?? iouReport?.policyID ?? '', recipient.accountID ?? 1, - ((iouReport?.total ?? 0) - (iouReport?.unheldTotal ?? 0)) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1), + ((iouReport?.total ?? 0) - (iouReport?.unheldTotal ?? 0)) * coefficient, iouReport?.currency ?? '', - false, + ((iouReport?.nonReimbursableTotal ?? 0) - (iouReport?.unheldNonReimbursableTotal ?? 0)) * coefficient, newParentReportActionID, ); const optimisticExpenseReportPreview = ReportUtils.buildOptimisticReportPreview( @@ -6630,6 +6631,7 @@ function getReportFromHoldRequestsOnyxData( value: { ...optimisticExpenseReport, unheldTotal: 0, + unheldNonReimbursableTotal: 0, }, }, // add preview report action to main chat From 6932d6052c6ce827272dcbfca1c1cb977566c43f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 23 Oct 2024 15:35:02 +0800 Subject: [PATCH 012/174] fix lint --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5b529f662b35..b288441cd79d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4557,7 +4557,7 @@ function buildOptimisticExpenseReport( payeeAccountID: number, total: number, currency: string, - nonReimbursableTotal: number = 0, + nonReimbursableTotal = 0, parentReportActionID?: string, ): OptimisticExpenseReport { // The amount for Expense reports are stored as negative value in the database From 8ce39ae2c6913a33fbc996d1456e0b791edb3d9b Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 24 Oct 2024 10:40:30 +0800 Subject: [PATCH 013/174] fix wrong non reimbursable amount --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 1064a23d818c..9ac974b47ed8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2304,7 +2304,7 @@ function getTrackExpenseInformation( shouldCreateNewMoneyRequestReport = ReportUtils.shouldCreateNewMoneyRequestReport(iouReport, chatReport); if (!iouReport || shouldCreateNewMoneyRequestReport) { - iouReport = ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID ?? '-1', payeeAccountID, amount, currency, 0); + iouReport = ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID ?? '-1', payeeAccountID, amount, currency, amount); } else { iouReport = {...iouReport}; // Because of the Expense reports are stored as negative values, we subtract the total from the amount From c278a1a6091c19ad549ac5e06b2b8cfeb756fd45 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 24 Oct 2024 11:28:07 +0800 Subject: [PATCH 014/174] optimistically update unheld total for IOU --- src/libs/IOUUtils.ts | 9 +++++++++ src/libs/ReportUtils.ts | 6 ++++++ src/libs/actions/IOU.ts | 20 ++++++++++++++++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index dcfdd4bbc73a..3ef36460c405 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -75,6 +75,7 @@ function updateIOUOwnerAndTotal>( currency: string, isDeleting = false, isUpdating = false, + isOnhold = false, ): TReport { // For the update case, we have calculated the diff amount in the calculateDiffAmount function so there is no need to compare currencies here if ((currency !== iouReport?.currency && !isUpdating) || !iouReport) { @@ -86,11 +87,18 @@ function updateIOUOwnerAndTotal>( // Let us ensure a valid value before updating the total amount. iouReportUpdate.total = iouReportUpdate.total ?? 0; + iouReportUpdate.unheldTotal = iouReportUpdate.unheldTotal ?? 0; if (actorAccountID === iouReport.ownerAccountID) { iouReportUpdate.total += isDeleting ? -amount : amount; + if (!isOnhold) { + iouReportUpdate.unheldTotal += isDeleting ? -amount : amount; + } } else { iouReportUpdate.total += isDeleting ? amount : -amount; + if (!isOnhold) { + iouReportUpdate.unheldTotal += isDeleting ? amount : -amount; + } } if (iouReportUpdate.total < 0) { @@ -98,6 +106,7 @@ function updateIOUOwnerAndTotal>( iouReportUpdate.ownerAccountID = iouReport.managerID; iouReportUpdate.managerID = iouReport.ownerAccountID; iouReportUpdate.total = -iouReportUpdate.total; + iouReportUpdate.unheldTotal = -iouReportUpdate.unheldTotal; } return iouReportUpdate; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 39a2043db339..e3c689af2247 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -449,6 +449,9 @@ type OptimisticIOUReport = Pick< | 'stateNum' | 'statusNum' | 'total' + | 'unheldTotal' + | 'nonReimbursableTotal' + | 'unheldNonReimbursableTotal' | 'reportName' | 'parentReportID' | 'lastVisibleActionCreated' @@ -4458,6 +4461,9 @@ function buildOptimisticIOUReport(payeeAccountID: number, payerAccountID: number stateNum: isSendingMoney ? CONST.REPORT.STATE_NUM.APPROVED : CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: isSendingMoney ? CONST.REPORT.STATUS_NUM.REIMBURSED : CONST.REPORT.STATE_NUM.SUBMITTED, total, + unheldTotal: total, + nonReimbursableTotal: 0, + unheldNonReimbursableTotal: 0, // We don't translate reportName because the server response is always in English reportName: `${payerEmail} owes ${formattedTotal}`, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9ac974b47ed8..88482c195ca5 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2644,7 +2644,15 @@ function getUpdateMoneyRequestParams( } } } else { - updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID ?? -1, diff, TransactionUtils.getCurrency(transaction), false, true); + updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal( + iouReport, + updatedReportAction.actorAccountID ?? -1, + diff, + TransactionUtils.getCurrency(transaction), + false, + true, + isTransactionOnHold, + ); } if (updatedMoneyRequestReport) { @@ -5716,7 +5724,15 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT } } } else { - updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, reportAction.actorAccountID ?? -1, TransactionUtils.getAmount(transaction, false), currency, true); + updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal( + iouReport, + reportAction.actorAccountID ?? -1, + TransactionUtils.getAmount(transaction, false), + currency, + true, + false, + isTransactionOnHold, + ); } if (updatedIOUReport) { From 08ff8aae191cd40e0a36ec43f0d9179f9f3f2bd8 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 24 Oct 2024 13:15:53 +0700 Subject: [PATCH 015/174] Fix editing a task, navigate app to other report Signed-off-by: Tsaqif --- src/libs/actions/Task.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index c5a2442048fc..2bfb3e32915c 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -425,7 +425,7 @@ function completeTask(taskReport: OnyxEntry) { playSound(SOUNDS.SUCCESS); API.write(WRITE_COMMANDS.COMPLETE_TASK, parameters, {optimisticData, successData, failureData}); - Report.notifyNewAction(taskReportID, currentUserAccountID); + // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } /** @@ -509,7 +509,7 @@ function reopenTask(taskReport: OnyxEntry) { }; API.write(WRITE_COMMANDS.REOPEN_TASK, parameters, {optimisticData, successData, failureData}); - Report.notifyNewAction(taskReportID, currentUserAccountID); + // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task) { @@ -586,7 +586,7 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task }; API.write(WRITE_COMMANDS.EDIT_TASK, parameters, {optimisticData, successData, failureData}); - Report.notifyNewAction(report.reportID, currentUserAccountID); + // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, assigneeEmail: string, assigneeAccountID: number | null = 0, assigneeChatReport?: OnyxEntry) { @@ -725,7 +725,7 @@ function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, as }; API.write(WRITE_COMMANDS.EDIT_TASK_ASSIGNEE, parameters, {optimisticData, successData, failureData}); - Report.notifyNewAction(report.reportID, currentUserAccountID); + // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } /** @@ -1102,7 +1102,7 @@ function deleteTask(report: OnyxEntry) { }; API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); - Report.notifyNewAction(report.reportID, currentUserAccountID); + // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. if (shouldDeleteTaskReport) { Navigation.goBack(); From d95b51f05716d81f024fcea7511182da2d61ded8 Mon Sep 17 00:00:00 2001 From: c3024 Date: Fri, 8 Nov 2024 18:08:04 +0530 Subject: [PATCH 016/174] wip-move-tasks-to-admin-2 --- src/CONST.ts | 2 ++ src/libs/actions/Report.ts | 10 +++++++--- src/pages/home/ReportScreen.tsx | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d5dcb0e33f63..924cf1ba7274 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1619,6 +1619,7 @@ const CONST = { NOTIFICATIONS: 'notifications@expensify.com', PAYROLL: 'payroll@expensify.com', QA: 'qa@expensify.com', + QA_GUIDES: 'qa.guide@team.expensify.com', QA_TRAVIS: 'qa+travisreceipts@expensify.com', RECEIPTS: 'receipts@expensify.com', STUDENT_AMBASSADOR: 'studentambassadors@expensify.com', @@ -2079,6 +2080,7 @@ const CONST = { NOTIFICATIONS: Number(Config?.EXPENSIFY_ACCOUNT_ID_NOTIFICATIONS ?? 11665625), PAYROLL: Number(Config?.EXPENSIFY_ACCOUNT_ID_PAYROLL ?? 9679724), QA: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA ?? 3126513), + QA_GUIDE: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA_GUIDE ?? 14365522), QA_TRAVIS: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA_TRAVIS ?? 8595733), RECEIPTS: Number(Config?.EXPENSIFY_ACCOUNT_ID_RECEIPTS ?? -1), REWARDS: Number(Config?.EXPENSIFY_ACCOUNT_ID_REWARDS ?? 11023767), // rewards@expensify.com diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e6a10d01b798..70b275102704 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3439,8 +3439,11 @@ function completeOnboarding( } const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; - const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; - const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); + const actorAccountID = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; + // const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; + const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; + const targetChatReport = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); + // const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Introductory message @@ -3507,7 +3510,8 @@ function completeOnboarding( targetChatPolicyID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, ); - const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(CONST.EMAIL.CONCIERGE); + const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.EMAIL.QA_GUIDES : CONST.EMAIL.CONCIERGE; + const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(emailCreatingAction); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( currentTask.reportID, taskTitle, diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 4c3ed5c705a5..2b0201b19148 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -55,6 +55,7 @@ import ReportActionsView from './report/ReportActionsView'; import ReportFooter from './report/ReportFooter'; import type {ActionListContextType, ReactionListRef, ScrollPosition} from './ReportScreenContext'; import {ActionListContext, ReactionListContext} from './ReportScreenContext'; +import { usePersonalDetails } from '@components/OnyxProvider'; type ReportScreenNavigationProps = StackScreenProps; @@ -97,6 +98,8 @@ function getParentReportAction(parentReportActions: OnyxEntry Date: Fri, 8 Nov 2024 18:47:55 +0530 Subject: [PATCH 017/174] fix qa guide personal detail --- src/libs/actions/Report.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 70b275102704..b1d2a9afbedb 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3445,7 +3445,16 @@ function completeOnboarding( const targetChatReport = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); // const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; - + console.log("actorAccountID ", actorAccountID, "currentUserAccountID ", currentUserAccountID, "targetChatReport ", targetChatReport, "targetChatReportID ", targetChatReportID, "targetChatPolicyID ", targetChatPolicyID); + if (engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM) { + const optimisticPersonalDetailForQAGuide = { + accountID: actorAccountID, + avatar: allPersonalDetails?.[actorAccountID]?.avatar, + displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDES, + login: CONST.EMAIL.QA_GUIDES, + }; + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); + }; // Introductory message const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; From 55a077a390eeffa5832e68410ebc69df01ca966b Mon Sep 17 00:00:00 2001 From: I Nyoman Jyotisa Date: Mon, 11 Nov 2024 16:16:58 +0800 Subject: [PATCH 018/174] Fix: Expensify card - Error when wrong 4 digit is used persists for a few sec after a refresh --- .../settings/Wallet/ActivatePhysicalCardPage.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx index 23b694fe0d40..1aa21668bb1c 100644 --- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useCallback, useEffect, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -70,15 +70,19 @@ function ActivatePhysicalCardPage({ Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID)); }, [cardID, cardList, inactiveCard?.isLoading, inactiveCard?.state]); - useEffect( - () => () => { + useLayoutEffect(() => { + if (!inactiveCard?.cardID) { + return; + } + CardSettings.clearCardListErrors(inactiveCard?.cardID); + + return () => { if (!inactiveCard?.cardID) { return; } CardSettings.clearCardListErrors(inactiveCard?.cardID); - }, - [inactiveCard?.cardID], - ); + }; + }, [inactiveCard?.cardID]); /** * Update lastPressedDigit with value that was pressed on BigNumberPad. From 41340042d1a472c869987bb09634a24b2cfd0bce Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 16 Oct 2024 00:02:56 +0530 Subject: [PATCH 019/174] minor update. Signed-off-by: krishna2323 --- src/libs/WorkspaceReportFieldUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/WorkspaceReportFieldUtils.ts b/src/libs/WorkspaceReportFieldUtils.ts index 503c1d440d69..b7d93b8dee3a 100644 --- a/src/libs/WorkspaceReportFieldUtils.ts +++ b/src/libs/WorkspaceReportFieldUtils.ts @@ -80,7 +80,7 @@ function getReportFieldInitialValue(reportField: PolicyReportField | null): stri } if (reportField.type === CONST.REPORT_FIELD_TYPES.DATE) { - return Localize.translateLocal('common.initialValue'); + return Localize.translateLocal('common.currentDate'); } return reportField.value ?? reportField.defaultValue; From d27f2733bd8e2a980b58e56595e3b1eefb2f4a93 Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 11 Nov 2024 20:43:06 +0530 Subject: [PATCH 020/174] troubleshoot LHN alternate text --- ios/NewExpensify.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index d8eceab72b95..cd38fcaaaf6c 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -45,7 +45,7 @@ D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0D3F9E814828D91464DF9D35 /* PrivacyInfo.xcprivacy */; }; DD79042B2792E76D004484B4 /* RCTBootSplash.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD79042A2792E76D004484B4 /* RCTBootSplash.mm */; }; DDCB2E57F334C143AC462B43 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D20D83B0E39BA6D21761E72 /* ExpoModulesProvider.swift */; }; - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; }; F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; }; @@ -178,8 +178,8 @@ buildActionMask = 2147483647; files = ( 383643682B6D4AE2005BB9AE /* DeviceCheck.framework in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, - E51DC681C7DEE40AEBDDFBFE /* (null) in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, + E51DC681C7DEE40AEBDDFBFE /* BuildFile in Frameworks */, 8744C5400E24E379441C04A4 /* libPods-NewExpensify.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; From 3b31e9f33777f8f6b68fd3f7cd13b855457f92ff Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 11 Nov 2024 20:43:54 +0530 Subject: [PATCH 021/174] troubleshoot LHN alternate text --- src/components/LHNOptionsList/OptionRowLHN.tsx | 1 + src/libs/SidebarUtils.ts | 1 + src/libs/actions/Report.ts | 9 --------- .../BaseOnboardingEmployees.tsx | 16 +++++++++++++++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 94116181bccb..a3662d94b9f2 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -163,6 +163,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; const firstIcon = optionItem.icons?.at(0); + console.log("optionItem ", optionItem); return ( 0 ? ReportUtils.formatReportLastMessageText(Parser.htmlToText(lastMessageText)) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e32d6cb971d2..fc5345fdd4cc 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3444,15 +3444,6 @@ function completeOnboarding( // const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; console.log("actorAccountID ", actorAccountID, "currentUserAccountID ", currentUserAccountID, "targetChatReport ", targetChatReport, "targetChatReportID ", targetChatReportID, "targetChatPolicyID ", targetChatPolicyID); - if (engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM) { - const optimisticPersonalDetailForQAGuide = { - accountID: actorAccountID, - avatar: allPersonalDetails?.[actorAccountID]?.avatar, - displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDES, - login: CONST.EMAIL.QA_GUIDES, - }; - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); - }; // Introductory message const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index 1dd8807cc26e..cb6708d991b0 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -1,5 +1,5 @@ import React, {useMemo, useState} from 'react'; -import {useOnyx} from 'react-native-onyx'; +import Onyx, {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import FormHelpMessage from '@components/FormHelpMessage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -32,6 +32,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout(); const [selectedCompanySize, setSelectedCompanySize] = useState(onboardingCompanySize); const [error, setError] = useState(''); + const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const companySizeOptions: OnboardingListItem[] = useMemo(() => { return Object.values(CONST.ONBOARDING_COMPANY_SIZE).map((companySize): OnboardingListItem => { @@ -43,6 +44,18 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE }); }, [translate, selectedCompanySize]); + const addOptimticQAGuideDetail = () => { + const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; + const optimisticPersonalDetailForQAGuide = { + accountID: actorAccountID, + avatar: allPersonalDetails?.[actorAccountID]?.avatar, + displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDES, + login: CONST.EMAIL.QA_GUIDES, + }; + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); + } + + const footerContent = ( <> {!!error && ( @@ -95,6 +108,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE onSelectRow={(item) => { setSelectedCompanySize(item.keyForList); setError(''); + addOptimticQAGuideDetail(); }} initiallyFocusedOptionKey={companySizeOptions.find((item) => item.keyForList === selectedCompanySize)?.keyForList} shouldUpdateFocusedIndex From ff29d0d8090a110f065cba736ac034e2e95b94d0 Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 12 Nov 2024 21:21:18 +0530 Subject: [PATCH 022/174] fix optimistic lhn message for admins room --- .../LHNOptionsList/OptionRowLHN.tsx | 1 - src/libs/SidebarUtils.ts | 3 --- src/libs/actions/Report.ts | 4 +-- .../BaseOnboardingEmployees.tsx | 26 +++++++++---------- .../BaseOnboardingPurpose.tsx | 1 + src/pages/home/ReportScreen.tsx | 3 +-- 6 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index a3662d94b9f2..94116181bccb 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -163,7 +163,6 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar; const firstIcon = optionItem.icons?.at(0); - console.log("optionItem ", optionItem); return ( | null = report.lastActorAccountID && personalDetails?.[report.lastActorAccountID] ? personalDetails[report.lastActorAccountID] : null; - if (!lastActorDetails && visibleReportActionItems[report.reportID]) { const lastActorDisplayName = visibleReportActionItems[report.reportID]?.person?.[0]?.text; lastActorDetails = lastActorDisplayName @@ -485,7 +483,6 @@ function getOptionData({ } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_INTEGRATION) { result.alternateText = ReportActionsUtils.getRemovedConnectionMessage(lastAction); } else { - console.log("in the last else block of getOptionData's alternateText ", "reportID: ", report.reportID, "lastAction: ", lastAction); result.alternateText = lastMessageTextFromReport.length > 0 ? ReportUtils.formatReportLastMessageText(Parser.htmlToText(lastMessageText)) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index fc5345fdd4cc..9e0a4ccec116 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3438,12 +3438,9 @@ function completeOnboarding( const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; const actorAccountID = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; - // const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; const targetChatReport = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); - // const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; - console.log("actorAccountID ", actorAccountID, "currentUserAccountID ", currentUserAccountID, "targetChatReport ", targetChatReport, "targetChatReportID ", targetChatReportID, "targetChatPolicyID ", targetChatPolicyID); // Introductory message const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; @@ -3689,6 +3686,7 @@ function completeOnboarding( lastMentionedTime: DateUtils.getDBTime(), hasOutstandingChildTask, lastVisibleActionCreated, + lastActorAccountID: actorAccountID, }, }, { diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index cb6708d991b0..5cc5b2b0890e 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -32,7 +32,6 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout(); const [selectedCompanySize, setSelectedCompanySize] = useState(onboardingCompanySize); const [error, setError] = useState(''); - const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const companySizeOptions: OnboardingListItem[] = useMemo(() => { return Object.values(CONST.ONBOARDING_COMPANY_SIZE).map((companySize): OnboardingListItem => { @@ -44,17 +43,18 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE }); }, [translate, selectedCompanySize]); - const addOptimticQAGuideDetail = () => { - const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; - const optimisticPersonalDetailForQAGuide = { - accountID: actorAccountID, - avatar: allPersonalDetails?.[actorAccountID]?.avatar, - displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDES, - login: CONST.EMAIL.QA_GUIDES, - }; - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); - } - + const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + + const addOptimticQAGuidePersonalDetail = () => { + const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; + const optimisticPersonalDetailForQAGuide = { + accountID: actorAccountID, + avatar: allPersonalDetails?.[actorAccountID]?.avatar, + displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDES, + login: CONST.EMAIL.QA_GUIDES, + }; + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); + }; const footerContent = ( <> @@ -80,6 +80,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const {adminsChatReportID, policyID} = Policy.createWorkspace(undefined, true, '', Policy.generatePolicyID(), CONST.ONBOARDING_CHOICES.MANAGE_TEAM); Welcome.setOnboardingAdminsChatReportID(adminsChatReportID); Welcome.setOnboardingPolicyID(policyID); + addOptimticQAGuidePersonalDetail(); } Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(route.params?.backTo)); @@ -108,7 +109,6 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE onSelectRow={(item) => { setSelectedCompanySize(item.keyForList); setError(''); - addOptimticQAGuideDetail(); }} initiallyFocusedOptionKey={companySizeOptions.find((item) => item.keyForList === selectedCompanySize)?.keyForList} shouldUpdateFocusedIndex diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index 3b05c6bb40a8..703abba69510 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -84,6 +84,7 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, ro if (choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM) { Navigation.navigate(ROUTES.ONBOARDING_EMPLOYEES.getRoute(route.params?.backTo)); + return; } Navigation.navigate(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute(route.params?.backTo)); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 2b0201b19148..39674d428b29 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -13,6 +13,7 @@ import DragAndDropProvider from '@components/DragAndDrop/Provider'; import MoneyReportHeader from '@components/MoneyReportHeader'; import MoneyRequestHeader from '@components/MoneyRequestHeader'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import {usePersonalDetails} from '@components/OnyxProvider'; import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; import ScreenWrapper from '@components/ScreenWrapper'; import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; @@ -55,7 +56,6 @@ import ReportActionsView from './report/ReportActionsView'; import ReportFooter from './report/ReportFooter'; import type {ActionListContextType, ReactionListRef, ScrollPosition} from './ReportScreenContext'; import {ActionListContext, ReactionListContext} from './ReportScreenContext'; -import { usePersonalDetails } from '@components/OnyxProvider'; type ReportScreenNavigationProps = StackScreenProps; @@ -99,7 +99,6 @@ function getParentReportAction(parentReportActions: OnyxEntry Date: Tue, 12 Nov 2024 21:26:49 +0530 Subject: [PATCH 023/174] fix lint --- src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx | 4 ++-- src/pages/home/ReportScreen.tsx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index 5cc5b2b0890e..14c8d167a93c 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -45,7 +45,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); - const addOptimticQAGuidePersonalDetail = () => { + const setOptimticQAGuidePersonalDetail = () => { const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; const optimisticPersonalDetailForQAGuide = { accountID: actorAccountID, @@ -80,7 +80,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const {adminsChatReportID, policyID} = Policy.createWorkspace(undefined, true, '', Policy.generatePolicyID(), CONST.ONBOARDING_CHOICES.MANAGE_TEAM); Welcome.setOnboardingAdminsChatReportID(adminsChatReportID); Welcome.setOnboardingPolicyID(policyID); - addOptimticQAGuidePersonalDetail(); + setOptimticQAGuidePersonalDetail(); } Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(route.params?.backTo)); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 39674d428b29..dbe6a5badcf4 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -98,7 +98,6 @@ function getParentReportAction(parentReportActions: OnyxEntry Date: Tue, 12 Nov 2024 21:29:22 +0530 Subject: [PATCH 024/174] rename QA_GUIDE --- src/CONST.ts | 2 +- src/libs/actions/Report.ts | 2 +- src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 4624535adc62..489933f3ae43 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1619,7 +1619,7 @@ const CONST = { NOTIFICATIONS: 'notifications@expensify.com', PAYROLL: 'payroll@expensify.com', QA: 'qa@expensify.com', - QA_GUIDES: 'qa.guide@team.expensify.com', + QA_GUIDE: 'qa.guide@team.expensify.com', QA_TRAVIS: 'qa+travisreceipts@expensify.com', RECEIPTS: 'receipts@expensify.com', STUDENT_AMBASSADOR: 'studentambassadors@expensify.com', diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9e0a4ccec116..2fa4437f0ce9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3505,7 +3505,7 @@ function completeOnboarding( targetChatPolicyID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, ); - const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.EMAIL.QA_GUIDES : CONST.EMAIL.CONCIERGE; + const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.EMAIL.QA_GUIDE : CONST.EMAIL.CONCIERGE; const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(emailCreatingAction); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( currentTask.reportID, diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index 14c8d167a93c..e527e50afadd 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -50,8 +50,8 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const optimisticPersonalDetailForQAGuide = { accountID: actorAccountID, avatar: allPersonalDetails?.[actorAccountID]?.avatar, - displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDES, - login: CONST.EMAIL.QA_GUIDES, + displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDE, + login: CONST.EMAIL.QA_GUIDE, }; Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); }; From 528b1ac07e24a2603bb75893223ce174a088ad25 Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 12 Nov 2024 21:33:07 +0530 Subject: [PATCH 025/174] fix lint --- src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx | 1 + src/pages/home/ReportScreen.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index e527e50afadd..d0d2569fb530 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -53,6 +53,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDE, login: CONST.EMAIL.QA_GUIDE, }; + // eslint-disable-next-line rulesdir/prefer-actions-set-data Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); }; diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index dbe6a5badcf4..4c3ed5c705a5 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -13,7 +13,6 @@ import DragAndDropProvider from '@components/DragAndDrop/Provider'; import MoneyReportHeader from '@components/MoneyReportHeader'; import MoneyRequestHeader from '@components/MoneyRequestHeader'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {usePersonalDetails} from '@components/OnyxProvider'; import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; import ScreenWrapper from '@components/ScreenWrapper'; import TaskHeaderActionButton from '@components/TaskHeaderActionButton'; From 164a73a73e8f49abebb17a5490740640dcc35092 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 14 Nov 2024 19:31:22 +0800 Subject: [PATCH 026/174] fix syntax error --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 812b1b137884..df10224925f3 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6324,7 +6324,7 @@ function getReportFromHoldRequestsOnyxData( const coefficient = ReportUtils.isExpenseReport(iouReport) ? -1 : 1; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport); const holdAmount = ((iouReport?.total ?? 0) - (iouReport?.unheldTotal ?? 0)) * coefficient; - const holdNonReimbursableAmount = ((iouReport?.nonReimbursableTotal ?? 0) - (iouReport?.unheldNonReimbursableTotal ?? 0)) * coefficient,; + const holdNonReimbursableAmount = ((iouReport?.nonReimbursableTotal ?? 0) - (iouReport?.unheldNonReimbursableTotal ?? 0)) * coefficient; const optimisticExpenseReport = isPolicyExpenseChat ? ReportUtils.buildOptimisticExpenseReport( chatReport.reportID, From 5b131b5cd018ad5f7de4c46bde941ef8429451c4 Mon Sep 17 00:00:00 2001 From: I Nyoman Jyotisa Date: Thu, 14 Nov 2024 22:58:21 +0800 Subject: [PATCH 027/174] fix and migrate to useOnyx --- .../Wallet/ActivatePhysicalCardPage.tsx | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx index 1aa21668bb1c..534d4a7c2d90 100644 --- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx @@ -1,8 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import BigNumberPad from '@components/BigNumberPad'; import Button from '@components/Button'; import IllustratedHeaderPageLayout from '@components/IllustratedHeaderPageLayout'; @@ -25,21 +24,14 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import type {Card} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type ActivatePhysicalCardPageOnyxProps = { - /** Card list propTypes */ - cardList: OnyxEntry>; -}; - -type ActivatePhysicalCardPageProps = ActivatePhysicalCardPageOnyxProps & StackScreenProps; +type ActivatePhysicalCardPageProps = StackScreenProps; const LAST_FOUR_DIGITS_LENGTH = 4; const MAGIC_INPUT_MIN_HEIGHT = 86; function ActivatePhysicalCardPage({ - cardList, route: { params: {cardID = ''}, }, @@ -49,10 +41,12 @@ function ActivatePhysicalCardPage({ const {isExtraSmallScreenHeight} = useResponsiveLayout(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); + const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST); const [formError, setFormError] = useState(''); const [lastFourDigits, setLastFourDigits] = useState(''); const [lastPressedDigit, setLastPressedDigit] = useState(''); + const [shouldShowError, setShouldShowError] = useState(false); const inactiveCard = cardList?.[cardID]; const cardError = ErrorUtils.getLatestErrorMessage(inactiveCard ?? {}); @@ -70,7 +64,7 @@ function ActivatePhysicalCardPage({ Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(cardID)); }, [cardID, cardList, inactiveCard?.isLoading, inactiveCard?.state]); - useLayoutEffect(() => { + useEffect(() => { if (!inactiveCard?.cardID) { return; } @@ -106,6 +100,7 @@ function ActivatePhysicalCardPage({ }; const submitAndNavigateToNextPage = useCallback(() => { + setShouldShowError(true); activateCardCodeInputRef.current?.blur(); if (lastFourDigits.replace(CONST.MAGIC_CODE_EMPTY_CHAR, '').length !== LAST_FOUR_DIGITS_LENGTH) { @@ -144,7 +139,7 @@ function ActivatePhysicalCardPage({ lastPressedDigit={lastPressedDigit} onChangeText={onCodeInput} onFulfill={submitAndNavigateToNextPage} - errorText={formError || cardError} + errorText={shouldShowError ? formError || cardError : ''} ref={activateCardCodeInputRef} /> @@ -168,8 +163,4 @@ function ActivatePhysicalCardPage({ ActivatePhysicalCardPage.displayName = 'ActivatePhysicalCardPage'; -export default withOnyx({ - cardList: { - key: ONYXKEYS.CARD_LIST, - }, -})(ActivatePhysicalCardPage); +export default ActivatePhysicalCardPage; From f49c46f746792e7edf6ae99ae68c5412ffced446 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Sat, 16 Nov 2024 02:25:34 +0700 Subject: [PATCH 028/174] add placeholder thumbnail to expenses with no receipt --- src/components/ReceiptEmptyState.tsx | 19 +++++++++++++++---- src/components/ReceiptImage.tsx | 8 ++++++++ .../ReportActionItemImage.tsx | 4 ++++ .../ReportActionItemImages.tsx | 3 ++- .../ReportActionItem/ReportPreview.tsx | 16 +++++++--------- src/libs/ReceiptUtils.ts | 4 ++++ src/styles/index.ts | 5 +++++ src/styles/variables.ts | 2 +- 8 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/components/ReceiptEmptyState.tsx b/src/components/ReceiptEmptyState.tsx index 71d64c7483f1..a64893fb3439 100644 --- a/src/components/ReceiptEmptyState.tsx +++ b/src/components/ReceiptEmptyState.tsx @@ -1,5 +1,6 @@ import React from 'react'; import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; import Icon from './Icon'; @@ -14,12 +15,15 @@ type ReceiptEmptyStateProps = { onPress?: () => void; disabled?: boolean; + + isThumbnail?: boolean; }; // Returns an SVG icon indicating that the user should attach a receipt -function ReceiptEmptyState({hasError = false, onPress = () => {}, disabled = false}: ReceiptEmptyStateProps) { +function ReceiptEmptyState({hasError = false, onPress = () => {}, disabled = false, isThumbnail = false}: ReceiptEmptyStateProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const theme = useTheme(); return ( {}, disabled = fal onPress={onPress} disabled={disabled} disabledStyle={styles.cursorDefault} - style={[styles.alignItemsCenter, styles.justifyContentCenter, styles.moneyRequestViewImage, styles.moneyRequestAttachReceipt, hasError && styles.borderColorDanger]} + style={[ + styles.alignItemsCenter, + styles.justifyContentCenter, + styles.moneyRequestViewImage, + isThumbnail ? styles.moneyRequestAttachReceiptThumbnail : styles.moneyRequestAttachReceipt, + hasError && styles.borderColorDanger, + ]} > ); diff --git a/src/components/ReceiptImage.tsx b/src/components/ReceiptImage.tsx index 8c980838b841..f8c714e5611c 100644 --- a/src/components/ReceiptImage.tsx +++ b/src/components/ReceiptImage.tsx @@ -7,6 +7,7 @@ import EReceiptThumbnail from './EReceiptThumbnail'; import type {IconSize} from './EReceiptThumbnail'; import Image from './Image'; import PDFThumbnail from './PDFThumbnail'; +import ReceiptEmptyState from './ReceiptEmptyState'; import ThumbnailImage from './ThumbnailImage'; type Style = {height: number; borderRadius: number; margin: number}; @@ -79,6 +80,8 @@ type ReceiptImageProps = ( /** The background color of fallback icon */ fallbackIconBackground?: string; + + isEmptyReceipt?: boolean; }; function ReceiptImage({ @@ -97,9 +100,14 @@ function ReceiptImage({ shouldUseInitialObjectPosition = false, fallbackIconColor, fallbackIconBackground, + isEmptyReceipt = false, }: ReceiptImageProps) { const styles = useThemeStyles(); + if (isEmptyReceipt) { + return ; + } + if (isPDFThumbnail) { return ( - {shownImages.map(({thumbnail, isThumbnail, image, transaction, isLocalFile, fileExtension, filename}, index) => { + {shownImages.map(({thumbnail, isThumbnail, image, isEmptyReceipt, transaction, isLocalFile, fileExtension, filename}, index) => { // Show a border to separate multiple images. Shown to the right for each except the last. const shouldShowBorder = shownImages.length > 1 && index < shownImages.length - 1; const borderStyle = shouldShowBorder ? styles.reportActionItemImageBorder : {}; @@ -81,6 +81,7 @@ function ReportActionItemImages({images, size, total, isHovered = false}: Report fileExtension={fileExtension} image={image} isLocalFile={isLocalFile} + isEmptyReceipt={isEmptyReceipt} filename={filename} transaction={transaction} isThumbnail={isThumbnail} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 5edeffd4dea4..5125b048fae2 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -162,8 +162,8 @@ function ReportPreview({ ReportUtils.hasWarningTypeViolations(iouReportID, transactionViolations, true) || (ReportUtils.isReportOwner(iouReport) && ReportUtils.hasReportViolations(iouReportID)) || ReportUtils.hasActionsWithErrors(iouReportID); - const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); - const lastThreeReceipts = lastThreeTransactionsWithReceipts.map((transaction) => ({...ReceiptUtils.getThumbnailAndImageURIs(transaction), transaction})); + const lastThreeTransactions = allTransactions.slice(-3); + const lastThreeReceipts = lastThreeTransactions.map((transaction) => ({...ReceiptUtils.getThumbnailAndImageURIs(transaction), transaction})); const showRTERViolationMessage = numberOfRequests === 1 && TransactionUtils.hasPendingUI(allTransactions.at(0), TransactionUtils.getTransactionViolations(allTransactions.at(0)?.transactionID ?? '-1', transactionViolations)); @@ -472,13 +472,11 @@ function ReportPreview({ accessibilityLabel={translate('iou.viewDetails')} > - {hasReceipts && ( - - )} + diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index ed7def2ed1ec..7082bcd17963 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -16,6 +16,7 @@ type ThumbnailAndImageURI = { isThumbnail?: boolean; filename?: string; fileExtension?: string; + isEmptyReceipt?: boolean; }; /** @@ -26,6 +27,9 @@ type ThumbnailAndImageURI = { * @param receiptFileName */ function getThumbnailAndImageURIs(transaction: OnyxEntry, receiptPath: ReceiptSource | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { + if (!TransactionUtils.hasReceipt(transaction)) { + return {isEmptyReceipt: true}; + } if (TransactionUtils.isFetchingWaypointsFromServer(transaction)) { return {isThumbnail: true, isLocalFile: true}; } diff --git a/src/styles/index.ts b/src/styles/index.ts index 8ebfc5582b02..3c4f02a3bf77 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4633,6 +4633,11 @@ const styles = (theme: ThemeColors) => borderWidth: 1, }, + moneyRequestAttachReceiptThumbnail: { + backgroundColor: theme.hoverComponentBG, + width: '100%', + }, + mapViewContainer: { ...flex.flex1, minHeight: 300, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 5a8927ede6d0..1fefca65e3ba 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -182,7 +182,7 @@ export default { eReceiptThumbnailCenterReceiptBreakpoint: 200, eReceiptIconHeight: 100, eReceiptIconWidth: 72, - eReceiptEmptyIconWidth: 76, + eReceiptEmptyIconWidth: 64, eReceiptMCCHeightWidth: 40, eReceiptIconHeightSmall: 65, eReceiptIconWidthSmall: 46, From c0ee29f818a7d572ae055cb1e92dd04666b2803f Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 18 Nov 2024 11:24:14 +0530 Subject: [PATCH 029/174] currentUserAccountID as actor --- src/libs/actions/Report.ts | 4 ++-- .../BaseOnboardingEmployees.tsx | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 514fbe3b44e7..75c88849b3f4 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3494,7 +3494,7 @@ function prepareOnboardingOptimisticData( } const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; - const actorAccountID = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; + const actorAccountID = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? currentUserAccountID : CONST.ACCOUNT_ID.CONCIERGE; const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; const targetChatReport = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; @@ -3567,7 +3567,7 @@ function prepareOnboardingOptimisticData( targetChatPolicyID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, ); - const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.EMAIL.QA_GUIDE : CONST.EMAIL.CONCIERGE; + const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? currentUserEmail ?? CONST.EMAIL.CONCIERGE : CONST.EMAIL.CONCIERGE; const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(emailCreatingAction); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( currentTask.reportID, diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index ed81bd70a88f..e274d90ee98b 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -45,17 +45,17 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); - const setOptimticQAGuidePersonalDetail = () => { - const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; - const optimisticPersonalDetailForQAGuide = { - accountID: actorAccountID, - avatar: allPersonalDetails?.[actorAccountID]?.avatar, - displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDE, - login: CONST.EMAIL.QA_GUIDE, - }; - // eslint-disable-next-line rulesdir/prefer-actions-set-data - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); - }; + // const setOptimticQAGuidePersonalDetail = () => { + // const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; + // const optimisticPersonalDetailForQAGuide = { + // accountID: actorAccountID, + // avatar: allPersonalDetails?.[actorAccountID]?.avatar, + // displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDE, + // login: CONST.EMAIL.QA_GUIDE, + // }; + // // eslint-disable-next-line rulesdir/prefer-actions-set-data + // Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); + // }; const footerContent = ( <> @@ -81,7 +81,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const {adminsChatReportID, policyID} = Policy.createWorkspace(undefined, true, '', Policy.generatePolicyID(), CONST.ONBOARDING_CHOICES.MANAGE_TEAM); Welcome.setOnboardingAdminsChatReportID(adminsChatReportID); Welcome.setOnboardingPolicyID(policyID); - setOptimticQAGuidePersonalDetail(); + // setOptimticQAGuidePersonalDetail(); } Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(route.params?.backTo)); From 0e876b436e4f24b143a5fea9ded61d9654d4dfea Mon Sep 17 00:00:00 2001 From: Rutika Pawar <183392827+twilight2294@users.noreply.github.com> Date: Tue, 19 Nov 2024 01:34:17 +0530 Subject: [PATCH 030/174] fix offline indicator style --- .../accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx index e75496caa451..992b42dd9478 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx @@ -39,6 +39,7 @@ function NetSuiteAutoSyncPage({policy, route}: WithPolicyConnectionsProps) { includeSafeAreaPaddingBottom={false} style={[styles.defaultModalContainer]} testID={NetSuiteAutoSyncPage.displayName} + offlineIndicatorStyle={styles.mtAuto} > Date: Mon, 18 Nov 2024 16:45:21 -0600 Subject: [PATCH 031/174] Update Configure-Sage-Intacct.md https://github.com/Expensify/App/issues/50430 --- .../connections/sage-intacct/Configure-Sage-Intacct.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md index 1f0be2f4571a..094071634e24 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md @@ -1,4 +1,4 @@ ---- +**--- title: Configure Sage Intacct description: Configure Sage Intacct's export, coding, and advanced settings. --- @@ -151,3 +151,4 @@ If a report has been exported to Intacct and marked as paid in Intacct, we'll au If a report has not been exported to Intacct, it will not be exported to Intacct automatically. {% include faq-end.md %} +** From f19ab25853e6933ed557a60247fcaa2834c09679 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Mon, 18 Nov 2024 16:49:05 -0600 Subject: [PATCH 032/174] Update Sage Intacct Connect and Configure docs with images and alt text https://github.com/Expensify/App/issues/50430 --- .../connections/sage-intacct/Configure-Sage-Intacct.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md index 094071634e24..1f0be2f4571a 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md @@ -1,4 +1,4 @@ -**--- +--- title: Configure Sage Intacct description: Configure Sage Intacct's export, coding, and advanced settings. --- @@ -151,4 +151,3 @@ If a report has been exported to Intacct and marked as paid in Intacct, we'll au If a report has not been exported to Intacct, it will not be exported to Intacct automatically. {% include faq-end.md %} -** From 936b87aba7dbc910f2498c6862c2e6375ad4380e Mon Sep 17 00:00:00 2001 From: John Schuster Date: Mon, 18 Nov 2024 16:49:37 -0600 Subject: [PATCH 033/174] Update Connect-To-Sage-Intacct.md --- .../sage-intacct/Connect-To-Sage-Intacct.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md index 76851a35ce4c..a4392a45dc20 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md @@ -53,8 +53,13 @@ Setup the user using these configurations: - **User Type:** "Business" - **Admin Privileges:** "Full" - **Status:** "Active" + +![Image of Sage Intacct Web Services User setup]({{site.url}}/assets/images/SageConnectSettingUpWebServicesUser.png){:width="100%"} + Once you've created the user, you'll need to set the correct permissions. To set those, go to the **subscription** link for this user in the user list, **click on the checkbox** next to the Application/Module and then click on the **Permissions** link to modify those. +![Insert alt text for accessibility here]({{site.url}}/assets/images/SageConnectSubscriptionSettings.png){:width="100%"} + These are the permissions required for a user to export reimbursable expenses as Expense Reports: - **Administration (All)** - **Company (Read-only)** @@ -64,8 +69,7 @@ These are the permissions required for a user to export reimbursable expenses as - **Projects (Read-only)** (only needed if using Projects and Customers) - **Accounts Payable (All)** (only needed for exporting non-reimbursable expenses as vendor bills) -**Note:** you can set permissions for each Application/Module by selecting the radio button next to the desired Permission and clicking **Save**. - +**Note:** You can set permissions for each Application/Module by selecting the radio button next to the desired Permission and clicking **Save**. ### Step 2: Enable the Time & Expenses Module (Only required if exporting reimbursable expenses as Expense Reports) The Time & Expenses (T&E) module is often included in your Sage Intacct instance, but if it wasn't part of your initial Sage Intacct setup, you may need to enable it. **Enabling the T&E module is a paid subscription through Sage Intacct. For information on the costs of enabling this module, please contact your Sage Intacct account manager**. It's necessary for our integration and only takes a few minutes to configure. @@ -76,6 +80,8 @@ The Time & Expenses (T&E) module is often included in your Sage Intacct instance - **Expense Report:** EXP - **Employee:** EMP - **Duplicate Numbers:** Select “Do not allow creation” + +![Image of Sage Intacct Time and Expense Auto-numbering Sequences Settings]({{site.url}}/assets/images/SageConnectTimeandExpenseSequenceNumbers.png){:width="100%"} - To create the EXP sequence, **click on the down arrow on the expense report line and select **Add**: - **Sequence ID:** EXP @@ -126,8 +132,14 @@ To enable Customization Services go to **Company > Subscriptions > Customization ### Step 6: Create a Test Workspace in Expensify and Download the [Expensify Package](https://www.expensify.com/tools/integrations/downloadPackage) Creating a test workspace in Expensify allows you to have a sandbox environment for testing before implementing the integration live. If you are already using Expensify, creating a test workspace ensures that your existing group workspace rules and approval workflows remain intact. Here's how to set it up: 1. Go to **expensify.com > Settings > Workspaces > New Workspace**. + +![Image of creating a new Workspace in Expensify]({{site.url}}/assets/images/SageConnectCreatingWorkspace.png){:width="100%"} + 2. Name the workspace something like "Sage Intacct Test Workspace." 3. Go to **Connections > Sage Intacct > Connect to Sage Intacct**. + +![Image of selecting the Sage Intacct integration in Expensify]({{site.url}}/assets/images/SageConnectEnableSage.png){:width="100%"} + 4. Select **Download Package** (You only need to download the file; we'll upload it from your Downloads folder later). @@ -150,6 +162,7 @@ If you use **Platform Services**: 1. Go to **Company > Company Info > Security** in Intacct and click **Edit**. 2. Scroll down to **Web Services Authorizations** and add "expensify" (all lower case) as a Sender ID. +![Image of Web Services Authorizations in Sage Intacct]({{site.url}}/assets/images/SageConnectWebServicesAuthorizations.png){:width="100%"} ### Step 9: Enter Credentials and Connect Expensify and Sage Intacct @@ -158,6 +171,8 @@ If you use **Platform Services**: 2. Click **Connect to Sage Intacct** and enter the credentials you've set for your web services user. 3. Click **Send** once you're done. +![Image of Sage Intacct credentials being entered in Expensify to connect the integration]({{site.url}}/assets/images/SageConnectEnterCredentials.png){:width="100%"} + Next, you’ll configure the Export, Coding, and Advanced tabs of the connection configuration in Expensify. From 224a164caba013e604cab92210b7fd63a3c1d6cb Mon Sep 17 00:00:00 2001 From: John Schuster Date: Mon, 18 Nov 2024 16:52:33 -0600 Subject: [PATCH 034/174] Update Connect-To-Sage-Intacct.md --- .../connections/sage-intacct/Connect-To-Sage-Intacct.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md index a4392a45dc20..fb340ad4b77f 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md @@ -58,7 +58,7 @@ Setup the user using these configurations: Once you've created the user, you'll need to set the correct permissions. To set those, go to the **subscription** link for this user in the user list, **click on the checkbox** next to the Application/Module and then click on the **Permissions** link to modify those. -![Insert alt text for accessibility here]({{site.url}}/assets/images/SageConnectSubscriptionSettings.png){:width="100%"} +![Image showing the Application/Module checkbox to click]({{site.url}}/assets/images/SageConnectSubscriptionSettings.png){:width="100%"} These are the permissions required for a user to export reimbursable expenses as Expense Reports: - **Administration (All)** From b09438cd668327f2bae747ff62f70d7b762e3e22 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Mon, 18 Nov 2024 16:53:54 -0600 Subject: [PATCH 035/174] Update Configure-Sage-Intacct.md --- .../connections/sage-intacct/Configure-Sage-Intacct.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md index 1f0be2f4571a..0c9e6c87f9ab 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md @@ -11,6 +11,8 @@ There are several options for exporting Expensify reports to Sage Intacct. Let's To access these settings, go to **Settings > Workspace > Group > Connections** and select the **Configure** button. +![Highlighting the Configure button for the Sage Intacct Integration]({{site.url}}/assets/images/SageConfigureIntegrationConfigureButton.png){:width="100%"} + ## Export Options ### Preferred Exporter @@ -95,6 +97,8 @@ To find the Integration Name in Sage Intacct: 1. Go to **Platform Services > Objects > List** 2. Set "filter by application" to "user-defined dimensions." +![Image of Sage Intacct Objects filtered by User Defined Dimension]({{site.url}}/assets/images/SageConfigureUserDefinedDimensionsFilter.png){:width="100%"} + Now, in Expensify, navigate to **Settings > Workspaces > Group > [Workspace Name] > Connections**, and click **Configure** under Sage Intacct. On the Coding tab, enable the toggle next to User Defined Dimensions. Enter the "Integration name" and choose whether to import it into Expensify as an expense-level Tag or as a Report Field, then click **Save**. You'll now see the values for your custom segment available under Tags settings or Report Fields settings in Expensify. From 55e4bce6f31b611d8c610af4ae1604323666b8a6 Mon Sep 17 00:00:00 2001 From: jacobkim9881 Date: Tue, 19 Nov 2024 10:45:57 +0900 Subject: [PATCH 036/174] fix: add navigating back to Calendar on Chrome mWeb --- src/components/DatePicker/CalendarPicker/YearPickerModal.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx index 6170b81073a2..3c4bb94d1b6d 100644 --- a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx +++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx @@ -6,6 +6,7 @@ import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as Browser from '@libs/Browser'; import CONST from '@src/CONST'; import type CalendarPickerListItem from './types'; @@ -53,6 +54,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear onModalHide={onClose} hideModalContentWhileAnimating useNativeDriver + shouldHandleNavigationBack={Browser.isMobileChrome()} > Date: Tue, 19 Nov 2024 09:13:06 +0530 Subject: [PATCH 037/174] Revert "currentUserAccountID as actor" This reverts commit c0ee29f818a7d572ae055cb1e92dd04666b2803f. --- src/libs/actions/Report.ts | 4 ++-- .../BaseOnboardingEmployees.tsx | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 75c88849b3f4..514fbe3b44e7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3494,7 +3494,7 @@ function prepareOnboardingOptimisticData( } const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; - const actorAccountID = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? currentUserAccountID : CONST.ACCOUNT_ID.CONCIERGE; + const actorAccountID = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; const targetChatReport = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; @@ -3567,7 +3567,7 @@ function prepareOnboardingOptimisticData( targetChatPolicyID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, ); - const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? currentUserEmail ?? CONST.EMAIL.CONCIERGE : CONST.EMAIL.CONCIERGE; + const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.EMAIL.QA_GUIDE : CONST.EMAIL.CONCIERGE; const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(emailCreatingAction); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( currentTask.reportID, diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index e274d90ee98b..ed81bd70a88f 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -45,17 +45,17 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); - // const setOptimticQAGuidePersonalDetail = () => { - // const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; - // const optimisticPersonalDetailForQAGuide = { - // accountID: actorAccountID, - // avatar: allPersonalDetails?.[actorAccountID]?.avatar, - // displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDE, - // login: CONST.EMAIL.QA_GUIDE, - // }; - // // eslint-disable-next-line rulesdir/prefer-actions-set-data - // Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); - // }; + const setOptimticQAGuidePersonalDetail = () => { + const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; + const optimisticPersonalDetailForQAGuide = { + accountID: actorAccountID, + avatar: allPersonalDetails?.[actorAccountID]?.avatar, + displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDE, + login: CONST.EMAIL.QA_GUIDE, + }; + // eslint-disable-next-line rulesdir/prefer-actions-set-data + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); + }; const footerContent = ( <> @@ -81,7 +81,7 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const {adminsChatReportID, policyID} = Policy.createWorkspace(undefined, true, '', Policy.generatePolicyID(), CONST.ONBOARDING_CHOICES.MANAGE_TEAM); Welcome.setOnboardingAdminsChatReportID(adminsChatReportID); Welcome.setOnboardingPolicyID(policyID); - // setOptimticQAGuidePersonalDetail(); + setOptimticQAGuidePersonalDetail(); } Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(route.params?.backTo)); From b2b933d38a0f598970b078187353be788f5d3dfe Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 19 Nov 2024 10:55:51 +0530 Subject: [PATCH 038/174] remove redundant optimistic introduction message --- src/libs/actions/Report.ts | 115 +++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 38 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 514fbe3b44e7..7c2845e83516 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3492,11 +3492,11 @@ function prepareOnboardingOptimisticData( data = CONST.COMBINED_TRACK_SUBMIT_ONBOARDING_MESSAGES[CONST.ONBOARDING_CHOICES.SUBMIT]; } } - + const isEngagementChoiceManageTeam = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM; const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; - const actorAccountID = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; + const actorAccountID = isEngagementChoiceManageTeam ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; - const targetChatReport = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); + const targetChatReport = isEngagementChoiceManageTeam ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Introductory message const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); @@ -3755,7 +3755,6 @@ function prepareOnboardingOptimisticData( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: { - [introductionCommentAction.reportActionID]: introductionCommentAction as ReportAction, [textCommentAction.reportActionID]: textCommentAction as ReportAction, }, }, @@ -3765,6 +3764,18 @@ function prepareOnboardingOptimisticData( value: {choice: engagementChoice}, }, ); + // "Manage team" engagement choice should not add the introduction message + // Backend returns a different introduction message in #admins room for "Manage team" engagement choice + // If we add the introduction message, it will be duplicated in the #admins room + if (!isEngagementChoiceManageTeam) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [introductionCommentAction.reportActionID]: introductionCommentAction as ReportAction, + }, + }); + } if (!wasInvited) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -3774,15 +3785,28 @@ function prepareOnboardingOptimisticData( } const successData: OnyxUpdate[] = [...tasksForSuccessData]; + successData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: { - [introductionCommentAction.reportActionID]: {pendingAction: null}, [textCommentAction.reportActionID]: {pendingAction: null}, }, }); + // "Manage team" engagement choice should not add the introduction message + // Backend returns a different introduction message in #admins room for "Manage team" engagement choice + // If we add the introduction message, it will be duplicated in the #admins room + if (!isEngagementChoiceManageTeam) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [introductionCommentAction.reportActionID]: {pendingAction: null}, + }, + }); + } + let failureReport: Partial = { lastMessageTranslationKey: '', lastMessageText: '', @@ -3813,9 +3837,6 @@ function prepareOnboardingOptimisticData( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: { - [introductionCommentAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), - } as ReportAction, [textCommentAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), } as ReportAction, @@ -3828,6 +3849,21 @@ function prepareOnboardingOptimisticData( }, ); + // "Manage team" engagement choice should not add the introduction message + // Backend returns a different introduction message in #admins room for "Manage team" engagement choice + // If we add the introduction message, it will be duplicated in the #admins room + if (!isEngagementChoiceManageTeam) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [introductionCommentAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), + } as ReportAction, + }, + }); + } + if (!wasInvited) { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -3868,39 +3904,42 @@ function prepareOnboardingOptimisticData( }); } - const guidedSetupData: GuidedSetupData = [ - {type: 'message', ...introductionMessage}, - {type: 'message', ...textMessage}, - ]; - - if ('video' in data && data.video && videoCommentAction && videoMessage) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [videoCommentAction.reportActionID]: videoCommentAction as ReportAction, - }, - }); + const guidedSetupData: GuidedSetupData = isEngagementChoiceManageTeam + ? [] + : [ + {type: 'message', ...introductionMessage}, + {type: 'message', ...textMessage}, + ]; + if (!isEngagementChoiceManageTeam) { + if ('video' in data && data.video && videoCommentAction && videoMessage) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [videoCommentAction.reportActionID]: videoCommentAction as ReportAction, + }, + }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [videoCommentAction.reportActionID]: {pendingAction: null}, - }, - }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [videoCommentAction.reportActionID]: {pendingAction: null}, + }, + }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [videoCommentAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), - } as ReportAction, - }, - }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [videoCommentAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), + } as ReportAction, + }, + }); - guidedSetupData.push({type: 'video', ...data.video, ...videoMessage}); + guidedSetupData.push({type: 'video', ...data.video, ...videoMessage}); + } } guidedSetupData.push(...tasksForParameters); From ff1669966969ccf8bcbb5320ec0b033eed222f00 Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 19 Nov 2024 17:35:34 +0530 Subject: [PATCH 039/174] remove introduction message --- src/libs/actions/Report.ts | 56 ++------------------------------------ 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7c2845e83516..4353c4929923 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3498,14 +3498,6 @@ function prepareOnboardingOptimisticData( const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; const targetChatReport = isEngagementChoiceManageTeam ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; - // Introductory message - const introductionComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ONBOARDING_INTRODUCTION, undefined, actorAccountID); - const introductionCommentAction: OptimisticAddCommentReportAction = introductionComment.reportAction; - const introductionMessage: AddCommentOrAttachementParams = { - reportID: targetChatReportID, - reportActionID: introductionCommentAction.reportActionID, - reportComment: introductionComment.commentText, - }; // Text message const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID, 1); @@ -3764,18 +3756,7 @@ function prepareOnboardingOptimisticData( value: {choice: engagementChoice}, }, ); - // "Manage team" engagement choice should not add the introduction message - // Backend returns a different introduction message in #admins room for "Manage team" engagement choice - // If we add the introduction message, it will be duplicated in the #admins room - if (!isEngagementChoiceManageTeam) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [introductionCommentAction.reportActionID]: introductionCommentAction as ReportAction, - }, - }); - } + if (!wasInvited) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -3794,19 +3775,6 @@ function prepareOnboardingOptimisticData( }, }); - // "Manage team" engagement choice should not add the introduction message - // Backend returns a different introduction message in #admins room for "Manage team" engagement choice - // If we add the introduction message, it will be duplicated in the #admins room - if (!isEngagementChoiceManageTeam) { - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [introductionCommentAction.reportActionID]: {pendingAction: null}, - }, - }); - } - let failureReport: Partial = { lastMessageTranslationKey: '', lastMessageText: '', @@ -3849,21 +3817,6 @@ function prepareOnboardingOptimisticData( }, ); - // "Manage team" engagement choice should not add the introduction message - // Backend returns a different introduction message in #admins room for "Manage team" engagement choice - // If we add the introduction message, it will be duplicated in the #admins room - if (!isEngagementChoiceManageTeam) { - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [introductionCommentAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), - } as ReportAction, - }, - }); - } - if (!wasInvited) { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -3904,12 +3857,7 @@ function prepareOnboardingOptimisticData( }); } - const guidedSetupData: GuidedSetupData = isEngagementChoiceManageTeam - ? [] - : [ - {type: 'message', ...introductionMessage}, - {type: 'message', ...textMessage}, - ]; + const guidedSetupData: GuidedSetupData = [{type: 'message', ...textMessage}]; if (!isEngagementChoiceManageTeam) { if ('video' in data && data.video && videoCommentAction && videoMessage) { optimisticData.push({ From 39dbcc724bcca58ee87ddcb332568b04019da70d Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 19 Nov 2024 17:51:53 +0530 Subject: [PATCH 040/174] retain existing introductory video --- src/libs/actions/Report.ts | 52 ++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4353c4929923..3334eb7fa927 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3858,36 +3858,34 @@ function prepareOnboardingOptimisticData( } const guidedSetupData: GuidedSetupData = [{type: 'message', ...textMessage}]; - if (!isEngagementChoiceManageTeam) { - if ('video' in data && data.video && videoCommentAction && videoMessage) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [videoCommentAction.reportActionID]: videoCommentAction as ReportAction, - }, - }); + if ('video' in data && data.video && videoCommentAction && videoMessage) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [videoCommentAction.reportActionID]: videoCommentAction as ReportAction, + }, + }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [videoCommentAction.reportActionID]: {pendingAction: null}, - }, - }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [videoCommentAction.reportActionID]: {pendingAction: null}, + }, + }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [videoCommentAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), - } as ReportAction, - }, - }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [videoCommentAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), + } as ReportAction, + }, + }); - guidedSetupData.push({type: 'video', ...data.video, ...videoMessage}); - } + guidedSetupData.push({type: 'video', ...data.video, ...videoMessage}); } guidedSetupData.push(...tasksForParameters); From 0362c6f7acf50def9d9575afc0d94faf12b79259 Mon Sep 17 00:00:00 2001 From: c3024 Date: Wed, 20 Nov 2024 15:19:54 +0530 Subject: [PATCH 041/174] remove needless text break changes --- src/libs/SidebarUtils.ts | 1 + src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 0eb62a8b795c..fe564353371b 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -397,6 +397,7 @@ function getOptionData({ undefined, ReportUtils.isSelfDM(report), ); + // If the last actor's details are not currently saved in Onyx Collection, // then try to get that from the last report action if that action is valid // to get data from. diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index 0aa27e1bb380..74d2facf55dc 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -84,7 +84,6 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, ro if (choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM) { Navigation.navigate(ROUTES.ONBOARDING_EMPLOYEES.getRoute(route.params?.backTo)); - return; } Navigation.navigate(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute(route.params?.backTo)); From 6ebfd279d5e142d9c587f7c8add1278602d9f8f6 Mon Sep 17 00:00:00 2001 From: c3024 Date: Wed, 20 Nov 2024 15:24:17 +0530 Subject: [PATCH 042/174] no changes in SidebarUtils --- src/libs/SidebarUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index fe564353371b..806cebd9bf7f 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -402,6 +402,7 @@ function getOptionData({ // then try to get that from the last report action if that action is valid // to get data from. let lastActorDetails: Partial | null = report.lastActorAccountID && personalDetails?.[report.lastActorAccountID] ? personalDetails[report.lastActorAccountID] : null; + if (!lastActorDetails && visibleReportActionItems[report.reportID]) { const lastActorDisplayName = visibleReportActionItems[report.reportID]?.person?.[0]?.text; lastActorDetails = lastActorDisplayName From 35238fbe0d1dfe77c31e041342d4f5f3a53b7d43 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 21 Nov 2024 07:04:01 +0700 Subject: [PATCH 043/174] revert some changes Signed-off-by: Tsaqif --- src/libs/actions/Task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 2bfb3e32915c..368db53db90c 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1102,7 +1102,7 @@ function deleteTask(report: OnyxEntry) { }; API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); - // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. + Report.notifyNewAction(report.reportID, currentUserAccountID); if (shouldDeleteTaskReport) { Navigation.goBack(); From 7cb535cb42dcec0695e37c604babd68b11d1b706 Mon Sep 17 00:00:00 2001 From: c3024 Date: Thu, 21 Nov 2024 11:18:03 +0530 Subject: [PATCH 044/174] tooltip on admins room for manageTeam intent --- src/components/LHNOptionsList/OptionRowLHN.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 3e3f4d1b8e5d..fc31360173f2 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -51,6 +51,9 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const [isOnboardingCompleted = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { selector: hasCompletedGuidedSetupFlowSelector, }); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const isOnboardingChoiceManageTeam = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM; + const shouldShowToooltipOnThisReport = isOnboardingChoiceManageTeam ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report); const [shouldHideGBRTooltip] = useOnyx(ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP, {initialValue: true}); const {translate} = useLocalize(); @@ -173,9 +176,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti needsOffscreenAlphaCompositing > Date: Thu, 21 Nov 2024 20:15:08 +0530 Subject: [PATCH 045/174] add selfGuidedTour task to trackAndSubmit beta --- src/CONST.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 06feb863a80a..66fca93c2f4b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -146,6 +146,7 @@ const onboardingEmployerOrSubmitMessage: OnboardingMessage = { const combinedTrackSubmitOnboardingEmployerOrSubmitMessage: OnboardingMessage = { ...onboardingEmployerOrSubmitMessage, tasks: [ + selfGuidedTourTask, { type: 'submitExpense', autoCompleted: false, @@ -191,6 +192,7 @@ const onboardingPersonalSpendMessage: OnboardingMessage = { height: 960, }, tasks: [ + selfGuidedTourTask, { type: 'trackExpense', autoCompleted: false, @@ -212,6 +214,7 @@ const onboardingPersonalSpendMessage: OnboardingMessage = { const combinedTrackSubmitOnboardingPersonalSpendMessage: OnboardingMessage = { ...onboardingPersonalSpendMessage, tasks: [ + selfGuidedTourTask, { type: 'trackExpense', autoCompleted: false, @@ -5045,10 +5048,7 @@ const CONST = { }, ], }, - [onboardingChoices.PERSONAL_SPEND]: { - ...onboardingPersonalSpendMessage, - tasks: [selfGuidedTourTask, ...onboardingPersonalSpendMessage.tasks], - }, + [onboardingChoices.PERSONAL_SPEND]: onboardingPersonalSpendMessage, [onboardingChoices.CHAT_SPLIT]: { message: 'Splitting bills with friends is as easy as sending a message. Here’s how.', video: { From 1c54290694a8eae08d7ff930c520a3d1769b6a74 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Thu, 21 Nov 2024 23:03:43 +0700 Subject: [PATCH 046/174] add plus icon --- assets/images/receipt-placeholder-plus.svg | 17 ++++++++++++++++ src/components/Icon/Expensicons.ts | 2 ++ src/components/ReceiptEmptyState.tsx | 23 ++++++++++++++++------ src/styles/index.ts | 9 +++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 assets/images/receipt-placeholder-plus.svg diff --git a/assets/images/receipt-placeholder-plus.svg b/assets/images/receipt-placeholder-plus.svg new file mode 100644 index 000000000000..3ebc08b40b06 --- /dev/null +++ b/assets/images/receipt-placeholder-plus.svg @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index bd4bb64da050..09f9a3b896ad 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -154,6 +154,7 @@ import Printer from '@assets/images/printer.svg'; import Profile from '@assets/images/profile.svg'; import QrCode from '@assets/images/qrcode.svg'; import QuestionMark from '@assets/images/question-mark-circle.svg'; +import ReceiptPlaceholderPlus from '@assets/images/receipt-placeholder-plus.svg'; import ReceiptPlus from '@assets/images/receipt-plus.svg'; import ReceiptScan from '@assets/images/receipt-scan.svg'; import ReceiptSearch from '@assets/images/receipt-search.svg'; @@ -343,6 +344,7 @@ export { QrCode, QuestionMark, Receipt, + ReceiptPlaceholderPlus, ReceiptPlus, ReceiptScan, ReceiptSlash, diff --git a/src/components/ReceiptEmptyState.tsx b/src/components/ReceiptEmptyState.tsx index a64893fb3439..046026190a5b 100644 --- a/src/components/ReceiptEmptyState.tsx +++ b/src/components/ReceiptEmptyState.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -40,12 +41,22 @@ function ReceiptEmptyState({hasError = false, onPress = () => {}, disabled = fal hasError && styles.borderColorDanger, ]} > - + + + {!isThumbnail && ( + + )} + ); } diff --git a/src/styles/index.ts b/src/styles/index.ts index 970f4cf3b773..44d37d0f07f9 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4638,6 +4638,15 @@ const styles = (theme: ThemeColors) => width: '100%', }, + moneyRequestAttachReceiptThumbnailIcon: { + position: 'absolute', + bottom: -4, + right: -4, + borderColor: theme.highlightBG, + borderWidth: 2, + borderRadius: '50%', + }, + mapViewContainer: { ...flex.flex1, minHeight: 300, From 0580544077a81b066e975f9d13e1468d9eb966b9 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Thu, 21 Nov 2024 23:04:01 +0700 Subject: [PATCH 047/174] show receipt placeholder for money request preview --- .../MoneyRequestPreviewContent.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 8d9def814549..e8d1a01f7a10 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -173,7 +173,7 @@ function MoneyRequestPreviewContent({ merchantOrDescription = description || ''; } - const receiptImages = hasReceipt ? [{...ReceiptUtils.getThumbnailAndImageURIs(transaction), transaction}] : []; + const receiptImages = [{...ReceiptUtils.getThumbnailAndImageURIs(transaction), transaction}]; const getSettledMessage = (): string => { if (isCardTransaction) { @@ -340,17 +340,15 @@ function MoneyRequestPreviewContent({ !onPreviewPressed ? [styles.moneyRequestPreviewBox, containerStyles] : {}, ]} > - {hasReceipt && ( - - )} + {isEmptyObject(transaction) && !ReportActionsUtils.isMessageDeleted(action) && action.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ? ( ) : ( - + From e179761ac0fda169e28012b393132d8c0a89f01a Mon Sep 17 00:00:00 2001 From: John Schuster Date: Thu, 21 Nov 2024 16:42:09 -0600 Subject: [PATCH 048/174] Update Connect-To-Sage-Intacct.md adding 8 spaces to the images to see if that fixes the formatting --- .../sage-intacct/Connect-To-Sage-Intacct.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md index fb340ad4b77f..53081563c7af 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md @@ -54,11 +54,11 @@ Setup the user using these configurations: - **Admin Privileges:** "Full" - **Status:** "Active" -![Image of Sage Intacct Web Services User setup]({{site.url}}/assets/images/SageConnectSettingUpWebServicesUser.png){:width="100%"} + ![Image of Sage Intacct Web Services User setup]({{site.url}}/assets/images/SageConnectSettingUpWebServicesUser.png){:width="100%"} Once you've created the user, you'll need to set the correct permissions. To set those, go to the **subscription** link for this user in the user list, **click on the checkbox** next to the Application/Module and then click on the **Permissions** link to modify those. -![Image showing the Application/Module checkbox to click]({{site.url}}/assets/images/SageConnectSubscriptionSettings.png){:width="100%"} + ![Image showing the Application/Module checkbox to click]({{site.url}}/assets/images/SageConnectSubscriptionSettings.png){:width="100%"} These are the permissions required for a user to export reimbursable expenses as Expense Reports: - **Administration (All)** @@ -81,7 +81,7 @@ The Time & Expenses (T&E) module is often included in your Sage Intacct instance - **Employee:** EMP - **Duplicate Numbers:** Select “Do not allow creation” -![Image of Sage Intacct Time and Expense Auto-numbering Sequences Settings]({{site.url}}/assets/images/SageConnectTimeandExpenseSequenceNumbers.png){:width="100%"} + ![Image of Sage Intacct Time and Expense Auto-numbering Sequences Settings]({{site.url}}/assets/images/SageConnectTimeandExpenseSequenceNumbers.png){:width="100%"} - To create the EXP sequence, **click on the down arrow on the expense report line and select **Add**: - **Sequence ID:** EXP @@ -133,12 +133,12 @@ To enable Customization Services go to **Company > Subscriptions > Customization Creating a test workspace in Expensify allows you to have a sandbox environment for testing before implementing the integration live. If you are already using Expensify, creating a test workspace ensures that your existing group workspace rules and approval workflows remain intact. Here's how to set it up: 1. Go to **expensify.com > Settings > Workspaces > New Workspace**. -![Image of creating a new Workspace in Expensify]({{site.url}}/assets/images/SageConnectCreatingWorkspace.png){:width="100%"} + ![Image of creating a new Workspace in Expensify]({{site.url}}/assets/images/SageConnectCreatingWorkspace.png){:width="100%"} 2. Name the workspace something like "Sage Intacct Test Workspace." 3. Go to **Connections > Sage Intacct > Connect to Sage Intacct**. -![Image of selecting the Sage Intacct integration in Expensify]({{site.url}}/assets/images/SageConnectEnableSage.png){:width="100%"} + ![Image of selecting the Sage Intacct integration in Expensify]({{site.url}}/assets/images/SageConnectEnableSage.png){:width="100%"} 4. Select **Download Package** (You only need to download the file; we'll upload it from your Downloads folder later). @@ -162,7 +162,7 @@ If you use **Platform Services**: 1. Go to **Company > Company Info > Security** in Intacct and click **Edit**. 2. Scroll down to **Web Services Authorizations** and add "expensify" (all lower case) as a Sender ID. -![Image of Web Services Authorizations in Sage Intacct]({{site.url}}/assets/images/SageConnectWebServicesAuthorizations.png){:width="100%"} + ![Image of Web Services Authorizations in Sage Intacct]({{site.url}}/assets/images/SageConnectWebServicesAuthorizations.png){:width="100%"} ### Step 9: Enter Credentials and Connect Expensify and Sage Intacct @@ -171,7 +171,7 @@ If you use **Platform Services**: 2. Click **Connect to Sage Intacct** and enter the credentials you've set for your web services user. 3. Click **Send** once you're done. -![Image of Sage Intacct credentials being entered in Expensify to connect the integration]({{site.url}}/assets/images/SageConnectEnterCredentials.png){:width="100%"} + ![Image of Sage Intacct credentials being entered in Expensify to connect the integration]({{site.url}}/assets/images/SageConnectEnterCredentials.png){:width="100%"} Next, you’ll configure the Export, Coding, and Advanced tabs of the connection configuration in Expensify. From 7ce21d0f47909d07ef928cb45b1bb3fb61555ad2 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Thu, 21 Nov 2024 16:43:50 -0600 Subject: [PATCH 049/174] Update Configure-Sage-Intacct.md adding 8 spaces to fix the rendering of the ordered list --- .../connections/sage-intacct/Configure-Sage-Intacct.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md index 0c9e6c87f9ab..25a6ecabd6bd 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md @@ -11,7 +11,7 @@ There are several options for exporting Expensify reports to Sage Intacct. Let's To access these settings, go to **Settings > Workspace > Group > Connections** and select the **Configure** button. -![Highlighting the Configure button for the Sage Intacct Integration]({{site.url}}/assets/images/SageConfigureIntegrationConfigureButton.png){:width="100%"} + ![Highlighting the Configure button for the Sage Intacct Integration]({{site.url}}/assets/images/SageConfigureIntegrationConfigureButton.png){:width="100%"} ## Export Options @@ -97,7 +97,7 @@ To find the Integration Name in Sage Intacct: 1. Go to **Platform Services > Objects > List** 2. Set "filter by application" to "user-defined dimensions." -![Image of Sage Intacct Objects filtered by User Defined Dimension]({{site.url}}/assets/images/SageConfigureUserDefinedDimensionsFilter.png){:width="100%"} + ![Image of Sage Intacct Objects filtered by User Defined Dimension]({{site.url}}/assets/images/SageConfigureUserDefinedDimensionsFilter.png){:width="100%"} Now, in Expensify, navigate to **Settings > Workspaces > Group > [Workspace Name] > Connections**, and click **Configure** under Sage Intacct. On the Coding tab, enable the toggle next to User Defined Dimensions. Enter the "Integration name" and choose whether to import it into Expensify as an expense-level Tag or as a Report Field, then click **Save**. From d35de24756165c32dc6edf48a6831563fd3fcb01 Mon Sep 17 00:00:00 2001 From: daledah Date: Fri, 22 Nov 2024 14:15:15 +0700 Subject: [PATCH 050/174] fix: Text in search field does not move to the end --- .../Search/SearchRouter/SearchRouter.tsx | 27 +++++++++++- .../Search/SearchRouter/SearchRouterInput.tsx | 43 +++++++++++-------- .../Search/SearchRouter/SearchRouterList.tsx | 4 +- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index b05d34b2351b..a49c856d6a9c 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -19,6 +19,7 @@ import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; +import type {ScrollToBottom} from '@libs/InputUtils/types'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import {getAllTaxRates} from '@libs/PolicyUtils'; import type {OptionData} from '@libs/ReportUtils'; @@ -324,13 +325,36 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) ], ); + const scrollToRight: ScrollToBottom = (input) => { + if (!('scrollLeft' in input)) { + return; + } + // Scroll to the far right + // eslint-disable-next-line no-param-reassign + input.scrollLeft = input.scrollWidth; + }; + + const shouldScrollRef = useRef(false); + const searchRouterInputRef = useRef(null); + // Trigger scrollToRight when input value changes and shouldScroll is true + useEffect(() => { + if (!searchRouterInputRef.current) { + return; + } + scrollToRight(searchRouterInputRef.current); + shouldScrollRef.current = false; + }, [debouncedInputValue]); + const prevUserQueryRef = useRef(null); useEffect(() => { Report.searchInServer(debouncedInputValue.trim()); }, [debouncedInputValue]); const onSearchChange = useCallback( - (userQuery: string) => { + (userQuery: string, autoScrollToRight = false) => { + if (autoScrollToRight) { + shouldScrollRef.current = true; + } let newUserQuery = userQuery; if (autocompleteSuggestions && userQuery.endsWith(',')) { newUserQuery = `${userQuery.slice(0, userQuery.length - 1).trim()},`; @@ -398,6 +422,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) )} { diff --git a/src/components/Search/SearchRouter/SearchRouterInput.tsx b/src/components/Search/SearchRouter/SearchRouterInput.tsx index 6b99588a21df..b252ca1db069 100644 --- a/src/components/Search/SearchRouter/SearchRouterInput.tsx +++ b/src/components/Search/SearchRouter/SearchRouterInput.tsx @@ -1,10 +1,11 @@ -import type {ReactNode, RefObject} from 'react'; -import React, {useState} from 'react'; +import type {ForwardedRef, ReactNode, RefObject} from 'react'; +import React, {forwardRef, useState} from 'react'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {View} from 'react-native'; import FormHelpMessage from '@components/FormHelpMessage'; import type {SelectionListHandle} from '@components/SelectionList/types'; import TextInput from '@components/TextInput'; +import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -50,22 +51,25 @@ type SearchRouterInputProps = { isSearchingForReports?: boolean; } & Pick; -function SearchRouterInput({ - value, - updateSearch, - onSubmit = () => {}, - routerListRef, - isFullWidth, - disabled = false, - shouldShowOfflineMessage = false, - autoFocus = true, - caretHidden = false, - wrapperStyle, - wrapperFocusedStyle, - outerWrapperStyle, - rightComponent, - isSearchingForReports, -}: SearchRouterInputProps) { +function SearchRouterInput( + { + value, + updateSearch, + onSubmit = () => {}, + routerListRef, + isFullWidth, + disabled = false, + shouldShowOfflineMessage = false, + autoFocus = true, + caretHidden = false, + wrapperStyle, + wrapperFocusedStyle, + outerWrapperStyle, + rightComponent, + isSearchingForReports, + }: SearchRouterInputProps, + ref: ForwardedRef, +) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [isFocused, setIsFocused] = useState(false); @@ -79,6 +83,7 @@ function SearchRouterInput({ void; + updateSearchValue: (newValue: string, autoScrollToRight?: boolean) => void; /** Callback to update text input value */ setTextInputValue: (text: string) => void; @@ -246,7 +246,7 @@ function SearchRouterList( } if (item.searchItemType === CONST.SEARCH.SEARCH_ROUTER_ITEM_TYPE.CONTEXTUAL_SUGGESTION) { const searchQuery = getContextualSearchQuery(item); - updateSearchValue(`${searchQuery} `); + updateSearchValue(`${searchQuery} `, true); if (item.roomType === CONST.SEARCH.DATA_TYPES.INVOICE && item.autocompleteID) { const autocompleteKey = `${CONST.SEARCH.SYNTAX_FILTER_KEYS.TO}:${item.searchQuery}`; From 3c67ca0291d285b9349f3e3bd9ae001533fe40c9 Mon Sep 17 00:00:00 2001 From: c3024 Date: Sat, 23 Nov 2024 06:50:04 +0530 Subject: [PATCH 051/174] post tasks to concierge chat for emails with + --- src/components/LHNOptionsList/OptionRowLHN.tsx | 6 ++++-- src/libs/actions/Report.ts | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index fc31360173f2..0f7fcbba5cda 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -9,6 +9,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import {useSession} from '@components/OnyxProvider'; import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction'; import SubscriptAvatar from '@components/SubscriptAvatar'; import Text from '@components/Text'; @@ -52,8 +53,9 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti selector: hasCompletedGuidedSetupFlowSelector, }); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); - const isOnboardingChoiceManageTeam = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM; - const shouldShowToooltipOnThisReport = isOnboardingChoiceManageTeam ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report); + const session = useSession(); + const isOnboardingGuideAssigned = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !session?.email?.includes('+'); + const shouldShowToooltipOnThisReport = isOnboardingGuideAssigned ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report); const [shouldHideGBRTooltip] = useOnyx(ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP, {initialValue: true}); const {translate} = useLocalize(); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8c38c83b9295..631912f76e6b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3490,11 +3490,11 @@ function prepareOnboardingOptimisticData( data = CONST.COMBINED_TRACK_SUBMIT_ONBOARDING_MESSAGES[CONST.ONBOARDING_CHOICES.SUBMIT]; } } - const isEngagementChoiceManageTeam = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM; + const shouldPostTasksInAdminsRoom = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !currentUserEmail?.includes('+'); const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; - const actorAccountID = isEngagementChoiceManageTeam ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; + const actorAccountID = shouldPostTasksInAdminsRoom ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; - const targetChatReport = isEngagementChoiceManageTeam ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); + const targetChatReport = shouldPostTasksInAdminsRoom ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Text message From a25d3fb0efa91726bddb4c8ea87ef5de3949c0ab Mon Sep 17 00:00:00 2001 From: abzokhattab Date: Sat, 23 Nov 2024 04:01:34 +0100 Subject: [PATCH 052/174] Fix Animation Freezing When Navigating Back --- src/components/Lottie/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Lottie/index.tsx b/src/components/Lottie/index.tsx index 017d68aa4b56..a6b1374b1c8f 100644 --- a/src/components/Lottie/index.tsx +++ b/src/components/Lottie/index.tsx @@ -62,6 +62,7 @@ function Lottie({source, webStyle, shouldLoadAfterInteractions, ...props}: Props } const unsubscribeNavigationFocus = navigator.addListener('focus', () => { setHasNavigatedAway(false); + animationRef.current?.play(); }); return unsubscribeNavigationFocus; }, [browser, navigationContainerRef, navigator]); From 2c4480d2998fadd4cdb6cb5955b82545e478cddd Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 23 Nov 2024 14:33:57 +0800 Subject: [PATCH 053/174] fix lint --- src/libs/DebugUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 671fb03f268b..f881d703d269 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -616,6 +616,7 @@ function validateReportDraftProperty(key: keyof Report, value: string) { participants: CONST.RED_BRICK_ROAD_PENDING_ACTION, total: CONST.RED_BRICK_ROAD_PENDING_ACTION, unheldTotal: CONST.RED_BRICK_ROAD_PENDING_ACTION, + unheldNonReimbursableTotal: CONST.RED_BRICK_ROAD_PENDING_ACTION, isWaitingOnBankAccount: CONST.RED_BRICK_ROAD_PENDING_ACTION, isCancelledIOU: CONST.RED_BRICK_ROAD_PENDING_ACTION, iouReportID: CONST.RED_BRICK_ROAD_PENDING_ACTION, From 9ade52c0d5a6abcde9a8468fe4380cb07ce6c6dd Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 23 Nov 2024 14:37:21 +0800 Subject: [PATCH 054/174] fix another lint --- src/libs/DebugUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index f881d703d269..061931aa0bd4 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -489,6 +489,7 @@ function validateReportDraftProperty(key: keyof Report, value: string) { case 'total': case 'unheldTotal': case 'nonReimbursableTotal': + case 'unheldNonReimbursableTotal': return validateNumber(value); case 'chatType': return validateConstantEnum(value, CONST.REPORT.CHAT_TYPE); From fa58c4f5c1ba9470e159862e316b5e7388fe794d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 23 Nov 2024 14:44:02 +0800 Subject: [PATCH 055/174] fix type error --- src/types/utils/whitelistedReportKeys.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/utils/whitelistedReportKeys.ts b/src/types/utils/whitelistedReportKeys.ts index 32aa0797d0f8..db03d887038c 100644 --- a/src/types/utils/whitelistedReportKeys.ts +++ b/src/types/utils/whitelistedReportKeys.ts @@ -47,6 +47,7 @@ type WhitelistedReport = OnyxCommon.OnyxValueWithOfflineFeedback< participants: unknown; total: unknown; unheldTotal: unknown; + unheldNonReimbursableTotal: unknown; currency: unknown; errorFields: unknown; isWaitingOnBankAccount: unknown; From bcdb8441eab07ba294d2c2b81f128cff64be52d6 Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 25 Nov 2024 14:29:33 +0530 Subject: [PATCH 056/174] clarify why tasks are posted to different reports --- src/components/LHNOptionsList/OptionRowLHN.tsx | 4 ++++ src/libs/actions/Report.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 0f7fcbba5cda..b420e779d8f7 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -54,6 +54,10 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti }); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const session = useSession(); + + // A guide is assigned to the user if they choose the 'MANAGE_TEAM' onboarding option, except for users with emails containing '+'. + // Onboarding tasks for guide-assigned users are posted in the #admins room. For others, the tasks are posted in the Concierge chat. + // So, we should show the "Get started here" tooltip accordingly on the #admins room or Concierge chat on LHN. const isOnboardingGuideAssigned = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !session?.email?.includes('+'); const shouldShowToooltipOnThisReport = isOnboardingGuideAssigned ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report); const [shouldHideGBRTooltip] = useOnyx(ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP, {initialValue: true}); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 631912f76e6b..0e4faacb5b84 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3490,6 +3490,9 @@ function prepareOnboardingOptimisticData( data = CONST.COMBINED_TRACK_SUBMIT_ONBOARDING_MESSAGES[CONST.ONBOARDING_CHOICES.SUBMIT]; } } + + // A guide is assigned to the user if they choose the 'MANAGE_TEAM' onboarding option, except for users with emails containing '+'. + // Onboarding tasks for guide-assigned users are posted in the #admins room. For others, the tasks are posted in the Concierge chat. const shouldPostTasksInAdminsRoom = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !currentUserEmail?.includes('+'); const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; const actorAccountID = shouldPostTasksInAdminsRoom ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; From b42261e9dcf452a747ca1306eca72fc479c6a213 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 25 Nov 2024 17:04:23 +0800 Subject: [PATCH 057/174] fix add payment card doesn't disappear after adding a card --- src/pages/home/report/ReportActionItem.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 399550069c0a..f055b2094c75 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -182,6 +182,7 @@ function ReportActionItem({ `${ONYXKEYS.COLLECTION.TRANSACTION}${ReportActionsUtils.isMoneyRequestAction(action) ? ReportActionsUtils.getOriginalMessage(action)?.IOUTransactionID ?? -1 : -1}`, {selector: (transaction) => transaction?.errorFields?.route ?? null}, ); + const [userBillingFundID] = useOnyx(ONYXKEYS.NVP_BILLING_FUND_ID); const theme = useTheme(); const styles = useThemeStyles(); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- This is needed to prevent the app from crashing when the app is using imported state. @@ -415,7 +416,7 @@ function ReportActionItem({ const mentionReportContextValue = useMemo(() => ({currentReportID: report?.reportID ?? '-1'}), [report?.reportID]); const actionableItemButtons: ActionableItem[] = useMemo(() => { - if (ReportActionsUtils.isActionableAddPaymentCard(action) && !doesUserHavePaymentCardAdded() && shouldRenderAddPaymentCard()) { + if (ReportActionsUtils.isActionableAddPaymentCard(action) && userBillingFundID === undefined && shouldRenderAddPaymentCard()) { return [ { text: 'subscription.cardSection.addCardButton', @@ -516,7 +517,7 @@ function ReportActionItem({ onPress: () => Report.resolveActionableMentionWhisper(reportID, action, CONST.REPORT.ACTIONABLE_MENTION_WHISPER_RESOLUTION.NOTHING), }, ]; - }, [action, isActionableWhisper, reportID]); + }, [action, isActionableWhisper, reportID, userBillingFundID]); /** * Get the content of ReportActionItem From de0ed86844e4c7db6cf77b033e275842612e3fe8 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 25 Nov 2024 17:07:45 +0800 Subject: [PATCH 058/174] remove unused import --- src/pages/home/report/ReportActionItem.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index f055b2094c75..2e1696d1c464 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -52,7 +52,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import SelectionScraper from '@libs/SelectionScraper'; import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; -import {doesUserHavePaymentCardAdded} from '@libs/SubscriptionUtils'; import {ReactionListContext} from '@pages/home/ReportScreenContext'; import * as BankAccounts from '@userActions/BankAccounts'; import * as EmojiPickerAction from '@userActions/EmojiPickerAction'; From 77ef2c913f3b4f4b223fedaf2622043c76096f8d Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 25 Nov 2024 14:38:35 +0530 Subject: [PATCH 059/174] make the comments clearer --- src/components/LHNOptionsList/OptionRowLHN.tsx | 1 + src/libs/actions/Report.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index b420e779d8f7..b9e2e9274e5e 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -56,6 +56,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const session = useSession(); // A guide is assigned to the user if they choose the 'MANAGE_TEAM' onboarding option, except for users with emails containing '+'. + // Posting tasks for users with emails containig '+' in the #admins room is not allowed by the backend. // Onboarding tasks for guide-assigned users are posted in the #admins room. For others, the tasks are posted in the Concierge chat. // So, we should show the "Get started here" tooltip accordingly on the #admins room or Concierge chat on LHN. const isOnboardingGuideAssigned = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !session?.email?.includes('+'); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 0e4faacb5b84..115a641a90b2 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3492,6 +3492,7 @@ function prepareOnboardingOptimisticData( } // A guide is assigned to the user if they choose the 'MANAGE_TEAM' onboarding option, except for users with emails containing '+'. + // Posting tasks for users with emails containig '+' in the #admins room is not allowed by the backend. // Onboarding tasks for guide-assigned users are posted in the #admins room. For others, the tasks are posted in the Concierge chat. const shouldPostTasksInAdminsRoom = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !currentUserEmail?.includes('+'); const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; From 95f85471072c6c3c14b17278447f6dc91c30d2a7 Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Mon, 25 Nov 2024 22:19:18 +0700 Subject: [PATCH 060/174] fix: search page top bar is hidden after deleting an expense --- src/components/Search/index.tsx | 4 +++- src/components/SelectionList/BaseSelectionList.tsx | 2 ++ src/components/SelectionList/types.ts | 3 +++ src/pages/Search/SearchPageBottomTab.tsx | 13 ++++++++++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index f7ebeb6907fe..8796ad0b4606 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -40,6 +40,7 @@ type SearchProps = { onSearchListScroll?: (event: NativeSyntheticEvent) => void; contentContainerStyle?: StyleProp; isSearchScreenFocused?: boolean; + onContentSizeChange?: (w: number, h: number) => void; }; const transactionItemMobileHeight = 100; @@ -86,7 +87,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact return {...selectedTransactions, [item.keyForList]: {isSelected: true, canDelete: item.canDelete, canHold: item.canHold, canUnhold: item.canUnhold, action: item.action}}; } -function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentContainerStyle}: SearchProps) { +function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentContainerStyle, onContentSizeChange}: SearchProps) { const {isOffline} = useNetwork(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); @@ -432,6 +433,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo } shouldAutoTurnOff={false} onScroll={onSearchListScroll} + onContentSizeChange={onContentSizeChange} canSelectMultiple={type !== CONST.SEARCH.DATA_TYPES.CHAT && canSelectMultiple} customListHeaderHeight={searchHeaderHeight} // To enhance the smoothness of scrolling and minimize the risk of encountering blank spaces during scrolling, diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 0e12e993cc79..3bb58c0afdad 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -114,6 +114,7 @@ function BaseSelectionList( shouldKeepFocusedItemAtTopOfViewableArea = false, shouldDebounceScrolling = false, shouldPreventActiveCellVirtualization = false, + onContentSizeChange, }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -795,6 +796,7 @@ function BaseSelectionList( getItemLayout={getItemLayout} onScroll={onScroll} onScrollBeginDrag={onScrollBeginDrag} + onContentSizeChange={onContentSizeChange} keyExtractor={(item, index) => item.keyForList ?? `${index}`} extraData={focusedIndex} // the only valid values on the new arch are "white", "black", and "default", other values will cause a crash diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index edf6ee955ecc..7164d1a0e9fa 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -612,6 +612,9 @@ type BaseSelectionListProps = Partial & { /** Whether to prevent the active cell from being virtualized and losing focus in browsers */ shouldPreventActiveCellVirtualization?: boolean; + + /** Called when scrollable content view of the ScrollView changes */ + onContentSizeChange?: (w: number, h: number) => void; } & TRightHandSideComponent; type SelectionListHandle = { diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index a91a64d3a8ed..7065d220a38e 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Animated, {clamp, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; @@ -59,6 +59,16 @@ function SearchPageBottomTab() { }, }); + const onContentSizeChange = useCallback( + (w: number, h: number) => { + if (windowHeight <= h) { + return; + } + topBarOffset.value = withTiming(variables.searchHeaderHeight, {duration: ANIMATION_DURATION_IN_MS}); + }, + [windowHeight, topBarOffset], + ); + const searchParams = activeCentralPaneRoute?.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]; const parsedQuery = SearchQueryUtils.buildSearchQueryJSON(searchParams?.q); const isSearchNameModified = searchParams?.name === searchParams?.q; @@ -132,6 +142,7 @@ function SearchPageBottomTab() { isSearchScreenFocused={isActiveCentralPaneRoute} queryJSON={queryJSON} onSearchListScroll={scrollHandler} + onContentSizeChange={onContentSizeChange} contentContainerStyle={!selectionMode?.isEnabled ? [styles.searchListContentContainerStyles] : undefined} /> )} From f7dbac4f26fbbfb66f9bf034e461b0972ec7bda8 Mon Sep 17 00:00:00 2001 From: daledah Date: Mon, 25 Nov 2024 23:35:19 +0700 Subject: [PATCH 061/174] refactor: move scrollToRight to InputUtils --- src/components/Search/SearchRouter/SearchRouter.tsx | 13 ++----------- src/libs/InputUtils/index.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index a49c856d6a9c..6d1964b5ecad 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -19,7 +19,7 @@ import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; -import type {ScrollToBottom} from '@libs/InputUtils/types'; +import * as InputUtils from '@libs/InputUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import {getAllTaxRates} from '@libs/PolicyUtils'; import type {OptionData} from '@libs/ReportUtils'; @@ -325,15 +325,6 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) ], ); - const scrollToRight: ScrollToBottom = (input) => { - if (!('scrollLeft' in input)) { - return; - } - // Scroll to the far right - // eslint-disable-next-line no-param-reassign - input.scrollLeft = input.scrollWidth; - }; - const shouldScrollRef = useRef(false); const searchRouterInputRef = useRef(null); // Trigger scrollToRight when input value changes and shouldScroll is true @@ -341,7 +332,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) if (!searchRouterInputRef.current) { return; } - scrollToRight(searchRouterInputRef.current); + InputUtils.scrollToRight(searchRouterInputRef.current); shouldScrollRef.current = false; }, [debouncedInputValue]); diff --git a/src/libs/InputUtils/index.ts b/src/libs/InputUtils/index.ts index 19943bf3132a..5e274ebfbbd3 100644 --- a/src/libs/InputUtils/index.ts +++ b/src/libs/InputUtils/index.ts @@ -8,6 +8,15 @@ const scrollToBottom: ScrollToBottom = (input) => { input.scrollTop = input.scrollHeight; }; +const scrollToRight: ScrollToBottom = (input) => { + if (!('scrollLeft' in input)) { + return; + } + // Scroll to the far right + // eslint-disable-next-line no-param-reassign + input.scrollLeft = input.scrollWidth; +}; + const moveSelectionToEnd: MoveSelectiontoEnd = (input) => { if (!('setSelectionRange' in input)) { return; @@ -16,4 +25,4 @@ const moveSelectionToEnd: MoveSelectiontoEnd = (input) => { input.setSelectionRange(length, length); }; -export {scrollToBottom, moveSelectionToEnd}; +export {scrollToBottom, moveSelectionToEnd, scrollToRight}; From 84ef2049dc3e014b74742fec6af8fcc3e19ea36e Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 25 Nov 2024 18:25:09 +0100 Subject: [PATCH 062/174] Fix console errors during PDF preview --- config/webpack/webpack.common.ts | 2 +- patches/react-fast-pdf+1.0.15.patch | 13 +++++++++++++ src/components/PDFThumbnail/index.tsx | 2 +- src/types/modules/pdf.worker.d.ts | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 patches/react-fast-pdf+1.0.15.patch diff --git a/config/webpack/webpack.common.ts b/config/webpack/webpack.common.ts index 8aa8f5aa566c..c60670c72324 100644 --- a/config/webpack/webpack.common.ts +++ b/config/webpack/webpack.common.ts @@ -175,7 +175,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment): // We are importing this worker as a string by using asset/source otherwise it will default to loading via an HTTPS request later. // This causes issues if we have gone offline before the pdfjs web worker is set up as we won't be able to load it from the server. { - test: new RegExp('node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs'), + test: new RegExp('node_modules/pdfjs-dist/legacy/build/pdf.worker.min.mjs'), type: 'asset/source', }, diff --git a/patches/react-fast-pdf+1.0.15.patch b/patches/react-fast-pdf+1.0.15.patch new file mode 100644 index 000000000000..bfca1b418964 --- /dev/null +++ b/patches/react-fast-pdf+1.0.15.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-fast-pdf/dist/PDFPreviewer.js b/node_modules/react-fast-pdf/dist/PDFPreviewer.js +index 53d4849..aea6027 100644 +--- a/node_modules/react-fast-pdf/dist/PDFPreviewer.js ++++ b/node_modules/react-fast-pdf/dist/PDFPreviewer.js +@@ -28,7 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { + Object.defineProperty(exports, "__esModule", { value: true }); + // @ts-expect-error - This line imports a module from 'pdfjs-dist' package which lacks TypeScript typings. + // eslint-disable-next-line import/extensions +-const pdf_worker_mjs_1 = __importDefault(require("pdfjs-dist/legacy/build/pdf.worker.mjs")); ++const pdf_worker_mjs_1 = __importDefault(require("pdfjs-dist/legacy/build/pdf.worker.min.mjs")); + const react_1 = __importStar(require("react")); + const times_1 = __importDefault(require("lodash/times")); + const react_window_1 = require("react-window"); diff --git a/src/components/PDFThumbnail/index.tsx b/src/components/PDFThumbnail/index.tsx index 1115ea21dad4..495c14ff76e1 100644 --- a/src/components/PDFThumbnail/index.tsx +++ b/src/components/PDFThumbnail/index.tsx @@ -1,6 +1,6 @@ import 'core-js/proposals/promise-with-resolvers'; // eslint-disable-next-line import/extensions -import pdfWorkerSource from 'pdfjs-dist/legacy/build/pdf.worker.mjs'; +import pdfWorkerSource from 'pdfjs-dist/legacy/build/pdf.worker.min.mjs'; import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import {Document, pdfjs, Thumbnail} from 'react-pdf'; diff --git a/src/types/modules/pdf.worker.d.ts b/src/types/modules/pdf.worker.d.ts index cc59c0cb95a9..a6d70e529b7f 100644 --- a/src/types/modules/pdf.worker.d.ts +++ b/src/types/modules/pdf.worker.d.ts @@ -1 +1 @@ -declare module 'pdfjs-dist/legacy/build/pdf.worker.mjs'; +declare module 'pdfjs-dist/legacy/build/pdf.worker.min.mjs'; From 42c1ff5fce6407d7c8477a09884b9a949b04b2e0 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 25 Nov 2024 18:58:20 +0100 Subject: [PATCH 063/174] Fix storybook test --- .storybook/webpack.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.storybook/webpack.config.ts b/.storybook/webpack.config.ts index 92cea8666bc2..1c22608160cf 100644 --- a/.storybook/webpack.config.ts +++ b/.storybook/webpack.config.ts @@ -95,7 +95,7 @@ const webpackConfig = ({config}: {config: Configuration}) => { }); config.module.rules?.push({ - test: /pdf\.worker\.mjs$/, + test: /pdf\.worker\.min\.mjs$/, type: 'asset/source', }); From 40080239b35e093f4f20ec3f0ccdf58bf8df7d3d Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Mon, 25 Nov 2024 22:22:03 +0000 Subject: [PATCH 064/174] fix: LHN shows unavailable workspace --- src/libs/ReportUtils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 952e0c2fe4cc..82f61f1d2c40 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3043,7 +3043,8 @@ function getMoneyRequestReportName(report: OnyxEntry, policy?: OnyxEntry let payerOrApproverName; if (isExpenseReport(report)) { - payerOrApproverName = getPolicyName(report, false, policy); + const parentReport = getParentReport(report); + payerOrApproverName = getPolicyName(parentReport ?? report, false, policy); } else if (isInvoiceReport(report)) { const chatReport = getReportOrDraftReport(report?.chatReportID); payerOrApproverName = getInvoicePayerName(chatReport, invoiceReceiverPolicy); @@ -3553,7 +3554,8 @@ function getReportPreviewMessage( const containsNonReimbursable = hasNonReimbursableTransactions(report.reportID); const totalAmount = getMoneyRequestSpendBreakdown(report).totalDisplaySpend; - const policyName = getPolicyName(report, false, policy); + const parentReport = getParentReport(report); + const policyName = getPolicyName(parentReport ?? report, false, policy); const payerName = isExpenseReport(report) ? policyName : getDisplayNameForParticipant(report.managerID, !isPreviewMessageForParentChatReport); const formattedAmount = CurrencyUtils.convertToDisplayString(totalAmount, report.currency); From 1817418cd97999b069a805b10d8cfad1a83dc408 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Mon, 25 Nov 2024 17:15:56 -0600 Subject: [PATCH 065/174] Update Configure-Sage-Intacct.md Fixing the broken indent --- .../connections/sage-intacct/Configure-Sage-Intacct.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md index 25a6ecabd6bd..0c9e6c87f9ab 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Configure-Sage-Intacct.md @@ -11,7 +11,7 @@ There are several options for exporting Expensify reports to Sage Intacct. Let's To access these settings, go to **Settings > Workspace > Group > Connections** and select the **Configure** button. - ![Highlighting the Configure button for the Sage Intacct Integration]({{site.url}}/assets/images/SageConfigureIntegrationConfigureButton.png){:width="100%"} +![Highlighting the Configure button for the Sage Intacct Integration]({{site.url}}/assets/images/SageConfigureIntegrationConfigureButton.png){:width="100%"} ## Export Options @@ -97,7 +97,7 @@ To find the Integration Name in Sage Intacct: 1. Go to **Platform Services > Objects > List** 2. Set "filter by application" to "user-defined dimensions." - ![Image of Sage Intacct Objects filtered by User Defined Dimension]({{site.url}}/assets/images/SageConfigureUserDefinedDimensionsFilter.png){:width="100%"} +![Image of Sage Intacct Objects filtered by User Defined Dimension]({{site.url}}/assets/images/SageConfigureUserDefinedDimensionsFilter.png){:width="100%"} Now, in Expensify, navigate to **Settings > Workspaces > Group > [Workspace Name] > Connections**, and click **Configure** under Sage Intacct. On the Coding tab, enable the toggle next to User Defined Dimensions. Enter the "Integration name" and choose whether to import it into Expensify as an expense-level Tag or as a Report Field, then click **Save**. From fb95e1bad7a65f0c5aa0a716b7139fd04b843b4d Mon Sep 17 00:00:00 2001 From: John Schuster Date: Mon, 25 Nov 2024 17:16:31 -0600 Subject: [PATCH 066/174] Update Connect-To-Sage-Intacct.md Fixing the broken indents --- .../sage-intacct/Connect-To-Sage-Intacct.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md index 53081563c7af..24f5d55fed80 100644 --- a/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md +++ b/docs/articles/expensify-classic/connections/sage-intacct/Connect-To-Sage-Intacct.md @@ -54,11 +54,11 @@ Setup the user using these configurations: - **Admin Privileges:** "Full" - **Status:** "Active" - ![Image of Sage Intacct Web Services User setup]({{site.url}}/assets/images/SageConnectSettingUpWebServicesUser.png){:width="100%"} +![Image of Sage Intacct Web Services User setup]({{site.url}}/assets/images/SageConnectSettingUpWebServicesUser.png){:width="100%"} Once you've created the user, you'll need to set the correct permissions. To set those, go to the **subscription** link for this user in the user list, **click on the checkbox** next to the Application/Module and then click on the **Permissions** link to modify those. - ![Image showing the Application/Module checkbox to click]({{site.url}}/assets/images/SageConnectSubscriptionSettings.png){:width="100%"} +![Image showing the Application/Module checkbox to click]({{site.url}}/assets/images/SageConnectSubscriptionSettings.png){:width="100%"} These are the permissions required for a user to export reimbursable expenses as Expense Reports: - **Administration (All)** @@ -133,12 +133,12 @@ To enable Customization Services go to **Company > Subscriptions > Customization Creating a test workspace in Expensify allows you to have a sandbox environment for testing before implementing the integration live. If you are already using Expensify, creating a test workspace ensures that your existing group workspace rules and approval workflows remain intact. Here's how to set it up: 1. Go to **expensify.com > Settings > Workspaces > New Workspace**. - ![Image of creating a new Workspace in Expensify]({{site.url}}/assets/images/SageConnectCreatingWorkspace.png){:width="100%"} +![Image of creating a new Workspace in Expensify]({{site.url}}/assets/images/SageConnectCreatingWorkspace.png){:width="100%"} 2. Name the workspace something like "Sage Intacct Test Workspace." 3. Go to **Connections > Sage Intacct > Connect to Sage Intacct**. - ![Image of selecting the Sage Intacct integration in Expensify]({{site.url}}/assets/images/SageConnectEnableSage.png){:width="100%"} +![Image of selecting the Sage Intacct integration in Expensify]({{site.url}}/assets/images/SageConnectEnableSage.png){:width="100%"} 4. Select **Download Package** (You only need to download the file; we'll upload it from your Downloads folder later). @@ -162,7 +162,7 @@ If you use **Platform Services**: 1. Go to **Company > Company Info > Security** in Intacct and click **Edit**. 2. Scroll down to **Web Services Authorizations** and add "expensify" (all lower case) as a Sender ID. - ![Image of Web Services Authorizations in Sage Intacct]({{site.url}}/assets/images/SageConnectWebServicesAuthorizations.png){:width="100%"} +![Image of Web Services Authorizations in Sage Intacct]({{site.url}}/assets/images/SageConnectWebServicesAuthorizations.png){:width="100%"} ### Step 9: Enter Credentials and Connect Expensify and Sage Intacct @@ -171,7 +171,7 @@ If you use **Platform Services**: 2. Click **Connect to Sage Intacct** and enter the credentials you've set for your web services user. 3. Click **Send** once you're done. - ![Image of Sage Intacct credentials being entered in Expensify to connect the integration]({{site.url}}/assets/images/SageConnectEnterCredentials.png){:width="100%"} +![Image of Sage Intacct credentials being entered in Expensify to connect the integration]({{site.url}}/assets/images/SageConnectEnterCredentials.png){:width="100%"} Next, you’ll configure the Export, Coding, and Advanced tabs of the connection configuration in Expensify. From a6dbf38b3aa8957749909a234cf45903882c59a6 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 26 Nov 2024 06:28:51 +0530 Subject: [PATCH 067/174] fix: Distance rate- WS owner can create multiple distance rate with same amount. Signed-off-by: krishna2323 --- src/languages/en.ts | 4 ++++ src/languages/es.ts | 4 ++++ src/languages/params.ts | 5 +++++ src/libs/PolicyDistanceRatesUtils.ts | 13 +++++++++++-- .../distanceRates/CreateDistanceRatePage.tsx | 4 ++-- .../distanceRates/PolicyDistanceRateEditPage.tsx | 4 ++-- 6 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index c1067e195985..c526534b7ef6 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -47,6 +47,7 @@ import type { ConnectionParams, CurrencyCodeParams, CustomersOrJobsLabelParams, + CustomUnitRateParams, DateParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, @@ -2545,6 +2546,9 @@ const translations = { title: 'Per diem', subtitle: 'Set per diem rates to control daily employee spend. Import rates from a spreadsheet to get started.', }, + errors: { + existingRateError: ({rate}: CustomUnitRateParams) => `A rate with value ${rate} already exists.`, + }, }, qbd: { exportOutOfPocketExpensesDescription: 'Set how out-of-pocket expenses export to QuickBooks Desktop.', diff --git a/src/languages/es.ts b/src/languages/es.ts index f7af1be45139..5c931a12e2f9 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -46,6 +46,7 @@ import type { ConnectionParams, CurrencyCodeParams, CustomersOrJobsLabelParams, + CustomUnitRateParams, DateParams, DateShouldBeAfterParams, DateShouldBeBeforeParams, @@ -2569,6 +2570,9 @@ const translations = { title: 'Per diem', subtitle: 'Establece dietas per diem para controlar el gasto diario de los empleados. Importa las tarifas desde una hoja de cálculo para comenzar.', }, + errors: { + existingRateError: ({rate}: CustomUnitRateParams) => `Ya existe una tasa con el valor ${rate}.`, + }, }, qbd: { exportOutOfPocketExpensesDescription: 'Establezca cómo se exportan los gastos de bolsillo a QuickBooks Desktop.', diff --git a/src/languages/params.ts b/src/languages/params.ts index 87a322775cca..dfdb31b1a05c 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -559,6 +559,10 @@ type CompanyNameParams = { companyName: string; }; +type CustomUnitRateParams = { + rate: number; +}; + export type { AuthenticationErrorParams, ImportMembersSuccessfullDescriptionParams, @@ -761,4 +765,5 @@ export type { ImportedTypesParams, CurrencyCodeParams, CompanyNameParams, + CustomUnitRateParams, }; diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index 11f65f0f07c0..1defd17a93e0 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -2,6 +2,7 @@ import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import CONST from '@src/CONST'; import type ONYXKEYS from '@src/ONYXKEYS'; import type {Rate} from '@src/types/onyx/Policy'; +import {convertToBackendAmount} from './CurrencyUtils'; import getPermittedDecimalSeparator from './getPermittedDecimalSeparator'; import * as Localize from './Localize'; import * as MoneyRequestUtils from './MoneyRequestUtils'; @@ -11,14 +12,22 @@ type RateValueForm = typeof ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM | ty type TaxReclaimableForm = typeof ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT_FORM; -function validateRateValue(values: FormOnyxValues, currency: string, toLocaleDigit: (arg: string) => string): FormInputErrors { +function validateRateValue( + values: FormOnyxValues, + customUnitRates: Record, + currency: string, + toLocaleDigit: (arg: string) => string, +): FormInputErrors { const errors: FormInputErrors = {}; const parsedRate = MoneyRequestUtils.replaceAllDigits(values.rate, toLocaleDigit); const decimalSeparator = toLocaleDigit('.'); + const ratesList = Object.values(customUnitRates); // Allow one more decimal place for accuracy const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CONST.MAX_TAX_RATE_DECIMAL_PLACES}})?$`, 'i'); - if (!rateValueRegex.test(parsedRate) || parsedRate === '') { + if (ratesList.some((r) => r.rate === convertToBackendAmount(Number(parsedRate)))) { + errors.rate = Localize.translateLocal('workspace.perDiem.errors.existingRateError', {rate: NumberUtils.parseFloatAnyLocale(parsedRate)}); + } else if (!rateValueRegex.test(parsedRate) || parsedRate === '') { errors.rate = Localize.translateLocal('common.error.invalidRateError'); } else if (NumberUtils.parseFloatAnyLocale(parsedRate) <= 0) { errors.rate = Localize.translateLocal('common.error.lowRateError'); diff --git a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx index 8d646471dd89..519c206e1dc7 100644 --- a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx +++ b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx @@ -41,8 +41,8 @@ function CreateDistanceRatePage({route}: CreateDistanceRatePageProps) { const FullPageBlockingView = !customUnitID ? FullPageOfflineBlockingView : View; const validate = useCallback( - (values: FormOnyxValues) => validateRateValue(values, currency, toLocaleDigit), - [currency, toLocaleDigit], + (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, currency, toLocaleDigit), + [currency, toLocaleDigit, customUnit?.rates], ); const submit = (values: FormOnyxValues) => { diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx index ffcbc0be529c..b5fbfb3863bd 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx @@ -52,8 +52,8 @@ function PolicyDistanceRateEditPage({route}: PolicyDistanceRateEditPageProps) { }; const validate = useCallback( - (values: FormOnyxValues) => validateRateValue(values, currency, toLocaleDigit), - [currency, toLocaleDigit], + (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, currency, toLocaleDigit), + [currency, toLocaleDigit, customUnit?.rates], ); if (!rate) { From 644aac75b8118b2544abee16158a96d1b135878f Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 26 Nov 2024 06:56:11 +0530 Subject: [PATCH 068/174] update validation. Signed-off-by: krishna2323 --- src/libs/PolicyDistanceRatesUtils.ts | 3 ++- .../workspace/distanceRates/PolicyDistanceRateEditPage.tsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index 1defd17a93e0..ccef36856766 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -17,6 +17,7 @@ function validateRateValue( customUnitRates: Record, currency: string, toLocaleDigit: (arg: string) => string, + currentRateValue?: number, ): FormInputErrors { const errors: FormInputErrors = {}; const parsedRate = MoneyRequestUtils.replaceAllDigits(values.rate, toLocaleDigit); @@ -25,7 +26,7 @@ function validateRateValue( // Allow one more decimal place for accuracy const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CONST.MAX_TAX_RATE_DECIMAL_PLACES}})?$`, 'i'); - if (ratesList.some((r) => r.rate === convertToBackendAmount(Number(parsedRate)))) { + if (ratesList.some((r) => r.rate === convertToBackendAmount(Number(parsedRate)) && !(currentRateValue && currentRateValue === r.rate))) { errors.rate = Localize.translateLocal('workspace.perDiem.errors.existingRateError', {rate: NumberUtils.parseFloatAnyLocale(parsedRate)}); } else if (!rateValueRegex.test(parsedRate) || parsedRate === '') { errors.rate = Localize.translateLocal('common.error.invalidRateError'); diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx index b5fbfb3863bd..db79e7a4462a 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx @@ -52,8 +52,8 @@ function PolicyDistanceRateEditPage({route}: PolicyDistanceRateEditPageProps) { }; const validate = useCallback( - (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, currency, toLocaleDigit), - [currency, toLocaleDigit, customUnit?.rates], + (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, currency, toLocaleDigit, rate?.rate), + [currency, toLocaleDigit, customUnit?.rates, rate?.rate], ); if (!rate) { From 022a7c25d1e88bd75a80aa2b767d9f1cb58fc52a Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 26 Nov 2024 07:33:12 +0530 Subject: [PATCH 069/174] update validation function to covert rate like done in BE. Signed-off-by: krishna2323 --- src/libs/PolicyDistanceRatesUtils.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index ccef36856766..a01913b53e7a 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -2,7 +2,6 @@ import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import CONST from '@src/CONST'; import type ONYXKEYS from '@src/ONYXKEYS'; import type {Rate} from '@src/types/onyx/Policy'; -import {convertToBackendAmount} from './CurrencyUtils'; import getPermittedDecimalSeparator from './getPermittedDecimalSeparator'; import * as Localize from './Localize'; import * as MoneyRequestUtils from './MoneyRequestUtils'; @@ -23,10 +22,20 @@ function validateRateValue( const parsedRate = MoneyRequestUtils.replaceAllDigits(values.rate, toLocaleDigit); const decimalSeparator = toLocaleDigit('.'); const ratesList = Object.values(customUnitRates); + // The following logic replicates the backend's handling of rates: + // - Multiply the rate by 100 (CUSTOM_UNIT_RATE_BASE_OFFSET) to scale it, ensuring precision. + // - This ensures rates are converted as follows: + // 12 -> 1200 + // 12.1 -> 1210 + // 12.01 -> 1201 + // 12.001 -> 1200.1 + // 12.0001 -> 1200.01 + // - Using parseFloat and toFixed(10) retains the necessary precision. + const convertedRate = parseFloat((Number(values.rate || 0) * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET).toFixed(10)); // Allow one more decimal place for accuracy const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CONST.MAX_TAX_RATE_DECIMAL_PLACES}})?$`, 'i'); - if (ratesList.some((r) => r.rate === convertToBackendAmount(Number(parsedRate)) && !(currentRateValue && currentRateValue === r.rate))) { + if (ratesList.some((r) => r.rate === convertedRate && !(currentRateValue && currentRateValue === r.rate))) { errors.rate = Localize.translateLocal('workspace.perDiem.errors.existingRateError', {rate: NumberUtils.parseFloatAnyLocale(parsedRate)}); } else if (!rateValueRegex.test(parsedRate) || parsedRate === '') { errors.rate = Localize.translateLocal('common.error.invalidRateError'); From 955f9d609f23cc08b1c25ed3b5104ad5c1b39380 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 26 Nov 2024 09:48:18 +0530 Subject: [PATCH 070/174] minor update. Signed-off-by: krishna2323 --- src/libs/PolicyDistanceRatesUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index a01913b53e7a..93bcd5a32b04 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -35,10 +35,10 @@ function validateRateValue( // Allow one more decimal place for accuracy const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CONST.MAX_TAX_RATE_DECIMAL_PLACES}})?$`, 'i'); - if (ratesList.some((r) => r.rate === convertedRate && !(currentRateValue && currentRateValue === r.rate))) { - errors.rate = Localize.translateLocal('workspace.perDiem.errors.existingRateError', {rate: NumberUtils.parseFloatAnyLocale(parsedRate)}); - } else if (!rateValueRegex.test(parsedRate) || parsedRate === '') { + if (!rateValueRegex.test(parsedRate) || parsedRate === '') { errors.rate = Localize.translateLocal('common.error.invalidRateError'); + } else if (ratesList.some((r) => r.rate === convertedRate && !(currentRateValue && currentRateValue === r.rate))) { + errors.rate = Localize.translateLocal('workspace.perDiem.errors.existingRateError', {rate: Number(values.rate)}); } else if (NumberUtils.parseFloatAnyLocale(parsedRate) <= 0) { errors.rate = Localize.translateLocal('common.error.lowRateError'); } From 5ded7b7d79258cc8fa90dcb8cc7b0bf56169677e Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 26 Nov 2024 12:59:48 +0530 Subject: [PATCH 071/174] use guide email returned in CreatePolicy as actor --- src/CONST.ts | 2 -- src/libs/actions/Report.ts | 19 +++++++++++++++++-- .../BaseOnboardingEmployees.tsx | 15 --------------- src/types/onyx/Policy.ts | 7 +++++++ 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 9a765ac3577b..ee70e3b29668 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1665,7 +1665,6 @@ const CONST = { NOTIFICATIONS: 'notifications@expensify.com', PAYROLL: 'payroll@expensify.com', QA: 'qa@expensify.com', - QA_GUIDE: 'qa.guide@team.expensify.com', QA_TRAVIS: 'qa+travisreceipts@expensify.com', RECEIPTS: 'receipts@expensify.com', STUDENT_AMBASSADOR: 'studentambassadors@expensify.com', @@ -2127,7 +2126,6 @@ const CONST = { NOTIFICATIONS: Number(Config?.EXPENSIFY_ACCOUNT_ID_NOTIFICATIONS ?? 11665625), PAYROLL: Number(Config?.EXPENSIFY_ACCOUNT_ID_PAYROLL ?? 9679724), QA: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA ?? 3126513), - QA_GUIDE: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA_GUIDE ?? 14365522), QA_TRAVIS: Number(Config?.EXPENSIFY_ACCOUNT_ID_QA_TRAVIS ?? 8595733), RECEIPTS: Number(Config?.EXPENSIFY_ACCOUNT_ID_RECEIPTS ?? -1), REWARDS: Number(Config?.EXPENSIFY_ACCOUNT_ID_REWARDS ?? 11023767), // rewards@expensify.com diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e3a68b3d4e4c..56e94a847abe 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -128,6 +128,7 @@ import { import * as Session from './Session'; import * as Welcome from './Welcome'; import * as OnboardingFlow from './Welcome/OnboardingFlow'; +import { generateAccountID } from '@libs/UserUtils'; type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string | undefined) => void; @@ -3494,10 +3495,24 @@ function prepareOnboardingOptimisticData( // Onboarding tasks for guide-assigned users are posted in the #admins room. For others, the tasks are posted in the Concierge chat. const shouldPostTasksInAdminsRoom = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !currentUserEmail?.includes('+'); const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; - const actorAccountID = shouldPostTasksInAdminsRoom ? CONST.ACCOUNT_ID.QA_GUIDE : CONST.ACCOUNT_ID.CONCIERGE; const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; - const targetChatReport = shouldPostTasksInAdminsRoom ? adminsChatReport : ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); + const targetChatReport = shouldPostTasksInAdminsRoom ? adminsChatReport : ReportUtils.getChatByParticipants([CONST.ACCOUNT_ID.CONCIERGE, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; + const assignedGuideEmail = getPolicy(targetChatPolicyID)?.assignedGuide?.email ?? "Setup Specialist"; + const assignedGuidePersonalDetail = Object.values(allPersonalDetails ?? {}).find((personalDetail) => personalDetail?.login === assignedGuideEmail); + let assignedGuideAccountID : number; + if (assignedGuidePersonalDetail) { + assignedGuideAccountID = assignedGuidePersonalDetail.accountID; + } else { + assignedGuideAccountID = generateAccountID(assignedGuideEmail); + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [assignedGuideAccountID]: { + login: assignedGuideEmail, + displayName: assignedGuideEmail, + }, + }); + } + const actorAccountID = shouldPostTasksInAdminsRoom ? assignedGuideAccountID : CONST.ACCOUNT_ID.CONCIERGE; // Text message const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID, 1); diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index ed81bd70a88f..68fac2c09eae 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -43,20 +43,6 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE }); }, [translate, selectedCompanySize]); - const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); - - const setOptimticQAGuidePersonalDetail = () => { - const actorAccountID = CONST.ACCOUNT_ID.QA_GUIDE; - const optimisticPersonalDetailForQAGuide = { - accountID: actorAccountID, - avatar: allPersonalDetails?.[actorAccountID]?.avatar, - displayName: allPersonalDetails?.[actorAccountID]?.displayName ?? CONST.EMAIL.QA_GUIDE, - login: CONST.EMAIL.QA_GUIDE, - }; - // eslint-disable-next-line rulesdir/prefer-actions-set-data - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[actorAccountID]: optimisticPersonalDetailForQAGuide}); - }; - const footerContent = ( <> {!!error && ( @@ -81,7 +67,6 @@ function BaseOnboardingEmployees({shouldUseNativeStyles, route}: BaseOnboardingE const {adminsChatReportID, policyID} = Policy.createWorkspace(undefined, true, '', Policy.generatePolicyID(), CONST.ONBOARDING_CHOICES.MANAGE_TEAM); Welcome.setOnboardingAdminsChatReportID(adminsChatReportID); Welcome.setOnboardingPolicyID(policyID); - setOptimticQAGuidePersonalDetail(); } Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(route.params?.backTo)); diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 88e41aed5bb4..527a291f9db8 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1842,6 +1842,13 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback< /** Workspace account ID configured for Expensify Card */ workspaceAccountID?: number; + + /** Setup specialist guide assigned for the policy */ + assignedGuide?: { + /** The guide's email */ + email: string; + }; + } & Partial, 'addWorkspaceRoom' | keyof ACHAccount | keyof Attributes >; From 34f5e1b38a0fb17a008e2f770ad60e31207a2bd1 Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 26 Nov 2024 13:01:00 +0530 Subject: [PATCH 072/174] prettier --- src/libs/actions/Report.ts | 6 +++--- src/types/onyx/Policy.ts | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 56e94a847abe..00c3d55533c0 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -87,6 +87,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; import {getNavatticURL} from '@libs/TourUtils'; +import {generateAccountID} from '@libs/UserUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import type {OnboardingAccounting, OnboardingCompanySize} from '@src/CONST'; @@ -128,7 +129,6 @@ import { import * as Session from './Session'; import * as Welcome from './Welcome'; import * as OnboardingFlow from './Welcome/OnboardingFlow'; -import { generateAccountID } from '@libs/UserUtils'; type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string | undefined) => void; @@ -3498,9 +3498,9 @@ function prepareOnboardingOptimisticData( const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; const targetChatReport = shouldPostTasksInAdminsRoom ? adminsChatReport : ReportUtils.getChatByParticipants([CONST.ACCOUNT_ID.CONCIERGE, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; - const assignedGuideEmail = getPolicy(targetChatPolicyID)?.assignedGuide?.email ?? "Setup Specialist"; + const assignedGuideEmail = getPolicy(targetChatPolicyID)?.assignedGuide?.email ?? 'Setup Specialist'; const assignedGuidePersonalDetail = Object.values(allPersonalDetails ?? {}).find((personalDetail) => personalDetail?.login === assignedGuideEmail); - let assignedGuideAccountID : number; + let assignedGuideAccountID: number; if (assignedGuidePersonalDetail) { assignedGuideAccountID = assignedGuidePersonalDetail.accountID; } else { diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 527a291f9db8..2628b7b44341 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1848,7 +1848,6 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback< /** The guide's email */ email: string; }; - } & Partial, 'addWorkspaceRoom' | keyof ACHAccount | keyof Attributes >; From de2b8b206daae467029be28185a6c428e5cb4923 Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 26 Nov 2024 13:07:19 +0530 Subject: [PATCH 073/174] fix missing and unused values --- src/libs/actions/Report.ts | 3 ++- src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 00c3d55533c0..702ed69d32a1 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3574,7 +3574,8 @@ function prepareOnboardingOptimisticData( targetChatPolicyID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, ); - const emailCreatingAction = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? CONST.EMAIL.QA_GUIDE : CONST.EMAIL.CONCIERGE; + const emailCreatingAction = + engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? allPersonalDetails?.[actorAccountID]?.login ?? CONST.EMAIL.CONCIERGE : CONST.EMAIL.CONCIERGE; const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(emailCreatingAction); const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction( currentTask.reportID, diff --git a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx index 68fac2c09eae..fee409c6480d 100644 --- a/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx +++ b/src/pages/OnboardingEmployees/BaseOnboardingEmployees.tsx @@ -1,5 +1,5 @@ import React, {useMemo, useState} from 'react'; -import Onyx, {useOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import FormHelpMessage from '@components/FormHelpMessage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; From 6ec519ffcfc7f796e390eb084971086357342f8b Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 26 Nov 2024 13:28:41 +0530 Subject: [PATCH 074/174] remove optimstic video to avoid duplication --- src/libs/actions/Report.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 702ed69d32a1..97048141080e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3877,7 +3877,9 @@ function prepareOnboardingOptimisticData( const guidedSetupData: GuidedSetupData = [{type: 'message', ...textMessage}]; - if ('video' in data && data.video && videoCommentAction && videoMessage) { + // If we post tasks in the #admins room, it means that a guide is assigned and this video is sent from the backend. + // If we add an optimistic video message as well, it will be duplicated because backend sends this as-is. + if (!shouldPostTasksInAdminsRoom && 'video' in data && data.video && videoCommentAction && videoMessage) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, From 3d31534a574382bc23be66018d095c7335e66a0f Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 26 Nov 2024 13:59:01 +0530 Subject: [PATCH 075/174] remove duplication of messages --- src/libs/actions/Report.ts | 58 ++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 97048141080e..42ac46e99de9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3759,13 +3759,6 @@ function prepareOnboardingOptimisticData( lastActorAccountID: actorAccountID, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [textCommentAction.reportActionID]: textCommentAction as ReportAction, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_INTRO_SELECTED, @@ -3773,6 +3766,17 @@ function prepareOnboardingOptimisticData( }, ); + // If we post tasks in the #admins room, it means that a guide is assigned and all messages except tasks are handled by the backend + if (!shouldPostTasksInAdminsRoom) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [textCommentAction.reportActionID]: textCommentAction as ReportAction, + }, + }); + } + if (!wasInvited) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -3783,13 +3787,16 @@ function prepareOnboardingOptimisticData( const successData: OnyxUpdate[] = [...tasksForSuccessData]; - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: { - [textCommentAction.reportActionID]: {pendingAction: null}, - }, - }); + // If we post tasks in the #admins room, it means that a guide is assigned and all messages except tasks are handled by the backend + if (!shouldPostTasksInAdminsRoom) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [textCommentAction.reportActionID]: {pendingAction: null}, + }, + }); + } let failureReport: Partial = { lastMessageTranslationKey: '', @@ -3819,7 +3826,16 @@ function prepareOnboardingOptimisticData( key: `${ONYXKEYS.COLLECTION.REPORT}${targetChatReportID}`, value: failureReport, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_INTRO_SELECTED, + value: {choice: null}, + }, + ); + // If we post tasks in the #admins room, it means that a guide is assigned and all messages except tasks are handled by the backend + if (!shouldPostTasksInAdminsRoom) { + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: { @@ -3827,13 +3843,8 @@ function prepareOnboardingOptimisticData( errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('report.genericAddCommentFailureMessage'), } as ReportAction, }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.NVP_INTRO_SELECTED, - value: {choice: null}, - }, - ); + }); + } if (!wasInvited) { failureData.push({ @@ -3875,10 +3886,9 @@ function prepareOnboardingOptimisticData( }); } - const guidedSetupData: GuidedSetupData = [{type: 'message', ...textMessage}]; + // If we post tasks in the #admins room, it means that a guide is assigned and all messages except tasks are handled by the backend + const guidedSetupData: GuidedSetupData = shouldPostTasksInAdminsRoom ? [] : [{type: 'message', ...textMessage}]; - // If we post tasks in the #admins room, it means that a guide is assigned and this video is sent from the backend. - // If we add an optimistic video message as well, it will be duplicated because backend sends this as-is. if (!shouldPostTasksInAdminsRoom && 'video' in data && data.video && videoCommentAction && videoMessage) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, From 14a9e7e56ebd6d51a04eac51a41e17724838b484 Mon Sep 17 00:00:00 2001 From: Rutika Pawar <183392827+twilight2294@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:36:24 +0530 Subject: [PATCH 076/174] set default value of success prop to true --- src/components/ButtonWithDropdownMenu/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx index f86e0c2da999..7cf752a61214 100644 --- a/src/components/ButtonWithDropdownMenu/index.tsx +++ b/src/components/ButtonWithDropdownMenu/index.tsx @@ -17,7 +17,7 @@ import type {AnchorPosition} from '@src/styles'; import type {ButtonWithDropdownMenuProps} from './types'; function ButtonWithDropdownMenu({ - success = false, + success = true, isSplitButton = true, isLoading = false, isDisabled = false, From d4170854d733cdd21fe1b3522f292832b5284480 Mon Sep 17 00:00:00 2001 From: Rutika Pawar <183392827+twilight2294@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:38:19 +0530 Subject: [PATCH 077/174] remove default success prop --- src/components/MoneyRequestConfirmationList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index b87c51cc6b64..6a888a09b60b 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -853,7 +853,6 @@ function MoneyRequestConfirmationList({ /> ) : ( confirm(value as PaymentMethodType)} options={splitOrRequestOptions} From 85a5a19fcc4726b187ad8cc1ebb29edf717266a7 Mon Sep 17 00:00:00 2001 From: Rutika Pawar <183392827+twilight2294@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:38:40 +0530 Subject: [PATCH 078/174] remove default success prop --- src/components/SettlementButton/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index ed1d8fd73565..d2b84f6cd54a 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -238,7 +238,6 @@ function SettlementButton({ > {(triggerKYCFlow, buttonRef) => ( - success onOptionsMenuShow={onPaymentOptionsShow} onOptionsMenuHide={onPaymentOptionsHide} buttonRef={buttonRef} From 841be8b8a8628ed13d25356d46028a86206416fa Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 26 Nov 2024 21:47:37 +0800 Subject: [PATCH 079/174] play sound when pay invoice --- src/libs/actions/IOU.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 511a5bf16283..2132a12f4439 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -38,6 +38,7 @@ import * as FileUtils from '@libs/fileDownload/FileUtils'; import GoogleTagManager from '@libs/GoogleTagManager'; import * as IOUUtils from '@libs/IOUUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; +import playSound, {SOUNDS} from '@libs/Sound'; import * as Localize from '@libs/Localize'; import isSearchTopmostCentralPane from '@libs/Navigation/isSearchTopmostCentralPane'; import Navigation from '@libs/Navigation/Navigation'; @@ -7544,6 +7545,7 @@ function payInvoice(paymentMethodType: PaymentMethodType, chatReport: OnyxTypes. }; } + playSound(SOUNDS.SUCCESS); API.write(WRITE_COMMANDS.PAY_INVOICE, params, {optimisticData, successData, failureData}); } From 5d325d80eefff65a72c707d95d4069d7fe178b2f Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 26 Nov 2024 21:48:20 +0800 Subject: [PATCH 080/174] play sound in payMoneyRequest instead --- src/components/ProcessMoneyReportHoldMenu.tsx | 2 -- src/components/SettlementButton/index.tsx | 4 ---- src/libs/actions/IOU.ts | 1 + 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index f1a72cc7fb8e..3d6ad9006dc5 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -4,7 +4,6 @@ import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import Navigation from '@libs/Navigation/Navigation'; import {isLinkedTransactionHeld} from '@libs/ReportActionsUtils'; -import playSound, {SOUNDS} from '@libs/Sound'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -75,7 +74,6 @@ function ProcessMoneyReportHoldMenu({ if (startAnimation) { startAnimation(); } - playSound(SOUNDS.SUCCESS); IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport, full); } onClose(); diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index ed1d8fd73565..001e561e70eb 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -10,7 +10,6 @@ import useNetwork from '@hooks/useNetwork'; import Navigation from '@libs/Navigation/Navigation'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import playSound, {SOUNDS} from '@libs/Sound'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as BankAccounts from '@userActions/BankAccounts'; import * as IOU from '@userActions/IOU'; @@ -213,9 +212,6 @@ function SettlementButton({ return; } - if (!ReportUtils.hasHeldExpenses(iouReport?.reportID)) { - playSound(SOUNDS.SUCCESS); - } onPress(iouPaymentType); }; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 2132a12f4439..bcde74a03f0b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7497,6 +7497,7 @@ function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.R // Expensify Wallets. const apiCommand = paymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY ? WRITE_COMMANDS.PAY_MONEY_REQUEST_WITH_WALLET : WRITE_COMMANDS.PAY_MONEY_REQUEST; + playSound(SOUNDS.SUCCESS); API.write(apiCommand, params, {optimisticData, successData, failureData}); } From 290ef8793570a825afa645886996bf1c829b5f3e Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 26 Nov 2024 21:54:30 +0800 Subject: [PATCH 081/174] prettier --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index bcde74a03f0b..72f14974e227 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -38,7 +38,6 @@ import * as FileUtils from '@libs/fileDownload/FileUtils'; import GoogleTagManager from '@libs/GoogleTagManager'; import * as IOUUtils from '@libs/IOUUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; -import playSound, {SOUNDS} from '@libs/Sound'; import * as Localize from '@libs/Localize'; import isSearchTopmostCentralPane from '@libs/Navigation/isSearchTopmostCentralPane'; import Navigation from '@libs/Navigation/Navigation'; @@ -53,6 +52,7 @@ import * as ReportConnection from '@libs/ReportConnection'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as SessionUtils from '@libs/SessionUtils'; +import playSound, {SOUNDS} from '@libs/Sound'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import {getCurrency, getTransaction} from '@libs/TransactionUtils'; From 2e68cb902c1a7126992fb1257a19f0192fd1f0ee Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 26 Nov 2024 22:57:55 +0800 Subject: [PATCH 082/174] clear pending action when success --- src/libs/actions/IOU.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 3fb01f916acd..35e138cb96d8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,7 +1,7 @@ import {format} from 'date-fns'; import {fastMerge, Str} from 'expensify-common'; import {InteractionManager} from 'react-native'; -import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxMergeInput, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {PartialDeep, SetRequired, ValueOf} from 'type-fest'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; @@ -6313,6 +6313,7 @@ function getReportFromHoldRequestsOnyxData( optimisticHoldActionID: string; optimisticHoldReportExpenseActionIDs: OptimisticHoldReportExpenseActionID[]; optimisticData: OnyxUpdate[]; + successData: OnyxUpdate[]; failureData: OnyxUpdate[]; } { const {holdReportActions, holdTransactions} = getHoldReportActionsAndTransactions(iouReport?.reportID ?? ''); @@ -6352,6 +6353,7 @@ function getReportFromHoldRequestsOnyxData( const updateHeldReports: Record> = {}; const addHoldReportActions: OnyxTypes.ReportActions = {}; + const addHoldReportActionsSuccess: OnyxCollection> = {}; const deleteHoldReportActions: Record> = {}; const optimisticHoldReportExpenseActionIDs: OptimisticHoldReportExpenseActionID[] = []; @@ -6378,6 +6380,9 @@ function getReportFromHoldRequestsOnyxData( }, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }; + addHoldReportActionsSuccess[reportActionID] = { + pendingAction: null, + }; const heldReport = ReportUtils.getReportOrDraftReport(holdReportAction.childReportID); if (heldReport) { @@ -6459,6 +6464,23 @@ function getReportFromHoldRequestsOnyxData( bringHeldTransactionsBack[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = transaction; }); + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + value: { + [optimisticExpenseReportPreview.reportActionID]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticExpenseReport.reportID}`, + value: addHoldReportActionsSuccess, + }, + ]; + const failureData: OnyxUpdate[] = [ // remove added optimistic expense report { @@ -6498,6 +6520,7 @@ function getReportFromHoldRequestsOnyxData( optimisticData, optimisticHoldActionID: optimisticExpenseReportPreview.reportActionID, failureData, + successData, optimisticHoldReportID: optimisticExpenseReport.reportID, optimisticHoldReportExpenseActionIDs, }; @@ -6758,6 +6781,7 @@ function getPayMoneyRequestParams( const holdReportOnyxData = getReportFromHoldRequestsOnyxData(chatReport, iouReport, recipient); optimisticData.push(...holdReportOnyxData.optimisticData); + successData.push(...holdReportOnyxData.successData); failureData.push(...holdReportOnyxData.failureData); optimisticHoldReportID = holdReportOnyxData.optimisticHoldReportID; optimisticHoldActionID = holdReportOnyxData.optimisticHoldActionID; @@ -7104,6 +7128,7 @@ function approveMoneyRequest(expenseReport: OnyxEntry, full?: const holdReportOnyxData = getReportFromHoldRequestsOnyxData(chatReport, expenseReport, {accountID: expenseReport.ownerAccountID}); optimisticData.push(...holdReportOnyxData.optimisticData); + successData.push(...holdReportOnyxData.successData); failureData.push(...holdReportOnyxData.failureData); optimisticHoldReportID = holdReportOnyxData.optimisticHoldReportID; optimisticHoldActionID = holdReportOnyxData.optimisticHoldActionID; From 66de3500327eeac908614d00bf63d18d7eb3d6e0 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 26 Nov 2024 23:01:40 +0800 Subject: [PATCH 083/174] remove unused import --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 35e138cb96d8..fb2ff2a9a628 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,7 +1,7 @@ import {format} from 'date-fns'; import {fastMerge, Str} from 'expensify-common'; import {InteractionManager} from 'react-native'; -import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxMergeInput, OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {PartialDeep, SetRequired, ValueOf} from 'type-fest'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; From b11540245ff4c93bdaa71fa0381e3972db011e5b Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Tue, 26 Nov 2024 23:10:13 +0700 Subject: [PATCH 084/174] remove redundant border --- src/styles/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/index.ts b/src/styles/index.ts index a60a759b99b2..778e289ad917 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4656,6 +4656,7 @@ const styles = (theme: ThemeColors) => moneyRequestAttachReceiptThumbnail: { backgroundColor: theme.hoverComponentBG, width: '100%', + borderWidth: 0, }, moneyRequestAttachReceiptThumbnailIcon: { From 61171d0da2948f0230867512df4bbc356840ea40 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Tue, 26 Nov 2024 23:53:22 +0700 Subject: [PATCH 085/174] pass onPress to open report when pressing empty receipt --- src/components/ReceiptImage.tsx | 12 +++++++++++- .../MoneyRequestPreviewContent.tsx | 5 +++-- .../ReportActionItem/ReportActionItemImage.tsx | 5 +++++ .../ReportActionItem/ReportActionItemImages.tsx | 6 +++++- src/components/ReportActionItem/ReportPreview.tsx | 13 ++++++++----- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/components/ReceiptImage.tsx b/src/components/ReceiptImage.tsx index f8c714e5611c..bca1ef8b74e6 100644 --- a/src/components/ReceiptImage.tsx +++ b/src/components/ReceiptImage.tsx @@ -82,6 +82,9 @@ type ReceiptImageProps = ( fallbackIconBackground?: string; isEmptyReceipt?: boolean; + + /** Callback to be called on pressing the image */ + onPress?: () => void; }; function ReceiptImage({ @@ -101,11 +104,18 @@ function ReceiptImage({ fallbackIconColor, fallbackIconBackground, isEmptyReceipt = false, + onPress, }: ReceiptImageProps) { const styles = useThemeStyles(); if (isEmptyReceipt) { - return ; + return ( + + ); } if (isPDFThumbnail) { diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 26fbb962fefa..1e27a5bbb7f0 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -319,6 +319,8 @@ function MoneyRequestPreviewContent({ } }; + const shouldDisableOnPress = isBillSplit && isEmptyObject(transaction); + const childContainer = ( {isEmptyObject(transaction) && !ReportActionsUtils.isMessageDeleted(action) && action.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE ? ( @@ -438,8 +441,6 @@ function MoneyRequestPreviewContent({ return childContainer; } - const shouldDisableOnPress = isBillSplit && isEmptyObject(transaction); - return ( void; }; /** @@ -82,6 +85,7 @@ function ReportActionItemImage({ readonly = false, shouldMapHaveBorderRadius, isFromReviewDuplicates = false, + onPress, }: ReportActionItemImageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -132,6 +136,7 @@ function ReportActionItemImage({ source: thumbnail ?? image ?? '', shouldUseInitialObjectPosition: isDistanceRequest, isEmptyReceipt, + onPress, }; } diff --git a/src/components/ReportActionItem/ReportActionItemImages.tsx b/src/components/ReportActionItem/ReportActionItemImages.tsx index ed892f7ca26f..9995b7f77860 100644 --- a/src/components/ReportActionItem/ReportActionItemImages.tsx +++ b/src/components/ReportActionItem/ReportActionItemImages.tsx @@ -27,6 +27,9 @@ type ReportActionItemImagesProps = { /** if the corresponding report action item is hovered */ isHovered?: boolean; + + /** Callback to be called on onPress */ + onPress?: () => void; }; /** @@ -38,7 +41,7 @@ type ReportActionItemImagesProps = { * additional number when subtracted from size. */ -function ReportActionItemImages({images, size, total, isHovered = false}: ReportActionItemImagesProps) { +function ReportActionItemImages({images, size, total, isHovered = false, onPress}: ReportActionItemImagesProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -87,6 +90,7 @@ function ReportActionItemImages({images, size, total, isHovered = false}: Report isThumbnail={isThumbnail} isSingleImage={numberOfShownImages === 1} shouldMapHaveBorderRadius={false} + onPress={onPress} /> ); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index af39e2f8aafd..f5080e3eab72 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -450,6 +450,12 @@ function ReportPreview({ } }, [isPaidAnimationRunning, iouSettled, checkMarkScale]); + const openReportFromPreview = useCallback(() => { + Performance.markStart(CONST.TIMING.OPEN_REPORT_FROM_PREVIEW); + Timing.start(CONST.TIMING.OPEN_REPORT_FROM_PREVIEW); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID)); + }, [iouReportID]); + return ( { - Performance.markStart(CONST.TIMING.OPEN_REPORT_FROM_PREVIEW); - Timing.start(CONST.TIMING.OPEN_REPORT_FROM_PREVIEW); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID)); - }} + onPress={openReportFromPreview} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action, checkIfContextMenuActive)} @@ -476,6 +478,7 @@ function ReportPreview({ images={lastThreeReceipts} total={allTransactions.length} size={CONST.RECEIPT.MAX_REPORT_PREVIEW_RECEIPTS} + onPress={openReportFromPreview} /> From 314657af49a8fb6af9856ff79f931180fe1f0a56 Mon Sep 17 00:00:00 2001 From: daledah Date: Wed, 27 Nov 2024 01:38:04 +0700 Subject: [PATCH 086/174] fix: use display create instead of submit expense --- src/components/ReportWelcomeText.tsx | 8 +++++++- src/languages/en.ts | 1 + src/languages/es.ts | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/ReportWelcomeText.tsx b/src/components/ReportWelcomeText.tsx index 1e3ce6119315..08c172c86c9c 100644 --- a/src/components/ReportWelcomeText.tsx +++ b/src/components/ReportWelcomeText.tsx @@ -6,6 +6,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import Permissions from '@libs/Permissions'; import {getPolicy} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; @@ -51,7 +52,12 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { item !== CONST.IOU.TYPE.INVOICE, ); const additionalText = filteredOptions - .map((item, index) => `${index === filteredOptions.length - 1 && index > 0 ? `${translate('common.or')} ` : ''}${translate(`reportActionsView.iouTypes.${item}`)}`) + .map( + (item, index) => + `${index === filteredOptions.length - 1 && index > 0 ? `${translate('common.or')} ` : ''}${translate( + Permissions.canUseCombinedTrackSubmit() && item === 'submit' ? `reportActionsView.create` : `reportActionsView.iouTypes.${item}`, + )}`, + ) .join(', '); const canEditPolicyDescription = ReportUtils.canEditPolicyDescription(policy); const reportName = ReportUtils.getReportName(report); diff --git a/src/languages/en.ts b/src/languages/en.ts index c1067e195985..75d7bb388a9a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -689,6 +689,7 @@ const translations = { welcomeToRoom: ({roomName}: WelcomeToRoomParams) => `Welcome to ${roomName}!`, usePlusButton: ({additionalText}: UsePlusButtonParams) => `\nUse the + button to ${additionalText} an expense.`, askConcierge: '\nAsk questions and get 24/7 realtime support.', + create: 'create', iouTypes: { pay: 'pay', split: 'split', diff --git a/src/languages/es.ts b/src/languages/es.ts index f7af1be45139..bb26ca2d95a3 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -684,6 +684,7 @@ const translations = { welcomeToRoom: ({roomName}: WelcomeToRoomParams) => `¡Bienvenido a ${roomName}!`, usePlusButton: ({additionalText}: UsePlusButtonParams) => `\nUsa el botón + para ${additionalText} un gasto`, askConcierge: 'Haz preguntas y obtén soporte en tiempo real las 24/7.', + create: 'crear', iouTypes: { pay: 'pagar', split: 'dividir', From e8dd2bd3496a313f378678028c2a7b62c0acf30b Mon Sep 17 00:00:00 2001 From: daledah Date: Wed, 27 Nov 2024 01:41:06 +0700 Subject: [PATCH 087/174] fix: use usePermission hook --- src/components/ReportWelcomeText.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/ReportWelcomeText.tsx b/src/components/ReportWelcomeText.tsx index 08c172c86c9c..cc7dbed1f6e9 100644 --- a/src/components/ReportWelcomeText.tsx +++ b/src/components/ReportWelcomeText.tsx @@ -3,10 +3,10 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import Permissions from '@libs/Permissions'; import {getPolicy} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; @@ -47,6 +47,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { const welcomeMessage = SidebarUtils.getWelcomeMessage(report, policy); const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, policy, participantAccountIDs); const canEditReportDescription = ReportUtils.canEditReportDescription(report, policy); + const {canUseCombinedTrackSubmit} = usePermissions(); const filteredOptions = moneyRequestOptions.filter( (item): item is Exclude => item !== CONST.IOU.TYPE.INVOICE, @@ -55,7 +56,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { .map( (item, index) => `${index === filteredOptions.length - 1 && index > 0 ? `${translate('common.or')} ` : ''}${translate( - Permissions.canUseCombinedTrackSubmit() && item === 'submit' ? `reportActionsView.create` : `reportActionsView.iouTypes.${item}`, + canUseCombinedTrackSubmit && item === 'submit' ? `reportActionsView.create` : `reportActionsView.iouTypes.${item}`, )}`, ) .join(', '); From eb61515becd648248b254385a0f6fbcbce13c58f Mon Sep 17 00:00:00 2001 From: daledah Date: Wed, 27 Nov 2024 02:16:43 +0700 Subject: [PATCH 088/174] fix: native --- src/libs/InputUtils/index.native.ts | 7 ++++--- src/libs/InputUtils/index.ts | 6 +++--- src/libs/InputUtils/types.ts | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libs/InputUtils/index.native.ts b/src/libs/InputUtils/index.native.ts index 3db4280b7d73..6cf00877ca9a 100644 --- a/src/libs/InputUtils/index.native.ts +++ b/src/libs/InputUtils/index.native.ts @@ -1,6 +1,7 @@ -import type {MoveSelectiontoEnd, ScrollToBottom} from './types'; +import type {MoveSelectiontoEnd, ScrollInput} from './types'; -const scrollToBottom: ScrollToBottom = () => {}; +const scrollToBottom: ScrollInput = () => {}; +const scrollToRight: ScrollInput = () => {}; const moveSelectionToEnd: MoveSelectiontoEnd = () => {}; -export {scrollToBottom, moveSelectionToEnd}; +export {scrollToBottom, moveSelectionToEnd, scrollToRight}; diff --git a/src/libs/InputUtils/index.ts b/src/libs/InputUtils/index.ts index 5e274ebfbbd3..e992be5ca233 100644 --- a/src/libs/InputUtils/index.ts +++ b/src/libs/InputUtils/index.ts @@ -1,6 +1,6 @@ -import type {MoveSelectiontoEnd, ScrollToBottom} from './types'; +import type {MoveSelectiontoEnd, ScrollInput} from './types'; -const scrollToBottom: ScrollToBottom = (input) => { +const scrollToBottom: ScrollInput = (input) => { if (!('scrollTop' in input)) { return; } @@ -8,7 +8,7 @@ const scrollToBottom: ScrollToBottom = (input) => { input.scrollTop = input.scrollHeight; }; -const scrollToRight: ScrollToBottom = (input) => { +const scrollToRight: ScrollInput = (input) => { if (!('scrollLeft' in input)) { return; } diff --git a/src/libs/InputUtils/types.ts b/src/libs/InputUtils/types.ts index 875ac6b602e4..394cdb8722ab 100644 --- a/src/libs/InputUtils/types.ts +++ b/src/libs/InputUtils/types.ts @@ -1,6 +1,6 @@ import type {TextInput} from 'react-native'; -type ScrollToBottom = (input: HTMLInputElement | TextInput) => void; +type ScrollInput = (input: HTMLInputElement | TextInput) => void; type MoveSelectiontoEnd = (input: HTMLInputElement | TextInput) => void; -export type {ScrollToBottom, MoveSelectiontoEnd}; +export type {ScrollInput, MoveSelectiontoEnd}; From be746b42581559afd8b0be2754a0bd469aab9452 Mon Sep 17 00:00:00 2001 From: jacobkim9881 Date: Wed, 27 Nov 2024 12:10:44 +0900 Subject: [PATCH 089/174] fix: shouldHandleNavigationBack works for YearPickerModal on all platforms --- src/components/DatePicker/CalendarPicker/YearPickerModal.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx index 3c4bb94d1b6d..7dcec93b1cd3 100644 --- a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx +++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx @@ -6,7 +6,6 @@ import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Browser from '@libs/Browser'; import CONST from '@src/CONST'; import type CalendarPickerListItem from './types'; @@ -54,7 +53,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear onModalHide={onClose} hideModalContentWhileAnimating useNativeDriver - shouldHandleNavigationBack={Browser.isMobileChrome()} + shouldHandleNavigationBack > Date: Wed, 27 Nov 2024 12:36:53 +0800 Subject: [PATCH 090/174] minor fix --- src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx index 534d4a7c2d90..3c034343de76 100644 --- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx @@ -46,7 +46,7 @@ function ActivatePhysicalCardPage({ const [formError, setFormError] = useState(''); const [lastFourDigits, setLastFourDigits] = useState(''); const [lastPressedDigit, setLastPressedDigit] = useState(''); - const [shouldShowError, setShouldShowError] = useState(false); + const [shouldAllowToShowError, setShouldAllowToShowError] = useState(false); const inactiveCard = cardList?.[cardID]; const cardError = ErrorUtils.getLatestErrorMessage(inactiveCard ?? {}); @@ -100,7 +100,7 @@ function ActivatePhysicalCardPage({ }; const submitAndNavigateToNextPage = useCallback(() => { - setShouldShowError(true); + setShouldAllowToShowError(true); activateCardCodeInputRef.current?.blur(); if (lastFourDigits.replace(CONST.MAGIC_CODE_EMPTY_CHAR, '').length !== LAST_FOUR_DIGITS_LENGTH) { @@ -139,7 +139,7 @@ function ActivatePhysicalCardPage({ lastPressedDigit={lastPressedDigit} onChangeText={onCodeInput} onFulfill={submitAndNavigateToNextPage} - errorText={shouldShowError ? formError || cardError : ''} + errorText={shouldAllowToShowError ? formError || cardError : ''} ref={activateCardCodeInputRef} /> From 4962b8e44bd6cb160645cb12c48f7196f84dc5aa Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 27 Nov 2024 15:11:23 +0800 Subject: [PATCH 091/174] fix skip confirmiaton is true for manual split --- src/pages/iou/request/step/IOURequestStepAmount.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 72a931bf359c..03850aa130dd 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -76,12 +76,12 @@ function IOURequestStepAmount({ // For quick button actions, we'll skip the confirmation page unless the report is archived or this is a workspace request, as // the user will have to add a merchant. const shouldSkipConfirmation: boolean = useMemo(() => { - if (!skipConfirmation || !report?.reportID) { + if (isSplitBill || !skipConfirmation || !report?.reportID) { return false; } return !(ReportUtils.isArchivedRoom(report, reportNameValuePairs) || ReportUtils.isPolicyExpenseChat(report)); - }, [report, skipConfirmation, reportNameValuePairs]); + }, [report, isSplitBill, skipConfirmation, reportNameValuePairs]); useFocusEffect( useCallback(() => { From 0a2ef8edc2d3244af2767518542286f3b6bd8637 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 27 Nov 2024 15:11:37 +0800 Subject: [PATCH 092/174] remove unnecessary logic --- .../iou/request/step/IOURequestStepAmount.tsx | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 03850aa130dd..2b3d639deaa9 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -178,27 +178,6 @@ function IOURequestStepAmount({ const backendAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); if (shouldSkipConfirmation) { - // Only skip confirmation when the split is not configurable, for now Smartscanned splits cannot be configured - if (iouType === CONST.IOU.TYPE.SPLIT && transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.SCAN) { - playSound(SOUNDS.DONE); - IOU.splitBill({ - participants, - currentUserLogin: currentUserPersonalDetails.login ?? '', - currentUserAccountID: currentUserPersonalDetails.accountID, - amount: backendAmount, - comment: '', - currency, - merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, - tag: '', - category: '', - created: transaction?.created ?? '', - billable: false, - iouRequestType: CONST.IOU.REQUEST_TYPE.MANUAL, - existingSplitChatReportID: report?.reportID, - }); - return; - } - if (iouType === CONST.IOU.TYPE.PAY || iouType === CONST.IOU.TYPE.SEND) { if (paymentMethod && paymentMethod === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { IOU.sendMoneyWithWallet(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0) ?? {}); From 984c35642279f66e1186f29d3d6a5b34b4b65c87 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 27 Nov 2024 10:32:58 +0100 Subject: [PATCH 093/174] fix disappearing background --- src/pages/Search/EmptySearchView.tsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index 507ab593eafb..14ed946fb9c5 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -121,8 +121,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { case CONST.SEARCH.DATA_TYPES.TRIP: return { headerMedia: LottieAnimations.TripsEmptyState, - headerStyles: StyleUtils.getBackgroundColorStyle(theme.travelBG), - headerContentStyles: StyleUtils.getWidthAndHeightStyle(375, 240), + headerContentStyles: [StyleUtils.getWidthAndHeightStyle(375, 240), StyleUtils.getBackgroundColorStyle(theme.travelBG)], title: translate('travel.title'), titleStyles: {...styles.textAlignLeft}, subtitle: subtitleComponent, @@ -137,7 +136,6 @@ function EmptySearchView({type}: EmptySearchViewProps) { case CONST.SEARCH.DATA_TYPES.EXPENSE: return { headerMedia: LottieAnimations.GenericEmptyState, - headerStyles: [StyleUtils.getBackgroundColorStyle(theme.emptyFolderBG)], title: translate('search.searchResults.emptyExpenseResults.title'), subtitle: translate('search.searchResults.emptyExpenseResults.subtitle'), buttons: [ @@ -166,17 +164,16 @@ function EmptySearchView({type}: EmptySearchViewProps) { success: true, }, ], - headerContentStyles: styles.emptyStateFolderWebStyles, + headerContentStyles: [styles.emptyStateFolderWebStyles, StyleUtils.getBackgroundColorStyle(theme.emptyFolderBG)], }; case CONST.SEARCH.DATA_TYPES.CHAT: case CONST.SEARCH.DATA_TYPES.INVOICE: default: return { headerMedia: LottieAnimations.GenericEmptyState, - headerStyles: [StyleUtils.getBackgroundColorStyle(theme.emptyFolderBG)], title: translate('search.searchResults.emptyResults.title'), subtitle: translate('search.searchResults.emptyResults.subtitle'), - headerContentStyles: styles.emptyStateFolderWebStyles, + headerContentStyles: [styles.emptyStateFolderWebStyles, StyleUtils.getBackgroundColorStyle(theme.emptyFolderBG)], }; } }, [ @@ -188,11 +185,11 @@ function EmptySearchView({type}: EmptySearchViewProps) { styles.textAlignLeft, styles.emptyStateFolderWebStyles, subtitleComponent, + hasSeenTour, ctaErrorMessage, navatticURL, - shouldRedirectToExpensifyClassic, - hasSeenTour, viewTourTaskReport, + shouldRedirectToExpensifyClassic, ]); return ( @@ -201,12 +198,12 @@ function EmptySearchView({type}: EmptySearchViewProps) { SkeletonComponent={SearchRowSkeleton} headerMediaType={CONST.EMPTY_STATE_MEDIA.ANIMATION} headerMedia={content.headerMedia} - headerStyles={[content.headerStyles, styles.emptyStateCardIllustrationContainer]} + headerStyles={[styles.emptyStateCardIllustrationContainer, styles.overflowHidden]} title={content.title} titleStyles={content.titleStyles} subtitle={content.subtitle} buttons={content.buttons} - headerContentStyles={[styles.h100, styles.w100, content.headerContentStyles]} + headerContentStyles={[styles.h100, styles.w100, ...content.headerContentStyles]} lottieWebViewStyles={styles.emptyStateFolderWebStyles} /> Date: Wed, 27 Nov 2024 15:49:14 +0530 Subject: [PATCH 094/174] make comments concise --- src/components/LHNOptionsList/OptionRowLHN.tsx | 5 +---- src/libs/actions/Report.ts | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index b9e2e9274e5e..8bf19a1edcbf 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -55,10 +55,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const session = useSession(); - // A guide is assigned to the user if they choose the 'MANAGE_TEAM' onboarding option, except for users with emails containing '+'. - // Posting tasks for users with emails containig '+' in the #admins room is not allowed by the backend. - // Onboarding tasks for guide-assigned users are posted in the #admins room. For others, the tasks are posted in the Concierge chat. - // So, we should show the "Get started here" tooltip accordingly on the #admins room or Concierge chat on LHN. + // Guides are assigned for the MANAGE_TEAM onboarding action, except for emails that have a '+'. const isOnboardingGuideAssigned = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !session?.email?.includes('+'); const shouldShowToooltipOnThisReport = isOnboardingGuideAssigned ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report); const [shouldHideGBRTooltip] = useOnyx(ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP, {initialValue: true}); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 42ac46e99de9..2c9a3ec77b49 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3490,9 +3490,7 @@ function prepareOnboardingOptimisticData( } } - // A guide is assigned to the user if they choose the 'MANAGE_TEAM' onboarding option, except for users with emails containing '+'. - // Posting tasks for users with emails containig '+' in the #admins room is not allowed by the backend. - // Onboarding tasks for guide-assigned users are posted in the #admins room. For others, the tasks are posted in the Concierge chat. + // Guides are assigned and tasks are posted in the #admins room for the MANAGE_TEAM onboarding action, except for emails that have a '+'. const shouldPostTasksInAdminsRoom = engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !currentUserEmail?.includes('+'); const integrationName = userReportedIntegration ? CONST.ONBOARDING_ACCOUNTING_MAPPING[userReportedIntegration] : ''; const adminsChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; From 1219cb65974efedaf5b01119579889be17e40f1d Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 27 Nov 2024 11:46:28 +0100 Subject: [PATCH 095/174] fix web background --- src/pages/Search/EmptySearchView.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index 14ed946fb9c5..a77363375caf 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -132,6 +132,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { success: true, }, ], + lottieWebViewStyles: {backgroundColor: theme.travelBG, ...styles.emptyStateFolderWebStyles}, }; case CONST.SEARCH.DATA_TYPES.EXPENSE: return { @@ -165,6 +166,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { }, ], headerContentStyles: [styles.emptyStateFolderWebStyles, StyleUtils.getBackgroundColorStyle(theme.emptyFolderBG)], + lottieWebViewStyles: {backgroundColor: theme.emptyFolderBG, ...styles.emptyStateFolderWebStyles}, }; case CONST.SEARCH.DATA_TYPES.CHAT: case CONST.SEARCH.DATA_TYPES.INVOICE: @@ -174,6 +176,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { title: translate('search.searchResults.emptyResults.title'), subtitle: translate('search.searchResults.emptyResults.subtitle'), headerContentStyles: [styles.emptyStateFolderWebStyles, StyleUtils.getBackgroundColorStyle(theme.emptyFolderBG)], + lottieWebViewStyles: {backgroundColor: theme.emptyFolderBG, ...styles.emptyStateFolderWebStyles}, }; } }, [ @@ -204,7 +207,7 @@ function EmptySearchView({type}: EmptySearchViewProps) { subtitle={content.subtitle} buttons={content.buttons} headerContentStyles={[styles.h100, styles.w100, ...content.headerContentStyles]} - lottieWebViewStyles={styles.emptyStateFolderWebStyles} + lottieWebViewStyles={content.lottieWebViewStyles} /> Date: Wed, 27 Nov 2024 17:55:32 +0530 Subject: [PATCH 096/174] make report field date section consistent. Signed-off-by: krishna2323 --- src/libs/WorkspaceReportFieldUtils.ts | 2 +- src/pages/workspace/reportFields/ReportFieldsSettingsPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/WorkspaceReportFieldUtils.ts b/src/libs/WorkspaceReportFieldUtils.ts index b7d93b8dee3a..503c1d440d69 100644 --- a/src/libs/WorkspaceReportFieldUtils.ts +++ b/src/libs/WorkspaceReportFieldUtils.ts @@ -80,7 +80,7 @@ function getReportFieldInitialValue(reportField: PolicyReportField | null): stri } if (reportField.type === CONST.REPORT_FIELD_TYPES.DATE) { - return Localize.translateLocal('common.currentDate'); + return Localize.translateLocal('common.initialValue'); } return reportField.value ?? reportField.defaultValue; diff --git a/src/pages/workspace/reportFields/ReportFieldsSettingsPage.tsx b/src/pages/workspace/reportFields/ReportFieldsSettingsPage.tsx index cc6460d20641..d947de43444a 100644 --- a/src/pages/workspace/reportFields/ReportFieldsSettingsPage.tsx +++ b/src/pages/workspace/reportFields/ReportFieldsSettingsPage.tsx @@ -113,7 +113,7 @@ function ReportFieldsSettingsPage({ style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} title={WorkspaceReportFieldUtils.getReportFieldInitialValue(reportField)} - description={translate('common.initialValue')} + description={translate('common.date')} shouldShowRightIcon={!isDateFieldType && !hasAccountingConnections} interactive={!isDateFieldType && !hasAccountingConnections} onPress={() => Navigation.navigate(ROUTES.WORKSPACE_EDIT_REPORT_FIELDS_INITIAL_VALUE.getRoute(policyID, reportFieldID))} From 7aa7abaef071589b63b64323a0b8a90c7aa785d3 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Wed, 27 Nov 2024 14:56:53 +0100 Subject: [PATCH 097/174] Update build.gradle for hybrid app adhoc builds --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 901ef0ccbbbf..2b53f5a40423 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -199,7 +199,7 @@ afterEvaluate { def hermesCTask = gradle.includedBuild("react-native").task(":packages:react-native:ReactAndroid:hermes-engine:buildHermesC") android.applicationVariants.configureEach { variant -> - if (variant.buildType.name == "release") { + if (variant.buildType.name == "release" || variant.buildType.name == "adhoc") { def variantName = variant.name.capitalize() def bundleTask = tasks.named("createBundle${variantName}JsAndAssets").getOrNull() From 7444aea3f2c0a198eb59fd1b5c1282845da69f9b Mon Sep 17 00:00:00 2001 From: I Nyoman Jyotisa Date: Wed, 27 Nov 2024 23:53:32 +0800 Subject: [PATCH 098/174] minor fix --- src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx index 3c034343de76..7c0d8ed38fb8 100644 --- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.tsx @@ -46,7 +46,7 @@ function ActivatePhysicalCardPage({ const [formError, setFormError] = useState(''); const [lastFourDigits, setLastFourDigits] = useState(''); const [lastPressedDigit, setLastPressedDigit] = useState(''); - const [shouldAllowToShowError, setShouldAllowToShowError] = useState(false); + const [canShowError, setCanShowError] = useState(false); const inactiveCard = cardList?.[cardID]; const cardError = ErrorUtils.getLatestErrorMessage(inactiveCard ?? {}); @@ -100,7 +100,7 @@ function ActivatePhysicalCardPage({ }; const submitAndNavigateToNextPage = useCallback(() => { - setShouldAllowToShowError(true); + setCanShowError(true); activateCardCodeInputRef.current?.blur(); if (lastFourDigits.replace(CONST.MAGIC_CODE_EMPTY_CHAR, '').length !== LAST_FOUR_DIGITS_LENGTH) { @@ -139,7 +139,7 @@ function ActivatePhysicalCardPage({ lastPressedDigit={lastPressedDigit} onChangeText={onCodeInput} onFulfill={submitAndNavigateToNextPage} - errorText={shouldAllowToShowError ? formError || cardError : ''} + errorText={canShowError ? formError || cardError : ''} ref={activateCardCodeInputRef} /> From b0cebbc63b2edad778ccad5560a58019b0e75b82 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 28 Nov 2024 10:31:05 +0530 Subject: [PATCH 099/174] remove currency param. Signed-off-by: krishna2323 --- src/libs/PolicyDistanceRatesUtils.ts | 1 - src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx | 4 ++-- .../workspace/distanceRates/PolicyDistanceRateEditPage.tsx | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libs/PolicyDistanceRatesUtils.ts b/src/libs/PolicyDistanceRatesUtils.ts index 93bcd5a32b04..e1c632902bee 100644 --- a/src/libs/PolicyDistanceRatesUtils.ts +++ b/src/libs/PolicyDistanceRatesUtils.ts @@ -14,7 +14,6 @@ type TaxReclaimableForm = typeof ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_TAX_RECLAIM function validateRateValue( values: FormOnyxValues, customUnitRates: Record, - currency: string, toLocaleDigit: (arg: string) => string, currentRateValue?: number, ): FormInputErrors { diff --git a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx index 519c206e1dc7..57e1f09d373f 100644 --- a/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx +++ b/src/pages/workspace/distanceRates/CreateDistanceRatePage.tsx @@ -41,8 +41,8 @@ function CreateDistanceRatePage({route}: CreateDistanceRatePageProps) { const FullPageBlockingView = !customUnitID ? FullPageOfflineBlockingView : View; const validate = useCallback( - (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, currency, toLocaleDigit), - [currency, toLocaleDigit, customUnit?.rates], + (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, toLocaleDigit), + [toLocaleDigit, customUnit?.rates], ); const submit = (values: FormOnyxValues) => { diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx index db79e7a4462a..4bd575498a3f 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRateEditPage.tsx @@ -52,8 +52,8 @@ function PolicyDistanceRateEditPage({route}: PolicyDistanceRateEditPageProps) { }; const validate = useCallback( - (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, currency, toLocaleDigit, rate?.rate), - [currency, toLocaleDigit, customUnit?.rates, rate?.rate], + (values: FormOnyxValues) => validateRateValue(values, customUnit?.rates ?? {}, toLocaleDigit, rate?.rate), + [toLocaleDigit, customUnit?.rates, rate?.rate], ); if (!rate) { From 4c547c80c9fec657822c485fd9f42732a2d50291 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 28 Nov 2024 10:36:19 +0530 Subject: [PATCH 100/174] minor fix. Signed-off-by: krishna2323 --- src/languages/params.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/params.ts b/src/languages/params.ts index a1d2c986b4f7..02e7ba6a4778 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -565,6 +565,7 @@ type CompanyNameParams = { type CustomUnitRateParams = { rate: number; +}; type ChatWithAccountManagerParams = { accountManagerDisplayName: string; From 7b3630a94c3a0a18534dee73399f2aee859869a5 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 28 Nov 2024 13:26:34 +0700 Subject: [PATCH 101/174] remove comments Signed-off-by: Tsaqif --- src/libs/actions/Task.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 368db53db90c..12afbb086c92 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -425,7 +425,6 @@ function completeTask(taskReport: OnyxEntry) { playSound(SOUNDS.SUCCESS); API.write(WRITE_COMMANDS.COMPLETE_TASK, parameters, {optimisticData, successData, failureData}); - // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } /** @@ -509,7 +508,6 @@ function reopenTask(taskReport: OnyxEntry) { }; API.write(WRITE_COMMANDS.REOPEN_TASK, parameters, {optimisticData, successData, failureData}); - // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task) { @@ -586,7 +584,6 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task }; API.write(WRITE_COMMANDS.EDIT_TASK, parameters, {optimisticData, successData, failureData}); - // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, assigneeEmail: string, assigneeAccountID: number | null = 0, assigneeChatReport?: OnyxEntry) { @@ -725,7 +722,6 @@ function editTaskAssignee(report: OnyxTypes.Report, sessionAccountID: number, as }; API.write(WRITE_COMMANDS.EDIT_TASK_ASSIGNEE, parameters, {optimisticData, successData, failureData}); - // Editing a task shouldn't scroll the report to the bottom, so we don't need to call Report.notifyNewAction. } /** From 0ac24b3243c5b2ec17f9303f8f2e2d9cb525a1a2 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 28 Nov 2024 09:26:55 +0100 Subject: [PATCH 102/174] Update react-fast-pdf version --- package-lock.json | 9 +++++---- package.json | 2 +- patches/react-fast-pdf+1.0.15.patch | 13 ------------- 3 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 patches/react-fast-pdf+1.0.15.patch diff --git a/package-lock.json b/package-lock.json index 78c655c256cb..01e63ab53f53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,7 +73,7 @@ "react-content-loader": "^7.0.0", "react-dom": "18.3.1", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "1.0.15", + "react-fast-pdf": "1.0.20", "react-map-gl": "^7.1.3", "react-native": "0.75.2", "react-native-android-location-enabler": "^2.0.1", @@ -34622,9 +34622,10 @@ } }, "node_modules/react-fast-pdf": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/react-fast-pdf/-/react-fast-pdf-1.0.15.tgz", - "integrity": "sha512-xXrwIfRUD3KSRrBdfAeGnLZTf0kYUa+d6GGee1Hu0PFAv5QPBeF3tcV+DU+Cm/JMjSuR7s5g0KK9bePQ/xiQ+w==", + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/react-fast-pdf/-/react-fast-pdf-1.0.20.tgz", + "integrity": "sha512-E2PJOO5oEqi6eNPllNOlQ8y0DiLZ3AW8t+MCN7AgJPp5pY04SeDveXHWvPN0nPU4X5sRBZ7CejeYce2QMMQDyg==", + "license": "MIT", "dependencies": { "react-pdf": "^9.1.1", "react-window": "^1.8.10" diff --git a/package.json b/package.json index 7278d11da7cd..a76dd7713271 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "react-content-loader": "^7.0.0", "react-dom": "18.3.1", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "1.0.15", + "react-fast-pdf": "1.0.20", "react-map-gl": "^7.1.3", "react-native": "0.75.2", "react-native-android-location-enabler": "^2.0.1", diff --git a/patches/react-fast-pdf+1.0.15.patch b/patches/react-fast-pdf+1.0.15.patch deleted file mode 100644 index bfca1b418964..000000000000 --- a/patches/react-fast-pdf+1.0.15.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/react-fast-pdf/dist/PDFPreviewer.js b/node_modules/react-fast-pdf/dist/PDFPreviewer.js -index 53d4849..aea6027 100644 ---- a/node_modules/react-fast-pdf/dist/PDFPreviewer.js -+++ b/node_modules/react-fast-pdf/dist/PDFPreviewer.js -@@ -28,7 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { - Object.defineProperty(exports, "__esModule", { value: true }); - // @ts-expect-error - This line imports a module from 'pdfjs-dist' package which lacks TypeScript typings. - // eslint-disable-next-line import/extensions --const pdf_worker_mjs_1 = __importDefault(require("pdfjs-dist/legacy/build/pdf.worker.mjs")); -+const pdf_worker_mjs_1 = __importDefault(require("pdfjs-dist/legacy/build/pdf.worker.min.mjs")); - const react_1 = __importStar(require("react")); - const times_1 = __importDefault(require("lodash/times")); - const react_window_1 = require("react-window"); From 3ed499fabc6f2ab80cba1a740daae776169fc6d7 Mon Sep 17 00:00:00 2001 From: Jan Nowakowski Date: Thu, 28 Nov 2024 09:49:33 +0100 Subject: [PATCH 103/174] Add new lane and new workflow --- .github/workflows/testBuildHybrid.yml | 352 ++++++++++++++++++++++++++ fastlane/Fastfile | 16 ++ 2 files changed, 368 insertions(+) create mode 100644 .github/workflows/testBuildHybrid.yml diff --git a/.github/workflows/testBuildHybrid.yml b/.github/workflows/testBuildHybrid.yml new file mode 100644 index 000000000000..616256cd323a --- /dev/null +++ b/.github/workflows/testBuildHybrid.yml @@ -0,0 +1,352 @@ +name: Build and deploy hybird apps for testing + +on: + workflow_dispatch: + inputs: + PULL_REQUEST_NUMBER: + description: Pull Request number for correct placement of apps + required: true + pull_request_target: + types: [opened, synchronize, labeled] + branches: ['*ci-test/**'] + +env: + PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} + +jobs: + validateActor: + runs-on: ubuntu-latest + outputs: + READY_TO_BUILD: ${{ fromJSON(steps.isExpensifyEmployee.outputs.IS_EXPENSIFY_EMPLOYEE) && fromJSON(steps.hasReadyToBuildLabel.outputs.HAS_READY_TO_BUILD_LABEL) }} + steps: + - name: Is Expensify employee + id: isExpensifyEmployee + run: | + if gh api /orgs/Expensify/teams/expensify-expensify/memberships/${{ github.actor }} --silent; then + echo "IS_EXPENSIFY_EMPLOYEE=true" >> "$GITHUB_OUTPUT" + else + echo "IS_EXPENSIFY_EMPLOYEE=false" >> "$GITHUB_OUTPUT" + fi + env: + GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} + + - id: hasReadyToBuildLabel + name: Set HAS_READY_TO_BUILD_LABEL flag + run: | + echo "HAS_READY_TO_BUILD_LABEL=$(gh pr view "${{ env.PULL_REQUEST_NUMBER }}" --repo Expensify/App --json labels --jq '.labels[].name' | grep -q 'Ready To Build' && echo 'true')" >> "$GITHUB_OUTPUT" + if [[ "$HAS_READY_TO_BUILD_LABEL" != 'true' ]]; then + echo "The 'Ready to Build' label is not attached to the PR #${{ env.PULL_REQUEST_NUMBER }}" + fi + env: + GITHUB_TOKEN: ${{ github.token }} + + getBranchRef: + runs-on: ubuntu-latest + needs: validateActor + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + outputs: + REF: ${{ steps.getHeadRef.outputs.REF }} + steps: + - name: Checkout + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: actions/checkout@v4 + + - name: Check if pull request number is correct + if: ${{ github.event_name == 'workflow_dispatch' }} + id: getHeadRef + run: | + set -e + echo "REF=$(gh pr view ${{ github.event.inputs.PULL_REQUEST_NUMBER }} --json headRefOid --jq '.headRefOid')" >> "$GITHUB_OUTPUT" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + postGitHubCommentBuildStarted: + runs-on: ubuntu-latest + needs: [validateActor, getBranchRef] + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + steps: + - name: Add build start comment + uses: actions/github-script@v7 + with: + github-token: ${{ github.token }} + script: | + const workflowURL = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: process.env.PULL_REQUEST_NUMBER, + body: `🚧 @${{ github.actor }} has triggered a test hybrid app build. You can view the [workflow run here](${workflowURL}).` + }); + + androidHybrid: + name: Build Android HybridApp + needs: [validateActor, getBranchRef] + runs-on: ubuntu-latest-xl + defaults: + run: + working-directory: Mobile-Expensify/react-native + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + repository: 'Expensify/Mobile-Expensify' + submodules: true + path: 'Mobile-Expensify' + token: ${{ secrets.OS_BOTIFY_TOKEN }} + # fetch-depth: 0 is required in order to fetch the correct submodule branch + fetch-depth: 0 + + - name: Update submodule + run: | + git submodule update --init + git fetch + git checkout ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} + + - name: Configure MapBox SDK + run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} + + - uses: actions/setup-node@v4 + with: + node-version-file: 'Mobile-Expensify/react-native/.nvmrc' + cache: npm + cache-dependency-path: 'Mobile-Expensify/react-native' + + - name: Setup dotenv + run: | + cp .env.staging .env.adhoc + sed -i 's/ENVIRONMENT=staging/ENVIRONMENT=adhoc/' .env.adhoc + echo "PULL_REQUEST_NUMBER=${{ inputs.pull_request_number }}" >> .env.adhoc + + - name: Install node modules + run: | + npm install + cd .. && npm install + + # Fixes https://github.com/Expensify/App/issues/51682 + npm run grunt:build:shared + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'oracle' + java-version: '17' + + - name: Setup Ruby + uses: ruby/setup-ruby@v1.190.0 + with: + bundler-cache: true + working-directory: 'Mobile-Expensify/react-native' + + - name: Install New Expensify Gems + run: bundle install + + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Load files from 1Password + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: | + op document get --output ./upload-key.keystore upload-key.keystore + op document get --output ./android-fastlane-json-key.json android-fastlane-json-key.json + # Copy the keystore to the Android directory for Fullstory + cp ./upload-key.keystore ../Android + + - name: Load Android upload keystore credentials from 1Password + id: load-credentials + uses: 1password/load-secrets-action@v2 + with: + export-env: false + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + ANDROID_UPLOAD_KEYSTORE_PASSWORD: op://Mobile-Deploy-CI/Repository-Secrets/ANDROID_UPLOAD_KEYSTORE_PASSWORD + ANDROID_UPLOAD_KEYSTORE_ALIAS: op://Mobile-Deploy-CI/Repository-Secrets/ANDROID_UPLOAD_KEYSTORE_ALIAS + ANDROID_UPLOAD_KEY_PASSWORD: op://Mobile-Deploy-CI/Repository-Secrets/ANDROID_UPLOAD_KEY_PASSWORD + + - name: Get Android native version + id: getAndroidVersion + run: echo "VERSION_CODE=$(grep -o 'versionCode\s\+[0-9]\+' android/app/build.gradle | awk '{ print $2 }')" >> "$GITHUB_OUTPUT" + + - name: Build Android app + run: bundle exec fastlane android build_adhoc_hybrid + env: + ANDROID_UPLOAD_KEYSTORE_PASSWORD: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEYSTORE_PASSWORD }} + ANDROID_UPLOAD_KEYSTORE_ALIAS: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEYSTORE_ALIAS }} + ANDROID_UPLOAD_KEY_PASSWORD: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEY_PASSWORD }} + + uploadAndroid: + name: Upload Android hybrid app to S3 + needs: [androidHybrid] + runs-on: ubuntu-latest + outputs: + S3_APK_PATH: ${{ steps.exportS3Path.outputs.S3_APK_PATH }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1.190.0 + with: + bundler-cache: true + + - name: Download Android build artifacts + uses: actions/download-artifact@v4 + with: + path: /tmp/artifacts + pattern: android-*-artifact + merge-multiple: true + + - name: Log downloaded artifact paths + run: ls -R /tmp/artifacts + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Upload AdHoc build to S3 + run: bundle exec fastlane android upload_s3 + env: + apkPath: /tmp/artifacts/${{ needs.androidHybrid.outputs.APK_FILE_NAME }} + S3_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }} + S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + S3_BUCKET: ad-hoc-expensify-cash + S3_REGION: us-east-1 + + - name: Export S3 paths + id: exportS3Path + run: | + # $s3APKPath is set from within the Fastfile, android upload_s3 lane + echo "S3_APK_PATH=$s3APKPath" >> "$GITHUB_OUTPUT" + + # iOS: + # name: Build and deploy iOS for testing + # needs: [validateActor, getBranchRef] + # if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + # env: + # DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer + # runs-on: macos-13-xlarge + # steps: + # - name: Checkout + # uses: actions/checkout@v4 + # with: + # ref: ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} + + # - name: Configure MapBox SDK + # run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} + + # - name: Create .env.adhoc file based on staging and add PULL_REQUEST_NUMBER env to it + # run: | + # cp .env.staging .env.adhoc + # sed -i '' 's/ENVIRONMENT=staging/ENVIRONMENT=adhoc/' .env.adhoc + # echo "PULL_REQUEST_NUMBER=$PULL_REQUEST_NUMBER" >> .env.adhoc + + # - name: Setup Node + # id: setup-node + # uses: ./.github/actions/composite/setupNode + + # - name: Setup XCode + # run: sudo xcode-select -switch /Applications/Xcode_15.2.0.app + + # - name: Setup Ruby + # uses: ruby/setup-ruby@v1.190.0 + # with: + # bundler-cache: true + + # - name: Cache Pod dependencies + # uses: actions/cache@v4 + # id: pods-cache + # with: + # path: ios/Pods + # key: ${{ runner.os }}-pods-cache-${{ hashFiles('ios/Podfile.lock', 'firebase.json') }} + + # - name: Compare Podfile.lock and Manifest.lock + # id: compare-podfile-and-manifest + # run: echo "IS_PODFILE_SAME_AS_MANIFEST=${{ hashFiles('ios/Podfile.lock') == hashFiles('ios/Pods/Manifest.lock') }}" >> "$GITHUB_OUTPUT" + + # - name: Install cocoapods + # uses: nick-fields/retry@3f757583fb1b1f940bc8ef4bf4734c8dc02a5847 + # if: steps.pods-cache.outputs.cache-hit != 'true' || steps.compare-podfile-and-manifest.outputs.IS_PODFILE_SAME_AS_MANIFEST != 'true' || steps.setup-node.outputs.cache-hit != 'true' + # with: + # timeout_minutes: 10 + # max_attempts: 5 + # command: scripts/pod-install.sh + + # - name: Decrypt AdHoc profile + # run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc.mobileprovision NewApp_AdHoc.mobileprovision.gpg + # env: + # LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + # - name: Decrypt AdHoc Notification Service profile + # run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc_Notification_Service.mobileprovision NewApp_AdHoc_Notification_Service.mobileprovision.gpg + # env: + # LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + # - name: Decrypt certificate + # run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg + # env: + # LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v4 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: us-east-1 + + # - name: Build AdHoc app + # run: bundle exec fastlane ios build_adhoc + + # - name: Upload AdHoc build to S3 + # run: bundle exec fastlane ios upload_s3 + # env: + # S3_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }} + # S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # S3_BUCKET: ad-hoc-expensify-cash + # S3_REGION: us-east-1 + + # - name: Upload Artifact + # uses: actions/upload-artifact@v4 + # with: + # name: ios + # path: ./ios_paths.json + + postGithubComment: + runs-on: ubuntu-latest + name: Post a GitHub comment with app download links for testing + needs: [validateActor, getBranchRef, uploadAndroid] #TODO add ios job + if: ${{ always() }} + steps: + - name: Checkout + uses: actions/checkout@v4 + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + with: + ref: ${{ github.event.pull_request.head.sha || needs.getBranchRef.outputs.REF }} + + - name: Download Artifact + uses: actions/download-artifact@v4 + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + + # - name: Read JSONs with iOS paths + # id: get_ios_path + # if: ${{ needs.iOS.result == 'success' }} + # run: | + # content_ios="$(cat ./ios/ios_paths.json)" + # content_ios="${content_ios//'%'/'%25'}" + # content_ios="${content_ios//$'\n'/'%0A'}" + # content_ios="${content_ios//$'\r'/'%0D'}" + # ios_path=$(echo "$content_ios" | jq -r '.html_path') + # echo "ios_path=$ios_path" >> "$GITHUB_OUTPUT" + + - name: Publish links to apps for download + if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} + uses: ./.github/actions/javascript/postTestBuildComment + with: + PR_NUMBER: ${{ env.PULL_REQUEST_NUMBER }} + GITHUB_TOKEN: ${{ github.token }} + ANDROID: ${{ needs.uploadAndroid.result }} + IOS: 'success' + ANDROID_LINK: ${{ needs.uploadAndroid.outputs.S3_APK_PATH }} + IOS_LINK: 'https://staging.new.expensify.com' diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 54084367040c..fa135b8c8a9d 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -86,6 +86,22 @@ platform :android do setGradleOutputsInEnv() end + desc "Generate AdHoc HybridApp apk" + lane :build_adhoc_hybrid do + ENV["ENVFILE"]="../.env.adhoc.hybridapp" + gradle( + project_dir: '../Android', + task: 'assembleAdhoc', + properties: { + "android.injected.signing.store.file" => './upload-key.keystore', + "android.injected.signing.store.password" => ENV["ANDROID_UPLOAD_KEYSTORE_PASSWORD"], + "android.injected.signing.key.alias" => ENV["ANDROID_UPLOAD_KEYSTORE_ALIAS"], + "android.injected.signing.key.password" => ENV["ANDROID_UPLOAD_KEY_PASSWORD"], + } + ) + setGradleOutputsInEnv() + end + desc "Generate a new local APK" lane :build_local do ENV["ENVFILE"]=".env.production" From 1aa3d8299b8a19644420fd53fbd9de5eae24abea Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Thu, 28 Nov 2024 12:17:14 +0200 Subject: [PATCH 104/174] Fix button top margin in SearchMultipleSelectionPicker --- src/components/Search/SearchMultipleSelectionPicker.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Search/SearchMultipleSelectionPicker.tsx b/src/components/Search/SearchMultipleSelectionPicker.tsx index d76f2e76ab02..30c31074db7a 100644 --- a/src/components/Search/SearchMultipleSelectionPicker.tsx +++ b/src/components/Search/SearchMultipleSelectionPicker.tsx @@ -9,6 +9,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {OptionData} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import useThemeStyles from '@hooks/useThemeStyles'; type SearchMultipleSelectionPickerItem = { name: string; @@ -25,6 +26,7 @@ type SearchMultipleSelectionPickerProps = { function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTitle, onSaveSelection, shouldShowTextInput = true}: SearchMultipleSelectionPickerProps) { const {translate} = useLocalize(); + const styles = useThemeStyles(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const [selectedItems, setSelectedItems] = useState(initiallySelectedItems ?? []); @@ -106,6 +108,7 @@ function SearchMultipleSelectionPicker({items, initiallySelectedItems, pickerTit () => (