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/312] 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/312] 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/312] 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 7d69e6634b3ac8b61303f1c3ecf06a768a81f4be Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sun, 6 Oct 2024 01:29:38 +0700 Subject: [PATCH 004/312] Change behavior, navigate before transaction delete to resolve [delete..] briefly appeared --- src/libs/actions/IOU.ts | 50 +++++++++++++++++++++++ src/libs/actions/Task.ts | 1 + src/pages/ReportDetailsPage.tsx | 72 +++++++++++++++++++++------------ 3 files changed, 98 insertions(+), 25 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8d8e25a3ffb6..4b2c4dcd199e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -73,6 +73,7 @@ import * as Category from './Policy/Category'; import * as Policy from './Policy/Policy'; import * as Tag from './Policy/Tag'; import * as Report from './Report'; +import * as Task from '@userActions/Task'; type IOURequestType = ValueOf; @@ -5684,6 +5685,51 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT }; } +function getUrlToNavigateBackForTask(report: OnyxEntry): string | undefined { + const parentReport = Task.getParentReport(report); + const shouldDeleteTaskReport = !ReportActionsUtils.doesReportHaveVisibleActions(report.reportID ?? '-1'); + if (shouldDeleteTaskReport) { + return ROUTES.REPORT_WITH_ID.getRoute(parentReport?.reportID ?? ''); + } + return undefined; +} +function getUrlToNavigateBackForMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView: boolean): string | undefined { + const { + shouldDeleteTransactionThread, + shouldDeleteIOUReport, + iouReport, + chatReport, + } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); + + let reportIDToNavigateBack: string | undefined; + if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { + reportIDToNavigateBack = iouReport.chatReportID; + } + + if (iouReport?.chatReportID && shouldDeleteIOUReport) { + reportIDToNavigateBack = iouReport.chatReportID; + } + + return reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined; +} +function getUrlToNavigateBackForTrackExpense(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView: boolean): string | undefined { + const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; + if (!ReportUtils.isSelfDM(chatReport)) { + return getUrlToNavigateBackForMoneyRequest(transactionID, reportAction, isSingleTransactionView); + } + + const { shouldDeleteTransactionThread } = getDeleteTrackExpenseInformation( + chatReportID, + transactionID, + reportAction, + ); + + if (shouldDeleteTransactionThread) { + return ROUTES.REPORT_WITH_ID.getRoute(chatReportID); + } + + return undefined; +} /** * * @param transactionID - The transactionID of IOU @@ -8476,5 +8522,9 @@ export { updateMoneyRequestTaxRate, mergeDuplicates, resolveDuplicates, + getUrlToNavigateBackForTask, + getUrlToNavigateBackForMoneyRequest, + getUrlToNavigateBackForTrackExpense, + }; export type {GPSPoint as GpsPoint, IOURequestType}; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 2f9ec060c1e8..ae3c2b596a7d 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1207,6 +1207,7 @@ export { canModifyTask, canActionTask, setNewOptimisticAssignee, + getParentReport, }; export type {PolicyValue, Assignee, ShareDestination}; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index e51b8e36704a..03c911305feb 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -718,24 +718,46 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const deleteTransaction = useCallback(() => { setIsDeleteModalVisible(false); - + + let urlToNavigateBack: string | undefined; + if (caseID === CASES.DEFAULT) { - navigateBackToAfterDelete.current = Task.deleteTask(report); + urlToNavigateBack = IOU.getUrlToNavigateBackForTask(report); + if (urlToNavigateBack) { + Navigation.goBack(urlToNavigateBack as Route); + } else { + Navigation.dismissModal(); + } + Task.deleteTask(report); return; } - + if (!requestParentReportAction) { return; } - - if (ReportActionsUtils.isTrackExpenseAction(requestParentReportAction)) { - navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + if (isTrackExpense) { + urlToNavigateBack = IOU.getUrlToNavigateBackForTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); } else { - navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); + urlToNavigateBack = IOU.getUrlToNavigateBackForMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); } - + + if (!urlToNavigateBack) { + Navigation.dismissModal(); + } else { + ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); + } + + if (isTrackExpense) { + IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + } else { + IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); + } + isTransactionDeleted.current = true; }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView]); + return ( @@ -826,23 +848,23 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { onConfirm={deleteTransaction} onCancel={() => setIsDeleteModalVisible(false)} onModalHide={() => { - // We use isTransactionDeleted to know if the modal hides because the user deletes the transaction. - if (!isTransactionDeleted.current) { - if (caseID === CASES.DEFAULT) { - if (navigateBackToAfterDelete.current) { - Navigation.goBack(navigateBackToAfterDelete.current); - } else { - Navigation.dismissModal(); - } - } - return; - } - - if (!navigateBackToAfterDelete.current) { - Navigation.dismissModal(); - } else { - ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); - } + // // We use isTransactionDeleted to know if the modal hides because the user deletes the transaction. + // if (!isTransactionDeleted.current) { + // if (caseID === CASES.DEFAULT) { + // if (navigateBackToAfterDelete.current) { + // Navigation.goBack(navigateBackToAfterDelete.current); + // } else { + // Navigation.dismissModal(); + // } + // } + // return; + // } + + // if (!navigateBackToAfterDelete.current) { + // Navigation.dismissModal(); + // } else { + // ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); + // } }} prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} From bc8f8edbd608afeb42020a776c2886b55c18fae4 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sun, 6 Oct 2024 23:46:32 +0700 Subject: [PATCH 005/312] fix incorrect reportIDToNavigateBack --- 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 4b2c4dcd199e..9304b3a4c08a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5703,7 +5703,7 @@ function getUrlToNavigateBackForMoneyRequest(transactionID: string, reportAction let reportIDToNavigateBack: string | undefined; if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { - reportIDToNavigateBack = iouReport.chatReportID; + reportIDToNavigateBack = iouReport.reportID; } if (iouReport?.chatReportID && shouldDeleteIOUReport) { From f6781e6e0f0cd6325759f3c6eb64ab8f8790495c Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Mon, 7 Oct 2024 00:08:15 +0700 Subject: [PATCH 006/312] actually delete the transaction after ReportDetailsPage unmounted --- src/pages/ReportDetailsPage.tsx | 34 +++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 03c911305feb..600b14117297 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -715,9 +715,35 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const isTransactionDeleted = useRef(false); // Where to go back after deleting the transaction and its report. It's empty if the transaction report isn't deleted. const navigateBackToAfterDelete = useRef(); + useEffect(() => { + return () => { + if(!isTransactionDeleted.current) + { + return; + } + + if (caseID === CASES.DEFAULT) { + Task.deleteTask(report); + return; + } + + if (!requestParentReportAction) { + return; + } + + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + + if (isTrackExpense) { + IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + } else { + IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); + } + }; + }, [isTransactionDeleted]); const deleteTransaction = useCallback(() => { setIsDeleteModalVisible(false); + isTransactionDeleted.current = true; let urlToNavigateBack: string | undefined; @@ -728,7 +754,6 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { } else { Navigation.dismissModal(); } - Task.deleteTask(report); return; } @@ -749,13 +774,6 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); } - if (isTrackExpense) { - IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); - } else { - IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); - } - - isTransactionDeleted.current = true; }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView]); return ( From 0bd45cc23e6c040a86e249353ab4da429efbfddb Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Mon, 7 Oct 2024 13:46:53 +0700 Subject: [PATCH 007/312] Refactor, remove unnecessary code --- src/pages/ReportDetailsPage.tsx | 75 +++++++++++++-------------------- 1 file changed, 29 insertions(+), 46 deletions(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 600b14117297..533709ff50d7 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -3,7 +3,7 @@ import {Str} from 'expensify-common'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx} from 'react-native-onyx'; +import Onyx, {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -713,35 +713,37 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { // A flag to indicate whether the user choose to delete the transaction or not const isTransactionDeleted = useRef(false); - // Where to go back after deleting the transaction and its report. It's empty if the transaction report isn't deleted. - const navigateBackToAfterDelete = useRef(); useEffect(() => { return () => { - if(!isTransactionDeleted.current) - { - return; - } - - if (caseID === CASES.DEFAULT) { - Task.deleteTask(report); - return; - } - - if (!requestParentReportAction) { - return; - } - - const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); - - if (isTrackExpense) { - IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); - } else { - IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); - } + deleteTransaction(); }; - }, [isTransactionDeleted]); + }, []); const deleteTransaction = useCallback(() => { + if (!isTransactionDeleted.current) { + return; + } + + if (caseID === CASES.DEFAULT) { + Task.deleteTask(report); + return; + } + + if (!requestParentReportAction) { + return; + } + + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + + if (isTrackExpense) { + IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + } else { + IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); + } + }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]); + + // Where to go back after deleting the transaction and its report. + const navigateAfterTransactionDeletion = useCallback(() => { setIsDeleteModalVisible(false); isTransactionDeleted.current = true; @@ -774,7 +776,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); } - }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView]); + }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView, setIsDeleteModalVisible, isTransactionDeleted]); return ( @@ -863,27 +865,8 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { setIsDeleteModalVisible(false)} - onModalHide={() => { - // // We use isTransactionDeleted to know if the modal hides because the user deletes the transaction. - // if (!isTransactionDeleted.current) { - // if (caseID === CASES.DEFAULT) { - // if (navigateBackToAfterDelete.current) { - // Navigation.goBack(navigateBackToAfterDelete.current); - // } else { - // Navigation.dismissModal(); - // } - // } - // return; - // } - - // if (!navigateBackToAfterDelete.current) { - // Navigation.dismissModal(); - // } else { - // ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); - // } - }} prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} From eb051bb80bce29294ea1552eb62163bd01fda5c1 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Mon, 7 Oct 2024 14:23:17 +0700 Subject: [PATCH 008/312] Add NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL to show loading transaction during delete process --- src/ONYXKEYS.ts | 4 ++++ src/libs/actions/IOU.ts | 16 ++++++++++++++++ src/libs/actions/Task.ts | 5 +++++ src/pages/ReportDetailsPage.tsx | 2 ++ src/pages/home/ReportScreen.tsx | 4 +++- 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index df1413620c20..ecc3d865c9e4 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -208,6 +208,9 @@ const ONYXKEYS = { /** The NVP containing all information related to educational tooltip in workspace chat */ NVP_WORKSPACE_TOOLTIP: 'workspaceTooltip', + /** The NVP contain url to back after deleting transaction */ + NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL: 'nvp_deleteTransactionNavigateBackURL', + /** Whether to show save search rename tooltip */ SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP: 'shouldShowSavedSearchRenameTooltip', @@ -990,6 +993,7 @@ type OnyxValuesMapping = { [ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: number; [ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END]: number; [ONYXKEYS.NVP_WORKSPACE_TOOLTIP]: OnyxTypes.WorkspaceTooltip; + [ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL]: string | undefined; [ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP]: boolean; [ONYXKEYS.NVP_PRIVATE_CANCELLATION_DETAILS]: OnyxTypes.CancellationDetails[]; [ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE]: string; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9304b3a4c08a..afaf4b3143b5 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5977,6 +5977,12 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }); } + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, + value: null, + }); + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -6260,6 +6266,15 @@ function getSendMoneyParams( [optimisticCreatedActionForTransactionThread?.reportActionID ?? '-1']: optimisticCreatedActionForTransactionThread, }, }; + const optimisticDeleteTransactionNavigateBackUrl: OnyxUpdate = { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, + value: null, +}; + + + + const successData: OnyxUpdate[] = []; @@ -6430,6 +6445,7 @@ function getSendMoneyParams( optimisticTransactionData, optimisticTransactionThreadData, optimisticTransactionThreadReportActionsData, + optimisticDeleteTransactionNavigateBackUrl, ]; if (!isEmptyObject(optimisticPersonalDetailListData)) { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index ae3c2b596a7d..b8edbaa8b245 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1005,6 +1005,11 @@ function deleteTask(report: OnyxEntry) { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReport?.reportID}`, value: optimisticReportActions as OnyxTypes.ReportActions, }, + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, + value: null, + }, ]; // Update optimistic data for parent report action if the report is a child report and the task report has no visible child diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 533709ff50d7..3e098e20b27d 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -751,6 +751,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { if (caseID === CASES.DEFAULT) { urlToNavigateBack = IOU.getUrlToNavigateBackForTask(report); + Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); if (urlToNavigateBack) { Navigation.goBack(urlToNavigateBack as Route); } else { @@ -769,6 +770,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { } else { urlToNavigateBack = IOU.getUrlToNavigateBackForMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); } + Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); if (!urlToNavigateBack) { Navigation.dismissModal(); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index dd38a0716377..ac7418062de7 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -343,13 +343,15 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro [currentUserAccountID, linkedAction], ); + const [deleteTransactionNavigateBackUrl] = useOnyx(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL); + /** * Using logical OR operator because with nullish coalescing operator, when `isLoadingApp` is false, the right hand side of the operator * is not evaluated. This causes issues where we have `isLoading` set to false and later set to true and then set to false again. * Ideally, `isLoading` should be set initially to true and then set to false. We can achieve this by using logical OR operator. */ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const isLoading = isLoadingApp || !reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty(); + const isLoading = isLoadingApp || !reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty() || (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID); const shouldShowSkeleton = (isLinkingToMessage && !isLinkedMessagePageReady) || From c2cecdc0da807aed28640c29fa411e37a3da8ab1 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Mon, 7 Oct 2024 14:33:52 +0700 Subject: [PATCH 009/312] 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 afaf4b3143b5..f8c10e21b006 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6270,7 +6270,7 @@ function getSendMoneyParams( onyxMethod: Onyx.METHOD.SET, key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, value: null, -}; + }; From 7e8d4f45dc89f0000da7969d918fce0251a1a8a9 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Mon, 7 Oct 2024 15:46:26 +0700 Subject: [PATCH 010/312] Refactor --- src/pages/ReportDetailsPage.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 3e098e20b27d..e82fdf97c1ae 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -715,15 +715,15 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const isTransactionDeleted = useRef(false); useEffect(() => { return () => { + if (!isTransactionDeleted.current) { + return; + } + deleteTransaction(); }; }, []); const deleteTransaction = useCallback(() => { - if (!isTransactionDeleted.current) { - return; - } - if (caseID === CASES.DEFAULT) { Task.deleteTask(report); return; @@ -743,7 +743,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]); // Where to go back after deleting the transaction and its report. - const navigateAfterTransactionDeletion = useCallback(() => { + const navigateToTargetUrl = useCallback(() => { setIsDeleteModalVisible(false); isTransactionDeleted.current = true; @@ -867,7 +867,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { setIsDeleteModalVisible(false)} prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} From 66e3de8f4bff7c7b1f0990e9efec698dfcea9f7e Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Mon, 7 Oct 2024 16:35:27 +0700 Subject: [PATCH 011/312] Partially DeleteTransactionThread to prevent not found page briefly appear --- src/libs/actions/IOU.ts | 46 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f8c10e21b006..ef2cc894c82b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1744,7 +1744,16 @@ function getDeleteTrackExpenseInformation( { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: null, + value: { + reportID: null, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + participants: { + [userAccountID]: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + }, + }, }, { onyxMethod: Onyx.METHOD.SET, @@ -1784,6 +1793,17 @@ function getDeleteTrackExpenseInformation( }, ]; + if (shouldDeleteTransactionThread && transactionThread) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + value: Object.keys(transactionThread).reduce>((acc, key) => { + acc[key] = null; + return acc; + }, {}), + }); + } + const failureData: OnyxUpdate[] = []; if (shouldDeleteTransactionFromOnyx) { @@ -5908,9 +5928,18 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor if (shouldDeleteTransactionThread) { optimisticData.push( { - onyxMethod: Onyx.METHOD.SET, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: null, + value: { + reportID: null, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + participants: { + [userAccountID]: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + }, + }, }, { onyxMethod: Onyx.METHOD.SET, @@ -6007,6 +6036,17 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, ]; + if (shouldDeleteTransactionThread && transactionThread) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + value: Object.keys(transactionThread).reduce>((acc, key) => { + acc[key] = null; + return acc; + }, {}), + }); + } + if (shouldDeleteIOUReport) { successData.push({ onyxMethod: Onyx.METHOD.SET, From c7e3f5a133d9359e9c1074086fca073fcbd50e3c Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 9 Oct 2024 22:54:21 +0300 Subject: [PATCH 012/312] fix the bottom line of the composer on composer full size mode change --- src/components/Composer/index.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 15087193a593..8a899d4e3562 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -102,6 +102,7 @@ function Composer( const [isRendered, setIsRendered] = useState(false); const isScrollBarVisible = useIsScrollBarVisible(textInput, value ?? ''); const [prevScroll, setPrevScroll] = useState(); + const [prevHeight, setPrevHeight] = useState(); const isReportFlatListScrolling = useRef(false); useEffect(() => { @@ -231,10 +232,18 @@ function Composer( } setPrevScroll(textInput.current.scrollTop); }, 100); + const debouncedSetPrevHeight = lodashDebounce(() => { + if (!textInput.current) { + return; + } + setPrevHeight(textInput.current.clientHeight); + }, 100); textInput.current.addEventListener('scroll', debouncedSetPrevScroll); + textInput.current.addEventListener('resize', debouncedSetPrevHeight); return () => { textInput.current?.removeEventListener('scroll', debouncedSetPrevScroll); + textInput.current?.removeEventListener('resize', debouncedSetPrevHeight); }; }, []); @@ -262,11 +271,11 @@ function Composer( }, []); useEffect(() => { - if (!textInput.current || prevScroll === undefined) { + if (!textInput.current || prevScroll === undefined || prevHeight === undefined) { return; } // eslint-disable-next-line react-compiler/react-compiler - textInput.current.scrollTop = prevScroll; + textInput.current.scrollTop = prevScroll + prevHeight - textInput.current.clientHeight; // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [isComposerFullSize]); From 26926f016bba9f99ca73b511ef3983db6ed77e35 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 9 Oct 2024 23:03:30 +0300 Subject: [PATCH 013/312] change listener --- src/components/Composer/index.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 8a899d4e3562..56a0a06e9c30 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -231,19 +231,12 @@ function Composer( return; } setPrevScroll(textInput.current.scrollTop); - }, 100); - const debouncedSetPrevHeight = lodashDebounce(() => { - if (!textInput.current) { - return; - } setPrevHeight(textInput.current.clientHeight); }, 100); textInput.current.addEventListener('scroll', debouncedSetPrevScroll); - textInput.current.addEventListener('resize', debouncedSetPrevHeight); return () => { textInput.current?.removeEventListener('scroll', debouncedSetPrevScroll); - textInput.current?.removeEventListener('resize', debouncedSetPrevHeight); }; }, []); From 447dc037c2e6f26b82f305160e7bd005559b2a48 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 9 Oct 2024 23:22:39 +0300 Subject: [PATCH 014/312] set prevHeight to content size height --- src/components/Composer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 56a0a06e9c30..21708d85612b 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -231,7 +231,6 @@ function Composer( return; } setPrevScroll(textInput.current.scrollTop); - setPrevHeight(textInput.current.clientHeight); }, 100); textInput.current.addEventListener('scroll', debouncedSetPrevScroll); @@ -395,6 +394,7 @@ function Composer( {...props} onSelectionChange={addCursorPositionToSelectionChange} onContentSizeChange={(e) => { + setPrevHeight(e.nativeEvent.contentSize.height); setTextInputWidth(`${e.nativeEvent.contentSize.width}px`); updateIsFullComposerAvailable({maxLines, isComposerFullSize, isDisabled, setIsFullComposerAvailable}, e, styles); }} From 1ccf57b2abac893a93e7f60d4e9438b3705d693a Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 15 Oct 2024 23:57:57 +0530 Subject: [PATCH 015/312] 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 016/312] 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 017/312] 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 a0617684a9eebdd6f38ecf132d58685a37667748 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 22 Oct 2024 01:49:20 +0530 Subject: [PATCH 018/312] fix height being cut for popover --- src/pages/settings/Security/SecuritySettingsPage.tsx | 11 +++-------- src/styles/variables.ts | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/pages/settings/Security/SecuritySettingsPage.tsx b/src/pages/settings/Security/SecuritySettingsPage.tsx index d1919f9ddf83..924c3c6c468c 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.tsx +++ b/src/pages/settings/Security/SecuritySettingsPage.tsx @@ -47,7 +47,7 @@ function SecuritySettingsPage() { const waitForNavigate = useWaitForNavigation(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {canUseNewDotCopilot} = usePermissions(); - const {windowWidth} = useWindowDimensions(); + const {windowWidth, windowHeight} = useWindowDimensions(); const personalDetails = usePersonalDetails(); const [account] = useOnyx(ONYXKEYS.ACCOUNT); @@ -58,8 +58,6 @@ function SecuritySettingsPage() { const [selectedDelegate, setSelectedDelegate] = useState(); const [anchorPosition, setAnchorPosition] = useState({ - anchorPositionHorizontal: 0, - anchorPositionVertical: 0, anchorPositionTop: 0, anchorPositionRight: 0, }); @@ -70,15 +68,12 @@ function SecuritySettingsPage() { } const position = getClickedTargetLocation(delegateButtonRef.current); - setAnchorPosition({ - anchorPositionTop: position.top + position.height - variables.bankAccountActionPopoverTopSpacing, + anchorPositionTop: Math.min(position.top + position.height - variables.bankAccountActionPopoverTopSpacing, windowHeight - variables.delegateAccessLevelModalHeight), // We want the position to be 23px to the right of the left border anchorPositionRight: windowWidth - position.right + variables.bankAccountActionPopoverRightSpacing, - anchorPositionHorizontal: position.x + variables.addBankAccountLeftSpacing, - anchorPositionVertical: position.y, }); - }, [windowWidth]); + }, [windowWidth, windowHeight, delegateButtonRef]); const isActingAsDelegate = !!account?.delegatedAccess?.delegate ?? false; const delegates = account?.delegatedAccess?.delegates ?? []; diff --git a/src/styles/variables.ts b/src/styles/variables.ts index dc6655791489..ad40c6688eed 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -259,6 +259,7 @@ export default { minimalTopBarOffset: -26, searchHeaderHeight: 80, searchListContentMarginTop: 116, + delegateAccessLevelModalHeight: 168, h20: 20, h28: 28, From fdbb357e1585e7bc98f05b6a5c23a368c461262f Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Tue, 22 Oct 2024 03:30:25 +0530 Subject: [PATCH 019/312] use popovermenu to set anchor position --- .../Security/SecuritySettingsPage.tsx | 94 ++++++++++--------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/src/pages/settings/Security/SecuritySettingsPage.tsx b/src/pages/settings/Security/SecuritySettingsPage.tsx index 924c3c6c468c..764412b236ff 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.tsx +++ b/src/pages/settings/Security/SecuritySettingsPage.tsx @@ -14,7 +14,8 @@ import MenuItem from '@components/MenuItem'; import type {MenuItemProps} from '@components/MenuItem'; import MenuItemList from '@components/MenuItemList'; import {usePersonalDetails} from '@components/OnyxProvider'; -import Popover from '@components/Popover'; +import PopoverMenu from '@components/PopoverMenu'; +import type {PopoverMenuItem} from '@components/PopoverMenu'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Section from '@components/Section'; @@ -32,7 +33,7 @@ import getClickedTargetLocation from '@libs/getClickedTargetLocation'; import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; -import variables from '@styles/variables'; +import type {AnchorPosition} from '@styles/index'; import * as Modal from '@userActions/Modal'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -47,7 +48,7 @@ function SecuritySettingsPage() { const waitForNavigate = useWaitForNavigation(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {canUseNewDotCopilot} = usePermissions(); - const {windowWidth, windowHeight} = useWindowDimensions(); + const {windowWidth} = useWindowDimensions(); const personalDetails = usePersonalDetails(); const [account] = useOnyx(ONYXKEYS.ACCOUNT); @@ -57,11 +58,18 @@ function SecuritySettingsPage() { const [shouldShowRemoveDelegateModal, setShouldShowRemoveDelegateModal] = useState(false); const [selectedDelegate, setSelectedDelegate] = useState(); - const [anchorPosition, setAnchorPosition] = useState({ - anchorPositionTop: 0, - anchorPositionRight: 0, + const [anchorPosition, setAnchorPosition] = useState({ + horizontal: 0, + vertical: 0, }); + const isActingAsDelegate = !!account?.delegatedAccess?.delegate; + const delegates = account?.delegatedAccess?.delegates ?? []; + const delegators = account?.delegatedAccess?.delegators ?? []; + + const hasDelegates = delegates.length > 0; + const hasDelegators = delegators.length > 0; + const setMenuPosition = useCallback(() => { if (!delegateButtonRef.current) { return; @@ -69,18 +77,10 @@ function SecuritySettingsPage() { const position = getClickedTargetLocation(delegateButtonRef.current); setAnchorPosition({ - anchorPositionTop: Math.min(position.top + position.height - variables.bankAccountActionPopoverTopSpacing, windowHeight - variables.delegateAccessLevelModalHeight), - // We want the position to be 23px to the right of the left border - anchorPositionRight: windowWidth - position.right + variables.bankAccountActionPopoverRightSpacing, + horizontal: windowWidth - position.x, + vertical: position.y + position.height, }); - }, [windowWidth, windowHeight, delegateButtonRef]); - const isActingAsDelegate = !!account?.delegatedAccess?.delegate ?? false; - - const delegates = account?.delegatedAccess?.delegates ?? []; - const delegators = account?.delegatedAccess?.delegators ?? []; - - const hasDelegates = delegates.length > 0; - const hasDelegators = delegators.length > 0; + }, [windowWidth, delegateButtonRef]); const showPopoverMenu = (nativeEvent: GestureResponderEvent | KeyboardEvent, delegate: Delegate) => { delegateButtonRef.current = nativeEvent?.currentTarget as HTMLDivElement; @@ -172,7 +172,7 @@ function SecuritySettingsPage() { }), // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps - [delegates, translate, styles, personalDetails], + [delegates, translate, styles, personalDetails, windowWidth], ); const delegatorMenuItems: MenuItemProps[] = useMemo( @@ -198,6 +198,27 @@ function SecuritySettingsPage() { [delegators, styles, translate, personalDetails], ); + const delegatePopoverMenuItems: PopoverMenuItem[] = [ + { + text: translate('delegate.changeAccessLevel'), + icon: Expensicons.Pencil, + onPress: () => { + Navigation.navigate(ROUTES.SETTINGS_UPDATE_DELEGATE_ROLE.getRoute(selectedDelegate?.email ?? '', selectedDelegate?.role ?? '')); + setShouldShowDelegatePopoverMenu(false); + setSelectedDelegate(undefined); + }, + }, + { + text: translate('delegate.removeCopilot'), + icon: Expensicons.Trashcan, + onPress: () => + Modal.close(() => { + setShouldShowDelegatePopoverMenu(false); + setShouldShowRemoveDelegateModal(true); + }), + }, + ]; + return ( )} - } anchorPosition={{ - top: anchorPosition.anchorPositionTop, - right: anchorPosition.anchorPositionRight, + horizontal: anchorPosition.horizontal, + vertical: anchorPosition.vertical, }} + anchorAlignment={{ + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, + }} + menuItems={delegatePopoverMenuItems} onClose={() => { setShouldShowDelegatePopoverMenu(false); }} - > - - { - Navigation.navigate(ROUTES.SETTINGS_UPDATE_DELEGATE_ROLE.getRoute(selectedDelegate?.email ?? '', selectedDelegate?.role ?? '')); - setShouldShowDelegatePopoverMenu(false); - setSelectedDelegate(undefined); - }} - wrapperStyle={[styles.pv3, styles.ph5, !shouldUseNarrowLayout ? styles.sidebarPopover : {}]} - /> - - Modal.close(() => { - setShouldShowDelegatePopoverMenu(false); - setShouldShowRemoveDelegateModal(true); - }) - } - wrapperStyle={[styles.pv3, styles.ph5, !shouldUseNarrowLayout ? styles.sidebarPopover : {}]} - /> - - + /> Date: Tue, 22 Oct 2024 03:40:17 +0530 Subject: [PATCH 020/312] rm unused --- src/styles/variables.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/variables.ts b/src/styles/variables.ts index ad40c6688eed..dc6655791489 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -259,7 +259,6 @@ export default { minimalTopBarOffset: -26, searchHeaderHeight: 80, searchListContentMarginTop: 116, - delegateAccessLevelModalHeight: 168, h20: 20, h28: 28, From 903d17d0a942dc4eed3d32fd1dde0c1183a9414d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 22 Oct 2024 11:33:56 +0800 Subject: [PATCH 021/312] 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 022/312] 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 ccb9ca5eca10055b51a4f084ba29e2e76d1a2f17 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 22 Oct 2024 16:02:17 +0300 Subject: [PATCH 023/312] resolve conflict --- src/components/Composer/implementation/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Composer/implementation/index.tsx b/src/components/Composer/implementation/index.tsx index 4431007793cb..6c4a1a4c723c 100755 --- a/src/components/Composer/implementation/index.tsx +++ b/src/components/Composer/implementation/index.tsx @@ -75,6 +75,7 @@ function Composer( const [isRendered, setIsRendered] = useState(false); const isScrollBarVisible = useIsScrollBarVisible(textInput, value ?? ''); const [prevScroll, setPrevScroll] = useState(); + const [prevHeight, setPrevHeight] = useState(); const isReportFlatListScrolling = useRef(false); useEffect(() => { @@ -243,11 +244,11 @@ function Composer( }, []); useEffect(() => { - if (!textInput.current || prevScroll === undefined) { + if (!textInput.current || prevScroll === undefined || prevHeight === undefined) { return; } // eslint-disable-next-line react-compiler/react-compiler - textInput.current.scrollTop = prevScroll; + textInput.current.scrollTop = prevScroll + prevHeight - textInput.current.clientHeight; // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [isComposerFullSize]); @@ -353,6 +354,7 @@ function Composer( {...props} onSelectionChange={addCursorPositionToSelectionChange} onContentSizeChange={(e) => { + setPrevHeight(e.nativeEvent.contentSize.height); updateIsFullComposerAvailable({maxLines, isComposerFullSize, isDisabled, setIsFullComposerAvailable}, e, styles); }} disabled={isDisabled} From 6e1f6e41d154ef365ab0fca7e9d9b0710dbdf8e5 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 23 Oct 2024 11:15:11 +0800 Subject: [PATCH 024/312] 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 025/312] 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 026/312] 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 027/312] 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 9bce4f03bb5c00cd93be04f841a23d7e54c9dc35 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 23 Oct 2024 21:54:54 +0700 Subject: [PATCH 028/312] decouple urlToNavigateBack functions --- src/libs/actions/IOU.ts | 116 ++++++++++++++++++++++++++++++++++----- src/libs/actions/Task.ts | 53 +++++++++++++++--- 2 files changed, 148 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 497f43f93317..01a86eef2b21 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5713,16 +5713,17 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT // STEP 5: Calculate the url that the user will be navigated back to // This depends on which page they are on and which resources were deleted - let reportIDToNavigateBack: string | undefined; - if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { - reportIDToNavigateBack = iouReport.reportID; - } + // let reportIDToNavigateBack: string | undefined; + // if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { + // reportIDToNavigateBack = iouReport.reportID; + // } - if (iouReport?.chatReportID && shouldDeleteIOUReport) { - reportIDToNavigateBack = iouReport.chatReportID; - } + // if (iouReport?.chatReportID && shouldDeleteIOUReport) { + // reportIDToNavigateBack = iouReport.chatReportID; + // } - const urlToNavigateBack = reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined; + // const urlToNavigateBack = reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined; + const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); return { shouldDeleteTransactionThread, @@ -5741,6 +5742,84 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT }; } +/** + * Calculate the URL to navigate to after a money request deletion + * @param transactionID - The ID of the money request being deleted + * @param reportAction - The report action associated with the money request + * @param isSingleTransactionView - Whether we're in single transaction view + * @returns The URL to navigate to + */ +function getNavigationUrlAfterMoneyRequestDelete( + transactionID: string, + reportAction: OnyxTypes.ReportAction, + isSingleTransactionView = false +): string | undefined { + // Get all collections we need for navigation + const allReports = ReportConnection.getAllReports(); + const iouReportID = ReportActionsUtils.isMoneyRequestAction(reportAction) + ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUReportID + : '-1'; + const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`] ?? null; + const transactionThreadID = reportAction.childReportID; + + // Calculate if resources would be deleted + const shouldDeleteTransactionThread = transactionThreadID + ? (reportAction?.childVisibleActionCount ?? 0) === 0 + : false; + + const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(iouReport?.reportID ?? '-1'); + const iouReportLastMessageText = ReportActionsUtils.getLastVisibleMessage(iouReport?.reportID ?? '-1').lastMessageText; + const shouldDeleteIOUReport = iouReportLastMessageText.length === 0 + && !ReportActionsUtils.isDeletedParentAction(lastVisibleAction) + && (!transactionThreadID || shouldDeleteTransactionThread); + + // Determine which report to navigate back to + if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { + return ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID); + } + + if (iouReport?.chatReportID && shouldDeleteIOUReport) { + return ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID); + } + + return undefined; +} + +/** + * Calculate the URL to navigate to after a track expense deletion + * @param chatReportID - The ID of the chat report containing the track expense + * @param transactionID - The ID of the track expense being deleted + * @param reportAction - The report action associated with the track expense + * @param isSingleTransactionView - Whether we're in single transaction view + * @returns The URL to navigate to + */ +function getNavigationUrlAfterTrackExpenseDelete( + chatReportID: string, + transactionID: string, + reportAction: OnyxTypes.ReportAction, + isSingleTransactionView = false +): string | undefined { + const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; + + // If not a self DM, handle it as a regular money request + if (!ReportUtils.isSelfDM(chatReport)) { + return getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); + } + + const transactionThreadID = reportAction.childReportID; + const shouldDeleteTransactionThread = transactionThreadID + ? (reportAction?.childVisibleActionCount ?? 0) === 0 + : false; + + // Only navigate if in single transaction view and the thread will be deleted + if (isSingleTransactionView && shouldDeleteTransactionThread && chatReport?.reportID) { + // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. + return ROUTES.REPORT_WITH_ID.getRoute(chatReport.reportID); + } + + return undefined; +} + /** * * @param transactionID - The transactionID of IOU @@ -6115,10 +6194,18 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor } function deleteTrackExpense(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { + const urlToNavigateBack = getNavigationUrlAfterTrackExpenseDelete( + chatReportID, + transactionID, + reportAction, + isSingleTransactionView + ); + // STEP 1: Get all collections we're updating const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; if (!ReportUtils.isSelfDM(chatReport)) { - return deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView); + deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView); + return urlToNavigateBack; } const whisperAction = ReportActionsUtils.getTrackExpenseActionableWhisper(transactionID, chatReportID); @@ -6138,10 +6225,11 @@ function deleteTrackExpense(chatReportID: string, transactionID: string, reportA CachedPDFPaths.clearByKey(transactionID); // STEP 7: Navigate the user depending on which page they are on and which resources were deleted - if (isSingleTransactionView && shouldDeleteTransactionThread) { - // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. - return ROUTES.REPORT_WITH_ID.getRoute(chatReport?.reportID ?? '-1'); - } + return urlToNavigateBack; + // if (isSingleTransactionView && shouldDeleteTransactionThread) { + // // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. + // return ROUTES.REPORT_WITH_ID.getRoute(chatReport?.reportID ?? '-1'); + // } } /** @@ -8565,5 +8653,7 @@ export { updateLastLocationPermissionPrompt, resolveDuplicates, getIOUReportActionToApproveOrPay, + getNavigationUrlAfterMoneyRequestDelete, + getNavigationUrlAfterTrackExpenseDelete, }; export type {GPSPoint as GpsPoint, IOURequestType}; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index b159e391f949..2031d5a42257 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -955,6 +955,36 @@ function getParentReport(report: OnyxEntry): OnyxEntry): string | undefined { + if (!report) { + return undefined; + } + + const shouldDeleteTaskReport = !ReportActionsUtils.doesReportHaveVisibleActions(report.reportID ?? '-1'); + if (!shouldDeleteTaskReport) { + return undefined; + } + + // First try to navigate to parent report + const parentReport = getParentReport(report); + if (parentReport?.reportID) { + return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID); + } + + // If no parent report, try to navigate to most recent report + const mostRecentReportID = Report.getMostRecentReportID(report); + if (mostRecentReportID) { + return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); + } + + return undefined; +} + /** * Cancels a task by setting the report state to SUBMITTED and status to CLOSED */ @@ -1109,15 +1139,21 @@ function deleteTask(report: OnyxEntry) { API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); Report.notifyNewAction(report.reportID, currentUserAccountID); - if (shouldDeleteTaskReport) { + // if (shouldDeleteTaskReport) { + // Navigation.goBack(); + // if (parentReport?.reportID) { + // return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID); + // } + // const mostRecentReportID = Report.getMostRecentReportID(report); + // if (mostRecentReportID) { + // return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); + // } + // } + + const urlToNavigateBack = getNavigationUrlAfterTaskDelete(report); + if (urlToNavigateBack) { Navigation.goBack(); - if (parentReport?.reportID) { - return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID); - } - const mostRecentReportID = Report.getMostRecentReportID(report); - if (mostRecentReportID) { - return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); - } + return urlToNavigateBack; } } @@ -1232,6 +1268,7 @@ export { canActionTask, setNewOptimisticAssignee, getParentReport, + getNavigationUrlAfterTaskDelete, }; export type {PolicyValue, Assignee, ShareDestination}; From e2cd64213c51a03b9d0a0836257d82430899e4b4 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 23 Oct 2024 22:06:39 +0700 Subject: [PATCH 029/312] actually delete transaction after reportdetailspage unmounted --- src/pages/ReportDetailsPage.tsx | 107 +++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 7de12eeda892..fcaa696d526b 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -3,7 +3,7 @@ import {Str} from 'expensify-common'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx} from 'react-native-onyx'; +import Onyx, {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -225,6 +225,18 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const canUnapproveRequest = ReportUtils.isExpenseReport(report) && (ReportUtils.isReportManager(report) || isPolicyAdmin) && ReportUtils.isReportApproved(report) && !PolicyUtils.isSubmitAndClose(policy); + + useEffect(() => { + return () => { + if (!isTransactionDeleted.current) { + return; + } + + deleteTransaction(); + }; + }, []); + + useEffect(() => { if (canDeleteRequest) { return; @@ -759,11 +771,48 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { // Where to go back after deleting the transaction and its report. It's empty if the transaction report isn't deleted. const navigateBackToAfterDelete = useRef(); - const deleteTransaction = useCallback(() => { + + // Where to go back after deleting the transaction and its report. + const navigateToTargetUrl = useCallback(() => { setIsDeleteModalVisible(false); + isTransactionDeleted.current = true; + + let urlToNavigateBack: string | undefined; + + if (caseID === CASES.DEFAULT) { + urlToNavigateBack = Task.getNavigationUrlAfterTaskDelete(report); + Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); + if (urlToNavigateBack) { + Navigation.goBack(urlToNavigateBack as Route); + } else { + Navigation.dismissModal(); + } + return; + } + + if (!requestParentReportAction) { + return; + } + + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + if (isTrackExpense) { + urlToNavigateBack = IOU.getNavigationUrlAfterTrackExpenseDelete(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + } else { + urlToNavigateBack = IOU.getNavigationUrlAfterMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); + } + Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); + + if (!urlToNavigateBack) { + Navigation.dismissModal(); + } else { + ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); + } + + }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView, setIsDeleteModalVisible, isTransactionDeleted]); + const deleteTransaction = useCallback(() => { if (caseID === CASES.DEFAULT) { - navigateBackToAfterDelete.current = Task.deleteTask(report); + Task.deleteTask(report); return; } @@ -771,14 +820,14 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { return; } - if (ReportActionsUtils.isTrackExpenseAction(requestParentReportAction)) { - navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + + if (isTrackExpense) { + IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); } else { - navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); + IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); } - - isTransactionDeleted.current = true; - }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView]); + }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]); const mentionReportContextValue = useMemo(() => ({currentReportID: report.reportID, exactlyMatch: true}), [report.reportID]); @@ -871,27 +920,27 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { setIsDeleteModalVisible(false)} - onModalHide={() => { - // We use isTransactionDeleted to know if the modal hides because the user deletes the transaction. - if (!isTransactionDeleted.current) { - if (caseID === CASES.DEFAULT) { - if (navigateBackToAfterDelete.current) { - Navigation.goBack(navigateBackToAfterDelete.current); - } else { - Navigation.dismissModal(); - } - } - return; - } - - if (!navigateBackToAfterDelete.current) { - Navigation.dismissModal(); - } else { - ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); - } - }} + // onModalHide={() => { + // // We use isTransactionDeleted to know if the modal hides because the user deletes the transaction. + // if (!isTransactionDeleted.current) { + // if (caseID === CASES.DEFAULT) { + // if (navigateBackToAfterDelete.current) { + // Navigation.goBack(navigateBackToAfterDelete.current); + // } else { + // Navigation.dismissModal(); + // } + // } + // return; + // } + + // if (!navigateBackToAfterDelete.current) { + // Navigation.dismissModal(); + // } else { + // ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); + // } + // }} prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} From dca3bcc040cf5545c1feabb88f63289ae65ce4e5 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 23 Oct 2024 18:47:15 +0300 Subject: [PATCH 030/312] scroll to the cursor on composer size change for native --- .../Composer/implementation/index.native.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 9f237dd02424..905fa537ff55 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -59,6 +59,17 @@ function Composer( inputCallbackRef(autoFocus ? textInput.current : null); }, [autoFocus, inputCallbackRef, autoFocusInputRef]); + useEffect(() => { + if (!textInput.current || !selection) { + return; + } + + // We are setting selection twice to trigger a scroll to the cursor on change of composer size. + textInput.current?.setSelection((selection.start || 1) - 1, selection.start); + textInput.current?.setSelection(selection.start, selection.start); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [isComposerFullSize]); + /** * Set the TextInput Ref * @param {Element} el From 980e8e5dc810f1e2e511228e2dfc817b89a3034c Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 24 Oct 2024 04:40:53 +0700 Subject: [PATCH 031/312] Resolve cyclic dependency --- src/libs/actions/IOU.ts | 42 ++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 01a86eef2b21..53a0b4f47e9e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5723,7 +5723,7 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT // } // const urlToNavigateBack = reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined; - const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); + const urlToNavigateBack = undefined; return { shouldDeleteTransactionThread, @@ -5754,24 +5754,32 @@ function getNavigationUrlAfterMoneyRequestDelete( reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false ): string | undefined { - // Get all collections we need for navigation - const allReports = ReportConnection.getAllReports(); - const iouReportID = ReportActionsUtils.isMoneyRequestAction(reportAction) - ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUReportID - : '-1'; - const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`] ?? null; - const transactionThreadID = reportAction.childReportID; + // // Get all collections we need for navigation + // const allReports = ReportConnection.getAllReports(); + // const iouReportID = ReportActionsUtils.isMoneyRequestAction(reportAction) + // ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUReportID + // : '-1'; + // const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`] ?? null; + // const transactionThreadID = reportAction.childReportID; + + // // Calculate if resources would be deleted + // const shouldDeleteTransactionThread = transactionThreadID + // ? (reportAction?.childVisibleActionCount ?? 0) === 0 + // : false; + + // const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(iouReport?.reportID ?? '-1'); + // const iouReportLastMessageText = ReportActionsUtils.getLastVisibleMessage(iouReport?.reportID ?? '-1').lastMessageText; + // const shouldDeleteIOUReport = iouReportLastMessageText.length === 0 + // && !ReportActionsUtils.isDeletedParentAction(lastVisibleAction) + // && (!transactionThreadID || shouldDeleteTransactionThread); - // Calculate if resources would be deleted - const shouldDeleteTransactionThread = transactionThreadID - ? (reportAction?.childVisibleActionCount ?? 0) === 0 - : false; + const { + shouldDeleteTransactionThread, + shouldDeleteIOUReport, + iouReport, + chatReport, + } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); - const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(iouReport?.reportID ?? '-1'); - const iouReportLastMessageText = ReportActionsUtils.getLastVisibleMessage(iouReport?.reportID ?? '-1').lastMessageText; - const shouldDeleteIOUReport = iouReportLastMessageText.length === 0 - && !ReportActionsUtils.isDeletedParentAction(lastVisibleAction) - && (!transactionThreadID || shouldDeleteTransactionThread); // Determine which report to navigate back to if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { From 0288efb572cf441b44066069362f6a1cc5c880f9 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 24 Oct 2024 08:58:00 +0700 Subject: [PATCH 032/312] extract navagateUrl from prepareToCleanUpMoneyRequest --- src/libs/actions/IOU.ts | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 53a0b4f47e9e..64dca4fd8517 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5723,7 +5723,7 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT // } // const urlToNavigateBack = reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined; - const urlToNavigateBack = undefined; + // const urlToNavigateBack = undefined; return { shouldDeleteTransactionThread, @@ -5738,7 +5738,7 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT transactionViolations, reportPreviewAction, iouReport, - urlToNavigateBack, + // urlToNavigateBack, }; } @@ -5754,30 +5754,10 @@ function getNavigationUrlAfterMoneyRequestDelete( reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false ): string | undefined { - // // Get all collections we need for navigation - // const allReports = ReportConnection.getAllReports(); - // const iouReportID = ReportActionsUtils.isMoneyRequestAction(reportAction) - // ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUReportID - // : '-1'; - // const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`] ?? null; - // const transactionThreadID = reportAction.childReportID; - - // // Calculate if resources would be deleted - // const shouldDeleteTransactionThread = transactionThreadID - // ? (reportAction?.childVisibleActionCount ?? 0) === 0 - // : false; - - // const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(iouReport?.reportID ?? '-1'); - // const iouReportLastMessageText = ReportActionsUtils.getLastVisibleMessage(iouReport?.reportID ?? '-1').lastMessageText; - // const shouldDeleteIOUReport = iouReportLastMessageText.length === 0 - // && !ReportActionsUtils.isDeletedParentAction(lastVisibleAction) - // && (!transactionThreadID || shouldDeleteTransactionThread); - const { shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport, - chatReport, } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); @@ -5846,9 +5826,10 @@ function cleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repo chatReport, iouReport, reportPreviewAction, - urlToNavigateBack, } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); + + const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); // build Onyx data // Onyx operations to delete the transaction, update the IOU report action and chat report action @@ -5984,9 +5965,10 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor transactionViolations, iouReport, reportPreviewAction, - urlToNavigateBack, } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); + const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); + // STEP 2: Build Onyx data // The logic mostly resembles the cleanUpMoneyRequest function const optimisticData: OnyxUpdate[] = [ From 8ce39ae2c6913a33fbc996d1456e0b791edb3d9b Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 24 Oct 2024 10:40:30 +0800 Subject: [PATCH 033/312] 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 5d982cc62be7074302d153b1d575c6ec63946d15 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 24 Oct 2024 09:45:21 +0700 Subject: [PATCH 034/312] Implement NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL --- src/libs/actions/IOU.ts | 10 ++++++++++ src/pages/home/ReportScreen.tsx | 11 +++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c125e0830568..b271baf93886 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1779,6 +1779,11 @@ function getDeleteTrackExpenseInformation( lastMessageHtml: !lastMessageHtml ? lastMessageText : lastMessageHtml, }, }, + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, + value: null, + } ); const successData: OnyxUpdate[] = [ @@ -6020,6 +6025,11 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, value: ReportUtils.getOutstandingChildRequest(updatedIOUReport), }, + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, + value: null, + } ); if (!shouldDeleteIOUReport && updatedReportPreviewAction?.childMoneyRequestCount === 0) { diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index baf3d02d7af3..d17ee485498a 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -342,8 +342,15 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro () => !!linkedAction && ReportActionsUtils.isWhisperAction(linkedAction) && !(linkedAction?.whisperedToAccountIDs ?? []).includes(currentUserAccountID), [currentUserAccountID, linkedAction], ); - - const isLoading = isLoadingApp ?? (!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty()); + const [deleteTransactionNavigateBackUrl] = useOnyx(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL); + console.log("[wildebug] ~ file: ReportScreen.tsx:346 ~ ReportScreen ~ deleteTransactionNavigateBackUrl:", deleteTransactionNavigateBackUrl) + console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ report?.reportID:", report?.reportID) + console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl):", ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl)) + console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID):", (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID)) + + const isLoading = isLoadingApp || ((!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty()) || (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID)); + // const isLoading = isLoadingApp || ((!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty())); + console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ isLoading:", isLoading) const shouldShowSkeleton = (isLinkingToMessage && !isLinkedMessagePageReady) || (!isLinkingToMessage && !isInitialPageReady) || From 44473445ee83fc22b70910ca11eb0a147776d03e Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 24 Oct 2024 10:07:01 +0700 Subject: [PATCH 035/312] change delete money request and track expense to merge instead of fast, to resolve not found page briefly appeared --- src/libs/actions/IOU.ts | 48 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b271baf93886..45f0a95e424b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1752,9 +1752,18 @@ function getDeleteTrackExpenseInformation( if (shouldDeleteTransactionThread) { optimisticData.push( { - onyxMethod: Onyx.METHOD.SET, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: null, + value: { + reportID: null, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + participants: { + [userAccountID]: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + }, + }, }, { onyxMethod: Onyx.METHOD.SET, @@ -1799,6 +1808,17 @@ function getDeleteTrackExpenseInformation( }, ]; + if (shouldDeleteTransactionThread && transactionThread) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + value: Object.keys(transactionThread).reduce>((acc, key) => { + acc[key] = null; + return acc; + }, {}), + }); + } + const failureData: OnyxUpdate[] = []; if (shouldDeleteTransactionFromOnyx) { @@ -5990,9 +6010,18 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor if (shouldDeleteTransactionThread) { optimisticData.push( { - onyxMethod: Onyx.METHOD.SET, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: null, + value: { + reportID: null, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + participants: { + [userAccountID]: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + }, + }, }, { onyxMethod: Onyx.METHOD.SET, @@ -6088,6 +6117,17 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, ]; + if (shouldDeleteTransactionThread && transactionThread) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + value: Object.keys(transactionThread).reduce>((acc, key) => { + acc[key] = null; + return acc; + }, {}), + }); + } + if (shouldDeleteIOUReport) { successData.push({ onyxMethod: Onyx.METHOD.SET, From c278a1a6091c19ad549ac5e06b2b8cfeb756fd45 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 24 Oct 2024 11:28:07 +0800 Subject: [PATCH 036/312] 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 71c3d417ef110eee161f8c51e5789d77a1ba5762 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 24 Oct 2024 11:04:06 +0700 Subject: [PATCH 037/312] show skeleton when deleting processed --- src/libs/actions/IOU.ts | 10 ---------- src/libs/actions/Task.ts | 5 ----- src/pages/home/ReportScreen.tsx | 25 +++++++++++++++++-------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 45f0a95e424b..39692bc10fe0 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1788,11 +1788,6 @@ function getDeleteTrackExpenseInformation( lastMessageHtml: !lastMessageHtml ? lastMessageText : lastMessageHtml, }, }, - { - onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, - value: null, - } ); const successData: OnyxUpdate[] = [ @@ -6054,11 +6049,6 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, value: ReportUtils.getOutstandingChildRequest(updatedIOUReport), }, - { - onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, - value: null, - } ); if (!shouldDeleteIOUReport && updatedReportPreviewAction?.childMoneyRequestCount === 0) { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 2031d5a42257..b2fc07bd8685 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1053,11 +1053,6 @@ function deleteTask(report: OnyxEntry) { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReport?.reportID}`, value: optimisticReportActions as OnyxTypes.ReportActions, }, - { - onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, - value: null, - }, ]; // Update optimistic data for parent report action if the report is a child report and the task report has no visible child diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index d17ee485498a..15c5e9b34877 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -55,6 +55,9 @@ import ReportActionsView from './report/ReportActionsView'; import ReportFooter from './report/ReportFooter'; import type {ActionListContextType, ReactionListRef, ScrollPosition} from './ReportScreenContext'; import {ActionListContext, ReactionListContext} from './ReportScreenContext'; +import Onyx from 'react-native-onyx'; +import lodashDefer from 'lodash/defer'; + type ReportScreenNavigationProps = StackScreenProps; @@ -343,20 +346,26 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro [currentUserAccountID, linkedAction], ); const [deleteTransactionNavigateBackUrl] = useOnyx(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL); - console.log("[wildebug] ~ file: ReportScreen.tsx:346 ~ ReportScreen ~ deleteTransactionNavigateBackUrl:", deleteTransactionNavigateBackUrl) - console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ report?.reportID:", report?.reportID) - console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl):", ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl)) - console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID):", (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID)) - - const isLoading = isLoadingApp || ((!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty()) || (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID)); - // const isLoading = isLoadingApp || ((!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty())); - console.log("[wildebug] ~ file: ReportScreen.tsx:348 ~ ReportScreen ~ isLoading:", isLoading) + + useEffect(() => { + if (!isFocused || !deleteTransactionNavigateBackUrl) { + return; + } + // Schedule the code to run after the current call stack is cleared, + // ensuring all updates are processed before hide the skeleton + lodashDefer(() => { + Onyx.merge(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, null); + }); + }, [isFocused]); + + const isLoading = isLoadingApp ?? ((!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty())); const shouldShowSkeleton = (isLinkingToMessage && !isLinkedMessagePageReady) || (!isLinkingToMessage && !isInitialPageReady) || isEmptyObject(reportOnyx) || isLoadingReportOnyx || !isCurrentReportLoadedFromOnyx || + (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID) || isLoading; const isLinkedActionBecomesDeleted = prevIsLinkedActionDeleted !== undefined && !prevIsLinkedActionDeleted && isLinkedActionDeleted; From 08ff8aae191cd40e0a36ec43f0d9179f9f3f2bd8 Mon Sep 17 00:00:00 2001 From: Tsaqif Date: Thu, 24 Oct 2024 13:15:53 +0700 Subject: [PATCH 038/312] 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 54402d18f585bfc1c34e61502c43d910d505e341 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 28 Oct 2024 18:40:39 +0300 Subject: [PATCH 039/312] comment out the set selection code --- .../Composer/implementation/index.native.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 905fa537ff55..23531ceafa83 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -59,16 +59,16 @@ function Composer( inputCallbackRef(autoFocus ? textInput.current : null); }, [autoFocus, inputCallbackRef, autoFocusInputRef]); - useEffect(() => { - if (!textInput.current || !selection) { - return; - } + // useEffect(() => { + // if (!textInput.current || !selection) { + // return; + // } - // We are setting selection twice to trigger a scroll to the cursor on change of composer size. - textInput.current?.setSelection((selection.start || 1) - 1, selection.start); - textInput.current?.setSelection(selection.start, selection.start); - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [isComposerFullSize]); + // // We are setting selection twice to trigger a scroll to the cursor on change of composer size. + // textInput.current?.setSelection((selection.start || 1) - 1, selection.start); + // textInput.current?.setSelection(selection.start, selection.start); + // // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + // }, [isComposerFullSize]); /** * Set the TextInput Ref From 4e65339fec300f16e3ff124bc5c4d1c88b7095d7 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 28 Oct 2024 23:33:16 +0300 Subject: [PATCH 040/312] revert --- .../Composer/implementation/index.native.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 23531ceafa83..905fa537ff55 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -59,16 +59,16 @@ function Composer( inputCallbackRef(autoFocus ? textInput.current : null); }, [autoFocus, inputCallbackRef, autoFocusInputRef]); - // useEffect(() => { - // if (!textInput.current || !selection) { - // return; - // } + useEffect(() => { + if (!textInput.current || !selection) { + return; + } - // // We are setting selection twice to trigger a scroll to the cursor on change of composer size. - // textInput.current?.setSelection((selection.start || 1) - 1, selection.start); - // textInput.current?.setSelection(selection.start, selection.start); - // // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - // }, [isComposerFullSize]); + // We are setting selection twice to trigger a scroll to the cursor on change of composer size. + textInput.current?.setSelection((selection.start || 1) - 1, selection.start); + textInput.current?.setSelection(selection.start, selection.start); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, [isComposerFullSize]); /** * Set the TextInput Ref From aa8156ddbb3b807b3226a693289a6df4ad5fa310 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Mon, 28 Oct 2024 23:36:09 +0300 Subject: [PATCH 041/312] only scroll to cursor on changing to composer to small size --- src/components/Composer/implementation/index.native.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 905fa537ff55..74c96555b644 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -60,11 +60,11 @@ function Composer( }, [autoFocus, inputCallbackRef, autoFocusInputRef]); useEffect(() => { - if (!textInput.current || !selection) { + if (!textInput.current || !selection || isComposerFullSize) { return; } - // We are setting selection twice to trigger a scroll to the cursor on change of composer size. + // We are setting selection twice to trigger a scroll to the cursor on toggling to smaller composer size. textInput.current?.setSelection((selection.start || 1) - 1, selection.start); textInput.current?.setSelection(selection.start, selection.start); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps From 913563b841946fbbe5bedee861233f19ab9484dc Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 29 Oct 2024 00:30:19 +0300 Subject: [PATCH 042/312] fix tests --- src/components/Composer/implementation/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 74c96555b644..9f136effa857 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -60,7 +60,7 @@ function Composer( }, [autoFocus, inputCallbackRef, autoFocusInputRef]); useEffect(() => { - if (!textInput.current || !selection || isComposerFullSize) { + if (!textInput.current || !textInput.current.setSelection || !selection || isComposerFullSize) { return; } From d8076c19009b08cfaf29cdeae29349bd873d9a6f Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 1 Nov 2024 23:00:47 +0300 Subject: [PATCH 043/312] applied small delay to setSelection --- src/components/Composer/implementation/index.native.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 9f136effa857..e5c2b309ad82 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -65,8 +65,13 @@ function Composer( } // We are setting selection twice to trigger a scroll to the cursor on toggling to smaller composer size. - textInput.current?.setSelection((selection.start || 1) - 1, selection.start); - textInput.current?.setSelection(selection.start, selection.start); + const timeoutID = setTimeout(() => { + textInput.current?.setSelection((selection.start || 1) - 1, selection.start); + textInput.current?.setSelection(selection.start, selection.start); + }, 100); + + return () => clearTimeout(timeoutID); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [isComposerFullSize]); From 295079ffbe9de26548ec2ed84a6e78ccf5082b82 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 1 Nov 2024 23:02:46 +0300 Subject: [PATCH 044/312] reduced the delay --- src/components/Composer/implementation/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index e5c2b309ad82..0eee0be257d4 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -68,7 +68,7 @@ function Composer( const timeoutID = setTimeout(() => { textInput.current?.setSelection((selection.start || 1) - 1, selection.start); textInput.current?.setSelection(selection.start, selection.start); - }, 100); + }, 0); return () => clearTimeout(timeoutID); From 36a58e35afcaed90cb4889cb3400fd5147e0a3bc Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Sat, 2 Nov 2024 21:33:34 +0700 Subject: [PATCH 045/312] fix gray-out and name issue in offline category update --- src/libs/CategoryUtils.ts | 19 +++++++++++++++++-- src/libs/Middleware/SaveResponseInOnyx.ts | 1 + src/libs/actions/Policy/Category.ts | 15 ++++++++++++++- src/types/onyx/Policy.ts | 1 + 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/libs/CategoryUtils.ts b/src/libs/CategoryUtils.ts index 479ae557eab6..7723f98e7345 100644 --- a/src/libs/CategoryUtils.ts +++ b/src/libs/CategoryUtils.ts @@ -1,7 +1,7 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider'; import CONST from '@src/CONST'; import type {Policy, TaxRate, TaxRatesWithDefault} from '@src/types/onyx'; -import type {ApprovalRule, ExpenseRule} from '@src/types/onyx/Policy'; +import type {ApprovalRule, ExpenseRule, MccGroup} from '@src/types/onyx/Policy'; import * as CurrencyUtils from './CurrencyUtils'; function formatDefaultTaxRateText(translate: LocaleContextProps['translate'], taxID: string, taxRate: TaxRate, policyTaxRates?: TaxRatesWithDefault) { @@ -68,4 +68,19 @@ function getCategoryDefaultTaxRate(expenseRules: ExpenseRule[], categoryName: st return categoryDefaultTaxRate; } -export {formatDefaultTaxRateText, formatRequireReceiptsOverText, getCategoryApproverRule, getCategoryExpenseRule, getCategoryDefaultTaxRate}; +function updateCategoryInMccGroup(mccGroups: Record, oldCategoryName: string, newCategoryName: string, shouldClearPendingAction?: boolean) { + if (oldCategoryName === newCategoryName) { + return mccGroups; + } + + const updatedGroups: Record = {}; + + for (const [key, group] of Object.entries(mccGroups || {})) { + updatedGroups[key] = + group.category === oldCategoryName ? {...group, category: newCategoryName, pendingAction: shouldClearPendingAction ? null : CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE} : group; + } + + return updatedGroups; +} + +export {formatDefaultTaxRateText, formatRequireReceiptsOverText, getCategoryApproverRule, getCategoryExpenseRule, getCategoryDefaultTaxRate, updateCategoryInMccGroup}; diff --git a/src/libs/Middleware/SaveResponseInOnyx.ts b/src/libs/Middleware/SaveResponseInOnyx.ts index 12c1931b0199..677939157e3b 100644 --- a/src/libs/Middleware/SaveResponseInOnyx.ts +++ b/src/libs/Middleware/SaveResponseInOnyx.ts @@ -10,6 +10,7 @@ const requestsToIgnoreLastUpdateID: string[] = [ SIDE_EFFECT_REQUEST_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.CLOSE_ACCOUNT, WRITE_COMMANDS.DELETE_MONEY_REQUEST, + WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, SIDE_EFFECT_REQUEST_COMMANDS.GET_MISSING_ONYX_MESSAGES, ]; diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index 78b0f2dec9e2..5454591b5b5f 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -34,7 +34,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, PolicyCategories, PolicyCategory, RecentlyUsedCategories, Report} from '@src/types/onyx'; -import type {ApprovalRule, CustomUnit, ExpenseRule} from '@src/types/onyx/Policy'; +import type {ApprovalRule, CustomUnit, ExpenseRule, MccGroup} from '@src/types/onyx/Policy'; import type {PolicyCategoryExpenseLimitType} from '@src/types/onyx/PolicyCategory'; import type {OnyxData} from '@src/types/onyx/Request'; @@ -542,8 +542,12 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string const policyCategoryExpenseRule = CategoryUtils.getCategoryExpenseRule(policy?.rules?.expenseRules ?? [], policyCategory.oldName); const approvalRules = policy?.rules?.approvalRules ?? []; const expenseRules = policy?.rules?.expenseRules ?? []; + const mccGroup = policy?.mccGroup ?? {}; const updatedApprovalRules: ApprovalRule[] = lodashCloneDeep(approvalRules); const updatedExpenseRules: ExpenseRule[] = lodashCloneDeep(expenseRules); + const clonedMccGroup: Record = lodashCloneDeep(mccGroup); + const updatedMccGroup = CategoryUtils.updateCategoryInMccGroup(clonedMccGroup, policyCategory.oldName, policyCategory.newName); + const updatedMccGroupWithClearedPendingAction = CategoryUtils.updateCategoryInMccGroup(clonedMccGroup, policyCategory.oldName, policyCategory.newName, true); if (policyCategoryExpenseRule) { const ruleIndex = updatedExpenseRules.findIndex((rule) => rule.id === policyCategoryExpenseRule.id); @@ -596,10 +600,18 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string approvalRules: updatedApprovalRules, expenseRules: updatedExpenseRules, }, + mccGroup: updatedMccGroup, }, }, ], successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + mccGroup: updatedMccGroupWithClearedPendingAction, + }, + }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, @@ -639,6 +651,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string rules: { approvalRules, }, + mccGroup: updatedMccGroupWithClearedPendingAction, }, }, ], diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index ecc5bd1f6606..39f36f152913 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1901,4 +1901,5 @@ export type { ApprovalRule, ExpenseRule, NetSuiteConnectionConfig, + MccGroup, }; From 58042056421a1db7097fa41c124baced6cf70546 Mon Sep 17 00:00:00 2001 From: Ren Jones <153645623+ren-jones@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:52:49 -0600 Subject: [PATCH 046/312] Update and rename Expense-Rules.md to Create-Expense-Rules.md Update article formatting and a few small tweaks, rename article to match standard naming convention --- .../expenses/Create-Expense-Rules.md | 61 +++++++++++++++++++ .../expenses/Expense-Rules.md | 55 ----------------- 2 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 docs/articles/expensify-classic/expenses/Create-Expense-Rules.md delete mode 100644 docs/articles/expensify-classic/expenses/Expense-Rules.md diff --git a/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md new file mode 100644 index 000000000000..c455be3dd721 --- /dev/null +++ b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md @@ -0,0 +1,61 @@ +--- +title: Create Expense Rules +description: Automatically categorize, tag, and report expenses based on the merchant's name + +--- +Expense rules allow you to automatically categorize, tag, and report expenses based on the merchant’s name. + +# Create expense rules + +1. Hover over **Settings** and click **Account**. +2. Click **Expense Rules**. +2. Click **New Rule**. +3. Add what the merchant name should contain in order for the rule to be applied. *Note: If you enter just a period, the rule will apply to all expenses regardless of the merchant name. Universal Rules will always take precedence over all other expense rules.* +4. Choose from the following rules: +- **Merchant:** Updates the merchant name (e.g., “Starbucks #238” could be changed to “Starbucks”) +- **Category:** Applies a workspace category to the expense +- **Tag:** Applies a tag to the expense (e.g., a Department or Location) +- **Description:** Adds a description to the description field on the expense +- **Reimbursability:** Determines whether the expense will be marked as reimbursable or non-reimbursable +- **Billable**: Determines whether the expense is billable +- **Add to a report named:** Adds the expense to a report with the name you type into the field. If no report with that name exists, a new report will be created if the "Create report if necessary" checkbox is selected. + +![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_01.png){:width="100%"} + +{:start="7"} +7. (Optional) To apply the rule to previously entered expenses, select the **Apply to existing matching expenses** checkbox. You can also click **Preview Matching Expenses** to see if your rule matches the intended expenses. + +# How rules are applied + +In general, your expense rules will be applied in order, from **top to bottom**, (i.e., from the first rule). However, other settings can impact how expense rules are applied. Here is the hierarchy that determines how these are applied: + +1. A Universal Rule will **always** be applied over any other expense category rules. Rules that would otherwise change the expense category will **not** override the Universal Rule. +2. If Scheduled Submit and the setting “Enforce Default Report Title” are enabled on the workspace, this will take precedence over any rules trying to add the expense to a report. +3. If the expense is from a company card that is forced to a workspace with strict rule enforcement, those rules will take precedence over individual expense rules. +4. If you belong to a workspace that is tied to an accounting integration, the configuration settings for this connection may update your expense details upon export, even if the expense rules were successfully applied to the expense. + +# Create an expense rule from changes made to an expense + +If you open an expense and change it, you can then create an expense rule based on those changes by selecting the “Create a rule based on your changes" checkbox. *Note: The expense must be saved, reopened, and edited for this option to appear.* + +![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_02.png){:width="100%"} + +# Delete an expense rule + +To delete an expense rule, + +1. Hover over **Settings** and click **Account**. +2. Click **Expense Rules**. +3. Scroll down to the rule you’d like to remove and click the trash can icon. + +![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_03.png){:width="100%"} + +{% include faq-begin.md %} + +## How can I use expense rules to vendor match when exporting to an accounting package? + +When exporting non-reimbursable expenses to your connected accounting package, the payee field will list "Credit Card Misc." if the merchant name on the expense in Expensify is not an exact match to a vendor in the accounting package. When an exact match is unavailable, "Credit Card Misc." prevents multiple variations of the same vendor (e.g., Starbucks and Starbucks #1234, as is often seen in credit card statements) from being created in your accounting package. + +For repeated expenses, the best practice is to use Expense Rules, which will automatically update the merchant name without having to do it manually each time. This only works for connections to QuickBooks Online, Desktop, and Xero. Vendor matching cannot be performed in this manner for NetSuite or Sage Intacct due to limitations in the API of the accounting package. + +{% include faq-end.md %} diff --git a/docs/articles/expensify-classic/expenses/Expense-Rules.md b/docs/articles/expensify-classic/expenses/Expense-Rules.md deleted file mode 100644 index 295aa8d00cc9..000000000000 --- a/docs/articles/expensify-classic/expenses/Expense-Rules.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Expense Rules -description: Expense rules allow you to automatically categorize, tag, and report expenses based on the merchant's name. - ---- -# Overview -Expense rules allow you to automatically categorize, tag, and report expenses based on the merchant’s name. - -# How to use Expense Rules -**To create an expense rule, follow these steps:** -1. Navigate to **Settings > Account > Expense Rules** -2. Click on **New Rule** -3. Fill in the required information to set up your rule - -When creating an expense rule, you will be able to apply the following rules to expenses: - -![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_01.png){:width="100%"} - -- **Merchant:** Updates the merchant name, e.g., “Starbucks #238” could be changed to “Starbucks” -- **Category:** Applies a workspace category to the expense -- **Tag:** Applies a tag to the expense, e.g., a Department or Location -- **Description:** Adds a description to the description field on the expense -- **Reimbursability:** Determines whether the expense will be marked as reimbursable or non-reimbursable -- **Billable**: Determines whether the expense is billable -- **Add to a report named:** Adds the expense to a report with the name you type into the field. If no report with that name exists, a new report will be created - -## Tips on using Expense Rules -- If you'd like to apply a rule to all expenses (“Universal Rule”) rather than just one merchant, simply enter a period [.] and nothing else into the **“When the merchant name contains:”** field. **Note:** Universal Rules will always take precedence over all other rules for category (more on this below). -- You can apply a rule to previously entered expenses by checking the **Apply to existing matching expenses** checkbox. Click “Preview Matching Expenses” to see if your rule matches the intended expenses. -- You can create expense rules while editing an expense. To do this, simply check the box **“Create a rule based on your changes"** at the time of editing. Note that the expense must be saved, reopened, and edited for this option to appear. - - -![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_02.png){:width="100%"} - - -To delete an expense rule, go to **Settings > Account > Expense Rules**, scroll down to the rule you’d like to remove, and then click the trash can icon in the upper right corner of the rule: - -![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_03.png){:width="100%"} - -# Deep Dive -In general, your expense rules will be applied in order, from **top to bottom**, i.e., from the first rule. However, other settings can impact how expense rules are applied. Here is the hierarchy that determines how these are applied: -1. A Universal Rule will **always** precede over any other expense category rules. Rules that would otherwise change the expense category will **not** override the Universal Rule. -2. If Scheduled Submit and the setting “Enforce Default Report Title” are enabled on the workspace, this will take precedence over any rules trying to add the expense to a report. -3. If the expense is from a Company Card that is forced to a workspace with strict rule enforcement, those rules will take precedence over individual expense rules. -4. If you belong to a workspace that is tied to an accounting integration, the configuration settings for this connection may update your expense details upon export, even if the expense rules were successfully applied to the expense. - - -{% include faq-begin.md %} -## How can I use Expense Rules to vendor match when exporting to an accounting package? -When exporting non-reimbursable expenses to your connected accounting package, the payee field will list "Credit Card Misc." if the merchant name on the expense in Expensify is not an exact match to a vendor in the accounting package. -When an exact match is unavailable, "Credit Card Misc." prevents multiple variations of the same vendor (e.g., Starbucks and Starbucks #1234, as is often seen in credit card statements) from being created in your accounting package. -For repeated expenses, the best practice is to use Expense Rules, which will automatically update the merchant name without having to do it manually each time. -This only works for connections to QuickBooks Online, Desktop, and Xero. Vendor matching cannot be performed in this manner for NetSuite or Sage Intacct due to limitations in the API of the accounting package. - -{% include faq-end.md %} From 1b8128a02178af9647ffe9967c2f162ba90d9d33 Mon Sep 17 00:00:00 2001 From: Ren Jones <153645623+ren-jones@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:59:01 -0600 Subject: [PATCH 047/312] Update redirects.csv Updating for new article name --- docs/redirects.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/redirects.csv b/docs/redirects.csv index 06fd7c1ef502..93238b11f55b 100644 --- a/docs/redirects.csv +++ b/docs/redirects.csv @@ -591,3 +591,4 @@ https://help.expensify.com/articles/expensify-classic/articles/expensify-classic https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Pay-Bills,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Create-and-Pay-Bills https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/add-a-payment-card-and-view-your-subscription,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Add-a-payment-card-and-view-your-subscription https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Billing-page-coming-soon,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Billing-page +https://help.expensify.com/articles/expensify-classic/expenses/Expense-Rules,https://help.expensify.com/articles/expensify-classic/expenses/Create-Expense-Rules From d582cc3caa56c81a2889533c07b664fe574f128f Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Wed, 6 Nov 2024 05:47:41 +0700 Subject: [PATCH 048/312] remove unused code --- src/libs/Middleware/SaveResponseInOnyx.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Middleware/SaveResponseInOnyx.ts b/src/libs/Middleware/SaveResponseInOnyx.ts index 677939157e3b..12c1931b0199 100644 --- a/src/libs/Middleware/SaveResponseInOnyx.ts +++ b/src/libs/Middleware/SaveResponseInOnyx.ts @@ -10,7 +10,6 @@ const requestsToIgnoreLastUpdateID: string[] = [ SIDE_EFFECT_REQUEST_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.CLOSE_ACCOUNT, WRITE_COMMANDS.DELETE_MONEY_REQUEST, - WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, SIDE_EFFECT_REQUEST_COMMANDS.GET_MISSING_ONYX_MESSAGES, ]; From 09a7c37af25f5f79a9f0a50f02ae65ed9328c39a Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:55:43 +0700 Subject: [PATCH 049/312] Add RENAME_WORKSPACE_CATEGORY to ignore last update ID for fixing first-time category update case --- src/libs/Middleware/SaveResponseInOnyx.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Middleware/SaveResponseInOnyx.ts b/src/libs/Middleware/SaveResponseInOnyx.ts index 12c1931b0199..677939157e3b 100644 --- a/src/libs/Middleware/SaveResponseInOnyx.ts +++ b/src/libs/Middleware/SaveResponseInOnyx.ts @@ -10,6 +10,7 @@ const requestsToIgnoreLastUpdateID: string[] = [ SIDE_EFFECT_REQUEST_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.CLOSE_ACCOUNT, WRITE_COMMANDS.DELETE_MONEY_REQUEST, + WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, SIDE_EFFECT_REQUEST_COMMANDS.GET_MISSING_ONYX_MESSAGES, ]; From d95b51f05716d81f024fcea7511182da2d61ded8 Mon Sep 17 00:00:00 2001 From: c3024 Date: Fri, 8 Nov 2024 18:08:04 +0530 Subject: [PATCH 050/312] 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 051/312] 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 0e0edac70988affb617c7e409ac8e3f08048afa9 Mon Sep 17 00:00:00 2001 From: Rushat Gabhane Date: Mon, 11 Nov 2024 01:32:40 +0530 Subject: [PATCH 052/312] add green color to menu dot for copilot --- src/pages/settings/Security/SecuritySettingsPage.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Security/SecuritySettingsPage.tsx b/src/pages/settings/Security/SecuritySettingsPage.tsx index 42ac7c52e58f..b13cf04cfb7b 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.tsx +++ b/src/pages/settings/Security/SecuritySettingsPage.tsx @@ -55,6 +55,8 @@ function SecuritySettingsPage() { const [shouldShowDelegatePopoverMenu, setShouldShowDelegatePopoverMenu] = useState(false); const [shouldShowRemoveDelegateModal, setShouldShowRemoveDelegateModal] = useState(false); const [selectedDelegate, setSelectedDelegate] = useState(); + const [selectedEmail, setSelectedEmail] = useState(); + const errorFields = account?.delegatedAccess?.errorFields ?? {}; const [anchorPosition, setAnchorPosition] = useState({ @@ -88,6 +90,7 @@ function SecuritySettingsPage() { setMenuPosition(); setShouldShowDelegatePopoverMenu(true); setSelectedDelegate(delegate); + setSelectedEmail(delegate.email); }; useLayoutEffect(() => { @@ -171,11 +174,12 @@ function SecuritySettingsPage() { onPendingActionDismiss: () => clearDelegateErrorsByField(email, 'addDelegate'), error, onPress, + success: selectedEmail === email, }; }), // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps - [delegates, translate, styles, personalDetails, errorFields, windowWidth], + [delegates, translate, styles, personalDetails, errorFields, windowWidth, selectedEmail], ); const delegatorMenuItems: MenuItemProps[] = useMemo( @@ -209,6 +213,7 @@ function SecuritySettingsPage() { Navigation.navigate(ROUTES.SETTINGS_UPDATE_DELEGATE_ROLE.getRoute(selectedDelegate?.email ?? '', selectedDelegate?.role ?? '')); setShouldShowDelegatePopoverMenu(false); setSelectedDelegate(undefined); + setSelectedEmail(undefined); }, }, { @@ -218,6 +223,7 @@ function SecuritySettingsPage() { Modal.close(() => { setShouldShowDelegatePopoverMenu(false); setShouldShowRemoveDelegateModal(true); + setSelectedEmail(undefined); }), }, ]; @@ -310,6 +316,7 @@ function SecuritySettingsPage() { menuItems={delegatePopoverMenuItems} onClose={() => { setShouldShowDelegatePopoverMenu(false); + setSelectedEmail(undefined); }} /> Date: Mon, 11 Nov 2024 16:16:58 +0800 Subject: [PATCH 053/312] 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 054/312] 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 055/312] 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 056/312] 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 057/312] 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 058/312] 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 059/312] 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 060/312] 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 fb9a37cb8d478aeb0d09dc3e9477284ca6f0f8d7 Mon Sep 17 00:00:00 2001 From: Ren Jones <153645623+ren-jones@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:25:39 -0600 Subject: [PATCH 061/312] Update docs/articles/expensify-classic/expenses/Create-Expense-Rules.md Co-authored-by: Daniel Gale-Rosen <5487802+dangrous@users.noreply.github.com> --- .../articles/expensify-classic/expenses/Create-Expense-Rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md index c455be3dd721..36ae8be74988 100644 --- a/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md +++ b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md @@ -1,8 +1,8 @@ --- title: Create Expense Rules description: Automatically categorize, tag, and report expenses based on the merchant's name - --- + Expense rules allow you to automatically categorize, tag, and report expenses based on the merchant’s name. # Create expense rules From 164c125e7837fff46d18de3bd6e5de1e0d824975 Mon Sep 17 00:00:00 2001 From: Ren Jones <153645623+ren-jones@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:28:05 -0600 Subject: [PATCH 062/312] Update docs/articles/expensify-classic/expenses/Create-Expense-Rules.md Co-authored-by: Daniel Gale-Rosen <5487802+dangrous@users.noreply.github.com> --- .../expensify-classic/expenses/Create-Expense-Rules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md index 36ae8be74988..5cc3599f0f9b 100644 --- a/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md +++ b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md @@ -22,8 +22,8 @@ Expense rules allow you to automatically categorize, tag, and report expenses ba ![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_01.png){:width="100%"} -{:start="7"} -7. (Optional) To apply the rule to previously entered expenses, select the **Apply to existing matching expenses** checkbox. You can also click **Preview Matching Expenses** to see if your rule matches the intended expenses. +{:start="6"} +6. (Optional) To apply the rule to previously entered expenses, select the **Apply to existing matching expenses** checkbox. You can also click **Preview Matching Expenses** to see if your rule matches the intended expenses. # How rules are applied From 75ed3c0ff0c539c1d680ffbc9150d1f00360bbda Mon Sep 17 00:00:00 2001 From: Ren Jones <153645623+ren-jones@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:44:42 -0600 Subject: [PATCH 063/312] Update Create-Expense-Rules.md Updating alt text --- .../expensify-classic/expenses/Create-Expense-Rules.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md index 5cc3599f0f9b..e83640403ce4 100644 --- a/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md +++ b/docs/articles/expensify-classic/expenses/Create-Expense-Rules.md @@ -20,7 +20,7 @@ Expense rules allow you to automatically categorize, tag, and report expenses ba - **Billable**: Determines whether the expense is billable - **Add to a report named:** Adds the expense to a report with the name you type into the field. If no report with that name exists, a new report will be created if the "Create report if necessary" checkbox is selected. -![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_01.png){:width="100%"} +![Fields to create a new expense rule, including the characters a merchant's name should contain for the rule to apply, as well as what changes should be applied to the expense including the merchant name, category, tag, description, reimbursability, whether it is billable, and what report it will be added to.](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_01.png){:width="100%"} {:start="6"} 6. (Optional) To apply the rule to previously entered expenses, select the **Apply to existing matching expenses** checkbox. You can also click **Preview Matching Expenses** to see if your rule matches the intended expenses. @@ -38,7 +38,7 @@ In general, your expense rules will be applied in order, from **top to bottom**, If you open an expense and change it, you can then create an expense rule based on those changes by selecting the “Create a rule based on your changes" checkbox. *Note: The expense must be saved, reopened, and edited for this option to appear.* -![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_02.png){:width="100%"} +![The "Create a rule based on your changes" checkbox is located in the bottom right corner of the popup window, to the left of the Save button.](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_02.png){:width="100%"} # Delete an expense rule @@ -48,7 +48,7 @@ To delete an expense rule, 2. Click **Expense Rules**. 3. Scroll down to the rule you’d like to remove and click the trash can icon. -![Insert alt text for accessibility here](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_03.png){:width="100%"} +![The Trash icon to delete an expense rule is located at the top right of the box containing the expense rule, to the left of the Edit icon.](https://help.expensify.com/assets/images/ExpensifyHelp_ExpenseRules_03.png){:width="100%"} {% include faq-begin.md %} From 390d35791eefee063aaa0a923de5ec4981034954 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 14 Nov 2024 13:15:42 +0700 Subject: [PATCH 064/312] remove unnecessary code --- src/libs/actions/IOU.ts | 15 --------------- src/pages/ReportDetailsPage.tsx | 19 ------------------- 2 files changed, 34 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index cf2ab1dda20f..9302ed0b0db6 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5487,20 +5487,6 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT updatedReportPreviewAction.childMoneyRequestCount = reportPreviewAction.childMoneyRequestCount - 1; } - // STEP 5: Calculate the url that the user will be navigated back to - // This depends on which page they are on and which resources were deleted - // let reportIDToNavigateBack: string | undefined; - // if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { - // reportIDToNavigateBack = iouReport.reportID; - // } - - // if (iouReport?.chatReportID && shouldDeleteIOUReport) { - // reportIDToNavigateBack = iouReport.chatReportID; - // } - - // const urlToNavigateBack = reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined; - // const urlToNavigateBack = undefined; - return { shouldDeleteTransactionThread, shouldDeleteIOUReport, @@ -5514,7 +5500,6 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT transactionViolations, reportPreviewAction, iouReport, - // urlToNavigateBack, }; } diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 53815b88bdee..027fa278cca6 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -962,25 +962,6 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { isVisible={isDeleteModalVisible} onConfirm={navigateToTargetUrl} onCancel={() => setIsDeleteModalVisible(false)} - // onModalHide={() => { - // // We use isTransactionDeleted to know if the modal hides because the user deletes the transaction. - // if (!isTransactionDeleted.current) { - // if (caseID === CASES.DEFAULT) { - // if (navigateBackToAfterDelete.current) { - // Navigation.goBack(navigateBackToAfterDelete.current); - // } else { - // Navigation.dismissModal(); - // } - // } - // return; - // } - - // if (!navigateBackToAfterDelete.current) { - // Navigation.dismissModal(); - // } else { - // ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current, true); - // } - // }} prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} From 576e19456c9b3ee6172d5113197d5a66c97f5e3a Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 14 Nov 2024 13:17:12 +0700 Subject: [PATCH 065/312] prettier --- src/libs/actions/IOU.ts | 36 +++++++-------------------------- src/pages/ReportDetailsPage.tsx | 14 +++++-------- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9302ed0b0db6..b6d6544ba5f6 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5510,17 +5510,8 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT * @param isSingleTransactionView - Whether we're in single transaction view * @returns The URL to navigate to */ -function getNavigationUrlAfterMoneyRequestDelete( - transactionID: string, - reportAction: OnyxTypes.ReportAction, - isSingleTransactionView = false -): string | undefined { - const { - shouldDeleteTransactionThread, - shouldDeleteIOUReport, - iouReport, - } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); - +function getNavigationUrlAfterMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): string | undefined { + const {shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport} = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); // Determine which report to navigate back to if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { @@ -5542,23 +5533,16 @@ function getNavigationUrlAfterMoneyRequestDelete( * @param isSingleTransactionView - Whether we're in single transaction view * @returns The URL to navigate to */ -function getNavigationUrlAfterTrackExpenseDelete( - chatReportID: string, - transactionID: string, - reportAction: OnyxTypes.ReportAction, - isSingleTransactionView = false -): string | undefined { +function getNavigationUrlAfterTrackExpenseDelete(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): string | undefined { const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; - + // If not a self DM, handle it as a regular money request if (!ReportUtils.isSelfDM(chatReport)) { return getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); } const transactionThreadID = reportAction.childReportID; - const shouldDeleteTransactionThread = transactionThreadID - ? (reportAction?.childVisibleActionCount ?? 0) === 0 - : false; + const shouldDeleteTransactionThread = transactionThreadID ? (reportAction?.childVisibleActionCount ?? 0) === 0 : false; // Only navigate if in single transaction view and the thread will be deleted if (isSingleTransactionView && shouldDeleteTransactionThread && chatReport?.reportID) { @@ -5589,7 +5573,6 @@ function cleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repo reportPreviewAction, } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); - const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); // build Onyx data @@ -5965,13 +5948,8 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor } function deleteTrackExpense(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { - const urlToNavigateBack = getNavigationUrlAfterTrackExpenseDelete( - chatReportID, - transactionID, - reportAction, - isSingleTransactionView - ); - + const urlToNavigateBack = getNavigationUrlAfterTrackExpenseDelete(chatReportID, transactionID, reportAction, isSingleTransactionView); + // STEP 1: Get all collections we're updating const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; if (!ReportUtils.isSelfDM(chatReport)) { diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 027fa278cca6..4bfc053131da 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -225,7 +225,6 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const canUnapproveRequest = ReportUtils.isExpenseReport(report) && (ReportUtils.isReportManager(report) || isPolicyAdmin) && ReportUtils.isReportApproved(report) && !PolicyUtils.isSubmitAndClose(policy); - useEffect(() => { return () => { if (!isTransactionDeleted.current) { @@ -236,7 +235,6 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { }; }, []); - useEffect(() => { if (canDeleteRequest) { return; @@ -811,14 +809,13 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { // Where to go back after deleting the transaction and its report. It's empty if the transaction report isn't deleted. const navigateBackToAfterDelete = useRef(); - // Where to go back after deleting the transaction and its report. const navigateToTargetUrl = useCallback(() => { setIsDeleteModalVisible(false); isTransactionDeleted.current = true; - + let urlToNavigateBack: string | undefined; - + if (caseID === CASES.DEFAULT) { urlToNavigateBack = Task.getNavigationUrlAfterTaskDelete(report); Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); @@ -829,11 +826,11 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { } return; } - + if (!requestParentReportAction) { return; } - + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); if (isTrackExpense) { urlToNavigateBack = IOU.getNavigationUrlAfterTrackExpenseDelete(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); @@ -841,13 +838,12 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { urlToNavigateBack = IOU.getNavigationUrlAfterMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); } Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); - + if (!urlToNavigateBack) { Navigation.dismissModal(); } else { ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); } - }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView, setIsDeleteModalVisible, isTransactionDeleted]); const deleteTransaction = useCallback(() => { From 7145f31c185f71a15ada45acfc5733fbd1f6cc7e Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 14 Nov 2024 14:00:43 +0700 Subject: [PATCH 066/312] lint & prettier --- src/libs/actions/IOU.ts | 11 +++--- src/libs/actions/Report.ts | 10 +++++ src/pages/ReportDetailsPage.tsx | 67 ++++++++++++++++----------------- src/pages/home/ReportScreen.tsx | 12 +++--- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b6d6544ba5f6..6eaec2c751f9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5387,10 +5387,9 @@ function updateMoneyRequestAmountAndCurrency({ * * @param transactionID - The transactionID of IOU * @param reportAction - The reportAction of the transaction in the IOU report - * @param isSingleTransactionView - whether we are in the transaction thread report * @return the url to navigate back once the money request is deleted */ -function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { +function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction) { // STEP 1: Get all collections we're updating const allReports = ReportConnection.getAllReports(); const iouReportID = ReportActionsUtils.isMoneyRequestAction(reportAction) ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUReportID : '-1'; @@ -5507,11 +5506,11 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT * Calculate the URL to navigate to after a money request deletion * @param transactionID - The ID of the money request being deleted * @param reportAction - The report action associated with the money request - * @param isSingleTransactionView - Whether we're in single transaction view + * @param isSingleTransactionView - whether we are in the transaction thread report * @returns The URL to navigate to */ function getNavigationUrlAfterMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): string | undefined { - const {shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport} = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); + const {shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport} = prepareToCleanUpMoneyRequest(transactionID, reportAction); // Determine which report to navigate back to if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { @@ -5571,7 +5570,7 @@ function cleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repo chatReport, iouReport, reportPreviewAction, - } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); + } = prepareToCleanUpMoneyRequest(transactionID, reportAction); const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); // build Onyx data @@ -5959,7 +5958,7 @@ function deleteTrackExpense(chatReportID: string, transactionID: string, reportA const whisperAction = ReportActionsUtils.getTrackExpenseActionableWhisper(transactionID, chatReportID); const actionableWhisperReportActionID = whisperAction?.reportActionID; - const {parameters, optimisticData, successData, failureData, shouldDeleteTransactionThread} = getDeleteTrackExpenseInformation( + const {parameters, optimisticData, successData, failureData} = getDeleteTrackExpenseInformation( chatReportID, transactionID, reportAction, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f2b4186fa566..cc28e22b15d0 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -4337,6 +4337,14 @@ function exportReportToCSV({reportID, transactionIDList}: ExportReportCSVParams, fileDownload(ApiUtils.getCommandURL({command: WRITE_COMMANDS.EXPORT_REPORT_TO_CSV}), 'Expensify.csv', '', false, formData, CONST.NETWORK.METHOD.POST, onDownloadFailed); } +function setDeleteTransactionNavigateBackUrl(url: string) { + Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, url); +} + +function clearDeleteTransactionNavigateBackUrl() { + Onyx.merge(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, null); +} + export type {Video}; export { @@ -4426,4 +4434,6 @@ export { updateReportName, updateRoomVisibility, updateWriteCapability, + setDeleteTransactionNavigateBackUrl, + clearDeleteTransactionNavigateBackUrl, }; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 4bfc053131da..be4e79916c3a 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -3,7 +3,7 @@ import {Str} from 'expensify-common'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import Onyx, {useOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -225,16 +225,6 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { const canUnapproveRequest = ReportUtils.isExpenseReport(report) && (ReportUtils.isReportManager(report) || isPolicyAdmin) && ReportUtils.isReportApproved(report) && !PolicyUtils.isSubmitAndClose(policy); - useEffect(() => { - return () => { - if (!isTransactionDeleted.current) { - return; - } - - deleteTransaction(); - }; - }, []); - useEffect(() => { if (canDeleteRequest) { return; @@ -804,10 +794,38 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { ); + const deleteTransaction = useCallback(() => { + if (caseID === CASES.DEFAULT) { + Task.deleteTask(report); + return; + } + + if (!requestParentReportAction) { + return; + } + + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + + if (isTrackExpense) { + IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + } else { + IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); + } + }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]); + // A flag to indicate whether the user choose to delete the transaction or not const isTransactionDeleted = useRef(false); - // Where to go back after deleting the transaction and its report. It's empty if the transaction report isn't deleted. - const navigateBackToAfterDelete = useRef(); + + useEffect(() => { + return () => { + // Perform the actual deletion after the details page is unmounted. This prevents the [Deleted ...] text from briefly appearing when dismissing the modal. + if (!isTransactionDeleted.current) { + return; + } + + deleteTransaction(); + }; + }, [deleteTransaction]); // Where to go back after deleting the transaction and its report. const navigateToTargetUrl = useCallback(() => { @@ -818,8 +836,8 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { if (caseID === CASES.DEFAULT) { urlToNavigateBack = Task.getNavigationUrlAfterTaskDelete(report); - Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); if (urlToNavigateBack) { + Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); Navigation.goBack(urlToNavigateBack as Route); } else { Navigation.dismissModal(); @@ -837,34 +855,15 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { } else { urlToNavigateBack = IOU.getNavigationUrlAfterMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); } - Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, urlToNavigateBack); if (!urlToNavigateBack) { Navigation.dismissModal(); } else { + Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); } }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView, setIsDeleteModalVisible, isTransactionDeleted]); - const deleteTransaction = useCallback(() => { - if (caseID === CASES.DEFAULT) { - Task.deleteTask(report); - return; - } - - if (!requestParentReportAction) { - return; - } - - const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); - - if (isTrackExpense) { - IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); - } else { - IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); - } - }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]); - const mentionReportContextValue = useMemo(() => ({currentReportID: report.reportID, exactlyMatch: true}), [report.reportID]); return ( diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 3d1490df03d1..7157d9d5afa6 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -1,6 +1,7 @@ import {PortalHost} from '@gorhom/portal'; import {useIsFocused} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; +import lodashDefer from 'lodash/defer'; import lodashIsEqual from 'lodash/isEqual'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {FlatList, ViewStyle} from 'react-native'; @@ -53,9 +54,6 @@ import ReportActionsView from './report/ReportActionsView'; import ReportFooter from './report/ReportFooter'; import type {ActionListContextType, ReactionListRef, ScrollPosition} from './ReportScreenContext'; import {ActionListContext, ReactionListContext} from './ReportScreenContext'; -import Onyx from 'react-native-onyx'; -import lodashDefer from 'lodash/defer'; - type ReportScreenNavigationProps = StackScreenProps; @@ -351,17 +349,19 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro // Schedule the code to run after the current call stack is cleared, // ensuring all updates are processed before hide the skeleton lodashDefer(() => { - Onyx.merge(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, null); + Report.clearDeleteTransactionNavigateBackUrl(); }); - }, [isFocused]); + }, [isFocused, deleteTransactionNavigateBackUrl]); + + const isLoading = isLoadingApp ?? (!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty()); - const isLoading = isLoadingApp ?? ((!reportIDFromRoute || (!isSidebarLoaded && !isInNarrowPaneModal) || PersonalDetailsUtils.isPersonalDetailsEmpty())); const shouldShowSkeleton = (isLinkingToMessage && !isLinkedMessagePageReady) || (!isLinkingToMessage && !isInitialPageReady) || isEmptyObject(reportOnyx) || isLoadingReportOnyx || !isCurrentReportLoadedFromOnyx || + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (deleteTransactionNavigateBackUrl && ReportUtils.getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID) || isLoading; From 164a73a73e8f49abebb17a5490740640dcc35092 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 14 Nov 2024 19:31:22 +0800 Subject: [PATCH 067/312] 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 068/312] 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 a343297798fa9425fc4e2767f4a3985043939422 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 15 Nov 2024 09:38:11 +0700 Subject: [PATCH 069/312] Ensure report skeleton hide after interaction and update completed --- src/pages/home/ReportScreen.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 7157d9d5afa6..bf2ca0488f6c 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -346,10 +346,11 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro if (!isFocused || !deleteTransactionNavigateBackUrl) { return; } - // Schedule the code to run after the current call stack is cleared, - // ensuring all updates are processed before hide the skeleton - lodashDefer(() => { - Report.clearDeleteTransactionNavigateBackUrl(); + // Clear the URL after all interactions are processed to ensure all updates are completed before hiding the skeleton + InteractionManager.runAfterInteractions(() => { + requestAnimationFrame(() => { + Report.clearDeleteTransactionNavigateBackUrl(); + }); }); }, [isFocused, deleteTransactionNavigateBackUrl]); From e41e18212dd320b54b4c9a42993fd6e505b7de72 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 15 Nov 2024 09:46:00 +0700 Subject: [PATCH 070/312] Remove unused import --- src/pages/home/ReportScreen.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index bf2ca0488f6c..e0e9286b3a5d 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -1,7 +1,6 @@ import {PortalHost} from '@gorhom/portal'; import {useIsFocused} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; -import lodashDefer from 'lodash/defer'; import lodashIsEqual from 'lodash/isEqual'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {FlatList, ViewStyle} from 'react-native'; From 7123b2ff987945f2abf46be661ad1b7d1cbb1397 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 15 Nov 2024 11:06:37 +0700 Subject: [PATCH 071/312] Move navigateToTargetUrl to onModalHide, fix potential bug, remove unnecessary code --- src/libs/actions/IOU.ts | 2 +- src/pages/ReportDetailsPage.tsx | 42 +++++++++++++++++---------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d81b3f0b48fe..18f849b3c6ea 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5694,7 +5694,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor transactionViolations, iouReport, reportPreviewAction, - } = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView); + } = prepareToCleanUpMoneyRequest(transactionID, reportAction); const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index be4e79916c3a..9a09df29f78c 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -829,31 +829,29 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { // Where to go back after deleting the transaction and its report. const navigateToTargetUrl = useCallback(() => { - setIsDeleteModalVisible(false); - isTransactionDeleted.current = true; - let urlToNavigateBack: string | undefined; - if (caseID === CASES.DEFAULT) { - urlToNavigateBack = Task.getNavigationUrlAfterTaskDelete(report); - if (urlToNavigateBack) { - Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); - Navigation.goBack(urlToNavigateBack as Route); - } else { - Navigation.dismissModal(); + if (!isTransactionDeleted.current) { + if (caseID === CASES.DEFAULT) { + urlToNavigateBack = Task.getNavigationUrlAfterTaskDelete(report); + if (urlToNavigateBack) { + Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); + Navigation.goBack(urlToNavigateBack as Route); + } else { + Navigation.dismissModal(); + } + return; } return; } - if (!requestParentReportAction) { - return; - } - - const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); - if (isTrackExpense) { - urlToNavigateBack = IOU.getNavigationUrlAfterTrackExpenseDelete(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); - } else { - urlToNavigateBack = IOU.getNavigationUrlAfterMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); + if (!isEmptyObject(requestParentReportAction)) { + const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + if (isTrackExpense) { + urlToNavigateBack = IOU.getNavigationUrlAfterTrackExpenseDelete(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); + } else { + urlToNavigateBack = IOU.getNavigationUrlAfterMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); + } } if (!urlToNavigateBack) { @@ -955,13 +953,17 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { { + setIsDeleteModalVisible(false); + isTransactionDeleted.current = true; + }} onCancel={() => setIsDeleteModalVisible(false)} prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} danger shouldEnableNewFocusManagement + onModalHide={navigateToTargetUrl} /> Date: Fri, 15 Nov 2024 12:58:21 +0700 Subject: [PATCH 072/312] lint --- src/pages/ReportDetailsPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 9a09df29f78c..9515ba583751 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -860,7 +860,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); } - }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView, setIsDeleteModalVisible, isTransactionDeleted]); + }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView, isTransactionDeleted]); const mentionReportContextValue = useMemo(() => ({currentReportID: report.reportID, exactlyMatch: true}), [report.reportID]); @@ -953,7 +953,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { { + onConfirm={() => { setIsDeleteModalVisible(false); isTransactionDeleted.current = true; }} From 6c96672536bbce89a642620b85ad1416b6ad34c8 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 15 Nov 2024 13:06:07 +0700 Subject: [PATCH 073/312] Refactor for more accurate function name --- src/components/MoneyReportHeader.tsx | 2 +- src/libs/ReportUtils.ts | 4 ++-- src/pages/ReportDetailsPage.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 2dc809f9ce68..2c1b11501494 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -500,7 +500,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea isVisible={isDeleteRequestModalVisible} onConfirm={deleteTransaction} onCancel={() => setIsDeleteRequestModalVisible(false)} - onModalHide={() => ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current)} + onModalHide={() => ReportUtils.navigateBackOnDeleteTransaction(navigateBackToAfterDelete.current)} prompt={translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ee9b303cb7d4..e993f5096c16 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4198,7 +4198,7 @@ function goBackToDetailsPage(report: OnyxEntry, backTo?: string) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report?.reportID ?? '-1', backTo)); } -function navigateBackAfterDeleteTransaction(backRoute: Route | undefined, isFromRHP?: boolean) { +function navigateBackOnDeleteTransaction(backRoute: Route | undefined, isFromRHP?: boolean) { if (!backRoute) { return; } @@ -8675,7 +8675,7 @@ export { canWriteInReport, navigateToDetailsPage, navigateToPrivateNotes, - navigateBackAfterDeleteTransaction, + navigateBackOnDeleteTransaction as navigateBackOnDeleteTransaction, parseReportRouteParams, parseReportActionHtmlToText, requiresAttentionFromCurrentUser, diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 9515ba583751..0d9b6dc626b8 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -858,7 +858,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { Navigation.dismissModal(); } else { Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); - ReportUtils.navigateBackAfterDeleteTransaction(urlToNavigateBack as Route, true); + ReportUtils.navigateBackOnDeleteTransaction(urlToNavigateBack as Route, true); } }, [caseID, iouTransactionID, moneyRequestReport?.reportID, report, requestParentReportAction, isSingleTransactionView, isTransactionDeleted]); From f49c46f746792e7edf6ae99ae68c5412ffced446 Mon Sep 17 00:00:00 2001 From: gijoe0295 Date: Sat, 16 Nov 2024 02:25:34 +0700 Subject: [PATCH 074/312] 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 075/312] 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 076/312] 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 077/312] 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 078/312] 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 079/312] 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 080/312] 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 081/312] 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 082/312] 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 083/312] 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 964626bb2bd862fe415c56276fbe3e8265895fc6 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Tue, 19 Nov 2024 11:07:59 +0700 Subject: [PATCH 084/312] 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 c0450dca283f..2a76e3cd1dd4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8694,7 +8694,7 @@ export { canWriteInReport, navigateToDetailsPage, navigateToPrivateNotes, - navigateBackOnDeleteTransaction as navigateBackOnDeleteTransaction, + navigateBackOnDeleteTransaction, parseReportRouteParams, parseReportActionHtmlToText, requiresAttentionFromCurrentUser, From b2b933d38a0f598970b078187353be788f5d3dfe Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 19 Nov 2024 10:55:51 +0530 Subject: [PATCH 085/312] 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 67eb8271724369aaa02800d51eb04e0d9d5e7558 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Tue, 19 Nov 2024 13:20:22 +0700 Subject: [PATCH 086/312] Remove unnecessary code --- src/libs/actions/Task.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 75e003655721..749918724d4e 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1138,17 +1138,6 @@ function deleteTask(report: OnyxEntry) { API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); Report.notifyNewAction(report.reportID, currentUserAccountID); - // if (shouldDeleteTaskReport) { - // Navigation.goBack(); - // if (parentReport?.reportID) { - // return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID); - // } - // const mostRecentReportID = Report.getMostRecentReportID(report); - // if (mostRecentReportID) { - // return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); - // } - // } - const urlToNavigateBack = getNavigationUrlAfterTaskDelete(report); if (urlToNavigateBack) { Navigation.goBack(); From 62aaa285fed1116ac798ffddccdaa85f90e5d830 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Tue, 19 Nov 2024 14:07:45 +0700 Subject: [PATCH 087/312] remove unnecessary export --- src/libs/actions/Task.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 749918724d4e..5f892aeff32e 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1255,7 +1255,6 @@ export { canModifyTask, canActionTask, setNewOptimisticAssignee, - getParentReport, getNavigationUrlAfterTaskDelete, }; From 051d3cca74b8a0fbfd75dbe62811f05b7cbd4d3b Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Tue, 19 Nov 2024 14:16:16 +0700 Subject: [PATCH 088/312] Adjust tests after change deleting approach from set to merge --- tests/actions/IOUTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 1c26390f7b09..85cf2b81c01b 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2217,7 +2217,7 @@ describe('actions/IOU', () => { }); }); - expect(report).toBeFalsy(); + expect(report?.reportID).toBeFalsy(); mockFetch?.resume?.(); // Then After resuming fetch, the report for the given thread ID still does not exist @@ -2232,7 +2232,7 @@ describe('actions/IOU', () => { }); }); - expect(report).toBeFalsy(); + expect(report?.reportID).toBeFalsy(); }); it('delete the transaction thread if there are only changelogs (i.e. MODIFIED_EXPENSE actions) in the thread', async () => { From 088777930cfeb8a4cc0fba447a91e57a26f1f582 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Tue, 19 Nov 2024 14:20:38 +0700 Subject: [PATCH 089/312] Adjust more test --- tests/actions/IOUTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 85cf2b81c01b..1ac0e85beca0 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -2339,7 +2339,7 @@ describe('actions/IOU', () => { }); }); - expect(report).toBeFalsy(); + expect(report?.reportID).toBeFalsy(); }); it('does not delete the transaction thread if there are visible comments in the thread', async () => { From ff1669966969ccf8bcbb5320ec0b033eed222f00 Mon Sep 17 00:00:00 2001 From: c3024 Date: Tue, 19 Nov 2024 17:35:34 +0530 Subject: [PATCH 090/312] 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 091/312] 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 092/312] 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 093/312] 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 094/312] 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 095/312] 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 21:16:19 +0700 Subject: [PATCH 096/312] remove renaming ws category --- src/libs/Middleware/SaveResponseInOnyx.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Middleware/SaveResponseInOnyx.ts b/src/libs/Middleware/SaveResponseInOnyx.ts index 677939157e3b..12c1931b0199 100644 --- a/src/libs/Middleware/SaveResponseInOnyx.ts +++ b/src/libs/Middleware/SaveResponseInOnyx.ts @@ -10,7 +10,6 @@ const requestsToIgnoreLastUpdateID: string[] = [ SIDE_EFFECT_REQUEST_COMMANDS.RECONNECT_APP, WRITE_COMMANDS.CLOSE_ACCOUNT, WRITE_COMMANDS.DELETE_MONEY_REQUEST, - WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, SIDE_EFFECT_REQUEST_COMMANDS.GET_MISSING_ONYX_MESSAGES, ]; From 4d847b059165e7c764db6ada1e28b3257d8c7741 Mon Sep 17 00:00:00 2001 From: c3024 Date: Thu, 21 Nov 2024 20:15:08 +0530 Subject: [PATCH 097/312] 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 098/312] 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 099/312] 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 100/312] 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 101/312] 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 78f8f71a22fbb6c12d0bb7d366bacee842a3e737 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 22 Nov 2024 13:15:43 +0700 Subject: [PATCH 102/312] Update src/ONYXKEYS.ts Co-authored-by: Carlos Martins --- src/ONYXKEYS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 9f39c7adef13..7310f4ab7340 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -219,7 +219,7 @@ const ONYXKEYS = { /** The NVP containing all information related to educational tooltip in workspace chat */ NVP_WORKSPACE_TOOLTIP: 'workspaceTooltip', - /** The NVP contain url to back after deleting transaction */ + /** The NVP containing the target url to navigate to when deleting a transaction */ NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL: 'nvp_deleteTransactionNavigateBackURL', /** Whether to show save search rename tooltip */ From df0100f4a6e0cb5b1feed3d18cb88c8d150f3f7c Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 22 Nov 2024 13:18:01 +0700 Subject: [PATCH 103/312] Refactor name --- src/libs/actions/IOU.ts | 10 +++++----- src/pages/ReportDetailsPage.tsx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 210d9f544686..4706578c16e1 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5513,7 +5513,7 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT * @param isSingleTransactionView - whether we are in the transaction thread report * @returns The URL to navigate to */ -function getNavigationUrlAfterMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): string | undefined { +function getNavigationUrlOnMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): string | undefined { const {shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport} = prepareToCleanUpMoneyRequest(transactionID, reportAction); // Determine which report to navigate back to @@ -5541,7 +5541,7 @@ function getNavigationUrlAfterTrackExpenseDelete(chatReportID: string, transacti // If not a self DM, handle it as a regular money request if (!ReportUtils.isSelfDM(chatReport)) { - return getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); + return getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); } const transactionThreadID = reportAction.childReportID; @@ -5576,7 +5576,7 @@ function cleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repo reportPreviewAction, } = prepareToCleanUpMoneyRequest(transactionID, reportAction); - const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); + const urlToNavigateBack = getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); // build Onyx data // Onyx operations to delete the transaction, update the IOU report action and chat report action @@ -5714,7 +5714,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor reportPreviewAction, } = prepareToCleanUpMoneyRequest(transactionID, reportAction); - const urlToNavigateBack = getNavigationUrlAfterMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); + const urlToNavigateBack = getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView); // STEP 2: Build Onyx data // The logic mostly resembles the cleanUpMoneyRequest function @@ -8443,7 +8443,7 @@ export { updateLastLocationPermissionPrompt, resolveDuplicates, getIOUReportActionToApproveOrPay, - getNavigationUrlAfterMoneyRequestDelete, + getNavigationUrlOnMoneyRequestDelete, getNavigationUrlAfterTrackExpenseDelete, }; export type {GPSPoint as GpsPoint, IOURequestType}; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 0d9b6dc626b8..5893ef4a76e9 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -850,7 +850,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { if (isTrackExpense) { urlToNavigateBack = IOU.getNavigationUrlAfterTrackExpenseDelete(moneyRequestReport?.reportID ?? '', iouTransactionID, requestParentReportAction, isSingleTransactionView); } else { - urlToNavigateBack = IOU.getNavigationUrlAfterMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); + urlToNavigateBack = IOU.getNavigationUrlOnMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); } } From 0ea2bbaf70cbe165a3068c928bebf40ff0e8ff3c Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 22 Nov 2024 13:18:44 +0700 Subject: [PATCH 104/312] remove unnecessary code --- src/libs/actions/IOU.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 4706578c16e1..d566fc2e5a5c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5978,10 +5978,6 @@ function deleteTrackExpense(chatReportID: string, transactionID: string, reportA // STEP 7: Navigate the user depending on which page they are on and which resources were deleted return urlToNavigateBack; - // if (isSingleTransactionView && shouldDeleteTransactionThread) { - // // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. - // return ROUTES.REPORT_WITH_ID.getRoute(chatReport?.reportID ?? '-1'); - // } } /** From 9cedf925aec661738bf33ef2affd94f9ce575422 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 22 Nov 2024 13:19:34 +0700 Subject: [PATCH 105/312] Update src/pages/ReportDetailsPage.tsx Co-authored-by: Carlos Martins --- src/pages/ReportDetailsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 5893ef4a76e9..19362fb312e8 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -813,7 +813,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { } }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]); - // A flag to indicate whether the user choose to delete the transaction or not + // A flag to indicate whether the user chose to delete the transaction or not const isTransactionDeleted = useRef(false); useEffect(() => { From 65199e2fb326c2dd920904aad0e21db4e1b08e1e Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 22 Nov 2024 13:19:42 +0700 Subject: [PATCH 106/312] Update src/pages/ReportDetailsPage.tsx Co-authored-by: Carlos Martins --- src/pages/ReportDetailsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 19362fb312e8..71ca94709982 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -827,7 +827,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { }; }, [deleteTransaction]); - // Where to go back after deleting the transaction and its report. + // Where to navigate back to after deleting the transaction and its report. const navigateToTargetUrl = useCallback(() => { let urlToNavigateBack: string | undefined; From 213fe69f82330b5db8f22bba6e785af14ffe14cf Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 22 Nov 2024 13:21:15 +0700 Subject: [PATCH 107/312] Refactor --- src/libs/actions/Task.ts | 6 +++--- src/pages/ReportDetailsPage.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 5f892aeff32e..20ea7b2b1418 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -964,7 +964,7 @@ function getParentReport(report: OnyxEntry): OnyxEntry): string | undefined { +function getNavigationUrlOnTaskDelete(report: OnyxEntry): string | undefined { if (!report) { return undefined; } @@ -1138,7 +1138,7 @@ function deleteTask(report: OnyxEntry) { API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); Report.notifyNewAction(report.reportID, currentUserAccountID); - const urlToNavigateBack = getNavigationUrlAfterTaskDelete(report); + const urlToNavigateBack = getNavigationUrlOnTaskDelete(report); if (urlToNavigateBack) { Navigation.goBack(); return urlToNavigateBack; @@ -1255,7 +1255,7 @@ export { canModifyTask, canActionTask, setNewOptimisticAssignee, - getNavigationUrlAfterTaskDelete, + getNavigationUrlOnTaskDelete, }; export type {PolicyValue, Assignee, ShareDestination}; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 5893ef4a76e9..a2af7a71928b 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -833,7 +833,7 @@ function ReportDetailsPage({policies, report, route}: ReportDetailsPageProps) { if (!isTransactionDeleted.current) { if (caseID === CASES.DEFAULT) { - urlToNavigateBack = Task.getNavigationUrlAfterTaskDelete(report); + urlToNavigateBack = Task.getNavigationUrlOnTaskDelete(report); if (urlToNavigateBack) { Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); Navigation.goBack(urlToNavigateBack as Route); From 51daaa0a6f7b46b26f55adda2ca1c23dd00588bc Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 22 Nov 2024 13:56:16 +0700 Subject: [PATCH 108/312] resolve typecheck error --- src/libs/actions/IOU.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7d1d7623ff55..1e042cb2d371 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -60,6 +60,7 @@ import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Attendee, Participant, Split} from '@src/types/onyx/IOU'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; @@ -5503,7 +5504,7 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT * @param isSingleTransactionView - whether we are in the transaction thread report * @returns The URL to navigate to */ -function getNavigationUrlOnMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): string | undefined { +function getNavigationUrlOnMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): Route | undefined { const {shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport} = prepareToCleanUpMoneyRequest(transactionID, reportAction); // Determine which report to navigate back to @@ -5526,7 +5527,7 @@ function getNavigationUrlOnMoneyRequestDelete(transactionID: string, reportActio * @param isSingleTransactionView - Whether we're in single transaction view * @returns The URL to navigate to */ -function getNavigationUrlAfterTrackExpenseDelete(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): string | undefined { +function getNavigationUrlAfterTrackExpenseDelete(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): Route | undefined { const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; // If not a self DM, handle it as a regular money request From d35de24756165c32dc6edf48a6831563fd3fcb01 Mon Sep 17 00:00:00 2001 From: daledah Date: Fri, 22 Nov 2024 14:15:15 +0700 Subject: [PATCH 109/312] 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 110/312] 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 5e5026d0f9b6bb7ad0e4e78007278d39f2ec27b1 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sat, 23 Nov 2024 08:51:18 +0700 Subject: [PATCH 111/312] Add comment why use merge instead of set when deleting report --- src/libs/actions/IOU.ts | 8 ++++++++ src/libs/actions/Report.ts | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 82666e12fce8..a25a5d3032b6 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1784,6 +1784,8 @@ function getDeleteTrackExpenseInformation( if (shouldDeleteTransactionThread) { optimisticData.push( + // Use merge instead of set to avoid deleting the report too quickly, which could cause a brief "not found" page to appear. + // The remaining parts of the report object will be removed after the API call is successful. { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, @@ -1836,6 +1838,8 @@ function getDeleteTrackExpenseInformation( }, ]; + // Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response. + // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. if (shouldDeleteTransactionThread && transactionThread) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -5768,6 +5772,8 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor if (shouldDeleteTransactionThread) { optimisticData.push( + // Use merge instead of set to avoid deleting the report too quickly, which could cause a brief "not found" page to appear. + // The remaining parts of the report object will be removed after the API call is successful. { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, @@ -5878,6 +5884,8 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, ]; + // Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response. + // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. if (shouldDeleteTransactionThread && transactionThread) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ef21096cb804..4fcfb0dde138 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2915,6 +2915,8 @@ function leaveGroupChat(reportID: string) { }); } + // Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response. + // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, From 5ddba66c78d1c8a39b75a2bb30b0d454fb54fad1 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sat, 23 Nov 2024 08:53:30 +0700 Subject: [PATCH 112/312] prettier --- src/libs/actions/IOU.ts | 4 ++-- src/libs/actions/Report.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index a25a5d3032b6..d4b6595600c0 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1839,7 +1839,7 @@ function getDeleteTrackExpenseInformation( ]; // Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response. - // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. + // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. if (shouldDeleteTransactionThread && transactionThread) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -5885,7 +5885,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor ]; // Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response. - // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. + // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. if (shouldDeleteTransactionThread && transactionThread) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4fcfb0dde138..5ce3245aa827 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2916,7 +2916,7 @@ function leaveGroupChat(reportID: string) { } // Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response. - // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. + // This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function. const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, From a25d3fb0efa91726bddb4c8ea87ef5de3949c0ab Mon Sep 17 00:00:00 2001 From: abzokhattab Date: Sat, 23 Nov 2024 04:01:34 +0100 Subject: [PATCH 113/312] 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 114/312] 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 115/312] 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 116/312] 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 7a4cb93cb004aa85a69962caeaeeba9d3fa37ceb Mon Sep 17 00:00:00 2001 From: daledah Date: Mon, 25 Nov 2024 15:45:22 +0700 Subject: [PATCH 117/312] fix: Header - Approve button appears briefly when it shouldn't --- src/libs/actions/IOU.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 10eee66428e8..b2e173ed0b26 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7190,6 +7190,8 @@ function submitReport(expenseReport: OnyxTypes.Report) { const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails?.accountID : undefined; const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED); + const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport); + const managerID = approvalChain.at(0); const optimisticData: OnyxUpdate[] = !isSubmitAndClosePolicy ? [ @@ -7208,6 +7210,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, value: { ...expenseReport, + managerID, lastMessageText: ReportActionsUtils.getReportActionText(optimisticSubmittedReportAction), lastMessageHtml: ReportActionsUtils.getReportActionHtml(optimisticSubmittedReportAction), stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, From bcdb8441eab07ba294d2c2b81f128cff64be52d6 Mon Sep 17 00:00:00 2001 From: c3024 Date: Mon, 25 Nov 2024 14:29:33 +0530 Subject: [PATCH 118/312] 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 119/312] 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 120/312] 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 121/312] 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 1b89a1f6f1e89dce8d7dbdc468bf89ec7d36a231 Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:53:42 +0700 Subject: [PATCH 122/312] Remove unused imports --- src/libs/actions/Policy/Category.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index 07f20c834fc5..ca9d6a623569 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -33,7 +33,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, PolicyCategories, PolicyCategory, RecentlyUsedCategories, Report} from '@src/types/onyx'; -import type {ApprovalRule, CustomUnit, ExpenseRule, MccGroup} from '@src/types/onyx/Policy'; +import type {ApprovalRule, ExpenseRule, MccGroup} from '@src/types/onyx/Policy'; import type {PolicyCategoryExpenseLimitType} from '@src/types/onyx/PolicyCategory'; import type {OnyxData} from '@src/types/onyx/Request'; From feffdeedf02fc84acadd54972a0d50baf3755711 Mon Sep 17 00:00:00 2001 From: daledah Date: Mon, 25 Nov 2024 18:43:16 +0700 Subject: [PATCH 123/312] fix 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 b2e173ed0b26..6710823df16a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7191,7 +7191,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID, adminAccountID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED); const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport); - const managerID = approvalChain.at(0); + const managerID = approvalChain.length ? Number(approvalChain.at(0)) : undefined; const optimisticData: OnyxUpdate[] = !isSubmitAndClosePolicy ? [ From 95f85471072c6c3c14b17278447f6dc91c30d2a7 Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Mon, 25 Nov 2024 22:19:18 +0700 Subject: [PATCH 124/312] 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 125/312] 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 126/312] 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 127/312] 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 128/312] 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 129/312] 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 130/312] 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 131/312] 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 132/312] 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 133/312] 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 134/312] 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 135/312] 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 136/312] 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 137/312] 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 138/312] 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 139/312] 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 140/312] 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 141/312] 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 142/312] 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 143/312] 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 144/312] 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 145/312] 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 146/312] 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 147/312] 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 148/312] 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 149/312] 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 150/312] 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 151/312] 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 152/312] 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 df821a91c55700aeb64a0eb1aaf93a71524788a4 Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 26 Nov 2024 15:42:03 -0500 Subject: [PATCH 153/312] Remove beta --- src/CONST.ts | 1 - src/libs/Permissions.ts | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 81911d3f996f..c739861536bd 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -668,7 +668,6 @@ const CONST = { BETAS: { ALL: 'all', DEFAULT_ROOMS: 'defaultRooms', - DUPE_DETECTION: 'dupeDetection', P2P_DISTANCE_REQUESTS: 'p2pDistanceRequests', SPOTNANA_TRAVEL: 'spotnanaTravel', REPORT_FIELDS_FEATURE: 'reportFieldsFeature', diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 1d3428dfcfce..4a7bba3932a3 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -13,10 +13,6 @@ function canUseDefaultRooms(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.DEFAULT_ROOMS) || canUseAllBetas(betas); } -function canUseDupeDetection(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.DUPE_DETECTION) || canUseAllBetas(betas); -} - function canUseSpotnanaTravel(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.SPOTNANA_TRAVEL) || canUseAllBetas(betas); } @@ -49,7 +45,6 @@ function canUseLinkPreviews(): boolean { export default { canUseDefaultRooms, canUseLinkPreviews, - canUseDupeDetection, canUseSpotnanaTravel, canUseNetSuiteUSATax, canUseCombinedTrackSubmit, From e791adc0a191047801129cb919a4c85ec55f7a85 Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 26 Nov 2024 15:43:19 -0500 Subject: [PATCH 154/312] Missed a couple of spots --- src/libs/TransactionUtils/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index e7eea51c7755..672b9b739287 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -826,10 +826,6 @@ function getRecentTransactions(transactions: Record, size = 2): * @param checkDismissed - whether to check if the violation has already been dismissed as well */ function isDuplicate(transactionID: string, checkDismissed = false): boolean { - if (!Permissions.canUseDupeDetection(allBetas ?? [])) { - return false; - } - const hasDuplicatedViolation = !!allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]?.some( (violation: TransactionViolation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION, ); @@ -892,7 +888,7 @@ function hasWarningTypeViolation(transactionID: string, transactionViolations: O ) ?? []; const hasOnlyDupeDetectionViolation = warningTypeViolations?.every((violation: TransactionViolation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION); - if (!Permissions.canUseDupeDetection(allBetas ?? []) && hasOnlyDupeDetectionViolation) { + if (hasOnlyDupeDetectionViolation) { return false; } From e363a396c28f11180c2d6e16c27ccec6d8165f06 Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 26 Nov 2024 15:51:03 -0500 Subject: [PATCH 155/312] Remove ony key --- src/libs/TransactionUtils/index.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 672b9b739287..4a0c05ddec0c 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -14,7 +14,6 @@ import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import {toLocaleDigit} from '@libs/LocaleDigitUtils'; import * as Localize from '@libs/Localize'; import * as NumberUtils from '@libs/NumberUtils'; -import Permissions from '@libs/Permissions'; import {getCleanedTagName, getDistanceRateCustomUnitRate} from '@libs/PolicyUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; // eslint-disable-next-line import/no-cycle @@ -73,12 +72,6 @@ Onyx.connect({ }, }); -let allBetas: OnyxEntry; -Onyx.connect({ - key: ONYXKEYS.BETAS, - callback: (value) => (allBetas = value), -}); - function isDistanceRequest(transaction: OnyxEntry): boolean { // This is used during the expense creation flow before the transaction has been saved to the server if (lodashHas(transaction, 'iouRequestType')) { From 3f375387b1881fe524e8b80cc3d1af4a456466de Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 26 Nov 2024 15:54:09 -0500 Subject: [PATCH 156/312] Remove unused import --- src/libs/TransactionUtils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 4a0c05ddec0c..460a329227c7 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -24,7 +24,7 @@ import type {IOURequestType} from '@userActions/IOU'; import CONST from '@src/CONST'; import type {IOUType} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Beta, OnyxInputOrEntry, Policy, RecentWaypoint, Report, ReviewDuplicates, TaxRate, TaxRates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {OnyxInputOrEntry, Policy, RecentWaypoint, Report, ReviewDuplicates, TaxRate, TaxRates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; import type {Attendee} from '@src/types/onyx/IOU'; import type {SearchPolicy, SearchReport} from '@src/types/onyx/SearchResults'; import type {Comment, Receipt, TransactionChanges, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; From 6990c38208bcf95be85d2006ba1d994c5f4ce655 Mon Sep 17 00:00:00 2001 From: Kevin Brian Bader Date: Tue, 26 Nov 2024 14:26:08 -0800 Subject: [PATCH 157/312] Remove payment card form Zip Code validation --- src/components/AddPaymentCard/PaymentCardForm.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 5aaa23b238f7..1fe4167b889c 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -165,10 +165,6 @@ function PaymentCardForm({ errors.addressStreet = translate(label.error.addressStreet); } - if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { - errors.addressZipCode = translate(label.error.addressZipCode); - } - if (!values.acceptTerms) { errors.acceptTerms = translate('common.error.acceptTerms'); } From 0e29ba13a5b15ca2567844e106fc6dbeba578699 Mon Sep 17 00:00:00 2001 From: Kevin Brian Bader Date: Tue, 26 Nov 2024 15:44:14 -0800 Subject: [PATCH 158/312] Removed inputMode to allow non-numeric Zip Codes (UK) --- src/components/AddPaymentCard/PaymentCardForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 1fe4167b889c..cee7edb3af61 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -282,7 +282,6 @@ function PaymentCardForm({ label={translate('common.zip')} aria-label={translate('common.zip')} role={CONST.ROLE.PRESENTATION} - inputMode={CONST.INPUT_MODE.NUMERIC} maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} containerStyles={[styles.mt5]} /> From be746b42581559afd8b0be2754a0bd469aab9452 Mon Sep 17 00:00:00 2001 From: jacobkim9881 Date: Wed, 27 Nov 2024 12:10:44 +0900 Subject: [PATCH 159/312] 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 11:02:25 +0700 Subject: [PATCH 160/312] Update MCC group for failure data --- src/libs/actions/Policy/Category.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index ca9d6a623569..873a8bfabc56 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -650,7 +650,7 @@ function renamePolicyCategory(policyID: string, policyCategory: {oldName: string rules: { approvalRules, }, - mccGroup: updatedMccGroupWithClearedPendingAction, + mccGroup: updatedMccGroup, }, }, ], From 3dff8e7f444397193683ada28a921ec1a73c331c Mon Sep 17 00:00:00 2001 From: I Nyoman Jyotisa Date: Wed, 27 Nov 2024 12:36:53 +0800 Subject: [PATCH 161/312] 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 162/312] 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 163/312] 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 a5e97d0ceba366fce0b22a7ce014a84bd028f3dc Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:46:46 +1300 Subject: [PATCH 164/312] Fix camera not working on the second time opening it --- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index bc8622072226..5cea87eb4475 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -167,7 +167,7 @@ function IOURequestStepScan({ setCameraPermissionState('denied'); }); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, []); + }, [videoConstraints]); useEffect(() => { if (!Browser.isMobile() || !isTabActive) { From d9922bfec235bf8ed1a399007780fb50f3c8adaa Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:01:13 +1300 Subject: [PATCH 165/312] Remove unnecessary eslint disabling comment --- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 5cea87eb4475..c2c81c19b992 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -166,7 +166,6 @@ function IOURequestStepScan({ setVideoConstraints(defaultConstraints); setCameraPermissionState('denied'); }); - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [videoConstraints]); useEffect(() => { From 984c35642279f66e1186f29d3d6a5b34b4b65c87 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 27 Nov 2024 10:32:58 +0100 Subject: [PATCH 166/312] 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 167/312] 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 168/312] 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 169/312] 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 170/312] 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 171/312] 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 173ed3cc5b3fc8c2325e8c21c77c48591018261b Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Wed, 27 Nov 2024 16:30:45 -0700 Subject: [PATCH 172/312] play sound when paying on search --- src/libs/API/types.ts | 4 ++-- src/libs/actions/Search.ts | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index d7258f1dd49e..a9e0a7646d57 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -315,7 +315,6 @@ const WRITE_COMMANDS = { DELETE_MONEY_REQUEST_ON_SEARCH: 'DeleteMoneyRequestOnSearch', HOLD_MONEY_REQUEST_ON_SEARCH: 'HoldMoneyRequestOnSearch', APPROVE_MONEY_REQUEST_ON_SEARCH: 'ApproveMoneyRequestOnSearch', - PAY_MONEY_REQUEST_ON_SEARCH: 'PayMoneyRequestOnSearch', UNHOLD_MONEY_REQUEST_ON_SEARCH: 'UnholdMoneyRequestOnSearch', REQUEST_REFUND: 'User_RefundPurchase', UPDATE_NETSUITE_SUBSIDIARY: 'UpdateNetSuiteSubsidiary', @@ -763,7 +762,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH]: Parameters.DeleteMoneyRequestOnSearchParams; [WRITE_COMMANDS.HOLD_MONEY_REQUEST_ON_SEARCH]: Parameters.HoldMoneyRequestOnSearchParams; [WRITE_COMMANDS.APPROVE_MONEY_REQUEST_ON_SEARCH]: Parameters.ApproveMoneyRequestOnSearchParams; - [WRITE_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH]: Parameters.PayMoneyRequestOnSearchParams; [WRITE_COMMANDS.UNHOLD_MONEY_REQUEST_ON_SEARCH]: Parameters.UnholdMoneyRequestOnSearchParams; [WRITE_COMMANDS.REQUEST_REFUND]: null; @@ -1025,6 +1023,7 @@ const SIDE_EFFECT_REQUEST_COMMANDS = { DISCONNECT_AS_DELEGATE: 'DisconnectAsDelegate', COMPLETE_HYBRID_APP_ONBOARDING: 'CompleteHybridAppOnboarding', CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP: 'ConnectPolicyToQuickbooksDesktop', + PAY_MONEY_REQUEST_ON_SEARCH: 'PayMoneyRequestOnSearch', } as const; type SideEffectRequestCommand = ValueOf; @@ -1045,6 +1044,7 @@ type SideEffectRequestCommandParameters = { [SIDE_EFFECT_REQUEST_COMMANDS.DISCONNECT_AS_DELEGATE]: EmptyObject; [SIDE_EFFECT_REQUEST_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING]: EmptyObject; [SIDE_EFFECT_REQUEST_COMMANDS.CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP]: Parameters.ConnectPolicyToQuickBooksDesktopParams; + [SIDE_EFFECT_REQUEST_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH]: Parameters.PayMoneyRequestOnSearchParams; }; type ApiRequestCommandParameters = WriteCommandParameters & ReadCommandParameters & SideEffectRequestCommandParameters; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 44b5bb7f7ce9..0a91c1dfff81 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -6,12 +6,13 @@ import type {PaymentData, SearchQueryJSON} from '@components/Search/types'; import type {ReportListItemType, TransactionListItemType} from '@components/SelectionList/types'; import * as API from '@libs/API'; import type {ExportSearchItemsToCSVParams} from '@libs/API/parameters'; -import {WRITE_COMMANDS} from '@libs/API/types'; +import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ApiUtils from '@libs/ApiUtils'; import fileDownload from '@libs/fileDownload'; import enhanceParameters from '@libs/Network/enhanceParameters'; import * as ReportUtils from '@libs/ReportUtils'; import {isReportListItemType, isTransactionListItemType} from '@libs/SearchUIUtils'; +import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import FILTER_KEYS from '@src/types/form/SearchAdvancedFiltersForm'; @@ -277,7 +278,15 @@ function payMoneyRequestOnSearch(hash: number, paymentData: PaymentData[], trans const optimisticData: OnyxUpdate[] = createActionLoadingData(true); const finallyData: OnyxUpdate[] = createActionLoadingData(false); - API.write(WRITE_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH, {hash, paymentData: JSON.stringify(paymentData)}, {optimisticData, finallyData}); + // eslint-disable-next-line rulesdir/no-api-side-effects-method + API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH, {hash, paymentData: JSON.stringify(paymentData)}, {optimisticData, finallyData}).then( + (response) => { + if (response?.jsonCode !== CONST.JSON_CODE.SUCCESS) { + return; + } + playSound(SOUNDS.SUCCESS); + }, + ); } function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[]) { From b0cebbc63b2edad778ccad5560a58019b0e75b82 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 28 Nov 2024 10:31:05 +0530 Subject: [PATCH 173/312] 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 174/312] 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 175/312] 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 176/312] 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 177/312] 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 178/312] 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 () => (