From 935591acdbd0538c1a27baf258c00f33b463e54c Mon Sep 17 00:00:00 2001 From: truph01 Date: Fri, 20 Dec 2024 01:11:22 +0700 Subject: [PATCH 001/109] feat: add distance label --- .../DistanceRequest/DistanceRequestFooter.tsx | 1 + .../MapView/MapViewImpl.website.tsx | 32 +++++++++++++++++++ src/components/MapView/MapViewTypes.ts | 2 ++ src/libs/DistanceRequestUtils.ts | 6 ++++ 4 files changed, 41 insertions(+) diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx index 8a4455e02bd6..d25037ec808a 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.tsx +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -114,6 +114,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig waypoints={waypointMarkers} styleURL={CONST.MAPBOX.STYLE_URL} overlayStyle={styles.mapEditView} + distance={TransactionUtils.getDistanceInMeters(transaction, 'km')} /> diff --git a/src/components/MapView/MapViewImpl.website.tsx b/src/components/MapView/MapViewImpl.website.tsx index 611c1b117fbf..31fdf0365a58 100644 --- a/src/components/MapView/MapViewImpl.website.tsx +++ b/src/components/MapView/MapViewImpl.website.tsx @@ -16,6 +16,7 @@ import usePrevious from '@hooks/usePrevious'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import type {GeolocationErrorCallback} from '@libs/getCurrentPosition/getCurrentPosition.types'; import {GeolocationErrorCode} from '@libs/getCurrentPosition/getCurrentPosition.types'; import * as UserLocation from '@userActions/UserLocation'; @@ -42,6 +43,7 @@ const MapViewImpl = forwardRef( directionCoordinates, initialState = {location: CONST.MAPBOX.DEFAULT_COORDINATE, zoom: CONST.MAPBOX.DEFAULT_ZOOM}, interactive = true, + distance, }, ref, ) => { @@ -232,6 +234,20 @@ const MapViewImpl = forwardRef( }; }, [waypoints, directionCoordinates, interactive, currentPosition, initialState.zoom]); + const distanceSymbolCoorinate = useMemo(() => { + const length = directionCoordinates?.length; + // If the array is empty, return undefined + if (!length) { + return undefined; + } + + // Find the index of the middle element + const middleIndex = Math.floor(length / 2); + + // Return the middle element + return directionCoordinates.at(middleIndex); + }, [directionCoordinates]); + return !isOffline && !!accessToken && !!initialViewState ? ( ( )} + {!!distanceSymbolCoorinate && !!distance && ( + + + + {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS)} + {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES)} + + + + )} {waypoints?.map(({coordinate, markerComponent, id}) => { const MarkerComponent = markerComponent; if (utils.areSameCoordinate([coordinate[0], coordinate[1]], [currentPosition?.longitude ?? 0, currentPosition?.latitude ?? 0]) && interactive) { diff --git a/src/components/MapView/MapViewTypes.ts b/src/components/MapView/MapViewTypes.ts index 41170694c9d2..14104f79779b 100644 --- a/src/components/MapView/MapViewTypes.ts +++ b/src/components/MapView/MapViewTypes.ts @@ -22,6 +22,8 @@ type MapViewProps = { onMapReady?: () => void; // Whether the map is interactable or not interactive?: boolean; + + distance?: number; }; type DirectionProps = { diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index fe40ea67f905..31d838af69d6 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -202,6 +202,11 @@ function getDistanceForDisplay( return `${distanceInUnits} ${unitString}`; } +function getDistanceForDisplayLabel(distanceInMeters: number, unit: Unit): string { + const distanceInUnits = getRoundedDistanceInUnits(distanceInMeters, unit); + return `${distanceInUnits} ${unit}`; +} + /** * @param hasRoute Whether the route exists for the distance expense * @param distanceInMeters Distance traveled @@ -394,6 +399,7 @@ export default { getUpdatedDistanceUnit, getRate, getRateByCustomUnitRateID, + getDistanceForDisplayLabel, }; export type {MileageRate}; From e21733748c4604638a5919105692b05f0c6e2936 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Fri, 20 Dec 2024 13:43:29 -0600 Subject: [PATCH 002/109] Update Configure-Netsuite.md --- .../connections/netsuite/Configure-Netsuite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md b/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md index 68bca5228913..3e464f82d723 100644 --- a/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md +++ b/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md @@ -59,7 +59,7 @@ This dictates when reimbursable expenses will export, according to your preferre **Journal Entries:** Non-reimbursable expenses will be posted to the Journal Entries posting account selected in your workspace's connection settings. If you centrally manage your company cards through Domains, you can export expenses from each card to a specific account in NetSuite. -- Expensify Card expenses will always export as Journal Entries, even if you have Expense Reports or Vendor Bills configured for non-reimbursable expenses on the Export tab +- Expensify Card expenses always export as individual, itemized Journal Entries, regardless of Expense Reports or Vendor Bills settings configured for non-reimbursable expenses on the Export tab. - Journal entry forms do not contain a customer column, so it is not possible to export customers or projects with this export option - The credit line and header level classifications are pulled from the employee record From 570156fbaff290b552e2d78ff7a16f2c0f795aaa Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Wed, 15 Jan 2025 17:07:14 +0700 Subject: [PATCH 003/109] refactor TrackExpense function --- src/libs/actions/IOU.ts | 81 ++++++++++++------- .../iou/request/step/IOURequestStepAmount.tsx | 26 +++--- .../step/IOURequestStepConfirmation.tsx | 56 +++++++------ .../step/IOURequestStepScan/index.native.tsx | 74 +++++++++-------- .../request/step/IOURequestStepScan/index.tsx | 74 +++++++++-------- tests/unit/GoogleTagManagerTest.tsx | 51 ++++++------ 6 files changed, 200 insertions(+), 162 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0ac396709b07..a590acec5d48 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -402,6 +402,35 @@ type CreateDistanceRequestInformation = { policyParams?: RequestMoneyPolicyParams; }; +type TrackExpenseTransactionParams = { + amount: number; + currency: string; + created: string | undefined; + merchant: string; + comment: string; + receipt?: Receipt; + category?: string; + tag?: string; + taxCode?: string; + taxAmount?: number; + billable?: boolean; + validWaypoints?: WaypointCollection; + gpsPoints?: GPSPoint; + actionableWhisperReportActionID?: string; + linkedTrackedExpenseReportAction?: OnyxTypes.ReportAction; + linkedTrackedExpenseReportID?: string; + customUnitRateID?: string; +}; + +type CreateTrackExpenseParams = { + report: OnyxTypes.Report; + isDraftPolicy: boolean; + action?: IOUAction; + participantParams: RequestMoneyParticipantParams; + policyParams?: RequestMoneyPolicyParams; + transactionParams: TrackExpenseTransactionParams; +}; + let allPersonalDetails: OnyxTypes.PersonalDetailsList = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, @@ -4154,34 +4183,30 @@ function sendInvoice( /** * Track an expense */ -function trackExpense( - report: OnyxTypes.Report, - amount: number, - currency: string, - created: string, - merchant: string, - payeeEmail: string | undefined, - payeeAccountID: number, - participant: Participant, - comment: string, - isDraftPolicy: boolean, - receipt?: Receipt, - category?: string, - tag?: string, - taxCode = '', - taxAmount = 0, - billable?: boolean, - policy?: OnyxEntry, - policyTagList?: OnyxEntry, - policyCategories?: OnyxEntry, - gpsPoints?: GPSPoint, - validWaypoints?: WaypointCollection, - action?: IOUAction, - actionableWhisperReportActionID?: string, - linkedTrackedExpenseReportAction?: OnyxTypes.ReportAction, - linkedTrackedExpenseReportID?: string, - customUnitRateID?: string, -) { +function trackExpense(params: CreateTrackExpenseParams) { + const {report, action, isDraftPolicy, participantParams, policyParams: policyData = {}, transactionParams: transactionData} = params; + const {participant, payeeAccountID, payeeEmail} = participantParams; + const {policy, policyCategories, policyTagList} = policyData; + const { + amount, + currency, + created = '', + merchant, + comment, + receipt, + category, + tag, + taxCode = '', + taxAmount = 0, + billable, + gpsPoints, + validWaypoints, + actionableWhisperReportActionID, + linkedTrackedExpenseReportAction, + linkedTrackedExpenseReportID, + customUnitRateID, + } = transactionData; + const isMoneyRequestReport = isMoneyRequestReportReportUtils(report); const currentChatReport = isMoneyRequestReport ? getReportOrDraftReport(report.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report.reportID : ''; diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 4814cd184ddf..776cded37d1b 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -208,18 +208,22 @@ function IOURequestStepAmount({ } if (iouType === CONST.IOU.TYPE.TRACK) { playSound(SOUNDS.DONE); - IOU.trackExpense( + IOU.trackExpense({ report, - backendAmount, - currency ?? 'USD', - transaction?.created ?? '', - CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, - currentUserPersonalDetails.login, - currentUserPersonalDetails.accountID, - participants.at(0) ?? {}, - '', - false, - ); + isDraftPolicy: false, + participantParams: { + payeeEmail: currentUserPersonalDetails.login, + payeeAccountID: currentUserPersonalDetails.accountID, + participant: participants.at(0) ?? {}, + }, + transactionParams: { + amount: backendAmount, + currency: currency ?? 'USD', + created: transaction?.created, + merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, + comment: '', + }, + }); return; } } diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 3c24f317c812..189932394ebc 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -275,34 +275,40 @@ function IOURequestStepConfirmation({ if (!participant) { return; } - IOU.trackExpense( + IOU.trackExpense({ report, - transaction.amount, - transaction.currency, - transaction.created, - transaction.merchant, - currentUserPersonalDetails.login, - currentUserPersonalDetails.accountID, - participant, - trimmedComment, isDraftPolicy, - receiptObj, - transaction.category, - transaction.tag, - transactionTaxCode, - transactionTaxAmount, - transaction.billable, - policy, - policyTags, - policyCategories, - gpsPoints, - Object.keys(transaction?.comment?.waypoints ?? {}).length ? TransactionUtils.getValidWaypoints(transaction.comment?.waypoints, true) : undefined, action, - transaction.actionableWhisperReportActionID, - transaction.linkedTrackedExpenseReportAction, - transaction.linkedTrackedExpenseReportID, - customUnitRateID, - ); + participantParams: { + payeeEmail: currentUserPersonalDetails.login, + payeeAccountID: currentUserPersonalDetails.accountID, + participant, + }, + policyParams: { + policy, + policyCategories, + policyTagList: policyTags, + }, + transactionParams: { + amount: transaction.amount, + currency: transaction.currency, + created: transaction.created, + merchant: transaction.merchant, + comment: trimmedComment, + receipt: receiptObj, + category: transaction.category, + tag: transaction.tag, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, + billable: transaction.billable, + gpsPoints, + validWaypoints: Object.keys(transaction?.comment?.waypoints ?? {}).length ? TransactionUtils.getValidWaypoints(transaction.comment?.waypoints, true) : undefined, + actionableWhisperReportActionID: transaction.actionableWhisperReportActionID, + linkedTrackedExpenseReportAction: transaction.linkedTrackedExpenseReportAction, + linkedTrackedExpenseReportID: transaction.linkedTrackedExpenseReportID, + customUnitRateID, + }, + }); }, [ report, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 180868c8ca51..cae8b46af6bb 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -246,19 +246,23 @@ function IOURequestStepScan({ const createTransaction = useCallback( (receipt: Receipt, participant: Participant) => { if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense( + IOU.trackExpense({ report, - 0, - transaction?.currency ?? 'USD', - transaction?.created ?? '', - '', - currentUserPersonalDetails.login, - currentUserPersonalDetails.accountID, - participant, - '', - false, - receipt, - ); + isDraftPolicy: false, + participantParams: { + payeeEmail: currentUserPersonalDetails.login, + payeeAccountID: currentUserPersonalDetails.accountID, + participant, + }, + transactionParams: { + amount: 0, + currency: transaction?.currency ?? 'USD', + created: transaction?.created, + merchant: '', + comment: '', + receipt, + }, + }); } else { IOU.requestMoney({ report, @@ -334,31 +338,31 @@ function IOURequestStepScan({ (successData) => { playSound(SOUNDS.DONE); if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense( + IOU.trackExpense({ report, - 0, - transaction?.currency ?? 'USD', - transaction?.created ?? '', - '', - currentUserPersonalDetails.login, - currentUserPersonalDetails.accountID, - participant, - '', - false, - receipt, - '', - '', - '', - 0, - false, - policy, - {}, - {}, - { - lat: successData.coords.latitude, - long: successData.coords.longitude, + isDraftPolicy: false, + participantParams: { + payeeEmail: currentUserPersonalDetails.login, + payeeAccountID: currentUserPersonalDetails.accountID, + participant, }, - ); + policyParams: { + policy, + }, + transactionParams: { + amount: 0, + currency: transaction?.currency ?? 'USD', + created: transaction?.created, + merchant: '', + comment: '', + receipt, + billable: false, + gpsPoints: { + lat: successData.coords.latitude, + long: successData.coords.longitude, + }, + }, + }); } else { IOU.requestMoney({ report, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 8dbe72d3f9fa..692381d3301f 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -271,19 +271,23 @@ function IOURequestStepScan({ const createTransaction = useCallback( (receipt: Receipt, participant: Participant) => { if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense( + IOU.trackExpense({ report, - 0, - transaction?.currency ?? 'USD', - transaction?.created ?? '', - '', - currentUserPersonalDetails.login, - currentUserPersonalDetails.accountID, - participant, - '', - false, - receipt, - ); + isDraftPolicy: false, + participantParams: { + payeeEmail: currentUserPersonalDetails.login, + payeeAccountID: currentUserPersonalDetails.accountID, + participant, + }, + transactionParams: { + amount: 0, + currency: transaction?.currency ?? 'USD', + created: transaction?.created, + merchant: '', + comment: '', + receipt, + }, + }); } else { IOU.requestMoney({ report, @@ -360,31 +364,31 @@ function IOURequestStepScan({ (successData) => { playSound(SOUNDS.DONE); if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense( + IOU.trackExpense({ report, - 0, - transaction?.currency ?? 'USD', - transaction?.created ?? '', - '', - currentUserPersonalDetails.login, - currentUserPersonalDetails.accountID, - participant, - '', - false, - receipt, - '', - '', - '', - 0, - false, - policy, - {}, - {}, - { - lat: successData.coords.latitude, - long: successData.coords.longitude, + isDraftPolicy: false, + participantParams: { + payeeEmail: currentUserPersonalDetails.login, + payeeAccountID: currentUserPersonalDetails.accountID, + participant, }, - ); + policyParams: { + policy, + }, + transactionParams: { + amount: 0, + currency: transaction?.currency ?? 'USD', + created: transaction?.created, + merchant: '', + comment: '', + receipt, + billable: false, + gpsPoints: { + lat: successData.coords.latitude, + long: successData.coords.longitude, + }, + }, + }); } else { IOU.requestMoney({ report, diff --git a/tests/unit/GoogleTagManagerTest.tsx b/tests/unit/GoogleTagManagerTest.tsx index dcb6bdea0eec..9a2041634443 100644 --- a/tests/unit/GoogleTagManagerTest.tsx +++ b/tests/unit/GoogleTagManagerTest.tsx @@ -67,34 +67,29 @@ describe('GoogleTagManagerTest', () => { }); test('workspace_created - categorizeTrackedExpense', () => { - // When we categorize a tracked expense with a draft policy - IOU.trackExpense( - {reportID: '123'}, - 1000, - 'USD', - '2024-10-30', - 'merchant', - undefined, - 0, - {accountID}, - 'comment', - true, - undefined, - 'category', - 'tag', - 'taxCode', - 0, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - CONST.IOU.ACTION.CATEGORIZE, - 'actionableWhisperReportActionID', - {actionName: 'IOU', reportActionID: 'linkedTrackedExpenseReportAction', created: '2024-10-30'}, - 'linkedTrackedExpenseReportID', - ); + IOU.trackExpense({ + report: {reportID: '123'}, + isDraftPolicy: true, + action: CONST.IOU.ACTION.CATEGORIZE, + participantParams: { + payeeEmail: undefined, + payeeAccountID: 0, + participant: {accountID}, + }, + transactionParams: { + amount: 1000, + currency: 'USD', + created: '2024-10-30', + merchant: 'merchant', + comment: 'comment', + category: 'category', + tag: 'tag', + taxCode: 'taxCode', + actionableWhisperReportActionID: 'actionableWhisperReportActionID', + linkedTrackedExpenseReportAction: {actionName: 'IOU', reportActionID: 'linkedTrackedExpenseReportAction', created: '2024-10-30'}, + linkedTrackedExpenseReportID: 'linkedTrackedExpenseReportID', + }, + }); // Then we publish a workspace_created event only once expect(GoogleTagManager.publishEvent).toBeCalledTimes(1); From 988005f8e29ffdd12d90563f87d155ea018710cb Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 15 Jan 2025 17:30:42 +0700 Subject: [PATCH 004/109] fix: style web --- src/components/MapView/MapViewImpl.website.tsx | 8 +++----- src/styles/index.ts | 10 ++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/MapView/MapViewImpl.website.tsx b/src/components/MapView/MapViewImpl.website.tsx index 31fdf0365a58..cdeee9b4b7da 100644 --- a/src/components/MapView/MapViewImpl.website.tsx +++ b/src/components/MapView/MapViewImpl.website.tsx @@ -280,11 +280,9 @@ const MapViewImpl = forwardRef( latitude={distanceSymbolCoorinate.at(1) ?? 0} > - - {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS)} - {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES)} + + {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS)} + {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES)} diff --git a/src/styles/index.ts b/src/styles/index.ts index d01aef6469f4..56146a0fa883 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4005,6 +4005,16 @@ const styles = (theme: ThemeColors) => paddingVertical: 8, borderRadius: variables.componentBorderRadiusMedium, }, + distanceLabelWrapper: { + backgroundColor: colors.green400, + paddingHorizontal: 8, + paddingVertical: 4, + borderRadius: 4, + textAlign: 'center', + fontSize: 13, + fontWeight: FontUtils.fontWeight.bold, + color: colors.productLight100, + }, productTrainingTooltipWrapper: { backgroundColor: theme.tooltipHighlightBG, From 738ab3e396a2e70ba691b22d4ba6c2bea1732307 Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Wed, 15 Jan 2025 21:36:39 +0700 Subject: [PATCH 005/109] update all trackExpense function --- .../request/step/IOURequestStepDistance.tsx | 49 +++--- tests/actions/IOUTest.ts | 156 ++++++++---------- 2 files changed, 94 insertions(+), 111 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 9a136f514a9b..e2855ba32eea 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -322,34 +322,29 @@ function IOURequestStepDistance({ const participant = participants.at(0); if (iouType === CONST.IOU.TYPE.TRACK && participant) { playSound(SOUNDS.DONE); - trackExpense( + trackExpense({ report, - 0, - transaction?.currency ?? 'USD', - transaction?.created ?? '', - translate('iou.fieldPending'), - currentUserPersonalDetails.login, - currentUserPersonalDetails.accountID, - participant, - '', - false, - {}, - '', - '', - '', - 0, - false, - policy, - undefined, - undefined, - undefined, - getValidWaypoints(waypoints, true), - undefined, - undefined, - undefined, - undefined, - customUnitRateID, - ); + isDraftPolicy: false, + participantParams: { + payeeEmail: currentUserPersonalDetails.login, + payeeAccountID: currentUserPersonalDetails.accountID, + participant, + }, + policyParams: { + policy, + }, + transactionParams: { + amount: 0, + currency: transaction?.currency ?? 'USD', + created: transaction?.created ?? '', + merchant: translate('iou.fieldPending'), + comment: '', + receipt: {}, + billable: false, + validWaypoints: getValidWaypoints(waypoints, true), + customUnitRateID, + }, + }); return; } diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 291d2d4ac1e3..1d7cb03dcb87 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -190,34 +190,29 @@ describe('actions/IOU', () => { mockFetch?.pause?.(); // When the user submits the transaction to the selfDM report - trackExpense( - selfDMReport, - fakeTransaction.amount, - fakeTransaction.currency, - format(new Date(), CONST.DATE.FNS_FORMAT_STRING), - fakeTransaction.merchant, - participant.login, - participant.accountID, - participant, - '', - true, - undefined, - '', - undefined, - '', - 0, - false, - undefined, - undefined, - undefined, - undefined, - fakeWayPoints, - CONST.IOU.ACTION.CREATE, - fakeTransaction?.actionableWhisperReportActionID, - fakeTransaction?.linkedTrackedExpenseReportAction, - fakeTransaction?.linkedTrackedExpenseReportID, - CONST.CUSTOM_UNITS.FAKE_P2P_ID, - ); + trackExpense({ + report: selfDMReport, + isDraftPolicy: true, + action: CONST.IOU.ACTION.CREATE, + participantParams: { + payeeEmail: participant.login, + payeeAccountID: participant.accountID, + participant, + }, + transactionParams: { + amount: fakeTransaction.amount, + currency: fakeTransaction.currency, + created: format(new Date(), CONST.DATE.FNS_FORMAT_STRING), + merchant: fakeTransaction.merchant, + comment: '', + billable: false, + validWaypoints: fakeWayPoints, + actionableWhisperReportActionID: fakeTransaction?.actionableWhisperReportActionID, + linkedTrackedExpenseReportAction: fakeTransaction?.linkedTrackedExpenseReportAction, + linkedTrackedExpenseReportID: fakeTransaction?.linkedTrackedExpenseReportID, + customUnitRateID: CONST.CUSTOM_UNITS.FAKE_P2P_ID, + }, + }); await waitForBatchedUpdates(); await mockFetch?.resume?.(); @@ -283,34 +278,33 @@ describe('actions/IOU', () => { const transactionDraft = allTransactionsDraft?.[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction?.transactionID}`]; // When the user confirms the category for the tracked expense - trackExpense( - expenseReport, - transactionDraft?.amount ?? fakeTransaction.amount, - transactionDraft?.currency ?? fakeTransaction.currency, - format(new Date(), CONST.DATE.FNS_FORMAT_STRING), - transactionDraft?.merchant ?? fakeTransaction.merchant, - participant.login, - participant.accountID, - {...participant, isPolicyExpenseChat: true}, - '', - false, - undefined, - Object.keys(fakeCategories).at(0) ?? '', - '', - '', - 0, - undefined, - fakePolicy, - undefined, - fakeCategories, - undefined, - Object.keys(transactionDraft?.comment?.waypoints ?? {}).length ? getValidWaypoints(transactionDraft?.comment?.waypoints, true) : undefined, - CONST.IOU.ACTION.CATEGORIZE, - transactionDraft?.actionableWhisperReportActionID, - transactionDraft?.linkedTrackedExpenseReportAction, - transactionDraft?.linkedTrackedExpenseReportID, - CONST.CUSTOM_UNITS.FAKE_P2P_ID, - ); + trackExpense({ + report: expenseReport, + isDraftPolicy: false, + action: CONST.IOU.ACTION.CATEGORIZE, + participantParams: { + payeeEmail: participant.login, + payeeAccountID: participant.accountID, + participant: {...participant, isPolicyExpenseChat: true}, + }, + policyParams: { + policy: fakePolicy, + policyCategories: fakeCategories, + }, + transactionParams: { + amount: transactionDraft?.amount ?? fakeTransaction.amount, + currency: transactionDraft?.currency ?? fakeTransaction.currency, + created: format(new Date(), CONST.DATE.FNS_FORMAT_STRING), + merchant: transactionDraft?.merchant ?? fakeTransaction.merchant, + comment: '', + category: Object.keys(fakeCategories).at(0) ?? '', + validWaypoints: Object.keys(transactionDraft?.comment?.waypoints ?? {}).length ? getValidWaypoints(transactionDraft?.comment?.waypoints, true) : undefined, + actionableWhisperReportActionID: transactionDraft?.actionableWhisperReportActionID, + linkedTrackedExpenseReportAction: transactionDraft?.linkedTrackedExpenseReportAction, + linkedTrackedExpenseReportID: transactionDraft?.linkedTrackedExpenseReportID, + customUnitRateID: CONST.CUSTOM_UNITS.FAKE_P2P_ID, + }, + }); await waitForBatchedUpdates(); await mockFetch?.resume?.(); @@ -4398,37 +4392,31 @@ describe('actions/IOU', () => { [WRITE_COMMANDS.SHARE_TRACKED_EXPENSE, CONST.IOU.ACTION.SHARE], ])('%s', async (expectedCommand: ApiCommand, action: IOUAction) => { // When a track expense is created - trackExpense( - {reportID: ''}, - 10000, - CONST.CURRENCY.USD, - '2024-10-30', - 'KFC', - RORY_EMAIL, - RORY_ACCOUNT_ID, - {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, - '', - false, - {}, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, + trackExpense({ + report: {reportID: ''}, + isDraftPolicy: false, action, - '1', - { - reportActionID: '', - actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + participantParams: { + payeeEmail: RORY_EMAIL, + payeeAccountID: RORY_ACCOUNT_ID, + participant: {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, + }, + transactionParams: { + amount: 10000, + currency: CONST.CURRENCY.USD, created: '2024-10-30', + merchant: 'KFC', + comment: '', + receipt: {}, + actionableWhisperReportActionID: '1', + linkedTrackedExpenseReportAction: { + reportActionID: '', + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + created: '2024-10-30', + }, + linkedTrackedExpenseReportID: '1', }, - '1', - ); + }); await waitForBatchedUpdates(); From c3bd9f11b88dfd305ff8c3f4d6bdc44de755dfd7 Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Thu, 16 Jan 2025 12:27:47 +0700 Subject: [PATCH 006/109] fix lint --- .../iou/request/step/IOURequestStepAmount.tsx | 89 +++++++++------- .../step/IOURequestStepConfirmation.tsx | 100 ++++++++++-------- .../step/IOURequestStepScan/index.native.tsx | 61 ++++++----- .../request/step/IOURequestStepScan/index.tsx | 73 +++++++------ tests/unit/GoogleTagManagerTest.tsx | 18 ++-- 5 files changed, 189 insertions(+), 152 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 776cded37d1b..3aa83a857530 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -6,22 +6,33 @@ import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; -import * as TransactionEdit from '@libs/actions/TransactionEdit'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import { + requestMoney, + resetSplitShares, + sendMoneyElsewhere, + sendMoneyWithWallet, + setDraftSplitTransaction, + setMoneyRequestAmount, + setMoneyRequestParticipantsFromReport, + setMoneyRequestTaxAmount, + setSplitShares, + trackExpense, + updateMoneyRequestAmountAndCurrency, +} from '@libs/actions/IOU'; +import {createDraftTransaction, removeDraftTransaction} from '@libs/actions/TransactionEdit'; +import {convertToBackendAmount, isValidCurrencyCode} from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; +import {getBankAccountRoute, getTransactionDetails, isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import * as TransactionUtils from '@libs/TransactionUtils'; -import {getRequestType} from '@libs/TransactionUtils'; +import {calculateTaxAmount, getAmount, getCurrency, getDefaultTaxCode, getRequestType, getTaxValue} from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/MoneyRequestAmountForm'; -import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type Transaction from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -36,7 +47,7 @@ type AmountParams = { type IOURequestStepAmountProps = WithCurrentUserPersonalDetailsProps & WithWritableReportOrNotFoundProps & { /** The transaction object being modified in Onyx */ - transaction: OnyxEntry; + transaction: OnyxEntry; /** Whether the user input should be kept or not */ shouldKeepUserInput?: boolean; @@ -69,9 +80,9 @@ function IOURequestStepAmount({ const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isEditingSplitBill = isEditing && isSplitBill; const currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction; - const {amount: transactionAmount} = ReportUtils.getTransactionDetails(currentTransaction) ?? {amount: 0}; - const {currency: originalCurrency} = ReportUtils.getTransactionDetails(isEditing && !isEmptyObject(draftTransaction) ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; - const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; + const {amount: transactionAmount} = getTransactionDetails(currentTransaction) ?? {amount: 0}; + const {currency: originalCurrency} = getTransactionDetails(isEditing && !isEmptyObject(draftTransaction) ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; + const currency = isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; // 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. @@ -80,7 +91,7 @@ function IOURequestStepAmount({ return false; } - return !(ReportUtils.isArchivedReport(report, reportNameValuePairs) || ReportUtils.isPolicyExpenseChat(report)); + return !(isArchivedReport(report, reportNameValuePairs) || isPolicyExpenseChat(report)); }, [report, isSplitBill, skipConfirmation, reportNameValuePairs]); useFocusEffect( @@ -102,13 +113,13 @@ function IOURequestStepAmount({ // A temporary solution to not prevent users from editing the currency // We create a backup transaction and use it to save the currency and remove this transaction backup if we don't save the amount // It should be removed after this issue https://github.com/Expensify/App/issues/34607 is fixed - TransactionEdit.createDraftTransaction(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction); + createDraftTransaction(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction); return () => { if (isSaveButtonPressed.current) { return; } - TransactionEdit.removeDraftTransaction(transaction?.transactionID); + removeDraftTransaction(transaction?.transactionID); }; // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); @@ -149,14 +160,14 @@ function IOURequestStepAmount({ const navigateToNextPage = ({amount, paymentMethod}: AmountParams) => { isSaveButtonPressed.current = true; - const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); + const amountInSmallestCurrencyUnits = convertToBackendAmount(Number.parseFloat(amount)); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - IOU.setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, shouldKeepUserInput); + setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, shouldKeepUserInput); // Initially when we're creating money request, we do not know the participant and hence if the request is with workspace with tax tracking enabled // So, we reset the taxAmount here and calculate it in the hook in MoneyRequestConfirmationList component - IOU.setMoneyRequestTaxAmount(transactionID, null); + setMoneyRequestTaxAmount(transactionID, null); if (backTo) { Navigation.goBack(backTo); @@ -169,27 +180,27 @@ function IOURequestStepAmount({ // In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight // to the confirm step. // If the user is started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. - if (report?.reportID && !ReportUtils.isArchivedReport(report, reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { - const selectedParticipants = IOU.setMoneyRequestParticipantsFromReport(transactionID, report); + if (report?.reportID && !isArchivedReport(report, reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { + const selectedParticipants = setMoneyRequestParticipantsFromReport(transactionID, report); const participants = selectedParticipants.map((participant) => { const participantAccountID = participant?.accountID ?? CONST.DEFAULT_NUMBER_ID; - return participantAccountID ? OptionsListUtils.getParticipantsOption(participant, personalDetails) : OptionsListUtils.getReportOption(participant); + return participantAccountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant); }); - const backendAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); + const backendAmount = convertToBackendAmount(Number.parseFloat(amount)); if (shouldSkipConfirmation) { 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) ?? {}); + sendMoneyWithWallet(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0) ?? {}); return; } - IOU.sendMoneyElsewhere(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0) ?? {}); + sendMoneyElsewhere(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0) ?? {}); return; } if (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.REQUEST) { playSound(SOUNDS.DONE); - IOU.requestMoney({ + requestMoney({ report, participantParams: { participant: participants.at(0) ?? {}, @@ -208,7 +219,7 @@ function IOURequestStepAmount({ } if (iouType === CONST.IOU.TYPE.TRACK) { playSound(SOUNDS.DONE); - IOU.trackExpense({ + trackExpense({ report, isDraftPolicy: false, participantParams: { @@ -227,10 +238,10 @@ function IOURequestStepAmount({ return; } } - IOU.setMoneyRequestParticipantsFromReport(transactionID, report); + setMoneyRequestParticipantsFromReport(transactionID, report); if (isSplitBill && !report.isOwnPolicyExpenseChat && report.participants) { const participantAccountIDs = Object.keys(report.participants).map((accountID) => Number(accountID)); - IOU.setSplitShares(transaction, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, participantAccountIDs); + setSplitShares(transaction, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, participantAccountIDs); } navigateToConfirmationPage(); return; @@ -242,11 +253,11 @@ function IOURequestStepAmount({ }; const saveAmountAndCurrency = ({amount, paymentMethod}: AmountParams) => { - const newAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); + const newAmount = convertToBackendAmount(Number.parseFloat(amount)); // Edits to the amount from the splits page should reset the split shares. if (transaction?.splitShares) { - IOU.resetSplitShares(transaction, newAmount, currency); + resetSplitShares(transaction, newAmount, currency); } if (!isEditing) { @@ -255,26 +266,26 @@ function IOURequestStepAmount({ } // If the value hasn't changed, don't request to save changes on the server and just close the modal - const transactionCurrency = TransactionUtils.getCurrency(currentTransaction); - if (newAmount === TransactionUtils.getAmount(currentTransaction) && currency === transactionCurrency) { + const transactionCurrency = getCurrency(currentTransaction); + if (newAmount === getAmount(currentTransaction) && currency === transactionCurrency) { navigateBack(); return; } // If currency has changed, then we get the default tax rate based on currency, otherwise we use the current tax rate selected in transaction, if we have it. - const transactionTaxCode = ReportUtils.getTransactionDetails(currentTransaction)?.taxCode; - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, currentTransaction, currency) ?? ''; + const transactionTaxCode = getTransactionDetails(currentTransaction)?.taxCode; + const defaultTaxCode = getDefaultTaxCode(policy, currentTransaction, currency) ?? ''; const taxCode = (currency !== transactionCurrency ? defaultTaxCode : transactionTaxCode) ?? defaultTaxCode; - const taxPercentage = TransactionUtils.getTaxValue(policy, currentTransaction, taxCode) ?? ''; - const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, newAmount, currency ?? CONST.CURRENCY.USD)); + const taxPercentage = getTaxValue(policy, currentTransaction, taxCode) ?? ''; + const taxAmount = convertToBackendAmount(calculateTaxAmount(taxPercentage, newAmount, currency ?? CONST.CURRENCY.USD)); if (isSplitBill) { - IOU.setDraftSplitTransaction(transactionID, {amount: newAmount, currency, taxCode, taxAmount}); + setDraftSplitTransaction(transactionID, {amount: newAmount, currency, taxCode, taxAmount}); navigateBack(); return; } - IOU.updateMoneyRequestAmountAndCurrency({transactionID, transactionThreadReportID: reportID, currency, amount: newAmount, taxAmount, policy, taxCode}); + updateMoneyRequestAmountAndCurrency({transactionID, transactionThreadReportID: reportID, currency, amount: newAmount, taxAmount, policy, taxCode}); navigateBack(); }; @@ -293,7 +304,7 @@ function IOURequestStepAmount({ skipConfirmation={shouldSkipConfirmation ?? false} iouType={iouType} policyID={policy?.id} - bankAccountRoute={ReportUtils.getBankAccountRoute(report)} + bankAccountRoute={getBankAccountRoute(report)} ref={(e) => (textInput.current = e)} shouldKeepUserInput={transaction?.shouldShowOriginalAmount} onCurrencyButtonPress={navigateToCurrencySelectionPage} diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 189932394ebc..c95647747938 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -15,18 +15,34 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import { + createDistanceRequest as createDistanceRequestFunc, + getIOURequestPolicyID, + navigateToStartStepIfScanFileCannotBeRead, + requestMoney as requestMoneyFunc, + sendInvoice, + sendMoneyElsewhere, + sendMoneyWithWallet, + setMoneyRequestBillable, + setMoneyRequestCategory, + splitBill, + splitBillAndOpenReport, + startSplitBill, + trackExpense as trackExpenseFunc, + updateLastLocationPermissionPrompt, +} from '@libs/actions/IOU'; +import type {GpsPoint} from '@libs/actions/IOU'; import DateUtils from '@libs/DateUtils'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import * as FileUtils from '@libs/fileDownload/FileUtils'; +import {canUseTouchScreen} from '@libs/DeviceCapabilities'; +import {isLocalFile as isLocalFileUtil} from '@libs/fileDownload/FileUtils'; import getCurrentPosition from '@libs/getCurrentPosition'; -import * as IOUUtils from '@libs/IOUUtils'; +import {isMovingTransactionFromTrackExpense as isMovingTransactionFromTrackExpenseUtil, navigateToStartMoneyRequestStep, shouldUseTransactionDraft} from '@libs/IOUUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; +import {getBankAccountRoute} from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import * as TransactionUtils from '@libs/TransactionUtils'; -import * as IOU from '@userActions/IOU'; +import {getDefaultTaxCode, getRateID, getRequestType, getValidWaypoints} from '@libs/TransactionUtils'; import {openDraftWorkspaceRequest} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -55,11 +71,11 @@ function IOURequestStepConfirmation({ const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalDetails = usePersonalDetails(); - const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${IOU.getIOURequestPolicyID(transaction, reportDraft)}`); - const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${IOU.getIOURequestPolicyID(transaction, reportReal)}`); - const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${IOU.getIOURequestPolicyID(transaction, reportReal)}`); - const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${IOU.getIOURequestPolicyID(transaction, reportDraft)}`); - const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${IOU.getIOURequestPolicyID(transaction, reportReal)}`); + const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${getIOURequestPolicyID(transaction, reportDraft)}`); + const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getIOURequestPolicyID(transaction, reportReal)}`); + const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${getIOURequestPolicyID(transaction, reportReal)}`); + const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${getIOURequestPolicyID(transaction, reportDraft)}`); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${getIOURequestPolicyID(transaction, reportReal)}`); const report = reportReal ?? reportDraft; const policy = policyReal ?? policyDraft; @@ -74,7 +90,7 @@ function IOURequestStepConfirmation({ const [selectedParticipantList, setSelectedParticipantList] = useState([]); const [receiptFile, setReceiptFile] = useState>(); - const requestType = TransactionUtils.getRequestType(transaction); + const requestType = getRequestType(transaction); const isDistanceRequest = requestType === CONST.IOU.REQUEST_TYPE.DISTANCE; const isPerDiemRequest = requestType === CONST.IOU.REQUEST_TYPE.PER_DIEM; const [lastLocationPermissionPrompt] = useOnyx(ONYXKEYS.NVP_LAST_LOCATION_PERMISSION_PROMPT); @@ -82,13 +98,13 @@ function IOURequestStepConfirmation({ const receiptFilename = transaction?.filename; const receiptPath = transaction?.receipt?.source; const receiptType = transaction?.receipt?.type; - const customUnitRateID = TransactionUtils.getRateID(transaction) ?? ''; - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction); + const customUnitRateID = getRateID(transaction) ?? ''; + const defaultTaxCode = getDefaultTaxCode(policy, transaction); const transactionTaxCode = (transaction?.taxCode ? transaction?.taxCode : defaultTaxCode) ?? ''; const transactionTaxAmount = transaction?.taxAmount ?? 0; const isSharingTrackExpense = action === CONST.IOU.ACTION.SHARE; const isCategorizingTrackExpense = action === CONST.IOU.ACTION.CATEGORIZE; - const isMovingTransactionFromTrackExpense = IOUUtils.isMovingTransactionFromTrackExpense(action); + const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseUtil(action); const payeePersonalDetails = useMemo(() => { if (personalDetails?.[transaction?.splitPayerAccountIDs?.at(0) ?? -1]) { return personalDetails?.[transaction?.splitPayerAccountIDs?.at(0) ?? -1]; @@ -127,14 +143,14 @@ function IOURequestStepConfirmation({ if (participant.isSender && iouType === CONST.IOU.TYPE.INVOICE) { return participant; } - return participant.accountID ? OptionsListUtils.getParticipantsOption(participant, personalDetails) : OptionsListUtils.getReportOption(participant); + return participant.accountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant); }) ?? [], [transaction?.participants, personalDetails, iouType], ); const isPolicyExpenseChat = useMemo(() => participants?.some((participant) => participant.isPolicyExpenseChat), [participants]); const formHasBeenSubmitted = useRef(false); - useFetchRoute(transaction, transaction?.comment?.waypoints, action, IOUUtils.shouldUseTransactionDraft(action) ? CONST.TRANSACTION.STATE.DRAFT : CONST.TRANSACTION.STATE.CURRENT); + useFetchRoute(transaction, transaction?.comment?.waypoints, action, shouldUseTransactionDraft(action) ? CONST.TRANSACTION.STATE.DRAFT : CONST.TRANSACTION.STATE.CURRENT); useEffect(() => { const policyExpenseChat = participants?.find((participant) => participant.isPolicyExpenseChat); @@ -149,7 +165,7 @@ function IOURequestStepConfirmation({ const defaultBillable = !!policy?.defaultBillable; useEffect(() => { - IOU.setMoneyRequestBillable(transactionID, defaultBillable); + setMoneyRequestBillable(transactionID, defaultBillable); }, [transactionID, defaultBillable]); useEffect(() => { @@ -157,7 +173,7 @@ function IOURequestStepConfirmation({ return; } if (policyCategories?.[transaction.category] && !policyCategories[transaction.category].enabled) { - IOU.setMoneyRequestCategory(transactionID, '', policy?.id); + setMoneyRequestCategory(transactionID, '', policy?.id); } }, [policy?.id, policyCategories, transaction?.category, transactionID]); @@ -168,7 +184,7 @@ function IOURequestStepConfirmation({ if (requestType !== CONST.IOU.REQUEST_TYPE.DISTANCE || !!transaction?.category) { return; } - IOU.setMoneyRequestCategory(transactionID, defaultCategory, policy?.id); + setMoneyRequestCategory(transactionID, defaultCategory, policy?.id); // Prevent resetting to default when unselect category // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [transactionID, requestType, defaultCategory, policy?.id]); @@ -191,7 +207,7 @@ function IOURequestStepConfirmation({ Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, transaction?.reportID || reportID, undefined, action)); return; } - IOUUtils.navigateToStartMoneyRequestStep(requestType, iouType, transactionID, reportID, action); + navigateToStartMoneyRequestStep(requestType, iouType, transactionID, reportID, action); }, [action, isPerDiemRequest, transaction?.participantsAutoAssigned, transaction?.reportID, participantsAutoAssignedFromRoute, requestType, iouType, transactionID, reportID]); const navigateToAddReceipt = useCallback(() => { @@ -204,7 +220,7 @@ function IOURequestStepConfirmation({ // skip this in case user is moving the transaction as the receipt path will be valid in that case useEffect(() => { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const isLocalFile = FileUtils.isLocalFile(receiptPath); + const isLocalFile = isLocalFileUtil(receiptPath); if (!isLocalFile) { setReceiptFile(transaction?.receipt); @@ -217,11 +233,11 @@ function IOURequestStepConfirmation({ setReceiptFile(receipt); }; - IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, onSuccess, requestType, iouType, transactionID, reportID, receiptType); + navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, onSuccess, requestType, iouType, transactionID, reportID, receiptType); }, [receiptType, receiptPath, receiptFilename, requestType, iouType, transactionID, reportID, action, transaction?.receipt]); const requestMoney = useCallback( - (selectedParticipants: Participant[], trimmedComment: string, receiptObj?: Receipt, gpsPoints?: IOU.GpsPoint) => { + (selectedParticipants: Participant[], trimmedComment: string, receiptObj?: Receipt, gpsPoints?: GpsPoint) => { if (!transaction) { return; } @@ -230,7 +246,7 @@ function IOURequestStepConfirmation({ if (!participant) { return; } - IOU.requestMoney({ + requestMoneyFunc({ report, participantParams: { payeeEmail: currentUserPersonalDetails.login, @@ -267,7 +283,7 @@ function IOURequestStepConfirmation({ ); const trackExpense = useCallback( - (selectedParticipants: Participant[], trimmedComment: string, receiptObj?: OnyxEntry, gpsPoints?: IOU.GpsPoint) => { + (selectedParticipants: Participant[], trimmedComment: string, receiptObj?: OnyxEntry, gpsPoints?: GpsPoint) => { if (!report || !transaction) { return; } @@ -275,7 +291,7 @@ function IOURequestStepConfirmation({ if (!participant) { return; } - IOU.trackExpense({ + trackExpenseFunc({ report, isDraftPolicy, action, @@ -302,7 +318,7 @@ function IOURequestStepConfirmation({ taxAmount: transactionTaxAmount, billable: transaction.billable, gpsPoints, - validWaypoints: Object.keys(transaction?.comment?.waypoints ?? {}).length ? TransactionUtils.getValidWaypoints(transaction.comment?.waypoints, true) : undefined, + validWaypoints: Object.keys(transaction?.comment?.waypoints ?? {}).length ? getValidWaypoints(transaction.comment?.waypoints, true) : undefined, actionableWhisperReportActionID: transaction.actionableWhisperReportActionID, linkedTrackedExpenseReportAction: transaction.linkedTrackedExpenseReportAction, linkedTrackedExpenseReportID: transaction.linkedTrackedExpenseReportID, @@ -331,7 +347,7 @@ function IOURequestStepConfirmation({ if (!transaction) { return; } - IOU.createDistanceRequest({ + createDistanceRequestFunc({ report, participants: selectedParticipants, currentUserLogin: currentUserPersonalDetails.login, @@ -355,7 +371,7 @@ function IOURequestStepConfirmation({ taxAmount: transactionTaxAmount, customUnitRateID, splitShares: transaction.splitShares, - validWaypoints: TransactionUtils.getValidWaypoints(transaction.comment?.waypoints, true), + validWaypoints: getValidWaypoints(transaction.comment?.waypoints, true), billable: transaction.billable, }, }); @@ -400,7 +416,7 @@ function IOURequestStepConfirmation({ // If we have a receipt let's start the split expense by creating only the action, the transaction, and the group DM if needed if (iouType === CONST.IOU.TYPE.SPLIT && receiptFile) { if (currentUserPersonalDetails.login && !!transaction) { - IOU.startSplitBill({ + startSplitBill({ participants: selectedParticipants, currentUserLogin: currentUserPersonalDetails.login, currentUserAccountID: currentUserPersonalDetails.accountID, @@ -422,7 +438,7 @@ function IOURequestStepConfirmation({ // Since the user is already viewing the report, we don't need to navigate them to the report if (iouType === CONST.IOU.TYPE.SPLIT && !transaction?.isFromGlobalCreate) { if (currentUserPersonalDetails.login && !!transaction) { - IOU.splitBill({ + splitBill({ participants: splitParticipants, currentUserLogin: currentUserPersonalDetails.login, currentUserAccountID: currentUserPersonalDetails.accountID, @@ -448,7 +464,7 @@ function IOURequestStepConfirmation({ // If the split expense is created from the global create menu, we also navigate the user to the group report if (iouType === CONST.IOU.TYPE.SPLIT) { if (currentUserPersonalDetails.login && !!transaction) { - IOU.splitBillAndOpenReport({ + splitBillAndOpenReport({ participants: splitParticipants, currentUserLogin: currentUserPersonalDetails.login, currentUserAccountID: currentUserPersonalDetails.accountID, @@ -471,7 +487,7 @@ function IOURequestStepConfirmation({ } if (iouType === CONST.IOU.TYPE.INVOICE) { - IOU.sendInvoice(currentUserPersonalDetails.accountID, transaction, report, receiptFile, policy, policyTags, policyCategories); + sendInvoice(currentUserPersonalDetails.accountID, transaction, report, receiptFile, policy, policyTags, policyCategories); return; } @@ -575,13 +591,13 @@ function IOURequestStepConfirmation({ if (paymentMethod === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { setIsConfirmed(true); - IOU.sendMoneyElsewhere(report, transaction.amount, currency, trimmedComment, currentUserPersonalDetails.accountID, participant); + sendMoneyElsewhere(report, transaction.amount, currency, trimmedComment, currentUserPersonalDetails.accountID, participant); return; } if (paymentMethod === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { setIsConfirmed(true); - IOU.sendMoneyWithWallet(report, transaction.amount, currency, trimmedComment, currentUserPersonalDetails.accountID, participant); + sendMoneyWithWallet(report, transaction.amount, currency, trimmedComment, currentUserPersonalDetails.accountID, participant); } }, [transaction?.amount, transaction?.comment, transaction?.currency, participants, currentUserPersonalDetails.accountID, report], @@ -589,7 +605,7 @@ function IOURequestStepConfirmation({ const setBillable = useCallback( (billable: boolean) => { - IOU.setMoneyRequestBillable(transactionID, billable); + setMoneyRequestBillable(transactionID, billable); }, [transactionID], ); @@ -622,7 +638,7 @@ function IOURequestStepConfirmation({ return ( @@ -648,7 +664,7 @@ function IOURequestStepConfirmation({ resetPermissionFlow={() => setStartLocationPermissionFlow(false)} onGrant={() => createTransaction(selectedParticipantList, true)} onDeny={() => { - IOU.updateLastLocationPermissionPrompt(); + updateLastLocationPermissionPrompt(); createTransaction(selectedParticipantList, false); }} /> @@ -670,8 +686,8 @@ function IOURequestStepConfirmation({ iouType={iouType} reportID={reportID} isPolicyExpenseChat={isPolicyExpenseChat} - policyID={IOU.getIOURequestPolicyID(transaction, report)} - bankAccountRoute={ReportUtils.getBankAccountRoute(report)} + policyID={getIOURequestPolicyID(transaction, report)} + bankAccountRoute={getBankAccountRoute(report)} iouMerchant={transaction?.merchant} iouCreated={transaction?.created} isDistanceRequest={isDistanceRequest} diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index cae8b46af6bb..feed191c86b9 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -28,22 +28,30 @@ import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as FileUtils from '@libs/fileDownload/FileUtils'; +import { + replaceReceipt, + requestMoney, + setMoneyRequestParticipantsFromReport, + setMoneyRequestReceipt, + startSplitBill, + trackExpense, + updateLastLocationPermissionPrompt, +} from '@libs/actions/IOU'; +import {readFileAsync, resizeImageIfNeeded, showCameraPermissionsAlert, splitExtensionFromFileName} from '@libs/fileDownload/FileUtils'; import getPhotoSource from '@libs/fileDownload/getPhotoSource'; import getCurrentPosition from '@libs/getCurrentPosition'; import getPlatform from '@libs/getPlatform'; import getReceiptsUploadFolderPath from '@libs/getReceiptsUploadFolderPath'; -import * as IOUUtils from '@libs/IOUUtils'; +import {shouldStartLocationPermissionFlow as shouldStartLocationPermissionFlowFunc} from '@libs/IOUUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; +import {isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {getDefaultTaxCode} from '@libs/TransactionUtils'; import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; -import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -87,7 +95,7 @@ function IOURequestStepScan({ const [pdfFile, setPdfFile] = useState(null); - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction); + const defaultTaxCode = getDefaultTaxCode(policy, transaction); const transactionTaxCode = (transaction?.taxCode ? transaction?.taxCode : defaultTaxCode) ?? ''; const transactionTaxAmount = transaction?.taxAmount ?? 0; @@ -98,10 +106,7 @@ function IOURequestStepScan({ return false; } - return ( - !ReportUtils.isArchivedReport(report, reportNameValuePairs) && - !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))) - ); + return !isArchivedReport(report, reportNameValuePairs) && !(isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))); }, [report, skipConfirmation, policy, reportNameValuePairs]); const {translate} = useLocalize(); @@ -114,7 +119,7 @@ function IOURequestStepScan({ setCameraPermissionStatus(status); if (status === RESULTS.BLOCKED) { - FileUtils.showCameraPermissionsAlert(); + showCameraPermissionsAlert(); } }) .catch(() => { @@ -188,7 +193,7 @@ function IOURequestStepScan({ ); const validateReceipt = (file: FileObject) => { - const {fileExtension} = FileUtils.splitExtensionFromFileName(file?.name ?? ''); + const {fileExtension} = splitExtensionFromFileName(file?.name ?? ''); if ( !CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS.includes( fileExtension.toLowerCase() as TupleToUnion, @@ -246,7 +251,7 @@ function IOURequestStepScan({ const createTransaction = useCallback( (receipt: Receipt, participant: Participant) => { if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense({ + trackExpense({ report, isDraftPolicy: false, participantParams: { @@ -264,7 +269,7 @@ function IOURequestStepScan({ }, }); } else { - IOU.requestMoney({ + requestMoney({ report, participantParams: { payeeEmail: currentUserPersonalDetails.login, @@ -301,10 +306,10 @@ function IOURequestStepScan({ // If the transaction was created from the + menu from the composer inside of a chat, the participants can automatically // be added to the transaction (taken from the chat report participants) and then the person is taken to the confirmation step. - const selectedParticipants = IOU.setMoneyRequestParticipantsFromReport(transactionID, report); + const selectedParticipants = setMoneyRequestParticipantsFromReport(transactionID, report); const participants = selectedParticipants.map((participant) => { const participantAccountID = participant?.accountID ?? CONST.DEFAULT_NUMBER_ID; - return participantAccountID ? OptionsListUtils.getParticipantsOption(participant, personalDetails) : OptionsListUtils.getReportOption(participant); + return participantAccountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant); }); if (shouldSkipConfirmation) { @@ -313,7 +318,7 @@ function IOURequestStepScan({ receipt.state = CONST.IOU.RECEIPT_STATE.SCANREADY; if (iouType === CONST.IOU.TYPE.SPLIT) { playSound(SOUNDS.DONE); - IOU.startSplitBill({ + startSplitBill({ participants, currentUserLogin: currentUserPersonalDetails?.login ?? '', currentUserAccountID: currentUserPersonalDetails.accountID, @@ -338,7 +343,7 @@ function IOURequestStepScan({ (successData) => { playSound(SOUNDS.DONE); if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense({ + trackExpense({ report, isDraftPolicy: false, participantParams: { @@ -364,7 +369,7 @@ function IOURequestStepScan({ }, }); } else { - IOU.requestMoney({ + requestMoney({ report, participantParams: { payeeEmail: currentUserPersonalDetails.login, @@ -435,7 +440,7 @@ function IOURequestStepScan({ const updateScanAndNavigate = useCallback( (file: FileObject, source: string) => { navigateBack(); - IOU.replaceReceipt(transactionID, file as File, source); + replaceReceipt(transactionID, file as File, source); }, [transactionID], ); @@ -459,12 +464,12 @@ function IOURequestStepScan({ if (Str.isImage(originalFile.name ?? '') && (originalFile?.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { setIsLoadingReceipt(true); } - FileUtils.resizeImageIfNeeded(originalFile).then((file) => { + resizeImageIfNeeded(originalFile).then((file) => { setIsLoadingReceipt(false); // Store the receipt on the transaction object in Onyx // On Android devices, fetching blob for a file with name containing spaces fails to retrieve the type of file. // So, let us also save the file type in receipt for later use during blob fetch - IOU.setMoneyRequestReceipt(transactionID, file?.uri ?? '', file.name ?? '', !isEditing, file.type); + setMoneyRequestReceipt(transactionID, file?.uri ?? '', file.name ?? '', !isEditing, file.type); if (isEditing) { updateScanAndNavigate(file, file?.uri ?? ''); @@ -476,7 +481,7 @@ function IOURequestStepScan({ const gpsRequired = transaction?.amount === 0 && iouType !== CONST.IOU.TYPE.SPLIT && file; if (gpsRequired) { - const shouldStartLocationPermissionFlow = IOUUtils.shouldStartLocationPermissionFlow(); + const shouldStartLocationPermissionFlow = shouldStartLocationPermissionFlowFunc(); if (shouldStartLocationPermissionFlow) { setStartLocationPermissionFlow(true); return; @@ -533,9 +538,9 @@ function IOURequestStepScan({ .then((photo: PhotoFile) => { // Store the receipt on the transaction object in Onyx const source = getPhotoSource(photo.path); - IOU.setMoneyRequestReceipt(transactionID, source, photo.path, !isEditing); + setMoneyRequestReceipt(transactionID, source, photo.path, !isEditing); - FileUtils.readFileAsync( + readFileAsync( source, photo.path, (file) => { @@ -548,7 +553,7 @@ function IOURequestStepScan({ setFileSource(source); const gpsRequired = transaction?.amount === 0 && iouType !== CONST.IOU.TYPE.SPLIT && file; if (gpsRequired) { - const shouldStartLocationPermissionFlow = IOUUtils.shouldStartLocationPermissionFlow(); + const shouldStartLocationPermissionFlow = shouldStartLocationPermissionFlowFunc(); if (shouldStartLocationPermissionFlow) { setStartLocationPermissionFlow(true); return; @@ -725,7 +730,7 @@ function IOURequestStepScan({ resetPermissionFlow={() => setStartLocationPermissionFlow(false)} onGrant={() => navigateToConfirmationStep(fileResize, fileSource, true)} onDeny={() => { - IOU.updateLastLocationPermissionPrompt(); + updateLastLocationPermissionPrompt(); navigateToConfirmationStep(fileResize, fileSource, false); }} /> diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 692381d3301f..fb05c6e72348 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -28,21 +28,29 @@ import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as Browser from '@libs/Browser'; -import * as FileUtils from '@libs/fileDownload/FileUtils'; +import { + replaceReceipt, + requestMoney, + setMoneyRequestParticipantsFromReport, + setMoneyRequestReceipt, + startSplitBill, + trackExpense, + updateLastLocationPermissionPrompt, +} from '@libs/actions/IOU'; +import {isMobile, isMobileWebKit} from '@libs/Browser'; +import {base64ToFile, resizeImageIfNeeded, splitExtensionFromFileName, validateImageForCorruption} from '@libs/fileDownload/FileUtils'; import getCurrentPosition from '@libs/getCurrentPosition'; -import * as IOUUtils from '@libs/IOUUtils'; +import {shouldStartLocationPermissionFlow as shouldStartLocationPermissionFlowFunc} from '@libs/IOUUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; +import {isArchivedReport, isPolicyExpenseChat} from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {getDefaultTaxCode} from '@libs/TransactionUtils'; import ReceiptDropUI from '@pages/iou/ReceiptDropUI'; import StepScreenDragAndDropWrapper from '@pages/iou/request/step/StepScreenDragAndDropWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; -import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -96,7 +104,7 @@ function IOURequestStepScan({ const isTabActive = useIsFocused(); const isEditing = action === CONST.IOU.ACTION.EDIT; - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction); + const defaultTaxCode = getDefaultTaxCode(policy, transaction); const transactionTaxCode = (transaction?.taxCode ? transaction?.taxCode : defaultTaxCode) ?? ''; const transactionTaxAmount = transaction?.taxAmount ?? 0; @@ -107,10 +115,7 @@ function IOURequestStepScan({ return false; } - return ( - !ReportUtils.isArchivedReport(report, reportNameValuePairs) && - !(ReportUtils.isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))) - ); + return !isArchivedReport(report, reportNameValuePairs) && !(isPolicyExpenseChat(report) && ((policy?.requiresCategory ?? false) || (policy?.requiresTag ?? false))); }, [report, skipConfirmation, policy, reportNameValuePairs]); /** @@ -118,7 +123,7 @@ function IOURequestStepScan({ * The last deviceId is of regular len camera. */ const requestCameraPermission = useCallback(() => { - if (!Browser.isMobile()) { + if (!isMobile()) { return; } @@ -129,7 +134,7 @@ function IOURequestStepScan({ setCameraPermissionState('granted'); stream.getTracks().forEach((track) => track.stop()); // Only Safari 17+ supports zoom constraint - if (Browser.isMobileWebKit() && stream.getTracks().length > 0) { + if (isMobileWebKit() && stream.getTracks().length > 0) { let deviceId; for (const track of stream.getTracks()) { const setting = track.getSettings(); @@ -170,7 +175,7 @@ function IOURequestStepScan({ }, []); useEffect(() => { - if (!Browser.isMobile() || !isTabActive) { + if (!isMobile() || !isTabActive) { setVideoConstraints(undefined); return; } @@ -209,9 +214,9 @@ function IOURequestStepScan({ }; function validateReceipt(file: FileObject) { - return FileUtils.validateImageForCorruption(file) + return validateImageForCorruption(file) .then(() => { - const {fileExtension} = FileUtils.splitExtensionFromFileName(file?.name ?? ''); + const {fileExtension} = splitExtensionFromFileName(file?.name ?? ''); if ( !CONST.API_ATTACHMENT_VALIDATIONS.ALLOWED_RECEIPT_EXTENSIONS.includes( fileExtension.toLowerCase() as TupleToUnion, @@ -271,7 +276,7 @@ function IOURequestStepScan({ const createTransaction = useCallback( (receipt: Receipt, participant: Participant) => { if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense({ + trackExpense({ report, isDraftPolicy: false, participantParams: { @@ -289,7 +294,7 @@ function IOURequestStepScan({ }, }); } else { - IOU.requestMoney({ + requestMoney({ report, participantParams: { payeeEmail: currentUserPersonalDetails.login, @@ -327,10 +332,10 @@ function IOURequestStepScan({ // If the transaction was created from the + menu from the composer inside of a chat, the participants can automatically // be added to the transaction (taken from the chat report participants) and then the person is taken to the confirmation step. - const selectedParticipants = IOU.setMoneyRequestParticipantsFromReport(transactionID, report); + const selectedParticipants = setMoneyRequestParticipantsFromReport(transactionID, report); const participants = selectedParticipants.map((participant) => { const participantAccountID = participant?.accountID ?? CONST.DEFAULT_NUMBER_ID; - return participantAccountID ? OptionsListUtils.getParticipantsOption(participant, personalDetails) : OptionsListUtils.getReportOption(participant); + return participantAccountID ? getParticipantsOption(participant, personalDetails) : getReportOption(participant); }); if (shouldSkipConfirmation) { @@ -339,7 +344,7 @@ function IOURequestStepScan({ receipt.state = CONST.IOU.RECEIPT_STATE.SCANREADY; if (iouType === CONST.IOU.TYPE.SPLIT) { playSound(SOUNDS.DONE); - IOU.startSplitBill({ + startSplitBill({ participants, currentUserLogin: currentUserPersonalDetails?.login ?? '', currentUserAccountID: currentUserPersonalDetails.accountID, @@ -364,7 +369,7 @@ function IOURequestStepScan({ (successData) => { playSound(SOUNDS.DONE); if (iouType === CONST.IOU.TYPE.TRACK && report) { - IOU.trackExpense({ + trackExpense({ report, isDraftPolicy: false, participantParams: { @@ -390,7 +395,7 @@ function IOURequestStepScan({ }, }); } else { - IOU.requestMoney({ + requestMoney({ report, participantParams: { payeeEmail: currentUserPersonalDetails.login, @@ -460,7 +465,7 @@ function IOURequestStepScan({ const updateScanAndNavigate = useCallback( (file: FileObject, source: string) => { - IOU.replaceReceipt(transactionID, file as File, source); + replaceReceipt(transactionID, file as File, source); navigateBack(); }, [transactionID, navigateBack], @@ -486,12 +491,12 @@ function IOURequestStepScan({ if (Str.isImage(originalFile.name ?? '') && (originalFile?.size ?? 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) { setIsLoadingReceipt(true); } - FileUtils.resizeImageIfNeeded(originalFile).then((file) => { + resizeImageIfNeeded(originalFile).then((file) => { setIsLoadingReceipt(false); // Store the receipt on the transaction object in Onyx const source = URL.createObjectURL(file as Blob); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - IOU.setMoneyRequestReceipt(transactionID, source, file.name || '', !isEditing); + setMoneyRequestReceipt(transactionID, source, file.name || '', !isEditing); if (isEditing) { updateScanAndNavigate(file, source); @@ -502,7 +507,7 @@ function IOURequestStepScan({ setFileSource(source); const gpsRequired = transaction?.amount === 0 && iouType !== CONST.IOU.TYPE.SPLIT && file; if (gpsRequired) { - const shouldStartLocationPermissionFlow = IOUUtils.shouldStartLocationPermissionFlow(); + const shouldStartLocationPermissionFlow = shouldStartLocationPermissionFlowFunc(); if (shouldStartLocationPermissionFlow) { setStartLocationPermissionFlow(true); @@ -540,9 +545,9 @@ function IOURequestStepScan({ } const filename = `receipt_${Date.now()}.png`; - const file = FileUtils.base64ToFile(imageBase64 ?? '', filename); + const file = base64ToFile(imageBase64 ?? '', filename); const source = URL.createObjectURL(file); - IOU.setMoneyRequestReceipt(transactionID, source, file.name, !isEditing); + setMoneyRequestReceipt(transactionID, source, file.name, !isEditing); if (isEditing) { updateScanAndNavigate(file, source); @@ -553,7 +558,7 @@ function IOURequestStepScan({ setFileSource(source); const gpsRequired = transaction?.amount === 0 && iouType !== CONST.IOU.TYPE.SPLIT && file; if (gpsRequired) { - const shouldStartLocationPermissionFlow = IOUUtils.shouldStartLocationPermissionFlow(); + const shouldStartLocationPermissionFlow = shouldStartLocationPermissionFlowFunc(); if (shouldStartLocationPermissionFlow) { setStartLocationPermissionFlow(true); return; @@ -792,8 +797,8 @@ function IOURequestStepScan({ {(isDraggingOverWrapper) => ( <> {isLoadingReceipt && } - - {!(isDraggingOver ?? isDraggingOverWrapper) && (Browser.isMobile() ? mobileCameraView() : desktopUploadView())} + + {!(isDraggingOver ?? isDraggingOverWrapper) && (isMobile() ? mobileCameraView() : desktopUploadView())} { const file = e?.dataTransfer?.files[0]; @@ -819,7 +824,7 @@ function IOURequestStepScan({ resetPermissionFlow={() => setStartLocationPermissionFlow(false)} onGrant={() => navigateToConfirmationStep(fileResize, fileSource, true)} onDeny={() => { - IOU.updateLastLocationPermissionPrompt(); + updateLastLocationPermissionPrompt(); navigateToConfirmationStep(fileResize, fileSource, false); }} /> diff --git a/tests/unit/GoogleTagManagerTest.tsx b/tests/unit/GoogleTagManagerTest.tsx index 9a2041634443..327867361081 100644 --- a/tests/unit/GoogleTagManagerTest.tsx +++ b/tests/unit/GoogleTagManagerTest.tsx @@ -1,9 +1,9 @@ import {NavigationContainer} from '@react-navigation/native'; import {render} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; -import * as IOU from '@libs/actions/IOU'; -import * as PaymentMethods from '@libs/actions/PaymentMethods'; -import * as Policy from '@libs/actions/Policy/Policy'; +import {trackExpense} from '@libs/actions/IOU'; +import {addPaymentCard, addSubscriptionPaymentCard} from '@libs/actions/PaymentMethods'; +import {createWorkspace} from '@libs/actions/Policy/Policy'; import GoogleTagManager from '@libs/GoogleTagManager'; import OnboardingModalNavigator from '@libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator'; import CONST from '@src/CONST'; @@ -55,11 +55,11 @@ describe('GoogleTagManagerTest', () => { test('workspace_created', async () => { // When we run the createWorkspace action a few times - Policy.createWorkspace(); + createWorkspace(); await waitForBatchedUpdates(); - Policy.createWorkspace(); + createWorkspace(); await waitForBatchedUpdates(); - Policy.createWorkspace(); + createWorkspace(); // Then we publish a workspace_created event only once expect(GoogleTagManager.publishEvent).toBeCalledTimes(1); @@ -67,7 +67,7 @@ describe('GoogleTagManagerTest', () => { }); test('workspace_created - categorizeTrackedExpense', () => { - IOU.trackExpense({ + trackExpense({ report: {reportID: '123'}, isDraftPolicy: true, action: CONST.IOU.ACTION.CATEGORIZE, @@ -98,7 +98,7 @@ describe('GoogleTagManagerTest', () => { test('paid_adoption - addPaymentCard', () => { // When we add a payment card - PaymentMethods.addPaymentCard(accountID, { + addPaymentCard(accountID, { expirationDate: '2077-10-30', addressZipCode: 'addressZipCode', cardNumber: 'cardNumber', @@ -113,7 +113,7 @@ describe('GoogleTagManagerTest', () => { test('paid_adoption - addSubscriptionPaymentCard', () => { // When we add a payment card - PaymentMethods.addSubscriptionPaymentCard(accountID, { + addSubscriptionPaymentCard(accountID, { cardNumber: 'cardNumber', cardYear: 'cardYear', cardMonth: 'cardMonth', From cbc63c1a690c9bc5555a71108ddc75b7e24c8476 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 21 Jan 2025 18:20:29 +0700 Subject: [PATCH 007/109] fix: toggle distance unit onpress --- .../DistanceRequest/DistanceRequestFooter.tsx | 20 ++++-- src/components/MapView/MapView.tsx | 67 ++++++++++++++++++- .../MapView/MapViewImpl.website.tsx | 31 +++++++-- src/components/MapView/MapViewTypes.ts | 7 +- .../request/step/IOURequestStepDistance.tsx | 1 + src/styles/index.ts | 2 + 6 files changed, 115 insertions(+), 13 deletions(-) diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx index d25037ec808a..56539a998f94 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.tsx +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -1,7 +1,7 @@ import React, {useCallback, useMemo} from 'react'; import type {ReactNode} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import Button from '@components/Button'; import DistanceMapView from '@components/DistanceMapView'; @@ -11,10 +11,12 @@ import type {WayPoint} from '@components/MapView/MapViewTypes'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; +import {getPersonalPolicy, getPolicy} from '@libs/PolicyUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {MapboxAccessToken} from '@src/types/onyx'; +import type {MapboxAccessToken, Policy} from '@src/types/onyx'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; import type Transaction from '@src/types/onyx/Transaction'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -35,16 +37,25 @@ type DistanceRequestFooterProps = DistanceRequestFooterOnyxProps & { /** The transaction being interacted with */ transaction: OnyxEntry; + + /** The policy */ + policy: OnyxEntry; }; -function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navigateToWaypointEditPage}: DistanceRequestFooterProps) { +function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navigateToWaypointEditPage, policy}: DistanceRequestFooterProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); + const activePolicy = getPolicy(activePolicyID); const numberOfWaypoints = Object.keys(waypoints ?? {}).length; const numberOfFilledWaypoints = Object.values(waypoints ?? {}).filter((waypoint) => waypoint?.address).length; const lastWaypointIndex = numberOfWaypoints - 1; + const defaultMileageRate = DistanceRequestUtils.getDefaultMileageRate(policy ?? activePolicy); + const policyCurrency = (policy ?? activePolicy)?.outputCurrency ?? getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policyCurrency, transaction) : defaultMileageRate; + const {unit} = mileageRate ?? {}; const getMarkerComponent = useCallback( (icon: IconAsset): ReactNode => ( @@ -114,7 +125,8 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig waypoints={waypointMarkers} styleURL={CONST.MAPBOX.STYLE_URL} overlayStyle={styles.mapEditView} - distance={TransactionUtils.getDistanceInMeters(transaction, 'km')} + distanceInMeters={TransactionUtils.getDistanceInMeters(transaction, undefined)} + unit={unit} /> diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 6f1c7aaee458..9e1bfc462153 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -6,9 +6,12 @@ import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; +import {PressableWithoutFeedback} from '@components/Pressable'; +import Text from '@components/Text'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as UserLocation from '@libs/actions/UserLocation'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import getCurrentPosition from '@libs/getCurrentPosition'; import type {GeolocationErrorCallback} from '@libs/getCurrentPosition/getCurrentPosition.types'; import {GeolocationErrorCode} from '@libs/getCurrentPosition/getCurrentPosition.types'; @@ -24,7 +27,7 @@ import responder from './responder'; import utils from './utils'; const MapView = forwardRef( - ({accessToken, style, mapPadding, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady, interactive = true}, ref) => { + ({accessToken, style, mapPadding, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady, interactive = true, distanceInMeters, unit}, ref) => { const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); const navigation = useNavigation(); const {isOffline} = useNetwork(); @@ -39,6 +42,25 @@ const MapView = forwardRef( const shouldInitializeCurrentPosition = useRef(true); const [isAccessTokenSet, setIsAccessTokenSet] = useState(false); + const [distanceUnit, setDistanceUnit] = useState(unit); + useEffect(() => { + if (!unit || distanceUnit) { + return; + } + setDistanceUnit(unit); + }, [unit, distanceUnit]); + + const toggleDistanceUnit = useCallback(() => { + setDistanceUnit((currentUnit) => + currentUnit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS ? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES : CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS, + ); + }, []); + + const distanceLabelText = useMemo( + () => DistanceRequestUtils.getDistanceForDisplayLabel(distanceInMeters ?? 0, distanceUnit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS), + [distanceInMeters, distanceUnit], + ); + // Determines if map can be panned to user's detected // location without bothering the user. It will return // false if user has already started dragging the map or @@ -205,6 +227,26 @@ const MapView = forwardRef( const initCenterCoordinate = useMemo(() => (interactive ? centerCoordinate : undefined), [interactive, centerCoordinate]); const initBounds = useMemo(() => (interactive ? undefined : waypointsBounds), [interactive, waypointsBounds]); + const distanceSymbolCoorinate = useMemo(() => { + const length = directionCoordinates?.length; + // If the array is empty, return undefined + if (!length) { + return undefined; + } + + // Find the index of the middle element + const middleIndex = Math.floor(length / 2); + + // Return the middle element + return directionCoordinates.at(middleIndex); + }, [directionCoordinates]); + + console.log('>>>>>>>>>>'); + console.log('>>>>>>>>>>'); + console.log('>>>>>>>>>>'); + console.log('>>>>>>>>>>'); + console.log('>>>>>>>>>>', DistanceRequestUtils.getDistanceForDisplayLabel(distanceInMeters, distanceUnit)); + return !isOffline && isAccessTokenSet && !!defaultSettings ? ( ( /> )} - {waypoints?.map(({coordinate, markerComponent, id}) => { const MarkerComponent = markerComponent; if (utils.areSameCoordinate([coordinate[0], coordinate[1]], [currentPosition?.longitude ?? 0, currentPosition?.latitude ?? 0]) && interactive) { @@ -276,6 +317,28 @@ const MapView = forwardRef( })} {!!directionCoordinates && } + {!!distanceSymbolCoorinate && !!distanceInMeters && !!distanceUnit && ( + + + + + {distanceLabelText} + + + + + )} {interactive && ( diff --git a/src/components/MapView/MapViewImpl.website.tsx b/src/components/MapView/MapViewImpl.website.tsx index cdeee9b4b7da..de59cae3df76 100644 --- a/src/components/MapView/MapViewImpl.website.tsx +++ b/src/components/MapView/MapViewImpl.website.tsx @@ -12,6 +12,7 @@ import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; +import {PressableWithoutFeedback} from '@components/Pressable'; import usePrevious from '@hooks/usePrevious'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -43,7 +44,8 @@ const MapViewImpl = forwardRef( directionCoordinates, initialState = {location: CONST.MAPBOX.DEFAULT_COORDINATE, zoom: CONST.MAPBOX.DEFAULT_ZOOM}, interactive = true, - distance, + distanceInMeters, + unit, }, ref, ) => { @@ -51,6 +53,19 @@ const MapViewImpl = forwardRef( const {isOffline} = useNetwork(); const {translate} = useLocalize(); + const [distanceUnit, setDistanceUnit] = useState(unit); + useEffect(() => { + if (!unit || distanceUnit) { + return; + } + setDistanceUnit(unit); + }, [unit, distanceUnit]); + + const toggleDistanceUnit = useCallback(() => { + setDistanceUnit((currentUnit) => + currentUnit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS ? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES : CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS, + ); + }, []); const theme = useTheme(); const styles = useThemeStyles(); @@ -273,18 +288,22 @@ const MapViewImpl = forwardRef( )} - {!!distanceSymbolCoorinate && !!distance && ( + {!!distanceSymbolCoorinate && !!distanceInMeters && !!distanceUnit && ( - + - {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS)} - {DistanceRequestUtils.getDistanceForDisplayLabel(distance, CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES)} + {DistanceRequestUtils.getDistanceForDisplayLabel(distanceInMeters, distanceUnit)} - + )} {waypoints?.map(({coordinate, markerComponent, id}) => { diff --git a/src/components/MapView/MapViewTypes.ts b/src/components/MapView/MapViewTypes.ts index 14104f79779b..2b706cd642a0 100644 --- a/src/components/MapView/MapViewTypes.ts +++ b/src/components/MapView/MapViewTypes.ts @@ -1,5 +1,6 @@ import type {ReactNode} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; +import {Unit} from '@src/types/onyx/Policy'; type MapViewProps = { // Public access token to be used to fetch map data from Mapbox. @@ -23,7 +24,11 @@ type MapViewProps = { // Whether the map is interactable or not interactive?: boolean; - distance?: number; + // Distance displayed on the map in meters. + distanceInMeters?: number; + + // Unit of measurement for distance + unit?: Unit; }; type DirectionProps = { diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 2eba9759027e..920b2b576b2a 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -535,6 +535,7 @@ function IOURequestStepDistance({ waypoints={waypoints} navigateToWaypointEditPage={navigateToWaypointEditPage} transaction={transaction} + policy={policy} /> } /> diff --git a/src/styles/index.ts b/src/styles/index.ts index fc4eeaa9fb65..90c1976f8780 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4007,6 +4007,8 @@ const styles = (theme: ThemeColors) => paddingVertical: 4, borderRadius: 4, textAlign: 'center', + }, + distanceLabelText: { fontSize: 13, fontWeight: FontUtils.fontWeight.bold, color: colors.productLight100, From b1dd8bab93878963bf7d22fad6ac0de779fc569c Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 22 Jan 2025 01:13:22 +0700 Subject: [PATCH 008/109] fix: lint --- src/components/MapView/MapView.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 9e1bfc462153..e4e10b61d3f7 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -6,7 +6,7 @@ import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; -import {PressableWithoutFeedback} from '@components/Pressable'; +import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import Text from '@components/Text'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -241,12 +241,6 @@ const MapView = forwardRef( return directionCoordinates.at(middleIndex); }, [directionCoordinates]); - console.log('>>>>>>>>>>'); - console.log('>>>>>>>>>>'); - console.log('>>>>>>>>>>'); - console.log('>>>>>>>>>>'); - console.log('>>>>>>>>>>', DistanceRequestUtils.getDistanceForDisplayLabel(distanceInMeters, distanceUnit)); - return !isOffline && isAccessTokenSet && !!defaultSettings ? ( ( coordinate={distanceSymbolCoorinate} id="distance-label" key="distance-label" - onTouchStart={toggleDistanceUnit} - onAccessibilityTap={toggleDistanceUnit} > Date: Wed, 22 Jan 2025 01:32:48 +0700 Subject: [PATCH 009/109] fix: lint --- .../DistanceRequest/DistanceRequestFooter.tsx | 28 +++++++------------ src/components/MapView/MapView.tsx | 6 ++-- .../MapView/MapViewImpl.website.tsx | 6 ++-- src/components/MapView/MapViewTypes.ts | 2 +- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx index 56539a998f94..b3e58467f14a 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.tsx +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -1,7 +1,7 @@ import React, {useCallback, useMemo} from 'react'; import type {ReactNode} from 'react'; import {View} from 'react-native'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import Button from '@components/Button'; import DistanceMapView from '@components/DistanceMapView'; @@ -13,22 +13,17 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import {getPersonalPolicy, getPolicy} from '@libs/PolicyUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import { getDistanceInMeters, getWaypointIndex, isCustomUnitRateIDForP2P } from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {MapboxAccessToken, Policy} from '@src/types/onyx'; +import type {Policy} from '@src/types/onyx'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; import type Transaction from '@src/types/onyx/Transaction'; import type IconAsset from '@src/types/utils/IconAsset'; const MAX_WAYPOINTS = 25; -type DistanceRequestFooterOnyxProps = { - /** Data about Mapbox token for calling Mapbox API */ - mapboxAccessToken: OnyxEntry; -}; - -type DistanceRequestFooterProps = DistanceRequestFooterOnyxProps & { +type DistanceRequestFooterProps = { /** The waypoints for the distance expense */ waypoints?: WaypointCollection; @@ -42,19 +37,20 @@ type DistanceRequestFooterProps = DistanceRequestFooterOnyxProps & { policy: OnyxEntry; }; -function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navigateToWaypointEditPage, policy}: DistanceRequestFooterProps) { +function DistanceRequestFooter({waypoints, transaction, navigateToWaypointEditPage, policy}: DistanceRequestFooterProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const activePolicy = getPolicy(activePolicyID); + const [mapboxAccessToken] = useOnyx(ONYXKEYS.MAPBOX_ACCESS_TOKEN); const numberOfWaypoints = Object.keys(waypoints ?? {}).length; const numberOfFilledWaypoints = Object.values(waypoints ?? {}).filter((waypoint) => waypoint?.address).length; const lastWaypointIndex = numberOfWaypoints - 1; const defaultMileageRate = DistanceRequestUtils.getDefaultMileageRate(policy ?? activePolicy); const policyCurrency = (policy ?? activePolicy)?.outputCurrency ?? getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; - const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policyCurrency, transaction) : defaultMileageRate; + const mileageRate = isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policyCurrency, transaction) : defaultMileageRate; const {unit} = mileageRate ?? {}; const getMarkerComponent = useCallback( @@ -77,7 +73,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig return; } - const index = TransactionUtils.getWaypointIndex(key); + const index = getWaypointIndex(key); let MarkerComponent: IconAsset; if (index === 0) { MarkerComponent = Expensicons.DotIndicatorUnfilled; @@ -125,7 +121,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig waypoints={waypointMarkers} styleURL={CONST.MAPBOX.STYLE_URL} overlayStyle={styles.mapEditView} - distanceInMeters={TransactionUtils.getDistanceInMeters(transaction, undefined)} + distanceInMeters={getDistanceInMeters(transaction, undefined)} unit={unit} /> @@ -135,8 +131,4 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig DistanceRequestFooter.displayName = 'DistanceRequestFooter'; -export default withOnyx({ - mapboxAccessToken: { - key: ONYXKEYS.MAPBOX_ACCESS_TOKEN, - }, -})(DistanceRequestFooter); +export default DistanceRequestFooter; diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index e4e10b61d3f7..ac202c1dc6a0 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -10,7 +10,7 @@ import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeed import Text from '@components/Text'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as UserLocation from '@libs/actions/UserLocation'; +import {clearUserLocation, setUserLocation} from '@libs/actions/UserLocation'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import getCurrentPosition from '@libs/getCurrentPosition'; import type {GeolocationErrorCallback} from '@libs/getCurrentPosition/getCurrentPosition.types'; @@ -72,7 +72,7 @@ const MapView = forwardRef( if (error?.code !== GeolocationErrorCode.PERMISSION_DENIED || !initialLocation) { return; } - UserLocation.clearUserLocation(); + clearUserLocation(); }, [initialLocation], ); @@ -96,7 +96,7 @@ const MapView = forwardRef( getCurrentPosition((params) => { const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - UserLocation.setUserLocation(currentCoords); + setUserLocation(currentCoords); }, setCurrentPositionToInitialState); }, [isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), ); diff --git a/src/components/MapView/MapViewImpl.website.tsx b/src/components/MapView/MapViewImpl.website.tsx index de59cae3df76..73d7abf8b21a 100644 --- a/src/components/MapView/MapViewImpl.website.tsx +++ b/src/components/MapView/MapViewImpl.website.tsx @@ -20,7 +20,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import type {GeolocationErrorCallback} from '@libs/getCurrentPosition/getCurrentPosition.types'; import {GeolocationErrorCode} from '@libs/getCurrentPosition/getCurrentPosition.types'; -import * as UserLocation from '@userActions/UserLocation'; +import {clearUserLocation, setUserLocation} from '@userActions/UserLocation'; import CONST from '@src/CONST'; import useLocalize from '@src/hooks/useLocalize'; import useNetwork from '@src/hooks/useNetwork'; @@ -91,7 +91,7 @@ const MapViewImpl = forwardRef( if (error?.code !== GeolocationErrorCode.PERMISSION_DENIED || !initialLocation) { return; } - UserLocation.clearUserLocation(); + clearUserLocation(); }, [initialLocation], ); @@ -115,7 +115,7 @@ const MapViewImpl = forwardRef( getCurrentPosition((params) => { const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - UserLocation.setUserLocation(currentCoords); + setUserLocation(currentCoords); }, setCurrentPositionToInitialState); }, [isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), ); diff --git a/src/components/MapView/MapViewTypes.ts b/src/components/MapView/MapViewTypes.ts index 2b706cd642a0..11e90a78fffc 100644 --- a/src/components/MapView/MapViewTypes.ts +++ b/src/components/MapView/MapViewTypes.ts @@ -1,6 +1,6 @@ import type {ReactNode} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; -import {Unit} from '@src/types/onyx/Policy'; +import type {Unit} from '@src/types/onyx/Policy'; type MapViewProps = { // Public access token to be used to fetch map data from Mapbox. From aff2281d380092f44f788b9d0f130cb2785014a2 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 22 Jan 2025 01:41:39 +0700 Subject: [PATCH 010/109] fix: lint --- src/components/DistanceRequest/DistanceRequestFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx index b3e58467f14a..7bf83f3b9b42 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.tsx +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -13,7 +13,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import {getPersonalPolicy, getPolicy} from '@libs/PolicyUtils'; -import { getDistanceInMeters, getWaypointIndex, isCustomUnitRateIDForP2P } from '@libs/TransactionUtils'; +import {getDistanceInMeters, getWaypointIndex, isCustomUnitRateIDForP2P} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy} from '@src/types/onyx'; From 6f7a06f6519cb1b5fbb2ba9e438a19192568b641 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 22 Jan 2025 01:45:20 +0700 Subject: [PATCH 011/109] fix: remove unused change --- src/styles/index.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 90c1976f8780..383ffc888fa1 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3996,11 +3996,6 @@ const styles = (theme: ThemeColors) => ...wordBreak.breakWord, }, - reportActionComposeTooltipWrapper: { - backgroundColor: theme.tooltipHighlightBG, - paddingVertical: 8, - borderRadius: variables.componentBorderRadiusMedium, - }, distanceLabelWrapper: { backgroundColor: colors.green400, paddingHorizontal: 8, From adc866b68048f7e94f7c5e81089688c2e87e6e36 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:54:00 -0700 Subject: [PATCH 012/109] Update Connect-to-NetSuite.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Resource Update GH: https://github.com/Expensify/Expensify/issues/463218 • Updating the formatting of the article https://help.expensify.com/articles/new-expensify/connections/netsuite/Configure-Netsuite • Mostly following the formatting of the classic article - https://help.expensify.com/articles/expensify-classic/connections/netsuite/Connect-To-NetSuite --- .../netsuite/Connect-to-NetSuite.md | 136 +++++++++--------- 1 file changed, 67 insertions(+), 69 deletions(-) diff --git a/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md b/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md index 990217523743..cf756fca613b 100644 --- a/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md +++ b/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md @@ -1,6 +1,6 @@ --- title: Connect to NetSuite -description: Integrate NetSuite with Expensify +description: Connect NetSuite to New Expensify for streamlined expense reporting and accounting integration. order: 1 --- @@ -10,7 +10,7 @@ To use the NetSuite connection, you must have a NetSuite account and an Expensif Expensify’s integration with NetSuite supports syncing data between the two systems. Before you start connecting Expensify with NetSuite, there are a few things to note: -- You must use NetSuite administrator credentials to initiate the connection. +- You must be able to login to NetSuite as an administrator to initiate the connection - A Control Plan in Expensify is required to integrate with NetSuite. - Employees don’t need NetSuite access or a NetSuite license to submit expense reports and sync them to NetSuite. - Each NetSuite subsidiary must be connected to a separate Expensify workspace. @@ -18,38 +18,40 @@ Expensify’s integration with NetSuite supports syncing data between the two sy # Step 1: Install the Expensify Bundle in NetSuite -While logged into NetSuite as an administrator, go to **Customization > SuiteBundler > Search & Install Bundles**, then search for “Expensify”. -Click on the Expensify Connect bundle (Bundle ID 283395). -Click **Install**. -If you already have the Expensify Connect bundle installed, head to **Customization > SuiteBundler > Search & Install Bundles > List**, and update it to the latest version. -Select "Show on Existing Custom Forms" for all available fields. +1. While logged into NetSuite as an administrator, go to Customization > SuiteBundler > Search & Install Bundles, then search for “Expensify”. +2. Click on the Expensify Connect bundle (Bundle ID 283395). +3. Click **Install**. +4. If you already have the Expensify Connect bundle installed, head to _Customization > SuiteBundler > Search & Install Bundles > List_, and update it to the latest version. +5. Select **Show on Existing Custom Forms** for all available fields. + # Step 2: Enable Token-Based Authentication -In NetSuite, go to **Setup > Company > Enable Features > SuiteCloud > Manage Authentication**. -Make sure “Token Based Authentication” is enabled. -Click **Save**. +1. In NetSuite, go to _Setup > Company > Enable Features > SuiteCloud > Manage Authentication_. +2. Make sure “Token Based Authentication” is enabled. +3. Click **Save**. + # Step 3: Add Expensify Integration Role to a User -In NetSuite, head to **Lists > Employees**, and find the user to who you would like to add the Expensify Integration role. The user you select must at least have access to the permissions included in the Expensify Integration Role, and Admin access works too, but Admin access is not required. -Click **Edit > Access**, then find the Expensify Integration role in the dropdown and add it to the user. -Click **Save**. +1. In NetSuite, head to Lists > Employees, and find the user to who you would like to add the Expensify Integration role. The user you select must at least have access to the permissions included in the Expensify Integration Role, and Admin access works too, but Admin access is not required. +2. Click _Edit > Access_, then find the Expensify Integration role in the dropdown and add it to the user. +3. Click **Save**. {% include info.html %} Remember that Tokens are linked to a **User** and a **Role**, not solely to a User. It’s important to note that you cannot establish a connection with tokens using one role and then switch to another role afterward. Once you’ve initiated a connection with tokens, you must continue using the same token/user/role combination for all subsequent sync or export actions. {% include end-info.html %} -# Step 4: Create Access Tokens +# Step 4: Create Access Tokens -In NetSuite, enter “page: tokens” in the Global Search. -Click **New Access Token**. -Select Expensify as the application (this must be the original Expensify integration from the bundle). -Select the role Expensify Integration. -Click **Save**. -Copy and paste the token and token ID to a saved location on your computer (this is the only time you will see these details.) +1. In NetSuite, enter “page: tokens” in the Global Search. +2. Click **New Access Token**. +3. Select Expensify as the application (this must be the original Expensify integration from the bundle). +4. Select the role Expensify Integration. +5. Click **Save**. +6. Copy and paste the token and token ID to a saved location on your computer (this is the only time you will see these details.) # Step 5: Confirm Expense Reports are enabled in NetSuite @@ -59,9 +61,9 @@ Expense Reports must be enabled in order to use Expensify’s integration with N {% include end-info.html %} -In NetSuite, go to **Setup > Company > Enable Features > Employees**. -Confirm the checkbox next to "Expense Reports" is checked. -If not, click the checkbox and then click **Save** to enable Expense Reports. +1. In NetSuite, go to Setup > Company > Enable Features > Employees. +2. Confirm the checkbox next to Expense Reports_ is checked. +3. If not, click the checkbox and then click **Save** to enable Expense Reports. # Step 6: Confirm Expense Categories are set up in NetSuite @@ -70,79 +72,75 @@ If not, click the checkbox and then click **Save** to enable Expense Reports. Once Expense Reports are enabled, Expense Categories can be set up in NetSuite. Expense Categories are synced to Expensify as Categories. Each Expense Category is an alias mapped to a General Ledger account so that employees can more easily categorize expenses. {% include end-info.html %} +1. In NetSuite, go to _Setup > Accounting > Expense Categories_ (a list of Expense Categories should show.) +2. If no Expense Categories are visible, click **New** to create new ones. -In NetSuite, go to **Setup > Accounting > Expense Categories** (a list of Expense Categories should show.) -If no Expense Categories are visible, click **New** to create new ones. # Step 7: Confirm Journal Entry Transaction Forms are Configured Properly -In NetSuite, go to **Customization > Forms > Transaction Forms.** -Click **Customize** or **Edit** next to the Standard Journal Entry form. -Click **Screen Fields > Main**. Please verify the “Created From” label has “Show” checked and the "Display Type" is set to "Normal." -Click the sub-header **Lines** and verify that the “Show” column for “Receipt URL” is checked. -Go to **Customization > Forms > Transaction Forms** and ensure that all other transaction forms with the journal type have this same configuration. +1. In NetSuite, go to _Customization > Forms > Transaction Forms_. +2. Click **Customize** or **Edit** next to the Standard Journal Entry form. +3. Click _Screen Fields > Main_. Please verify the “Created From” label has “Show” checked and the "Display Type" is set to "Normal." +4. Click the sub-header **Lines** and verify that the “Show” column for “Receipt URL” is checked. +5. Go to _Customization > Forms > Transaction Forms_ and ensure that all other transaction forms with the journal type have this same configuration. # Step 8: Confirm Expense Report Transaction Forms are Configured Properly - -In NetSuite, go to **Customization > Forms > Transaction Forms.** -Click **Customize** or **Edit** next to the Standard Expense Report form, then click **Screen Fields > Main.** -Verify the “Created From” label has “Show” checked and the "Display Type" is set to "Normal." -Click the second sub-header, **Expenses**, and verify that the "Show" column for "Receipt URL" is checked. -Go to **Customization > Forms > Transaction Forms** and ensure that all other transaction forms with the expense report type have this same configuration. +1. In NetSuite, go to _Customization > Forms > Transaction Forms_. +2. Click **Customize** or **Edit** next to the Standard Expense Report form, then click _Screen Fields > Main_. +3. Verify the “Created From” label has “Show” checked and the "Display Type" is set to "Normal." +4. Click the second sub-header, **Expenses**, and verify that the "Show" column for "Receipt URL" is checked. +5. Go to _Customization > Forms > Transaction Forms_ and ensure that all other transaction forms with the expense report type have this same configuration. # Step 9: Confirm Vendor Bill Transactions Forms are Configured Properly - -In NetSuite, go to **Customization > Forms > Transaction Forms.** -Click **Customize** or **Edit** next to your preferred Vendor Bill form. -Click **Screen Fields > Main** and verify that the “Created From” label has “Show” checked and that Departments, Classes, and Locations have the “Show” label unchecked. -Under the **Expenses** sub-header (make sure to click the “Expenses” sub-header at the very bottom and not “Expenses & Items”), ensure “Show” is checked for Receipt URL, Department, Location, and Class. -Go to **Customization > Forms > Transaction Forms** and ensure that all other transaction forms with the vendor bill type have this same configuration. +1. In NetSuite, go to _Customization > Forms > Transaction Forms_. +2. Click **Customize** or **Edit** next to your preferred Vendor Bill form. +3. Click _Screen Fields > Main_ and verify that the “Created From” label has “Show” checked and that Departments, Classes, and Locations have the “Show” label unchecked. +4. Under the **Expenses** sub-header (make sure to click the “Expenses” sub-header at the very bottom and not “Expenses & Items”), ensure “Show” is checked for Receipt URL, Department, Location, and Class. +5. Go to _Customization > Forms > Transaction Forms_ and ensure that all other transaction forms with the vendor bill type have this same configuration. # Step 10: Confirm Vendor Credit Transactions Forms are Configured Properly - -In NetSuite, go to **Customization > Forms > Transaction Forms**. -Click **Customize** or **Edit** next to your preferred Vendor Credit form, then click **Screen Fields > Main** and verify that the “Created From” label has “Show” checked and that Departments, Classes, and Locations have the “Show” label unchecked. -Under the **Expenses** sub-header (make sure to click the “Expenses” sub-header at the very bottom and not “Expenses & Items”), ensure “Show” is checked for Receipt URL, Department, Location, and Class. -Go to **Customization > Forms > Transaction Forms** and ensure that all other transaction forms with the vendor credit type have this same configuration. +1. In NetSuite, go to _Customization > Forms > Transaction Forms_. +2. Click **Customize** or **Edit** next to your preferred Vendor Credit form, then click _Screen Fields > Main_ and verify that the “Created From” label has “Show” checked and that Departments, Classes, and Locations have the “Show” label unchecked. +3. Under the Expenses sub-header (make sure to click the “Expenses” sub-header at the very bottom and not “Expenses & Items”), ensure “Show” is checked for Receipt URL, Department, Location, and Class. +4. Go to _Customization > Forms > Transaction Forms_ and ensure that all other transaction forms with the vendor credit type have this same configuration. # Step 11: Set up Tax Groups (only applicable if tracking taxes) {% include info.html %} **Things to note about tax.** -Expensify imports NetSuite Tax Groups (not Tax Codes). To ensure Tax Groups can be applied to expenses go to **Setup > Accounting > Set Up Taxes** and set the _Tax Code Lists Include_ preference to “Tax Groups And Tax Codes” or “Tax Groups Only.” If this field does not display, it’s not needed for that specific country. +Expensify imports NetSuite Tax Groups (not Tax Codes). To ensure Tax Groups can be applied to expenses go to _Setup > Accounting > Set Up Taxes_ and set the _Tax Code Lists Include_ preference to “Tax Groups And Tax Codes” or “Tax Groups Only.” If this field does not display, it’s not needed for that specific country. Tax Groups are an alias for Tax Codes in NetSuite and can contain one or more Tax Codes (Please note: for UK and Ireland subsidiaries, please ensure your Tax Groups do not have more than one Tax Code). We recommend naming Tax Groups so your employees can easily understand them, as the name and rate will be displayed in Expensify. {% include end-info.html %} -Go to **Setup > Accounting > Tax Groups**. -Click **New**. -Select the country for your Tax Group. -Enter the Tax Name (this is what employees will see in Expensify.) -Select the subsidiary for this Tax Group. -Select the Tax Code from the table you wish to include in this Tax Group. -Click **Add**. -Click **Save**. -Create one NetSuite Tax Group for each tax rate you want to show in Expensify. +1. Go to _Setup > Accounting > Tax Groups_. +2. Click **New**. +3. Select the country for your Tax Group. +4. Enter the Tax Name (this is what employees will see in Expensify.) +5. Select the subsidiary for this Tax Group. +6. Select the Tax Code from the table you wish to include in this Tax Group. +7. Click **Add**. +8. Click **Save**. +9. Create one NetSuite Tax Group for each tax rate you want to show in Expensify. # Step 12: Connect Expensify to NetSuite -Click your profile image or icon in the bottom left menu. -Scroll down and click **Workspaces** in the left menu. -Select the workspace you want to connect to NetSuite. -Click **More features** in the left menu. -Click **More features** in the left menu. -Scroll down to the Integrate section and enable the Accounting toggle. -Click **Accounting** in the left menu. -Click **Connect** next to NetSuite. -Click **Next** until you reach setup step 5 (If you followed the instructions above, then the first four setup steps will already be complete.) -On setup step 5, enter your NetSuite Account ID, Token ID, and Token Secret (the NetSuite Account ID can be found in NetSuite by going to **Setup > Integration > Web Services Preferences**.) -Click **Confirm** to complete the setup. +1. Click your profile image or icon in the bottom left menu. +2. Scroll down and click **Workspaces** in the left menu. +3. Select the workspace you want to connect to NetSuite. +4. Click **More features** in the left menu. +5. Scroll down to the Integrate section and enable the **Accounting** toggle. +6. Click **Accounting** in the left menu. +7. Click **Connect** next to NetSuite. +8. Click **Next** until you reach setup step 5 (If you followed the instructions above, then the first four setup steps will already be complete.) +9. On setup step 5, enter your NetSuite Account ID, Token ID, and Token Secret (the NetSuite Account ID can be found in NetSuite by going to Setup > Integration > Web Services Preferences_.) +10. Click **Confirm** to complete the setup. ![The New Expensify workspace setting is open and the More Features tab is selected and visible. The toggle to enable Accounting is highlighted with an orange call out and is currently in the grey disabled position.]({{site.url}}/assets/images/ExpensifyHelp-Xero-1.png) @@ -151,7 +149,7 @@ Click **Confirm** to complete the setup. After completing the setup, the NetSuite connection will sync. It can take 1-2 minutes to sync with NetSuite. -Once connected, all newly approved and paid reports exported from Expensify will be generated in NetSuite using SOAP Web Services (the term NetSuite employs when records are created through the integration). +Once connected, all newly approved and paid reports exported from Expensify will be generated in NetSuite using SOAP Web Services (the term NetSuite employs when records are created through the integration). You can then move forward with [configuring the NetSuite settings](https://help.expensify.com/articles/new-expensify/connections/netsuite/Configure-Netsuite) in Expensify. {% include faq-begin.md %} From af5b6fb00059cf55c860393dc9d1527dd0348ba0 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:01:22 -0700 Subject: [PATCH 013/109] Update Connect-to-NetSuite.md Capitalising the title to match https://help.expensify.com/articles/expensify-classic/connections/netsuite/Connect-To-NetSuite --- .../new-expensify/connections/netsuite/Connect-to-NetSuite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md b/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md index cf756fca613b..d2489315f88c 100644 --- a/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md +++ b/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md @@ -1,5 +1,5 @@ --- -title: Connect to NetSuite +title: Connect To NetSuite description: Connect NetSuite to New Expensify for streamlined expense reporting and accounting integration. order: 1 --- From e4ece8748689911cd93ea9ca12026f29ea79e2f8 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:09:09 -0700 Subject: [PATCH 014/109] Rename Connect-to-NetSuite.md to Connect-To-NetSuite.md Updating the title of the article with a capital "T" so it matches the title of this article https://help.expensify.com/articles/expensify-classic/connections/netsuite/Connect-To-NetSuite --- .../netsuite/{Connect-to-NetSuite.md => Connect-To-NetSuite.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/articles/new-expensify/connections/netsuite/{Connect-to-NetSuite.md => Connect-To-NetSuite.md} (100%) diff --git a/docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md b/docs/articles/new-expensify/connections/netsuite/Connect-To-NetSuite.md similarity index 100% rename from docs/articles/new-expensify/connections/netsuite/Connect-to-NetSuite.md rename to docs/articles/new-expensify/connections/netsuite/Connect-To-NetSuite.md From dd428f5b0788f906986ab64bff105b34825ba8f8 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:12:35 -0700 Subject: [PATCH 015/109] Update redirects.csv Creating a redirect for https://help.expensify.com/articles/new-expensify/connections/netsuite/Connect-to-NetSuite to https://help.expensify.com/articles/new-expensify/connections/netsuite/Connect-To-NetSuite --- docs/redirects.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/redirects.csv b/docs/redirects.csv index 40e8b8d0ca61..c95e201877ef 100644 --- a/docs/redirects.csv +++ b/docs/redirects.csv @@ -620,3 +620,4 @@ https://help.expensify.com/articles/expensify-classic/connect-credit-cards/compa https://help.expensify.com/articles/expensify-classic/connect-credit-cards/company-cards/Reconciliation,https://help.expensify.com/articles/expensify-classic/connect-credit-cards/Reconcile-Company-Card-Expenses/ https://help.expensify.com/articles/expensify-classic/connect-credit-cards/company-cards/Troubleshooting,https://help.expensify.com/articles/expensify-classic/connect-credit-cards/Connect-Company-Cards/ https://help.expensify.com/articles/new-expensify/expensify-card/Dispute-Expensify-Card-transaction,https://help.expensify.com/articles/new-expensify/expensify-card/Disputing-Expensify-Card-Transactions +https://help.expensify.com/articles/new-expensify/connections/netsuite/Connect-to-NetSuite,https://help.expensify.com/articles/new-expensify/connections/netsuite/Connect-To-NetSuite From 9c994785dafb0135f3a407dbacd858d1d0fc5ca5 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:25:00 -0700 Subject: [PATCH 016/109] Update Connect-To-NetSuite.md Updated some minor formatting --- .../connections/netsuite/Connect-To-NetSuite.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/articles/new-expensify/connections/netsuite/Connect-To-NetSuite.md b/docs/articles/new-expensify/connections/netsuite/Connect-To-NetSuite.md index d2489315f88c..99a67a577500 100644 --- a/docs/articles/new-expensify/connections/netsuite/Connect-To-NetSuite.md +++ b/docs/articles/new-expensify/connections/netsuite/Connect-To-NetSuite.md @@ -10,7 +10,7 @@ To use the NetSuite connection, you must have a NetSuite account and an Expensif Expensify’s integration with NetSuite supports syncing data between the two systems. Before you start connecting Expensify with NetSuite, there are a few things to note: -- You must be able to login to NetSuite as an administrator to initiate the connection +- You must be able to login to NetSuite as an administrator to initiate the connection. - A Control Plan in Expensify is required to integrate with NetSuite. - Employees don’t need NetSuite access or a NetSuite license to submit expense reports and sync them to NetSuite. - Each NetSuite subsidiary must be connected to a separate Expensify workspace. @@ -18,7 +18,7 @@ Expensify’s integration with NetSuite supports syncing data between the two sy # Step 1: Install the Expensify Bundle in NetSuite -1. While logged into NetSuite as an administrator, go to Customization > SuiteBundler > Search & Install Bundles, then search for “Expensify”. +1. While logged into NetSuite as an administrator, go to _Customization > SuiteBundler > Search & Install Bundles_, then search for “Expensify”. 2. Click on the Expensify Connect bundle (Bundle ID 283395). 3. Click **Install**. 4. If you already have the Expensify Connect bundle installed, head to _Customization > SuiteBundler > Search & Install Bundles > List_, and update it to the latest version. @@ -34,7 +34,7 @@ Expensify’s integration with NetSuite supports syncing data between the two sy # Step 3: Add Expensify Integration Role to a User -1. In NetSuite, head to Lists > Employees, and find the user to who you would like to add the Expensify Integration role. The user you select must at least have access to the permissions included in the Expensify Integration Role, and Admin access works too, but Admin access is not required. +1. In NetSuite, head to _Lists > Employees_, and find the user to who you would like to add the Expensify Integration role. The user you select must have access to at least the permissions included in the Expensify Integration Role, but they’re not required to be a NetSuite admin. 2. Click _Edit > Access_, then find the Expensify Integration role in the dropdown and add it to the user. 3. Click **Save**. @@ -61,8 +61,8 @@ Expense Reports must be enabled in order to use Expensify’s integration with N {% include end-info.html %} -1. In NetSuite, go to Setup > Company > Enable Features > Employees. -2. Confirm the checkbox next to Expense Reports_ is checked. +1. In NetSuite, go to _Setup > Company > Enable Features > Employees_. +2. Confirm the checkbox next to Expense Reports is checked. 3. If not, click the checkbox and then click **Save** to enable Expense Reports. From 4e4e6a818de50912831e22bfa9707037aec337d5 Mon Sep 17 00:00:00 2001 From: shahid Date: Fri, 24 Jan 2025 21:34:06 +0530 Subject: [PATCH 017/109] Update the Export icon in report details page --- assets/images/export.svg | 6 ++++++ src/components/Icon/Expensicons.ts | 2 ++ src/pages/ReportDetailsPage.tsx | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 assets/images/export.svg diff --git a/assets/images/export.svg b/assets/images/export.svg new file mode 100644 index 000000000000..ed6ae9897368 --- /dev/null +++ b/assets/images/export.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index e4072504f3d6..1f9b8c1a01e7 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -72,6 +72,7 @@ import DotIndicatorUnfilled from '@assets/images/dot-indicator-unfilled.svg'; import DotIndicator from '@assets/images/dot-indicator.svg'; import DownArrow from '@assets/images/down.svg'; import Download from '@assets/images/download.svg'; +import Export from '@assets/images/export.svg'; import DragAndDrop from '@assets/images/drag-and-drop.svg'; import DragHandles from '@assets/images/drag-handles.svg'; import Emoji from '@assets/images/emoji.svg'; @@ -269,6 +270,7 @@ export { DotIndicatorUnfilled, DownArrow, Download, + Export, DragAndDrop, DragHandles, EReceiptIcon, diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index fc112cb09ba3..5aa3c1c71a61 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -553,7 +553,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.EXPORT, translationKey: 'common.export', - icon: Expensicons.Upload, + icon: Expensicons.Export, isAnonymousAction: false, action: () => { Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS_EXPORT.getRoute(report?.reportID, connectedIntegration, backTo)); From 8a04d7b19f7c7786c0ab0a98d953304cbfc90394 Mon Sep 17 00:00:00 2001 From: shahid Date: Fri, 24 Jan 2025 21:59:13 +0530 Subject: [PATCH 018/109] fix lint --- src/components/Icon/Expensicons.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 1f9b8c1a01e7..0bcfc05ba9c2 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -72,7 +72,6 @@ import DotIndicatorUnfilled from '@assets/images/dot-indicator-unfilled.svg'; import DotIndicator from '@assets/images/dot-indicator.svg'; import DownArrow from '@assets/images/down.svg'; import Download from '@assets/images/download.svg'; -import Export from '@assets/images/export.svg'; import DragAndDrop from '@assets/images/drag-and-drop.svg'; import DragHandles from '@assets/images/drag-handles.svg'; import Emoji from '@assets/images/emoji.svg'; @@ -89,6 +88,7 @@ import ExpensifyFooterLogoVertical from '@assets/images/expensify-footer-logo-ve import ExpensifyFooterLogo from '@assets/images/expensify-footer-logo.svg'; import ExpensifyLogoNew from '@assets/images/expensify-logo-new.svg'; import ExpensifyWordmark from '@assets/images/expensify-wordmark.svg'; +import Export from '@assets/images/export.svg'; import EyeDisabled from '@assets/images/eye-disabled.svg'; import Eye from '@assets/images/eye.svg'; import Feed from '@assets/images/feed.svg'; @@ -270,7 +270,6 @@ export { DotIndicatorUnfilled, DownArrow, Download, - Export, DragAndDrop, DragHandles, EReceiptIcon, @@ -285,6 +284,7 @@ export { ExpensifyFooterLogo, ExpensifyFooterLogoVertical, Expand, + Export, Eye, EyeDisabled, FallbackAvatar, From 400a28ab591890921cc16432229d0dd458329e3d Mon Sep 17 00:00:00 2001 From: Mohit Date: Sat, 25 Jan 2025 12:02:19 +0530 Subject: [PATCH 019/109] Report isn't showing most recent chats until refreshed --- src/components/Search/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 03b6c820da00..27ce4c24dcdb 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -215,7 +215,8 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo transactions, previousTransactions, queryJSON, - offset, + // Set offset to 0 to retrieve the most recent chat messages. + offset: 0, reportActions, previousReportActions, }); From 45641833f15a494be9ceda4c4c7d301a74a14ed5 Mon Sep 17 00:00:00 2001 From: Mohit Date: Sat, 25 Jan 2025 14:54:10 +0530 Subject: [PATCH 020/109] Fix eslint issues --- src/components/Search/index.tsx | 67 +++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 27ce4c24dcdb..7938fd598304 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -15,16 +15,25 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSearchHighlightAndScroll from '@hooks/useSearchHighlightAndScroll'; import useThemeStyles from '@hooks/useThemeStyles'; import {turnOffMobileSelectionMode, turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; -import * as SearchActions from '@libs/actions/Search'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import {createTransactionThread, search} from '@libs/actions/Search'; +import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import memoize from '@libs/memoize'; import isSearchTopmostCentralPane from '@libs/Navigation/isSearchTopmostCentralPane'; import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as SearchQueryUtils from '@libs/SearchQueryUtils'; -import * as SearchUIUtils from '@libs/SearchUIUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {generateReportID} from '@libs/ReportUtils'; +import {buildSearchQueryString} from '@libs/SearchQueryUtils'; +import { + getListItem, + getSections, + getSortedSections, + isReportActionListItemType, + isReportListItemType, + isSearchResultsEmpty as isSearchResultsEmptyFromUiUtils, + isTransactionListItemType, + shouldShowYear as shouldShowYearFromUiUtils, +} from '@libs/SearchUIUtils'; +import {isOnHold} from '@libs/TransactionUtils'; import Navigation from '@navigation/Navigation'; import type {AuthScreensParamList} from '@navigation/types'; import EmptySearchView from '@pages/Search/EmptySearchView'; @@ -57,7 +66,7 @@ function mapTransactionItemToSelectedEntry(item: TransactionListItemType): [stri isSelected: true, canDelete: item.canDelete, canHold: item.canHold, - isHeld: TransactionUtils.isOnHold(item), + isHeld: isOnHold(item), canUnhold: item.canUnhold, action: item.action, reportID: item.reportID, @@ -77,14 +86,14 @@ function mapToItemWithSelectionInfo( canSelectMultiple: boolean, shouldAnimateInHighlight: boolean, ) { - if (SearchUIUtils.isReportActionListItemType(item)) { + if (isReportActionListItemType(item)) { return { ...item, shouldAnimateInHighlight, }; } - return SearchUIUtils.isTransactionListItemType(item) + return isTransactionListItemType(item) ? mapToTransactionItemWithSelectionInfo(item, selectedTransactions, canSelectMultiple, shouldAnimateInHighlight) : { ...item, @@ -107,7 +116,7 @@ function prepareTransactionsList(item: TransactionListItemType, selectedTransact isSelected: true, canDelete: item.canDelete, canHold: item.canHold, - isHeld: TransactionUtils.isOnHold(item), + isHeld: isOnHold(item), canUnhold: item.canUnhold, action: item.action, reportID: item.reportID, @@ -176,12 +185,12 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo return; } - SearchActions.search({queryJSON, offset}); + search({queryJSON, offset}); }, [isOffline, offset, queryJSON]); const getItemHeight = useCallback( (item: TransactionListItemType | ReportListItemType | ReportActionListItemType) => { - if (SearchUIUtils.isTransactionListItemType(item) || SearchUIUtils.isReportActionListItemType(item)) { + if (isTransactionListItemType(item) || isReportActionListItemType(item)) { return isLargeScreenWidth ? variables.optionRowHeight + listItemPadding : transactionItemMobileHeight + listItemPadding; } @@ -230,14 +239,14 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo const shouldShowLoadingState = !isOffline && !isDataLoaded; const shouldShowLoadingMoreItems = !shouldShowLoadingState && searchResults?.search?.isLoading && searchResults?.search?.offset > 0; - const isSearchResultsEmpty = !searchResults?.data || SearchUIUtils.isSearchResultsEmpty(searchResults); + const isSearchResultsEmpty = !searchResults?.data || isSearchResultsEmptyFromUiUtils(searchResults); const prevIsSearchResultEmpty = usePrevious(isSearchResultsEmpty); const data = useMemo(() => { if (searchResults === undefined) { return []; } - return SearchUIUtils.getSections(type, status, searchResults.data, searchResults.search); + return getSections(type, status, searchResults.data, searchResults.search); }, [searchResults, status, type]); useEffect(() => { @@ -261,7 +270,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo newTransactionList[transaction.transactionID] = { action: transaction.action, canHold: transaction.canHold, - isHeld: TransactionUtils.isOnHold(transaction), + isHeld: isOnHold(transaction), canUnhold: transaction.canUnhold, isSelected: selectedTransactions[transaction.transactionID].isSelected, canDelete: transaction.canDelete, @@ -282,7 +291,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo newTransactionList[transaction.transactionID] = { action: transaction.action, canHold: transaction.canHold, - isHeld: TransactionUtils.isOnHold(transaction), + isHeld: isOnHold(transaction), canUnhold: transaction.canUnhold, isSelected: selectedTransactions[transaction.transactionID].isSelected, canDelete: transaction.canDelete, @@ -329,8 +338,8 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo return {null}; } - const ListItem = SearchUIUtils.getListItem(type, status); - const sortedData = SearchUIUtils.getSortedSections(type, status, data, sortBy, sortOrder); + const ListItem = getListItem(type, status); + const sortedData = getSortedSections(type, status, data, sortBy, sortOrder); const isChat = type === CONST.SEARCH.DATA_TYPES.CHAT; const sortedSelectedData = sortedData.map((item) => { const baseKey = isChat @@ -365,10 +374,10 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo } const toggleTransaction = (item: TransactionListItemType | ReportListItemType | ReportActionListItemType) => { - if (SearchUIUtils.isReportActionListItemType(item)) { + if (isReportActionListItemType(item)) { return; } - if (SearchUIUtils.isTransactionListItemType(item)) { + if (isTransactionListItemType(item)) { if (!item.keyForList) { return; } @@ -399,21 +408,21 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo const openReport = (item: TransactionListItemType | ReportListItemType | ReportActionListItemType) => { const isFromSelfDM = item.reportID === CONST.REPORT.UNREPORTED_REPORTID; - let reportID = SearchUIUtils.isTransactionListItemType(item) && (!item.isFromOneTransactionReport || isFromSelfDM) ? item.transactionThreadReportID : item.reportID; + let reportID = isTransactionListItemType(item) && (!item.isFromOneTransactionReport || isFromSelfDM) ? item.transactionThreadReportID : item.reportID; if (!reportID) { return; } // If we're trying to open a legacy transaction without a transaction thread, let's create the thread and navigate the user - if (SearchUIUtils.isTransactionListItemType(item) && reportID === '0' && item.moneyRequestReportActionID) { - reportID = ReportUtils.generateReportID(); - SearchActions.createTransactionThread(hash, item.transactionID, reportID, item.moneyRequestReportActionID); + if (isTransactionListItemType(item) && reportID === '0' && item.moneyRequestReportActionID) { + reportID = generateReportID(); + createTransactionThread(hash, item.transactionID, reportID, item.moneyRequestReportActionID); } const backTo = Navigation.getActiveRoute(); - if (SearchUIUtils.isReportActionListItemType(item)) { + if (isReportActionListItemType(item)) { const reportActionID = item.reportActionID; Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, reportActionID, backTo})); return; @@ -449,11 +458,11 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo }; const onSortPress = (column: SearchColumnType, order: SortOrder) => { - const newQuery = SearchQueryUtils.buildSearchQueryString({...queryJSON, sortBy: column, sortOrder: order}); + const newQuery = buildSearchQueryString({...queryJSON, sortBy: column, sortOrder: order}); navigation.setParams({q: newQuery}); }; - const shouldShowYear = SearchUIUtils.shouldShowYear(searchResults?.data); + const shouldShowYear = shouldShowYearFromUiUtils(searchResults?.data); const shouldShowSorting = !Array.isArray(status) && sortableSearchStatuses.includes(status); return ( @@ -478,7 +487,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo ) } isSelected={(item) => - status !== CONST.SEARCH.STATUS.EXPENSE.ALL && SearchUIUtils.isReportListItemType(item) + status !== CONST.SEARCH.STATUS.EXPENSE.ALL && isReportListItemType(item) ? item.transactions.some((transaction) => selectedTransactions[transaction.keyForList]?.isSelected) : !!item.isSelected } @@ -502,7 +511,7 @@ function Search({queryJSON, onSearchListScroll, isSearchScreenFocused, contentCo onSelectRow={openReport} getItemHeight={getItemHeightMemoized} shouldSingleExecuteRowSelect - shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} + shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()} shouldPreventDefault={false} listHeaderWrapperStyle={[styles.ph8, styles.pt3]} containerStyle={[styles.pv0, type === CONST.SEARCH.DATA_TYPES.CHAT && !isSmallScreenWidth && styles.pt3]} From 9f403a75224ab13c749148a9ebc0d7435e9c6ea3 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 28 Jan 2025 17:19:39 +0700 Subject: [PATCH 021/109] fix: update label color to green500 --- src/styles/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index dd6a4173305e..0001f5b77c3d 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3994,7 +3994,7 @@ const styles = (theme: ThemeColors) => }, distanceLabelWrapper: { - backgroundColor: colors.green400, + backgroundColor: colors.green500, paddingHorizontal: 8, paddingVertical: 4, borderRadius: 4, From c5cdd198ca57993e909e2a59813377e14145e046 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 30 Jan 2025 11:40:24 +0100 Subject: [PATCH 022/109] Refactor invoice report name generation to include the current date --- src/libs/ReportUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5e50f69e28fa..739147afb9ad 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4914,7 +4914,6 @@ function buildOptimisticInvoiceReport( total: number, currency: string, ): OptimisticExpenseReport { - const formattedTotal = convertToDisplayString(total, currency); const invoiceReport = { reportID: generateReportID(), chatReportID, @@ -4924,7 +4923,7 @@ function buildOptimisticInvoiceReport( managerID: receiverAccountID, currency, // We don’t translate reportName because the server response is always in English - reportName: `${receiverName} owes ${formattedTotal}`, + reportName: `Invoice ${DateUtils.extractDate(new Date().toString())}`, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.OPEN, total, From bf4bbfcd3425831a52dbb5b062510bdd349b493b Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 30 Jan 2025 16:16:08 +0100 Subject: [PATCH 023/109] create isNewDotInvoice --- src/libs/ReportUtils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 739147afb9ad..2a4bf7adf790 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1043,6 +1043,10 @@ function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): boole return report?.type === CONST.REPORT.TYPE.INVOICE; } +function isNewDotInvoice(report: OnyxEntry): boolean { + return isInvoiceRoom(getReport(report?.chatReportID)); +} + /** * Checks if a report is an Expense report. */ @@ -9144,6 +9148,7 @@ export { isInvoiceRoom, isInvoiceRoomWithID, isInvoiceReport, + isNewDotInvoice, isOpenInvoiceReport, getDefaultNotificationPreferenceForReport, canWriteInReport, From 0d4ff8e48ed6b50b4721daea9e6cec4f343b438d Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 30 Jan 2025 16:30:01 +0100 Subject: [PATCH 024/109] update isNewDotInvoice function to accept invoiceRoomID as parameter --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2a4bf7adf790..9e693492b0c3 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1043,8 +1043,8 @@ function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): boole return report?.type === CONST.REPORT.TYPE.INVOICE; } -function isNewDotInvoice(report: OnyxEntry): boolean { - return isInvoiceRoom(getReport(report?.chatReportID)); +function isNewDotInvoice(invoiceRoomID: string | undefined): boolean { + return isInvoiceRoom(getReport(invoiceRoomID)); } /** From edb48d87bd429a0e91abe7d0c6c4e3f76c350f71 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 30 Jan 2025 16:30:23 +0100 Subject: [PATCH 025/109] integrate isNewDotInvoice --- src/libs/ReportUtils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9e693492b0c3..b3aa2dceb052 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4322,7 +4322,9 @@ function getReportName( } if (isInvoiceReport(report)) { - formattedName = report?.reportName ?? getMoneyRequestReportName(report, policy, invoiceReceiverPolicy); + const moneyRequestReportName = getMoneyRequestReportName(report, policy, invoiceReceiverPolicy); + const ODInvoiceName = report?.reportName ?? moneyRequestReportName; + formattedName = isNewDotInvoice(report?.chatReportID) ? moneyRequestReportName : ODInvoiceName; } if (isInvoiceRoom(report)) { @@ -4918,6 +4920,7 @@ function buildOptimisticInvoiceReport( total: number, currency: string, ): OptimisticExpenseReport { + const formattedTotal = convertToDisplayString(total, currency); const invoiceReport = { reportID: generateReportID(), chatReportID, @@ -4927,7 +4930,7 @@ function buildOptimisticInvoiceReport( managerID: receiverAccountID, currency, // We don’t translate reportName because the server response is always in English - reportName: `Invoice ${DateUtils.extractDate(new Date().toString())}`, + reportName: isNewDotInvoice(chatReportID) ? `${receiverName} owes ${formattedTotal}` : `Invoice ${DateUtils.extractDate(new Date().toString())}`, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.OPEN, total, From fb1e21b54084e336492ecb61cabfe53297791477 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 30 Jan 2025 16:35:48 +0100 Subject: [PATCH 026/109] revert reportName of buildOptimisticInvoiceReport --- src/libs/ReportUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b3aa2dceb052..516071a7e578 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4184,6 +4184,7 @@ function buildReportNameFromParticipantNames({report, personalDetails}: {report: return participantsWithoutCurrentUser.map((accountID) => getDisplayNameForParticipant(accountID, isMultipleParticipantReport, true, false, personalDetails)).join(', '); } +// #region - getReportName /** * Get the title for a report. */ @@ -4911,6 +4912,7 @@ function populateOptimisticReportFormula(formula: string, report: OptimisticExpe return result.trim().length ? result : formula; } +// #region - buildOptimisticInvoiceReport /** Builds an optimistic invoice report with a randomly generated reportID */ function buildOptimisticInvoiceReport( chatReportID: string, @@ -4930,7 +4932,7 @@ function buildOptimisticInvoiceReport( managerID: receiverAccountID, currency, // We don’t translate reportName because the server response is always in English - reportName: isNewDotInvoice(chatReportID) ? `${receiverName} owes ${formattedTotal}` : `Invoice ${DateUtils.extractDate(new Date().toString())}`, + reportName: `${receiverName} owes ${formattedTotal}`, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.OPEN, total, From bc93838b0a8e6745ff735c16a1f42286aedab50f Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 30 Jan 2025 16:36:32 +0100 Subject: [PATCH 027/109] clear --- src/libs/ReportUtils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 516071a7e578..9dcdb40eeab7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4184,7 +4184,6 @@ function buildReportNameFromParticipantNames({report, personalDetails}: {report: return participantsWithoutCurrentUser.map((accountID) => getDisplayNameForParticipant(accountID, isMultipleParticipantReport, true, false, personalDetails)).join(', '); } -// #region - getReportName /** * Get the title for a report. */ @@ -4912,7 +4911,6 @@ function populateOptimisticReportFormula(formula: string, report: OptimisticExpe return result.trim().length ? result : formula; } -// #region - buildOptimisticInvoiceReport /** Builds an optimistic invoice report with a randomly generated reportID */ function buildOptimisticInvoiceReport( chatReportID: string, From 19f0d68f498d056d43c591cd8855e1a1d0992732 Mon Sep 17 00:00:00 2001 From: M00rish Date: Fri, 31 Jan 2025 12:27:09 +0100 Subject: [PATCH 028/109] fix Not found page blinking --- src/pages/home/ReportScreen.tsx | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 39a670b43284..c1b809c932b6 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -386,7 +386,9 @@ function ReportScreen({route, navigation}: ReportScreenProps) { () => !!linkedAction && !shouldReportActionBeVisible(linkedAction, linkedAction.reportActionID, canUserPerformWriteAction(report)), [linkedAction, report], ); - const prevIsLinkedActionDeleted = usePrevious(linkedAction ? isLinkedActionDeleted : undefined); + + const [isClearingDeletedLinkedAction, setIsClearingDeletedLinkedAction] = useState(); + const isLinkedActionInaccessibleWhisper = useMemo( () => !!linkedAction && isWhisperAction(linkedAction) && !(linkedAction?.whisperedToAccountIDs ?? []).includes(currentUserAccountID), [currentUserAccountID, linkedAction], @@ -416,11 +418,9 @@ function ReportScreen({route, navigation}: ReportScreenProps) { (!!deleteTransactionNavigateBackUrl && getReportIDFromLink(deleteTransactionNavigateBackUrl) === report?.reportID) || (!reportMetadata.isOptimisticReport && isLoading); - const isLinkedActionBecomesDeleted = prevIsLinkedActionDeleted !== undefined && !prevIsLinkedActionDeleted && isLinkedActionDeleted; - // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundLinkedAction = - (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && !isLinkedActionBecomesDeleted) || + (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && isClearingDeletedLinkedAction === false) || (shouldShowSkeleton && !reportMetadata.isLoadingInitialReportActions && !!reportActionIDFromRoute && @@ -735,13 +735,23 @@ function ReportScreen({route, navigation}: ReportScreenProps) { }, [fetchReport]); useEffect(() => { - // If the linked action is previously available but now deleted, - // remove the reportActionID from the params to not link to the deleted action. - if (!isLinkedActionBecomesDeleted) { + + if (!isLinkedActionDeleted || isClearingDeletedLinkedAction) + { return; } + Navigation.setParams({reportActionID: ''}); - }, [isLinkedActionBecomesDeleted]); + setIsClearingDeletedLinkedAction(true); + }, [isLinkedActionDeleted, isClearingDeletedLinkedAction]); + + + useEffect(() => { + if (!isClearingDeletedLinkedAction || reportActionIDFromRoute) { + return; + } + setIsClearingDeletedLinkedAction(false); + }, [isClearingDeletedLinkedAction, reportActionIDFromRoute]); // If user redirects to an inaccessible whisper via a deeplink, on a report they have access to, // then we set reportActionID as empty string, so we display them the report and not the "Not found page". From 0cf5ebbad04eb8c78b637bc29bc6bf599b1e5a35 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:03:51 +0100 Subject: [PATCH 029/109] working highlighting --- src/libs/runOnLiveMarkdownRuntime/index.native.tsx | 8 ++++++++ src/libs/runOnLiveMarkdownRuntime/index.tsx | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 src/libs/runOnLiveMarkdownRuntime/index.native.tsx create mode 100644 src/libs/runOnLiveMarkdownRuntime/index.tsx diff --git a/src/libs/runOnLiveMarkdownRuntime/index.native.tsx b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx new file mode 100644 index 000000000000..8927fe0a936c --- /dev/null +++ b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx @@ -0,0 +1,8 @@ +import {getWorkletRuntime} from '@expensify/react-native-live-markdown'; +import {runOnRuntime} from 'react-native-reanimated'; + +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { + return runOnRuntime(getWorkletRuntime(), worklet); +} + +export default runOnLiveMarkdownRuntime; diff --git a/src/libs/runOnLiveMarkdownRuntime/index.tsx b/src/libs/runOnLiveMarkdownRuntime/index.tsx new file mode 100644 index 000000000000..c8e072693323 --- /dev/null +++ b/src/libs/runOnLiveMarkdownRuntime/index.tsx @@ -0,0 +1,6 @@ +// Reanimated does not support runOnRuntime() on web +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { + return worklet; +} + +export default runOnLiveMarkdownRuntime; From 85d543554cc3b1a4320de755eb2a95518334ac19 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:13:16 +0100 Subject: [PATCH 030/109] add syntax highlighting --- .../Search/SearchAutocompleteInput.tsx | 89 +++++++++++++++++-- .../SearchInputSelectionWrapper/index.tsx | 2 +- .../Search/SearchPageHeaderInput.tsx | 1 + .../Search/SearchRouter/SearchRouter.tsx | 1 + src/libs/SearchAutocompleteUtils.ts | 52 ++++++++--- 5 files changed, 126 insertions(+), 19 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 215599952c50..3e260a9bda96 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,20 +1,27 @@ +import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; -import React, {forwardRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import {useSharedValue} from 'react-native-reanimated'; 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 useActiveWorkspace from '@hooks/useActiveWorkspace'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; -import {parseForLiveMarkdown} from '@libs/SearchAutocompleteUtils'; +import runOnLiveMarkdownRuntime from '@libs/runOnLiveMarkdownRuntime'; +import {getAutocompleteCategories, getAutocompleteTags, parseForLiveMarkdown} from '@libs/SearchAutocompleteUtils'; import handleKeyPress from '@libs/SearchInputOnKeyPress'; import shouldDelayFocus from '@libs/shouldDelayFocus'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {SubstitutionMap} from './SearchRouter/getQueryWithSubstitutions'; type SearchAutocompleteInputProps = { /** Value of TextInput */ @@ -59,6 +66,9 @@ type SearchAutocompleteInputProps = { /** Whether the search reports API call is running */ isSearchingForReports?: boolean; + /** Map of autocomplete suggestions. Required for highlighting to work properly */ + substitutionMap: SubstitutionMap; + /** input style */ inputStyle?: StyleProp; } & Pick; @@ -83,6 +93,7 @@ function SearchAutocompleteInput( isSearchingForReports, selection, inputStyle, + substitutionMap, }: SearchAutocompleteInputProps, ref: ForwardedRef, ) { @@ -90,9 +101,75 @@ function SearchAutocompleteInput( const {translate} = useLocalize(); const [isFocused, setIsFocused] = useState(false); const {isOffline} = useNetwork(); + const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const personalDetailsSharedValue = useSharedValue({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); + const lastMap = useRef({}); + const [map, setMap] = useState({}); + + const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); + const currencyAutocompleteList = Object.keys(currencyList ?? {}); + const currencySharedValue = useSharedValue(currencyAutocompleteList); + + const [allPolicyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES); + const categoryAutocompleteList = useMemo(() => { + return getAutocompleteCategories(allPolicyCategories, activeWorkspaceID); + }, [activeWorkspaceID, allPolicyCategories]); + const categorySharedValue = useSharedValue(categoryAutocompleteList); + + const [allPoliciesTags] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS); + const tagAutocompleteList = useMemo(() => { + return getAutocompleteTags(allPoliciesTags, activeWorkspaceID); + }, [activeWorkspaceID, allPoliciesTags]); + const tagSharedValue = useSharedValue(tagAutocompleteList); + + useEffect(() => { + if (lastMap.current && !isEqual(lastMap.current, substitutionMap)) { + lastMap.current = substitutionMap; + } + setMap(lastMap.current ?? {}); + }, [substitutionMap, lastMap]); + const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + personalDetailsSharedValue.set({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); + })(); + }, [currentUserPersonalDetails, personalDetailsSharedValue]); + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + currencySharedValue.set(currencyAutocompleteList); + })(); + }, [currencyAutocompleteList, currencySharedValue]); + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + categorySharedValue.set(categoryAutocompleteList); + })(); + }, [categorySharedValue, categoryAutocompleteList]); + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + tagSharedValue.set(tagAutocompleteList); + }); + }, [tagSharedValue, tagAutocompleteList]); + + const parser = useCallback( + (input: string) => { + 'worklet'; + + return parseForLiveMarkdown(input, personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue); + }, + [personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue], + ); + const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; return ( @@ -121,7 +198,7 @@ function SearchAutocompleteInput( maxLength={CONST.SEARCH_QUERY_LIMIT} onSubmitEditing={onSubmit} shouldUseDisabledStyles={false} - textInputContainerStyles={[styles.borderNone, styles.pb0]} + textInputContainerStyles={[styles.borderNone, styles.pb0, styles.pr3]} inputStyle={[inputWidth, inputStyle]} onFocus={() => { setIsFocused(true); @@ -138,11 +215,7 @@ function SearchAutocompleteInput( onKeyPress={handleKeyPress(onSubmit)} isMarkdownEnabled multiline={false} - parser={(input: string) => { - 'worklet'; - - return parseForLiveMarkdown(input, currentUserPersonalDetails.login ?? '', currentUserPersonalDetails.displayName ?? ''); - }} + parser={parser} selection={selection} /> diff --git a/src/components/Search/SearchInputSelectionWrapper/index.tsx b/src/components/Search/SearchInputSelectionWrapper/index.tsx index a806cea9afd4..1a0a18ffaa47 100644 --- a/src/components/Search/SearchInputSelectionWrapper/index.tsx +++ b/src/components/Search/SearchInputSelectionWrapper/index.tsx @@ -10,7 +10,7 @@ function SearchInputSelectionWrapper({selection, ...props}: SearchAutocompleteIn return ( , + map: SubstitutionMap, + currencyList: SharedValue, + categoryList: SharedValue, + tagList: SharedValue, +) { 'worklet'; const parsedAutocomplete = parse(input) as SearchAutocompleteResult; const ranges = parsedAutocomplete.ranges; - return ranges.map((range) => { - let type = 'mention-user'; - - if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (range.value === userLogin || range.value === userDisplayName)) { - type = 'mention-here'; - } - - return {...range, type}; - }) as MarkdownRange[]; + const typeList = Object.values(CONST.SEARCH.DATA_TYPES) as string[]; + const expenseTypeList = Object.values(CONST.SEARCH.TRANSACTION_TYPE) as string[]; + const statusList = Object.values({...CONST.SEARCH.STATUS.TRIP, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP}) as string[]; + const subMap = map; + return ranges + .filter( + (range) => + !( + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || + range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID + ) || subMap[`${range.key}:${range.value}`] !== undefined, + ) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || currencyList.get().includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE || typeList.includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE || expenseTypeList.includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS || statusList.includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY || categoryList.get().includes(range.value)) + .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG || tagList.get().includes(range.value)) + .map((range) => { + let type = 'mention-user'; + if ( + (range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && + (range.value === details.get().login || range.value === details.get().userDisplayName) + ) { + type = 'mention-here'; + } + + return {...range, type}; + }) as MarkdownRange[]; } export { From 944fbdd2344a2663746588dc29ed65d064922b40 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:35:45 +0100 Subject: [PATCH 031/109] add supporting text --- .../Search/SearchRouter/buildSubstitutionsMap.ts | 2 +- .../Search/SearchRouter/getQueryWithSubstitutions.ts | 2 +- .../Search/SearchRouter/getUpdatedSubstitutionsMap.ts | 2 +- src/components/Search/types.ts | 4 +++- src/libs/SearchAutocompleteUtils.ts | 3 +++ src/libs/SearchParser/autocompleteParser.js | 9 +++++---- src/libs/SearchParser/autocompleteParser.peggy | 9 +++++---- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts index 892aa050ef9d..0a3c49819b38 100644 --- a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts @@ -33,7 +33,7 @@ function buildSubstitutionsMap( ): SubstitutionMap { const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges; + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts index 84895efb33a5..b976371135a6 100644 --- a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts +++ b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts @@ -21,7 +21,7 @@ const getSubstitutionMapKey = (filterKey: SearchFilterKey, value: string) => `${ function getQueryWithSubstitutions(changedQuery: string, substitutions: SubstitutionMap) { const parsed = parse(changedQuery) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsed.ranges; + const searchAutocompleteQueryRanges = parsed.ranges.filter((range) => range.key !== 'syntax'); if (searchAutocompleteQueryRanges.length === 0) { return changedQuery; diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index ee7bf3850259..a681ae6030f0 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -18,7 +18,7 @@ const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${fi function getUpdatedSubstitutionsMap(query: string, substitutions: SubstitutionMap): SubstitutionMap { const parsedQuery = parser.parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges; + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index 0a402358d73e..b5c58c798b9f 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -95,7 +95,8 @@ type SearchFilterKey = | ValueOf | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS - | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID; + | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID + | 'syntax'; type UserFriendlyKey = ValueOf; @@ -128,6 +129,7 @@ type SearchAutocompleteResult = { ranges: SearchAutocompleteQueryRange[]; }; +// TODO FIX types type SearchAutocompleteQueryRange = { key: SearchFilterKey; length: number; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index a591e08c2314..d9abec7abf3e 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -182,6 +182,9 @@ function parseForLiveMarkdown( ) { type = 'mention-here'; } + if (range.key === 'syntax') { + type = 'syntax'; + } return {...range, type}; }) as MarkdownRange[]; diff --git a/src/libs/SearchParser/autocompleteParser.js b/src/libs/SearchParser/autocompleteParser.js index 5a787c5d8048..16e135deb222 100644 --- a/src/libs/SearchParser/autocompleteParser.js +++ b/src/libs/SearchParser/autocompleteParser.js @@ -282,20 +282,21 @@ function peg$parse(input, options) { start: location().end.offset, length: 0, }; - return; + return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; } autocomplete = { key, ...value[value.length - 1], }; - - return value + const result = value .filter((filter) => filter.length > 0) .map((filter) => ({ key, ...filter, - })); + })) + + return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; }; var peg$f3 = function() { autocomplete = null; }; var peg$f4 = function(parts, empty) { diff --git a/src/libs/SearchParser/autocompleteParser.peggy b/src/libs/SearchParser/autocompleteParser.peggy index 928d1751f8ce..93b1b7e52fa7 100644 --- a/src/libs/SearchParser/autocompleteParser.peggy +++ b/src/libs/SearchParser/autocompleteParser.peggy @@ -31,20 +31,21 @@ defaultFilter start: location().end.offset, length: 0, }; - return; + return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; } autocomplete = { key, ...value[value.length - 1], }; - - return value + const result = value .filter((filter) => filter.length > 0) .map((filter) => ({ key, ...filter, - })); + })) + + return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; } freeTextFilter = _ (identifier/ ",") _ { autocomplete = null; } From 7eb5861937a61f2f1ff5408dcc7c171bccd987b6 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Fri, 31 Jan 2025 13:47:35 +0100 Subject: [PATCH 032/109] grammar formatting --- src/libs/SearchParser/autocompleteParser.js | 23 +++++++++++++---- .../SearchParser/autocompleteParser.peggy | 25 ++++++++++++++----- src/libs/SearchParser/searchParser.js | 18 ++++++------- src/libs/SearchParser/searchParser.peggy | 18 +++++++------ 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/libs/SearchParser/autocompleteParser.js b/src/libs/SearchParser/autocompleteParser.js index 16e135deb222..092a0022889f 100644 --- a/src/libs/SearchParser/autocompleteParser.js +++ b/src/libs/SearchParser/autocompleteParser.js @@ -278,11 +278,16 @@ function peg$parse(input, options) { if (!value) { autocomplete = { key, - value: '', + value: "", start: location().end.offset, length: 0, }; - return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; + return { + key: "syntax", + value: key, + start: location().start.offset, + length: location().end.offset - location().start.offset, + }; } autocomplete = { @@ -294,9 +299,17 @@ function peg$parse(input, options) { .map((filter) => ({ key, ...filter, - })) - - return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; + })); + + return [ + { + key: "syntax", + value: key, + start: location().start.offset, + length: result[0].start - location().start.offset, + }, + ...result, + ]; }; var peg$f3 = function() { autocomplete = null; }; var peg$f4 = function(parts, empty) { diff --git a/src/libs/SearchParser/autocompleteParser.peggy b/src/libs/SearchParser/autocompleteParser.peggy index 93b1b7e52fa7..0b8e15f3364a 100644 --- a/src/libs/SearchParser/autocompleteParser.peggy +++ b/src/libs/SearchParser/autocompleteParser.peggy @@ -27,11 +27,16 @@ defaultFilter if (!value) { autocomplete = { key, - value: '', + value: "", start: location().end.offset, length: 0, }; - return {key:"syntax",value:key, start:location().start.offset, length:location().end.offset-location().start.offset}; + return { + key: "syntax", + value: key, + start: location().start.offset, + length: location().end.offset - location().start.offset, + }; } autocomplete = { @@ -43,12 +48,20 @@ defaultFilter .map((filter) => ({ key, ...filter, - })) - - return [{key:"syntax",value:key, start:location().start.offset, length:result[0].start - location().start.offset}, ...result]; + })); + + return [ + { + key: "syntax", + value: key, + start: location().start.offset, + length: result[0].start - location().start.offset, + }, + ...result, + ]; } -freeTextFilter = _ (identifier/ ",") _ { autocomplete = null; } +freeTextFilter = _ (identifier / ",") _ { autocomplete = null; } autocompleteKey "key" = @( diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index 804a5928a909..aee1033bef31 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -298,7 +298,9 @@ function peg$parse(input, options) { const keywordFilter = buildFilter( "eq", "keyword", - keywords.map((filter) => filter.right).flat() + keywords + .map((filter) => filter.right.replace(/^(['"])(.*)\1$/, "$2")) + .flat() ); if (keywordFilter.right.length > 0) { nonKeywords.push(keywordFilter); @@ -310,20 +312,18 @@ function peg$parse(input, options) { var peg$f2 = function(key, op, value) { updateDefaultValues(key, value); }; - var peg$f3 = function(value) { //handle no-breaking space - let word + var peg$f3 = function(value) { + //handle no-breaking space + let word; if (Array.isArray(value)) { - word = value.join("") - // return buildFilter("eq", "keyword", value.join("")); - }else{ - word = value + word = value.join(""); + } else { + word = value; } if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { return buildFilter("eq", "keyword", word.slice(1, -1)); } return buildFilter("eq", "keyword", word); - - // return buildFilter("eq", "keyword", value); }; var peg$f4 = function(field, op, values) { return buildFilter(op, field, values); diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index 28f5c3d8a96f..3bbe57722b88 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -66,7 +66,9 @@ filterList const keywordFilter = buildFilter( "eq", "keyword", - keywords.map((filter) => filter.right.replace(/^(['"])(.*)\1$/, '$2')).flat() + keywords + .map((filter) => filter.right.replace(/^(['"])(.*)\1$/, "$2")) + .flat() ); if (keywordFilter.right.length > 0) { nonKeywords.push(keywordFilter); @@ -84,12 +86,13 @@ defaultFilter } freeTextFilter - = _ value:(quotedString / [^ \t\r\n\xA0]+) _ { //handle no-breaking space - let word + = _ value:(quotedString / [^ \t\r\n\xA0]+) _ { + //handle no-breaking space + let word; if (Array.isArray(value)) { - word = value.join("") - }else{ - word = value + word = value.join(""); + } else { + word = value; } if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { return buildFilter("eq", "keyword", word.slice(1, -1)); @@ -126,8 +129,7 @@ key "key" / posted ) -defaultKey "default key" - = @(type / status / sortBy / sortOrder / policyID) +defaultKey "default key" = @(type / status / sortBy / sortOrder / policyID) identifier = (","+)? parts:(quotedString / alphanumeric)|1.., ","+| empty:(","+)? { From 7b55bf539911900a559c652e00613674865423b4 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sun, 2 Feb 2025 20:02:35 +0530 Subject: [PATCH 033/109] Fixed submit button layout in Company card transaction start date page --- .../assignCard/TransactionStartDateSelectorModal.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx index 8c0990800bb4..5e5e258912a8 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx @@ -51,7 +51,6 @@ function TransactionStartDateSelectorModal({isVisible, date, handleSelectDate, o Date: Sun, 2 Feb 2025 20:13:09 +0530 Subject: [PATCH 034/109] Fix lint --- .../assignCard/TransactionStartDateSelectorModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx b/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx index 5e5e258912a8..1b2ba15bdbd7 100644 --- a/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx +++ b/src/pages/workspace/companyCards/assignCard/TransactionStartDateSelectorModal.tsx @@ -8,7 +8,7 @@ import Modal from '@components/Modal'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ValidationUtils from '@libs/ValidationUtils'; +import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import INPUT_IDS from '@src/types/form/AssignCardForm'; @@ -32,7 +32,7 @@ function TransactionStartDateSelectorModal({isVisible, date, handleSelectDate, o const {translate} = useLocalize(); const validate = (values: FormOnyxValues): FormInputErrors => - ValidationUtils.getFieldRequiredErrors(values, [INPUT_IDS.START_DATE]); + getFieldRequiredErrors(values, [INPUT_IDS.START_DATE]); const submit = (values: FormOnyxValues) => { handleSelectDate(values[INPUT_IDS.START_DATE]); From 146b89e5caa812bee780355cab67ecb33a4651fa Mon Sep 17 00:00:00 2001 From: M00rish Date: Sun, 2 Feb 2025 20:36:41 +0100 Subject: [PATCH 035/109] refactor to show not found page only when navigating to a deleted Action --- src/pages/home/ReportScreen.tsx | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index c1b809c932b6..11737aae66b5 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -387,7 +387,11 @@ function ReportScreen({route, navigation}: ReportScreenProps) { [linkedAction, report], ); - const [isClearingDeletedLinkedAction, setIsClearingDeletedLinkedAction] = useState(); + const previsLinkedActionDeleted = usePrevious(linkedAction ? isLinkedActionDeleted : undefined); + + const lastReportActionIDFromRoute = usePrevious(reportActionIDFromRoute); + + const [isNavigatingToDeletedMessage, setIsNavigatingToDeletedMessage] = useState(false); const isLinkedActionInaccessibleWhisper = useMemo( () => !!linkedAction && isWhisperAction(linkedAction) && !(linkedAction?.whisperedToAccountIDs ?? []).includes(currentUserAccountID), @@ -420,7 +424,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundLinkedAction = - (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && isClearingDeletedLinkedAction === false) || + (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && isNavigatingToDeletedMessage) || (shouldShowSkeleton && !reportMetadata.isLoadingInitialReportActions && !!reportActionIDFromRoute && @@ -735,23 +739,23 @@ function ReportScreen({route, navigation}: ReportScreenProps) { }, [fetchReport]); useEffect(() => { - - if (!isLinkedActionDeleted || isClearingDeletedLinkedAction) - { + // Only handle deletion cases when there's a deleted action + if (!isLinkedActionDeleted) { + setIsNavigatingToDeletedMessage(false); return; } - Navigation.setParams({reportActionID: ''}); - setIsClearingDeletedLinkedAction(true); - }, [isLinkedActionDeleted, isClearingDeletedLinkedAction]); - - - useEffect(() => { - if (!isClearingDeletedLinkedAction || reportActionIDFromRoute) { + // Set navigation state when user clicks a deleted message link + if (lastReportActionIDFromRoute !== reportActionIDFromRoute) { + setIsNavigatingToDeletedMessage(true); return; } - setIsClearingDeletedLinkedAction(false); - }, [isClearingDeletedLinkedAction, reportActionIDFromRoute]); + + // Clear params when message gets deleted while viewing + if (!isNavigatingToDeletedMessage && previsLinkedActionDeleted === false) { + Navigation.setParams({reportActionID: ''}); + } + }, [isLinkedActionDeleted, previsLinkedActionDeleted, lastReportActionIDFromRoute, reportActionIDFromRoute, isNavigatingToDeletedMessage]); // If user redirects to an inaccessible whisper via a deeplink, on a report they have access to, // then we set reportActionID as empty string, so we display them the report and not the "Not found page". @@ -785,7 +789,6 @@ function ReportScreen({route, navigation}: ReportScreenProps) { !isDeletedAction(mostRecentReportAction); const lastRoute = usePrevious(route); - const lastReportActionIDFromRoute = usePrevious(reportActionIDFromRoute); const onComposerFocus = useCallback(() => setIsComposerFocus(true), []); const onComposerBlur = useCallback(() => setIsComposerFocus(false), []); From 72da60199a4785953921c3003ade0dfa5c846f6f Mon Sep 17 00:00:00 2001 From: M00rish Date: Sun, 2 Feb 2025 20:47:25 +0100 Subject: [PATCH 036/109] improve naming --- src/pages/home/ReportScreen.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 11737aae66b5..4cf7e0ca6ee3 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -388,10 +388,10 @@ function ReportScreen({route, navigation}: ReportScreenProps) { ); const previsLinkedActionDeleted = usePrevious(linkedAction ? isLinkedActionDeleted : undefined); - + const lastReportActionIDFromRoute = usePrevious(reportActionIDFromRoute); - const [isNavigatingToDeletedMessage, setIsNavigatingToDeletedMessage] = useState(false); + const [isNavigatingToAction, setIsNavigatingToAction] = useState(false); const isLinkedActionInaccessibleWhisper = useMemo( () => !!linkedAction && isWhisperAction(linkedAction) && !(linkedAction?.whisperedToAccountIDs ?? []).includes(currentUserAccountID), @@ -424,7 +424,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundLinkedAction = - (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && isNavigatingToDeletedMessage) || + (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && isNavigatingToAction) || (shouldShowSkeleton && !reportMetadata.isLoadingInitialReportActions && !!reportActionIDFromRoute && @@ -741,21 +741,21 @@ function ReportScreen({route, navigation}: ReportScreenProps) { useEffect(() => { // Only handle deletion cases when there's a deleted action if (!isLinkedActionDeleted) { - setIsNavigatingToDeletedMessage(false); + setIsNavigatingToAction(false); return; } - // Set navigation state when user clicks a deleted message link + // Set navigation state when user clicks a deleted Action link if (lastReportActionIDFromRoute !== reportActionIDFromRoute) { - setIsNavigatingToDeletedMessage(true); + setIsNavigatingToAction(true); return; } - // Clear params when message gets deleted while viewing - if (!isNavigatingToDeletedMessage && previsLinkedActionDeleted === false) { + // Clear params when Action gets deleted while heighlighted + if (!isNavigatingToAction && previsLinkedActionDeleted === false) { Navigation.setParams({reportActionID: ''}); } - }, [isLinkedActionDeleted, previsLinkedActionDeleted, lastReportActionIDFromRoute, reportActionIDFromRoute, isNavigatingToDeletedMessage]); + }, [isLinkedActionDeleted, previsLinkedActionDeleted, lastReportActionIDFromRoute, reportActionIDFromRoute, isNavigatingToAction]); // If user redirects to an inaccessible whisper via a deeplink, on a report they have access to, // then we set reportActionID as empty string, so we display them the report and not the "Not found page". From 0adb14c6002d568a6ea759e72322198e5327fbeb Mon Sep 17 00:00:00 2001 From: OmarKoueifi Date: Mon, 3 Feb 2025 03:31:37 -0500 Subject: [PATCH 037/109] Prevent It's not here message when logging in #55721 --- src/libs/actions/Report.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b77b8115cb59..024d23762aa1 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -68,6 +68,7 @@ import * as Localize from '@libs/Localize'; import Log from '@libs/Log'; import {registerPaginationConfig} from '@libs/Middleware/Pagination'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import shouldOpenOnAdminRoom from '@libs/Navigation/shouldOpenOnAdminRoom'; import {isOnboardingFlowName} from '@libs/NavigationUtils'; import enhanceParameters from '@libs/Network/enhanceParameters'; import type {NetworkStatus} from '@libs/NetworkConnection'; @@ -2942,6 +2943,20 @@ function openReportFromDeepLink(url: string) { return; } + // Check if the report exists in the collection + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + // If the report does not exist, navigate to the last accessed report or Concierge chat + if (!report) { + const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), undefined, reportID)?.reportID; + if (lastAccessedReportID) { + const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID); + Navigation.navigate(lastAccessedReportRoute, CONST.NAVIGATION.ACTION_TYPE.PUSH); + return; + } + navigateToConciergeChat(false, () => true, CONST.NAVIGATION.ACTION_TYPE.PUSH); + return; + } + Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); }; From 68b3262906132b97b367e102d50a4564ad073c4c Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Mon, 3 Feb 2025 16:24:26 +0100 Subject: [PATCH 038/109] fix merge --- .../Search/SearchAutocompleteInput.tsx | 16 +++------------- src/libs/SearchAutocompleteUtils.ts | 9 ++------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 0c2dd8124b46..6a3c9c8c5e5e 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -2,11 +2,9 @@ import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; -import {useSharedValue} from 'react-native-reanimated'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import {useSharedValue} from 'react-native-reanimated'; import FormHelpMessage from '@components/FormHelpMessage'; import type {SelectionListHandle} from '@components/SelectionList/types'; import TextInput from '@components/TextInput'; @@ -101,7 +99,6 @@ function SearchAutocompleteInput( const {isOffline} = useNetwork(); const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const personalDetailsSharedValue = useSharedValue({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); const lastMap = useRef({}); const [map, setMap] = useState({}); @@ -133,13 +130,6 @@ function SearchAutocompleteInput( const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; - useEffect(() => { - runOnLiveMarkdownRuntime(() => { - 'worklet'; - - personalDetailsSharedValue.set({login: currentUserPersonalDetails.login, userDisplayName: currentUserPersonalDetails.displayName}); - })(); - }, [currentUserPersonalDetails, personalDetailsSharedValue]); useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -166,9 +156,9 @@ function SearchAutocompleteInput( (input: string) => { 'worklet'; - return parseForLiveMarkdown(input, emailList, personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue); + return parseForLiveMarkdown(input, emailList, currentUserPersonalDetails.displayName ?? '', map, currencySharedValue, categorySharedValue, tagSharedValue); }, - [personalDetailsSharedValue, map, currencySharedValue, categorySharedValue, tagSharedValue, emailList], + [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailList], ); const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index fc5836a2c1da..e798e88a5514 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -143,7 +143,7 @@ function getAutocompleteQueryWithComma(prevQuery: string, newQuery: string) { function parseForLiveMarkdown( input: string, userLogins: string[], - details: SharedValue<{login: string | undefined; userDisplayName: string | undefined}>, + userDisplayName: string, map: SubstitutionMap, currencyList: SharedValue, categoryList: SharedValue, @@ -177,12 +177,7 @@ function parseForLiveMarkdown( .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG || tagList.get().includes(range.value)) .map((range) => { let type = 'mention-user'; - if ( - (range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && - (range.value === details.get().login || range.value === details.get().userDisplayName) - ) { - type = 'mention-here'; - } + if (range.key === 'syntax') { type = 'syntax'; } From d2f0a5033be9180bd7f31309b22205ef7f12edd4 Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Mon, 3 Feb 2025 22:41:23 +0700 Subject: [PATCH 039/109] fix: chat gbr flickers when marking task as done --- 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 2864b989ed3d..7132e870ed70 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -352,7 +352,7 @@ function getOutstandingChildTask(taskReport: OnyxEntry) { reportAction.childType === CONST.REPORT.TYPE.TASK && reportAction?.childStateNum === CONST.REPORT.STATE_NUM.OPEN && reportAction?.childStatusNum === CONST.REPORT.STATUS_NUM.OPEN && - ReportActionsUtils.getReportActionMessage(reportAction)?.isDeletedParentAction + !ReportActionsUtils.getReportActionMessage(reportAction)?.isDeletedParentAction ) { return true; } From d5acdeee562539eef52d46ef60ab8d9bcd220e64 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Mon, 3 Feb 2025 17:14:44 +0100 Subject: [PATCH 040/109] make emailList shared value --- .../Search/SearchAutocompleteInput.tsx | 21 ++++++++++++++----- src/libs/SearchAutocompleteUtils.ts | 4 ++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 6a3c9c8c5e5e..ab6ecd333dfe 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -118,6 +118,10 @@ function SearchAutocompleteInput( }, [activeWorkspaceID, allPoliciesTags]); const tagSharedValue = useSharedValue(tagAutocompleteList); + const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); + const emailList = Object.keys(loginList ?? {}); + const emailListSharedValue = useSharedValue(emailList); + useEffect(() => { if (lastMap.current && !isEqual(lastMap.current, substitutionMap)) { lastMap.current = substitutionMap; @@ -125,11 +129,16 @@ function SearchAutocompleteInput( setMap(lastMap.current ?? {}); }, [substitutionMap, lastMap]); - const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); - const emailList = Object.keys(loginList ?? {}); - const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; + useEffect(() => { + runOnLiveMarkdownRuntime(() => { + 'worklet'; + + emailListSharedValue.set(emailList); + })(); + }, [emailList, emailListSharedValue]); + useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -137,6 +146,7 @@ function SearchAutocompleteInput( currencySharedValue.set(currencyAutocompleteList); })(); }, [currencyAutocompleteList, currencySharedValue]); + useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -144,6 +154,7 @@ function SearchAutocompleteInput( categorySharedValue.set(categoryAutocompleteList); })(); }, [categorySharedValue, categoryAutocompleteList]); + useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -156,9 +167,9 @@ function SearchAutocompleteInput( (input: string) => { 'worklet'; - return parseForLiveMarkdown(input, emailList, currentUserPersonalDetails.displayName ?? '', map, currencySharedValue, categorySharedValue, tagSharedValue); + return parseForLiveMarkdown(input, currentUserPersonalDetails.displayName ?? '', map, emailListSharedValue, currencySharedValue, categorySharedValue, tagSharedValue); }, - [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailList], + [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailListSharedValue], ); const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index e798e88a5514..73b35ac0ed21 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -142,9 +142,9 @@ function getAutocompleteQueryWithComma(prevQuery: string, newQuery: string) { */ function parseForLiveMarkdown( input: string, - userLogins: string[], userDisplayName: string, map: SubstitutionMap, + userLogins: SharedValue, currencyList: SharedValue, categoryList: SharedValue, tagList: SharedValue, @@ -182,7 +182,7 @@ function parseForLiveMarkdown( type = 'syntax'; } - if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (userLogins.includes(range.value) || range.value === userDisplayName)) { + if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (userLogins.get().includes(range.value) || range.value === userDisplayName)) { type = 'mention-here'; } From fd2925a5ade3aef075ebd46247c76353070f9081 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 4 Feb 2025 01:06:36 +0300 Subject: [PATCH 041/109] fix navigation back from private notes pages --- src/libs/ReportUtils.ts | 8 ++++++-- src/pages/PrivateNotes/PrivateNotesEditPage.tsx | 2 +- src/pages/PrivateNotes/PrivateNotesListPage.tsx | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f91df123c2a1..6036fdf8917f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4456,7 +4456,7 @@ function navigateToDetailsPage(report: OnyxEntry, backTo?: string) { /** * Go back to the details page of a given report */ -function goBackToDetailsPage(report: OnyxEntry, backTo?: string) { +function goBackToDetailsPage(report: OnyxEntry, backTo?: string, shouldGoBackToDetailsPage = false) { const isOneOnOneChatReport = isOneOnOneChat(report); const participantAccountID = getParticipantsAccountIDsForDisplay(report); @@ -4466,7 +4466,11 @@ function goBackToDetailsPage(report: OnyxEntry, backTo?: string) { } if (report?.reportID) { - Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID, backTo)); + if (shouldGoBackToDetailsPage) { + Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID, backTo)); + } else { + Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID, backTo)); + } } else { Log.warn('Missing reportID during navigation back to the details page'); } diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx index deab122e3006..2eb416932644 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx @@ -94,7 +94,7 @@ function PrivateNotesEditPage({route, report, accountID}: PrivateNotesEditPagePr Keyboard.dismiss(); if (!Object.values({...report.privateNotes, [route.params.accountID]: {note: editedNote}}).some((item) => item.note)) { - ReportUtils.navigateToDetailsPage(report, backTo); + ReportUtils.goBackToDetailsPage(report, backTo, true); } else { Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST.getRoute(report.reportID, backTo))); } diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.tsx b/src/pages/PrivateNotes/PrivateNotesListPage.tsx index d2cdda140bb8..2ac33c89d174 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.tsx +++ b/src/pages/PrivateNotes/PrivateNotesListPage.tsx @@ -13,6 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {PrivateNotesNavigatorParamList} from '@libs/Navigation/types'; +import {goBackToDetailsPage} from '@libs/ReportUtils'; import type {WithReportAndPrivateNotesOrNotFoundProps} from '@pages/home/report/withReportAndPrivateNotesOrNotFound'; import withReportAndPrivateNotesOrNotFound from '@pages/home/report/withReportAndPrivateNotesOrNotFound'; import CONST from '@src/CONST'; @@ -94,7 +95,7 @@ function PrivateNotesListPage({report, accountID: sessionAccountID}: PrivateNote Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID, backTo))} + onBackButtonPress={() => goBackToDetailsPage(report, route.params.backTo, true)} onCloseButtonPress={() => Navigation.dismissModal()} /> Date: Tue, 4 Feb 2025 01:30:01 +0300 Subject: [PATCH 042/109] revert change --- src/pages/PrivateNotes/PrivateNotesEditPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx index 2eb416932644..deab122e3006 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx @@ -94,7 +94,7 @@ function PrivateNotesEditPage({route, report, accountID}: PrivateNotesEditPagePr Keyboard.dismiss(); if (!Object.values({...report.privateNotes, [route.params.accountID]: {note: editedNote}}).some((item) => item.note)) { - ReportUtils.goBackToDetailsPage(report, backTo, true); + ReportUtils.navigateToDetailsPage(report, backTo); } else { Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST.getRoute(report.reportID, backTo))); } From 3341f9ca7ee4ca0074e7b3d6eafbf4316c7472bb Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:55:23 -0800 Subject: [PATCH 043/109] Update Cardholder-Settings-and-Features.md Tracking GH: https://github.com/Expensify/Expensify/issues/463351 Removing mention of the free plan - I don't think it makes sense to edit the sentence to anything else since this article is for the cardholder --- .../expensify-card/Cardholder-Settings-and-Features.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/articles/expensify-classic/expensify-card/Cardholder-Settings-and-Features.md b/docs/articles/expensify-classic/expensify-card/Cardholder-Settings-and-Features.md index 38686462a1c2..ee03a18033ea 100644 --- a/docs/articles/expensify-classic/expensify-card/Cardholder-Settings-and-Features.md +++ b/docs/articles/expensify-classic/expensify-card/Cardholder-Settings-and-Features.md @@ -54,8 +54,6 @@ To add your Expensify Card to a digital wallet, follow the steps below: - **Restricted Country**: Transactions from restricted countries will be declined. {% include faq-begin.md %} -## Can I use Smart Limits with a free Expensify account? -If you're on the Free plan, you won't have the option to use Smart Limits. Your card limit will simply reset at the end of each calendar month. ## I still haven't received my Expensify Card. What should I do? For more information on why your card hasn't arrived, you can check out this resource on [Requesting a Card](https://help.expensify.com/articles/expensify-classic/expensify-card/Request-the-Card#what-if-i-havent-received-my-card-after-multiple-weeks). From 068f04c8aac3de91d1401979843f1c9f081ab56d Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:07:29 +0100 Subject: [PATCH 044/109] fix changed files lint --- .../Search/SearchRouter/getUpdatedSubstitutionsMap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index a681ae6030f0..fca4ad525600 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -1,5 +1,5 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; -import * as parser from '@libs/SearchParser/autocompleteParser'; +import {parse} from '@libs/SearchParser/autocompleteParser'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`; @@ -16,7 +16,7 @@ const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${fi * return: {} */ function getUpdatedSubstitutionsMap(query: string, substitutions: SubstitutionMap): SubstitutionMap { - const parsedQuery = parser.parse(query) as {ranges: SearchAutocompleteQueryRange[]}; + const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); From 62b16170858494acd9386bf6fb3d02e68584fde5 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:24:42 +0100 Subject: [PATCH 045/109] fix parser --- src/libs/SearchParser/searchParser.peggy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index a369b8aa754b..cfbf076e381c 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -98,7 +98,7 @@ freeTextFilter } else { word = value; } - return buildFilter("eq", "keyword", value); + return buildFilter("eq", "keyword", word); } standardFilter From 56a5c8dad6f7e2b40e3d668e2d44ba8834bff2af Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:26:10 +0100 Subject: [PATCH 046/109] generate serach parser --- src/libs/SearchParser/searchParser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index cbae90c1461b..66846116ca35 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -324,7 +324,7 @@ function peg$parse(input, options) { } else { word = value; } - return buildFilter("eq", "keyword", value); + return buildFilter("eq", "keyword", word); }; var peg$f4 = function(field, op, values) { return buildFilter(op, field, values); From 568d42f85f32eb674d327acd95c82c40cac60bde Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 13:56:36 +0100 Subject: [PATCH 047/109] fix types and remove unnecessary ref --- src/CONST.ts | 1 + src/components/Search/SearchAutocompleteInput.tsx | 9 ++++----- src/components/Search/types.ts | 3 +-- src/libs/SearchAutocompleteUtils.ts | 15 ++++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 1250092cb910..e5e6c7593532 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6136,6 +6136,7 @@ const CONST = { LOWER_THAN: 'lt', LOWER_THAN_OR_EQUAL_TO: 'lte', }, + SYNTAX_KEY: 'syntax', SYNTAX_ROOT_KEYS: { TYPE: 'type', STATUS: 'status', diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index ca828679cdfe..f59558f2740a 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -99,7 +99,6 @@ function SearchAutocompleteInput( const {isOffline} = useNetwork(); const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const lastMap = useRef({}); const [map, setMap] = useState({}); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); @@ -123,11 +122,11 @@ function SearchAutocompleteInput( const emailListSharedValue = useSharedValue(emailList); useEffect(() => { - if (lastMap.current && !isEqual(lastMap.current, substitutionMap)) { - lastMap.current = substitutionMap; + if (isEqual(map, substitutionMap)) { + return; } - setMap(lastMap.current ?? {}); - }, [substitutionMap, lastMap]); + setMap(substitutionMap); + }, [substitutionMap, map]); const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index b5c58c798b9f..b4c7d9d4dd20 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -96,7 +96,7 @@ type SearchFilterKey = | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID - | 'syntax'; + | typeof CONST.SEARCH.SYNTAX_KEY; type UserFriendlyKey = ValueOf; @@ -129,7 +129,6 @@ type SearchAutocompleteResult = { ranges: SearchAutocompleteQueryRange[]; }; -// TODO FIX types type SearchAutocompleteQueryRange = { key: SearchFilterKey; length: number; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index 73b35ac0ed21..e458b186b6a8 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -161,13 +161,14 @@ function parseForLiveMarkdown( return ranges .filter( (range) => - !( - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || - range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID - ) || subMap[`${range.key}:${range.value}`] !== undefined, + !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) || + subMap[`${range.key}:${range.value}`] !== undefined, + ) + .filter( + (range) => + !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) || + subMap[`${range.key}:${range.value}`] !== undefined || + userLogins.get().includes(range.value), ) .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || currencyList.get().includes(range.value)) .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE || typeList.includes(range.value)) From ee8297b9ca09e070be9c36ec14669188a9416fa0 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 15:30:42 +0100 Subject: [PATCH 048/109] fix tests for autocomplete parser --- .../Search/SearchAutocompleteInput.tsx | 2 +- tests/unit/SearchAutocompleteParserTest.ts | 42 +++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index f59558f2740a..358b82d380a0 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,6 +1,6 @@ import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; -import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; diff --git a/tests/unit/SearchAutocompleteParserTest.ts b/tests/unit/SearchAutocompleteParserTest.ts index 995d23eaeff5..e9ef145ed9ad 100644 --- a/tests/unit/SearchAutocompleteParserTest.ts +++ b/tests/unit/SearchAutocompleteParserTest.ts @@ -13,7 +13,9 @@ const tests = [ length: 3, }, ranges: [ + {key: 'syntax', value: 'type', start: 0, length: 5}, {key: 'type', value: 'expense', start: 5, length: 7}, + {key: 'syntax', value: 'status', start: 13, length: 7}, {key: 'status', value: 'all', start: 20, length: 3}, ], }, @@ -23,8 +25,11 @@ const tests = [ expected: { autocomplete: null, ranges: [ + {key: 'syntax', value: 'taxRate', start: 0, length: 9}, {key: 'taxRate', value: 'rate1', start: 9, length: 5}, + {key: 'syntax', value: 'expenseType', start: 15, length: 13}, {key: 'expenseType', value: 'card', start: 28, length: 4}, + {key: 'syntax', value: 'cardID', start: 33, length: 5}, {key: 'cardID', value: 'Big Bank', start: 38, length: 10}, ], }, @@ -34,8 +39,11 @@ const tests = [ expected: { autocomplete: null, ranges: [ + {key: 'syntax', value: 'taxRate', start: 0, length: 8}, {key: 'taxRate', value: 'rate1', start: 8, length: 5}, + {key: 'syntax', value: 'expenseType', start: 14, length: 12}, {key: 'expenseType', value: 'card', start: 26, length: 4}, + {key: 'syntax', value: 'cardID', start: 31, length: 7}, {key: 'cardID', value: 'Big Bank', start: 38, length: 10}, ], }, @@ -50,8 +58,10 @@ const tests = [ value: 'meal & entertainment', }, ranges: [ + {key: 'syntax', value: 'expenseType', start: 11, length: 13}, {key: 'expenseType', length: 4, start: 24, value: 'cash'}, {key: 'expenseType', length: 4, start: 29, value: 'card'}, + {key: 'syntax', value: 'category', start: 80, length: 9}, {key: 'category', length: 6, start: 89, value: 'travel'}, {key: 'category', length: 5, start: 96, value: 'hotel'}, {key: 'category', length: 22, start: 102, value: 'meal & entertainment'}, @@ -68,8 +78,11 @@ const tests = [ value: 'a b', }, ranges: [ + {key: 'syntax', value: 'type', start: 0, length: 5}, {key: 'type', value: 'expense', start: 5, length: 7}, + {key: 'syntax', value: 'status', start: 13, length: 7}, {key: 'status', value: 'all', start: 20, length: 3}, + {key: 'syntax', value: 'category', start: 24, length: 9}, {key: 'category', value: 'a b', start: 33, length: 5}, ], }, @@ -92,7 +105,7 @@ const tests = [ query: 'tag:,,', expected: { autocomplete: null, - ranges: [], + ranges: [{key: 'syntax', value: 'tag', start: 0, length: 4}], }, }, { @@ -105,7 +118,9 @@ const tests = [ length: 3, }, ranges: [ + {key: 'syntax', value: 'in', start: 0, length: 3}, {key: 'in', value: '123456', start: 3, length: 6}, + {key: 'syntax', value: 'currency', start: 10, length: 9}, {key: 'currency', value: 'USD', start: 19, length: 3}, ], }, @@ -120,6 +135,7 @@ const tests = [ length: 4, }, ranges: [ + {key: 'syntax', value: 'tag', start: 0, length: 4}, {key: 'tag', value: 'aa', start: 4, length: 2}, {key: 'tag', value: 'bbb', start: 7, length: 3}, {key: 'tag', value: 'cccc', start: 11, length: 4}, @@ -135,7 +151,7 @@ const tests = [ start: 9, length: 0, }, - ranges: [], + ranges: [{key: 'syntax', value: 'category', start: 0, length: 9}], }, }, { @@ -147,7 +163,10 @@ const tests = [ start: 21, length: 0, }, - ranges: [{key: 'category', value: 'Advertising', start: 9, length: 11}], + ranges: [ + {key: 'syntax', value: 'category', start: 0, length: 9}, + {key: 'category', value: 'Advertising', start: 9, length: 11}, + ], }, }, { @@ -160,6 +179,7 @@ const tests = [ length: 12, }, ranges: [ + {key: 'syntax', value: 'in', start: 0, length: 3}, {key: 'in', value: 'Big Room', start: 3, length: 10}, {key: 'in', value: 'small room', start: 14, length: 12}, ], @@ -174,7 +194,10 @@ const tests = [ start: 12, length: 3, }, - ranges: [{key: 'category', value: 'Car', start: 12, length: 3}], + ranges: [ + {key: 'syntax', value: 'category', start: 0, length: 12}, + {key: 'category', value: 'Car', start: 12, length: 3}, + ], }, }, { @@ -182,7 +205,9 @@ const tests = [ expected: { autocomplete: null, ranges: [ + {key: 'syntax', value: 'type', start: 0, length: 5}, {key: 'type', value: 'expense', start: 5, length: 7}, + {key: 'syntax', value: 'status', start: 13, length: 7}, {key: 'status', value: 'all', start: 20, length: 3}, ], }, @@ -197,11 +222,16 @@ const tests = [ length: 4, }, ranges: [ + {key: 'syntax', value: 'in', start: 0, length: 3}, {key: 'in', value: 'Big Room', start: 3, length: 10}, + {key: 'syntax', value: 'from', start: 14, length: 5}, {key: 'from', value: 'Friend', start: 19, length: 6}, + {key: 'syntax', value: 'category', start: 26, length: 9}, {key: 'category', value: 'Car', start: 35, length: 3}, {key: 'category', value: 'Cell Phone', start: 39, length: 12}, + {key: 'syntax', value: 'status', start: 52, length: 7}, {key: 'status', value: 'all', start: 59, length: 3}, + {key: 'syntax', value: 'expenseType', start: 63, length: 13}, {key: 'expenseType', value: 'card', start: 76, length: 4}, {key: 'expenseType', value: 'cash', start: 81, length: 4}, ], @@ -217,11 +247,15 @@ const tests = [ length: 8, }, ranges: [ + {key: 'syntax', value: 'currency', start: 0, length: 9}, {key: 'currency', value: 'PLN', start: 9, length: 3}, {key: 'currency', value: 'USD', start: 13, length: 3}, + {key: 'syntax', value: 'taxRate', start: 25, length: 9}, {key: 'taxRate', value: 'tax', start: 34, length: 3}, + {key: 'syntax', value: 'tag', start: 66, length: 4}, {key: 'tag', value: 'General Overhead', start: 70, length: 18}, {key: 'tag', value: 'IT', start: 89, length: 2}, + {key: 'syntax', value: 'expenseType', start: 92, length: 13}, {key: 'expenseType', value: 'card', start: 105, length: 4}, {key: 'expenseType', value: 'distance', start: 110, length: 8}, ], From efb018a5445ef87564e51f85dc5bb2076c074541 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Tue, 4 Feb 2025 16:03:56 +0100 Subject: [PATCH 049/109] fix types and perf-tests --- .../Search/SearchRouter/getQueryWithSubstitutions.ts | 3 ++- .../Search/SearchRouter/getUpdatedSubstitutionsMap.ts | 3 ++- src/components/Search/types.ts | 5 ++--- tests/perf-test/SearchRouter.perf-test.tsx | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts index b976371135a6..4262f1df8529 100644 --- a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts +++ b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts @@ -1,10 +1,11 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; import {sanitizeSearchValue} from '@libs/SearchQueryUtils'; +import type CONST from '@src/CONST'; type SubstitutionMap = Record; -const getSubstitutionMapKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`; +const getSubstitutionMapKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, this function will return a transformed query where: diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index fca4ad525600..ac9f8a7a807d 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -1,8 +1,9 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; +import type CONST from '@src/CONST'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; -const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`; +const getSubstitutionsKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index b4c7d9d4dd20..436cc33ad373 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -95,8 +95,7 @@ type SearchFilterKey = | ValueOf | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS - | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID - | typeof CONST.SEARCH.SYNTAX_KEY; + | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID; type UserFriendlyKey = ValueOf; @@ -130,7 +129,7 @@ type SearchAutocompleteResult = { }; type SearchAutocompleteQueryRange = { - key: SearchFilterKey; + key: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY; length: number; start: number; value: string; diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index 089f08b14fb3..ea260eb4b83d 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -117,6 +117,7 @@ function SearchAutocompleteInputWrapper() { value={value} onSearchQueryChange={(searchTerm) => setValue(searchTerm)} isFullWidth={false} + substitutionMap={{}} /> ); From d649ac77e6674a991351371ed5a87559db5b24fa Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 4 Feb 2025 23:00:22 +0700 Subject: [PATCH 050/109] fix: per diem options disappears after changing currency offline --- src/libs/actions/Policy/Policy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 35272f6c231c..040a54882a46 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1308,6 +1308,7 @@ function updateGeneralSettings(policyID: string | undefined, name: string, curre outputCurrency: currency, ...(customUnitID && { customUnits: { + ...policy.customUnits, [customUnitID]: { ...distanceUnit, rates: optimisticRates, From 3913ded0e5803949be7364ef609157ca59d06a38 Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 4 Feb 2025 11:19:37 -0800 Subject: [PATCH 051/109] Update constants for mileage --- src/CONST.ts | 1320 +++++++++++++++++++++++++------------------------- 1 file changed, 662 insertions(+), 658 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 1250092cb910..a980e14bf57d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2778,7 +2778,7 @@ const CONST = { NAME_PER_DIEM_INTERNATIONAL: 'Per Diem International', DISTANCE_UNIT_MILES: 'mi', DISTANCE_UNIT_KILOMETERS: 'km', - MILEAGE_IRS_RATE: 0.67, + MILEAGE_IRS_RATE: 0.70, DEFAULT_RATE: 'Default Rate', RATE_DECIMALS: 3, FAKE_P2P_ID: '_FAKE_P2P_ID_', @@ -5325,663 +5325,667 @@ const CONST = { }, }, CURRENCY_TO_DEFAULT_MILEAGE_RATE: JSON.parse(`{ - "AED": { - "rate": 396, - "unit": "km" - }, - "AFN": { - "rate": 8369, - "unit": "km" - }, - "ALL": { - "rate": 11104, - "unit": "km" - }, - "AMD": { - "rate": 56842, - "unit": "km" - }, - "ANG": { - "rate": 193, - "unit": "km" - }, - "AOA": { - "rate": 67518, - "unit": "km" - }, - "ARS": { - "rate": 9873, - "unit": "km" - }, - "AUD": { - "rate": 85, - "unit": "km" - }, - "AWG": { - "rate": 195, - "unit": "km" - }, - "AZN": { - "rate": 183, - "unit": "km" - }, - "BAM": { - "rate": 177, - "unit": "km" - }, - "BBD": { - "rate": 216, - "unit": "km" - }, - "BDT": { - "rate": 9130, - "unit": "km" - }, - "BGN": { - "rate": 177, - "unit": "km" - }, - "BHD": { - "rate": 40, - "unit": "km" - }, - "BIF": { - "rate": 210824, - "unit": "km" - }, - "BMD": { - "rate": 108, - "unit": "km" - }, - "BND": { - "rate": 145, - "unit": "km" - }, - "BOB": { - "rate": 745, - "unit": "km" - }, - "BRL": { - "rate": 594, - "unit": "km" - }, - "BSD": { - "rate": 108, - "unit": "km" - }, - "BTN": { - "rate": 7796, - "unit": "km" - }, - "BWP": { - "rate": 1180, - "unit": "km" - }, - "BYN": { - "rate": 280, - "unit": "km" - }, - "BYR": { - "rate": 2159418, - "unit": "km" - }, - "BZD": { - "rate": 217, - "unit": "km" - }, - "CAD": { - "rate": 70, - "unit": "km" - }, - "CDF": { - "rate": 213674, - "unit": "km" - }, - "CHF": { - "rate": 70, - "unit": "km" - }, - "CLP": { - "rate": 77249, - "unit": "km" - }, - "CNY": { - "rate": 702, - "unit": "km" - }, - "COP": { - "rate": 383668, - "unit": "km" - }, - "CRC": { - "rate": 65899, - "unit": "km" - }, - "CUC": { - "rate": 108, - "unit": "km" - }, - "CUP": { - "rate": 2776, - "unit": "km" - }, - "CVE": { - "rate": 6112, - "unit": "km" - }, - "CZK": { - "rate": 2356, - "unit": "km" - }, - "DJF": { - "rate": 19151, - "unit": "km" - }, - "DKK": { - "rate": 379, - "unit": "km" - }, - "DOP": { - "rate": 6144, - "unit": "km" - }, - "DZD": { - "rate": 14375, - "unit": "km" - }, - "EEK": { - "rate": 1576, - "unit": "km" - }, - "EGP": { - "rate": 1696, - "unit": "km" - }, - "ERN": { - "rate": 1617, - "unit": "km" - }, - "ETB": { - "rate": 4382, - "unit": "km" - }, - "EUR": { - "rate": 30, - "unit": "km" - }, - "FJD": { - "rate": 220, - "unit": "km" - }, - "FKP": { - "rate": 77, - "unit": "km" - }, - "GBP": { - "rate": 45, - "unit": "mi" - }, - "GEL": { - "rate": 359, - "unit": "km" - }, - "GHS": { - "rate": 620, - "unit": "km" - }, - "GIP": { - "rate": 77, - "unit": "km" - }, - "GMD": { - "rate": 5526, - "unit": "km" - }, - "GNF": { - "rate": 1081319, - "unit": "km" - }, - "GTQ": { - "rate": 832, - "unit": "km" - }, - "GYD": { - "rate": 22537, - "unit": "km" - }, - "HKD": { - "rate": 837, - "unit": "km" - }, - "HNL": { - "rate": 2606, - "unit": "km" - }, - "HRK": { - "rate": 684, - "unit": "km" - }, - "HTG": { - "rate": 8563, - "unit": "km" - }, - "HUF": { - "rate": 33091, - "unit": "km" - }, - "IDR": { - "rate": 1555279, - "unit": "km" - }, - "ILS": { - "rate": 540, - "unit": "km" - }, - "INR": { - "rate": 7805, - "unit": "km" - }, - "IQD": { - "rate": 157394, - "unit": "km" - }, - "IRR": { - "rate": 4539961, - "unit": "km" - }, - "ISK": { - "rate": 13518, - "unit": "km" - }, - "JMD": { - "rate": 15794, - "unit": "km" - }, - "JOD": { - "rate": 77, - "unit": "km" - }, - "JPY": { - "rate": 11748, - "unit": "km" - }, - "KES": { - "rate": 11845, - "unit": "km" - }, - "KGS": { - "rate": 9144, - "unit": "km" - }, - "KHR": { - "rate": 437658, - "unit": "km" - }, - "KMF": { - "rate": 44418, - "unit": "km" - }, - "KPW": { - "rate": 97043, - "unit": "km" - }, - "KRW": { - "rate": 121345, - "unit": "km" - }, - "KWD": { - "rate": 32, - "unit": "km" - }, - "KYD": { - "rate": 90, - "unit": "km" - }, - "KZT": { - "rate": 45396, - "unit": "km" - }, - "LAK": { - "rate": 1010829, - "unit": "km" - }, - "LBP": { - "rate": 164153, - "unit": "km" - }, - "LKR": { - "rate": 21377, - "unit": "km" - }, - "LRD": { - "rate": 18709, - "unit": "km" - }, - "LSL": { - "rate": 1587, - "unit": "km" - }, - "LTL": { - "rate": 348, - "unit": "km" - }, - "LVL": { - "rate": 71, - "unit": "km" - }, - "LYD": { - "rate": 486, - "unit": "km" - }, - "MAD": { - "rate": 967, - "unit": "km" - }, - "MDL": { - "rate": 1910, - "unit": "km" - }, - "MGA": { - "rate": 406520, - "unit": "km" - }, - "MKD": { - "rate": 5570, - "unit": "km" - }, - "MMK": { - "rate": 152083, - "unit": "km" - }, - "MNT": { - "rate": 306788, - "unit": "km" - }, - "MOP": { - "rate": 863, - "unit": "km" - }, - "MRO": { - "rate": 38463, - "unit": "km" - }, - "MRU": { - "rate": 3862, - "unit": "km" - }, - "MUR": { - "rate": 4340, - "unit": "km" - }, - "MVR": { - "rate": 1667, - "unit": "km" - }, - "MWK": { - "rate": 84643, - "unit": "km" - }, - "MXN": { - "rate": 93, - "unit": "km" - }, - "MYR": { - "rate": 444, - "unit": "km" - }, - "MZN": { - "rate": 7772, - "unit": "km" - }, - "NAD": { - "rate": 1587, - "unit": "km" - }, - "NGN": { - "rate": 42688, - "unit": "km" - }, - "NIO": { - "rate": 3772, - "unit": "km" - }, - "NOK": { - "rate": 350, - "unit": "km" - }, - "NPR": { - "rate": 12474, - "unit": "km" - }, - "NZD": { - "rate": 95, - "unit": "km" - }, - "OMR": { - "rate": 42, - "unit": "km" - }, - "PAB": { - "rate": 108, - "unit": "km" - }, - "PEN": { - "rate": 401, - "unit": "km" - }, - "PGK": { - "rate": 380, - "unit": "km" - }, - "PHP": { - "rate": 5234, - "unit": "km" - }, - "PKR": { - "rate": 16785, - "unit": "km" - }, - "PLN": { - "rate": 89, - "unit": "km" - }, - "PYG": { - "rate": 704732, - "unit": "km" - }, - "QAR": { - "rate": 393, - "unit": "km" - }, - "RON": { - "rate": 443, - "unit": "km" - }, - "RSD": { - "rate": 10630, - "unit": "km" - }, - "RUB": { - "rate": 8074, - "unit": "km" - }, - "RWF": { - "rate": 107182, - "unit": "km" - }, - "SAR": { - "rate": 404, - "unit": "km" - }, - "SBD": { - "rate": 859, - "unit": "km" - }, - "SCR": { - "rate": 2287, - "unit": "km" - }, - "SDG": { - "rate": 41029, - "unit": "km" - }, - "SEK": { - "rate": 250, - "unit": "km" - }, - "SGD": { - "rate": 145, - "unit": "km" - }, - "SHP": { - "rate": 77, - "unit": "km" - }, - "SLL": { - "rate": 1102723, - "unit": "km" - }, - "SOS": { - "rate": 62604, - "unit": "km" - }, - "SRD": { - "rate": 1526, - "unit": "km" - }, - "STD": { - "rate": 2223309, - "unit": "km" - }, - "STN": { - "rate": 2232, - "unit": "km" - }, - "SVC": { - "rate": 943, - "unit": "km" - }, - "SYP": { - "rate": 82077, - "unit": "km" - }, - "SZL": { - "rate": 1585, - "unit": "km" - }, - "THB": { - "rate": 3328, - "unit": "km" - }, - "TJS": { - "rate": 1230, - "unit": "km" - }, - "TMT": { - "rate": 378, - "unit": "km" - }, - "TND": { - "rate": 295, - "unit": "km" - }, - "TOP": { - "rate": 245, - "unit": "km" - }, - "TRY": { - "rate": 845, - "unit": "km" - }, - "TTD": { - "rate": 732, - "unit": "km" - }, - "TWD": { - "rate": 3055, - "unit": "km" - }, - "TZS": { - "rate": 250116, - "unit": "km" - }, - "UAH": { - "rate": 2985, - "unit": "km" - }, - "UGX": { - "rate": 395255, - "unit": "km" - }, - "USD": { - "rate": 67, - "unit": "mi" - }, - "UYU": { - "rate": 4777, - "unit": "km" - }, - "UZS": { - "rate": 1131331, - "unit": "km" - }, - "VEB": { - "rate": 679346, - "unit": "km" - }, - "VEF": { - "rate": 26793449, - "unit": "km" - }, - "VES": { - "rate": 194381905, - "unit": "km" - }, - "VND": { - "rate": 2487242, - "unit": "km" - }, - "VUV": { - "rate": 11748, - "unit": "km" - }, - "WST": { - "rate": 272, - "unit": "km" - }, - "XAF": { - "rate": 59224, - "unit": "km" - }, - "XCD": { - "rate": 291, - "unit": "km" - }, - "XOF": { - "rate": 59224, - "unit": "km" - }, - "XPF": { - "rate": 10783, - "unit": "km" - }, - "YER": { - "rate": 27037, - "unit": "km" - }, - "ZAR": { - "rate": 464, - "unit": "km" - }, - "ZMK": { - "rate": 566489, - "unit": "km" - }, - "ZMW": { - "rate": 2377, - "unit": "km" - } - }`) as Record, + "AED": { + "rate": 414, + "unit": "km" + }, + "AFN": { + "rate": 8851, + "unit": "km" + }, + "ALL": { + "rate": 10783, + "unit": "km" + }, + "AMD": { + "rate": 45116, + "unit": "km" + }, + "ANG": { + "rate": 203, + "unit": "km" + }, + "AOA": { + "rate": 102929, + "unit": "km" + }, + "ARS": { + "rate": 118428, + "unit": "km" + }, + "AUD": { + "rate": 88, + "unit": "km" + }, + "AWG": { + "rate": 203, + "unit": "km" + }, + "AZN": { + "rate": 192, + "unit": "km" + }, + "BAM": { + "rate": 212, + "unit": "km" + }, + "BBD": { + "rate": 225, + "unit": "km" + }, + "BDT": { + "rate": 13697, + "unit": "km" + }, + "BGN": { + "rate": 211, + "unit": "km" + }, + "BHD": { + "rate": 42, + "unit": "km" + }, + "BIF": { + "rate": 331847, + "unit": "km" + }, + "BMD": { + "rate": 113, + "unit": "km" + }, + "BND": { + "rate": 153, + "unit": "km" + }, + "BOB": { + "rate": 779, + "unit": "km" + }, + "BRL": { + "rate": 660, + "unit": "km" + }, + "BSD": { + "rate": 113, + "unit": "km" + }, + "BTN": { + "rate": 9761, + "unit": "km" + }, + "BWP": { + "rate": 1569, + "unit": "km" + }, + "BYN": { + "rate": 369, + "unit": "km" + }, + "BYR": { + "rate": 2255979, + "unit": "km" + }, + "BZD": { + "rate": 227, + "unit": "km" + }, + "CAD": { + "rate": 72, + "unit": "km" + }, + "CDF": { + "rate": 321167, + "unit": "km" + }, + "CHF": { + "rate": 76, + "unit": "km" + }, + "CLP": { + "rate": 111689, + "unit": "km" + }, + "CNY": { + "rate": 808, + "unit": "km" + }, + "COP": { + "rate": 473791, + "unit": "km" + }, + "CRC": { + "rate": 57190, + "unit": "km" + }, + "CUC": { + "rate": 113, + "unit": "km" + }, + "CUP": { + "rate": 2902, + "unit": "km" + }, + "CVE": { + "rate": 11961, + "unit": "km" + }, + "CZK": { + "rate": 2715, + "unit": "km" + }, + "DJF": { + "rate": 19956, + "unit": "km" + }, + "DKK": { + "rate": 381, + "unit": "km" + }, + "DOP": { + "rate": 6948, + "unit": "km" + }, + "DZD": { + "rate": 15226, + "unit": "km" + }, + "EEK": { + "rate": 1646, + "unit": "km" + }, + "EGP": { + "rate": 5657, + "unit": "km" + }, + "ERN": { + "rate": 1690, + "unit": "km" + }, + "ETB": { + "rate": 14326, + "unit": "km" + }, + "EUR": { + "rate": 30, + "unit": "km" + }, + "FJD": { + "rate": 264, + "unit": "km" + }, + "FKP": { + "rate": 90, + "unit": "km" + }, + "GBP": { + "rate": 45, + "unit": "mi" + }, + "GEL": { + "rate": 323, + "unit": "km" + }, + "GHS": { + "rate": 1724, + "unit": "km" + }, + "GIP": { + "rate": 90, + "unit": "km" + }, + "GMD": { + "rate": 8111, + "unit": "km" + }, + "GNF": { + "rate": 974619, + "unit": "km" + }, + "GTQ": { + "rate": 872, + "unit": "km" + }, + "GYD": { + "rate": 23585, + "unit": "km" + }, + "HKD": { + "rate": 877, + "unit": "km" + }, + "HNL": { + "rate": 2881, + "unit": "km" + }, + "HRK": { + "rate": 814, + "unit": "km" + }, + "HTG": { + "rate": 14734, + "unit": "km" + }, + "HUF": { + "rate": 44127, + "unit": "km" + }, + "IDR": { + "rate": 1830066, + "unit": "km" + }, + "ILS": { + "rate": 540, + "unit": "km" + }, + "INR": { + "rate": 9761, + "unit": "km" + }, + "IQD": { + "rate": 147577, + "unit": "km" + }, + "IRR": { + "rate": 4741290, + "unit": "km" + }, + "ISK": { + "rate": 15772, + "unit": "km" + }, + "JMD": { + "rate": 17738, + "unit": "km" + }, + "JOD": { + "rate": 80, + "unit": "km" + }, + "JPY": { + "rate": 17542, + "unit": "km" + }, + "KES": { + "rate": 14589, + "unit": "km" + }, + "KGS": { + "rate": 9852, + "unit": "km" + }, + "KHR": { + "rate": 453066, + "unit": "km" + }, + "KMF": { + "rate": 53269, + "unit": "km" + }, + "KPW": { + "rate": 101389, + "unit": "km" + }, + "KRW": { + "rate": 162705, + "unit": "km" + }, + "KWD": { + "rate": 35, + "unit": "km" + }, + "KYD": { + "rate": 93, + "unit": "km" + }, + "KZT": { + "rate": 58319, + "unit": "km" + }, + "LAK": { + "rate": 2452802, + "unit": "km" + }, + "LBP": { + "rate": 10093809, + "unit": "km" + }, + "LKR": { + "rate": 33423, + "unit": "km" + }, + "LRD": { + "rate": 22185, + "unit": "km" + }, + "LSL": { + "rate": 2099, + "unit": "km" + }, + "LTL": { + "rate": 364, + "unit": "km" + }, + "LVL": { + "rate": 74, + "unit": "km" + }, + "LYD": { + "rate": 554, + "unit": "km" + }, + "MAD": { + "rate": 1127, + "unit": "km" + }, + "MDL": { + "rate": 2084, + "unit": "km" + }, + "MGA": { + "rate": 529635, + "unit": "km" + }, + "MKD": { + "rate": 6650, + "unit": "km" + }, + "MMK": { + "rate": 236413, + "unit": "km" + }, + "MNT": { + "rate": 382799, + "unit": "km" + }, + "MOP": { + "rate": 904, + "unit": "km" + }, + "MRO": { + "rate": 40234, + "unit": "km" + }, + "MRU": { + "rate": 4506, + "unit": "km" + }, + "MUR": { + "rate": 5226, + "unit": "km" + }, + "MVR": { + "rate": 1735, + "unit": "km" + }, + "MWK": { + "rate": 195485, + "unit": "km" + }, + "MXN": { + "rate": 93, + "unit": "km" + }, + "MYR": { + "rate": 494, + "unit": "km" + }, + "MZN": { + "rate": 7199, + "unit": "km" + }, + "NAD": { + "rate": 2099, + "unit": "km" + }, + "NGN": { + "rate": 174979, + "unit": "km" + }, + "NIO": { + "rate": 4147, + "unit": "km" + }, + "NOK": { + "rate": 350, + "unit": "km" + }, + "NPR": { + "rate": 15617, + "unit": "km" + }, + "NZD": { + "rate": 104, + "unit": "km" + }, + "OMR": { + "rate": 43, + "unit": "km" + }, + "PAB": { + "rate": 113, + "unit": "km" + }, + "PEN": { + "rate": 420, + "unit": "km" + }, + "PGK": { + "rate": 455, + "unit": "km" + }, + "PHP": { + "rate": 6582, + "unit": "km" + }, + "PKR": { + "rate": 31411, + "unit": "km" + }, + "PLN": { + "rate": 89, + "unit": "km" + }, + "PYG": { + "rate": 890772, + "unit": "km" + }, + "QAR": { + "rate": 410, + "unit": "km" + }, + "RON": { + "rate": 538, + "unit": "km" + }, + "RSD": { + "rate": 12656, + "unit": "km" + }, + "RUB": { + "rate": 11182, + "unit": "km" + }, + "RWF": { + "rate": 156589, + "unit": "km" + }, + "SAR": { + "rate": 423, + "unit": "km" + }, + "SBD": { + "rate": 951, + "unit": "km" + }, + "SCR": { + "rate": 1611, + "unit": "km" + }, + "SDG": { + "rate": 67705, + "unit": "km" + }, + "SEK": { + "rate": 250, + "unit": "km" + }, + "SGD": { + "rate": 151, + "unit": "km" + }, + "SHP": { + "rate": 90, + "unit": "km" + }, + "SLL": { + "rate": 2362357, + "unit": "km" + }, + "SLE": { + "rate": 2363, + "unit": "km" + }, + "SOS": { + "rate": 64374, + "unit": "km" + }, + "SRD": { + "rate": 3954, + "unit": "km" + }, + "STD": { + "rate": 2510095, + "unit": "km" + }, + "STN": { + "rate": 2683, + "unit": "km" + }, + "SVC": { + "rate": 987, + "unit": "km" + }, + "SYP": { + "rate": 1464664, + "unit": "km" + }, + "SZL": { + "rate": 2099, + "unit": "km" + }, + "THB": { + "rate": 3801, + "unit": "km" + }, + "TJS": { + "rate": 1228, + "unit": "km" + }, + "TMT": { + "rate": 394, + "unit": "km" + }, + "TND": { + "rate": 360, + "unit": "km" + }, + "TOP": { + "rate": 274, + "unit": "km" + }, + "TRY": { + "rate": 4035, + "unit": "km" + }, + "TTD": { + "rate": 763, + "unit": "km" + }, + "TWD": { + "rate": 3703, + "unit": "km" + }, + "TZS": { + "rate": 286235, + "unit": "km" + }, + "UAH": { + "rate": 4725, + "unit": "km" + }, + "UGX": { + "rate": 416016, + "unit": "km" + }, + "USD": { + "rate": 70, + "unit": "mi" + }, + "UYU": { + "rate": 4888, + "unit": "km" + }, + "UZS": { + "rate": 1462038, + "unit": "km" + }, + "VEB": { + "rate": 709737, + "unit": "km" + }, + "VEF": { + "rate": 27993155, + "unit": "km" + }, + "VES": { + "rate": 6457, + "unit": "km" + }, + "VND": { + "rate": 2825526, + "unit": "km" + }, + "VUV": { + "rate": 13358, + "unit": "km" + }, + "WST": { + "rate": 315, + "unit": "km" + }, + "XAF": { + "rate": 70811, + "unit": "km" + }, + "XCD": { + "rate": 304, + "unit": "km" + }, + "XOF": { + "rate": 70811, + "unit": "km" + }, + "XPF": { + "rate": 12875, + "unit": "km" + }, + "YER": { + "rate": 28003, + "unit": "km" + }, + "ZAR": { + "rate": 484, + "unit": "km" + }, + "ZMK": { + "rate": 591756, + "unit": "km" + }, + "ZMW": { + "rate": 3148, + "unit": "km" + } +}`) as Record, EXIT_SURVEY: { REASONS: { From d5c785dc71cd249d2f9dc24517a43886c4dbc3d8 Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 4 Feb 2025 11:41:16 -0800 Subject: [PATCH 052/109] Prettier --- src/CONST.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index a980e14bf57d..d72ee5851744 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2778,7 +2778,7 @@ const CONST = { NAME_PER_DIEM_INTERNATIONAL: 'Per Diem International', DISTANCE_UNIT_MILES: 'mi', DISTANCE_UNIT_KILOMETERS: 'km', - MILEAGE_IRS_RATE: 0.70, + MILEAGE_IRS_RATE: 0.7, DEFAULT_RATE: 'Default Rate', RATE_DECIMALS: 3, FAKE_P2P_ID: '_FAKE_P2P_ID_', From 823dacea1aad979cacb5e9aa8af9638790906823 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Tue, 4 Feb 2025 12:11:23 -0800 Subject: [PATCH 053/109] Create personal-cards.md Tracking GH: https://github.com/Expensify/Expensify/issues/462432 Creating a personal card page in NewDot help site. This feature isn't available yet so this will be a placeholder. The intention of the article is to provide customers with a self-service method to find out more about personal cards in NewDot. --- .../connect-credit-cards/personal-cards.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/articles/new-expensify/connect-credit-cards/personal-cards.md diff --git a/docs/articles/new-expensify/connect-credit-cards/personal-cards.md b/docs/articles/new-expensify/connect-credit-cards/personal-cards.md new file mode 100644 index 000000000000..3b01ccca553d --- /dev/null +++ b/docs/articles/new-expensify/connect-credit-cards/personal-cards.md @@ -0,0 +1,12 @@ +--- +title: personal-cards.md +description: Learn how to track and manage your personal credit card expenses in Expensify through automatic imports or manual uploads. +--- + +# Overview + +Expensify makes it easy to track expenses and get reimbursed by linking your personal credit card. Once connected, transactions can be imported automatically, or you can upload a CSV file for manual entry. These transactions will be merged with SmartScanned receipts and, if enabled, can generate IRS-compliant eReceipts. + +--- + +We are currently developing the personal card connection feature for New Expensify. Once available, we will update this article with step-by-step instructions on how to connect your card. Stay tuned! From 808750b2da55af00ac3200e0db444b3e2a6ddbea Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 4 Feb 2025 14:09:05 -0800 Subject: [PATCH 054/109] Tests --- tests/unit/ModifiedExpenseMessageTest.ts | 12 ++++++------ tests/unit/PolicyDistanceRatesUtilsTest.ts | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/unit/ModifiedExpenseMessageTest.ts b/tests/unit/ModifiedExpenseMessageTest.ts index b9e1869c72c1..0d5774b8444c 100644 --- a/tests/unit/ModifiedExpenseMessageTest.ts +++ b/tests/unit/ModifiedExpenseMessageTest.ts @@ -364,10 +364,10 @@ describe('ModifiedExpenseMessage', () => { ...createRandomReportAction(1), actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, originalMessage: { - oldMerchant: '1.00 mi @ $0.67 / mi', - merchant: '10.00 mi @ $0.67 / mi', - oldAmount: 67, - amount: 670, + oldMerchant: '1.00 mi @ $0.70 / mi', + merchant: '10.00 mi @ $0.70 / mi', + oldAmount: 70, + amount: 700, oldCurrency: CONST.CURRENCY.USD, currency: CONST.CURRENCY.USD, }, @@ -385,9 +385,9 @@ describe('ModifiedExpenseMessage', () => { ...createRandomReportAction(1), actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE, originalMessage: { - oldMerchant: '56.36 mi @ $0.67 / mi', + oldMerchant: '56.36 mi @ $0.70 / mi', merchant: '56.36 mi @ $0.99 / mi', - oldAmount: 3776, + oldAmount: 3945, amount: 5580, oldCurrency: CONST.CURRENCY.USD, currency: CONST.CURRENCY.USD, diff --git a/tests/unit/PolicyDistanceRatesUtilsTest.ts b/tests/unit/PolicyDistanceRatesUtilsTest.ts index c7116fcf6f82..1191adc6800e 100644 --- a/tests/unit/PolicyDistanceRatesUtilsTest.ts +++ b/tests/unit/PolicyDistanceRatesUtilsTest.ts @@ -6,17 +6,17 @@ describe('PolicyDistanceRatesUtils', () => { // Given a tax claimable value inserted for a distance rate // When the taxClaimableValue is equal to the tax rate - const validate = validateTaxClaimableValue({taxClaimableValue: '0.67'}, {rate: 67, customUnitRateID: ''}); + const validate = validateTaxClaimableValue({taxClaimableValue: '0.70'}, {rate: 70, customUnitRateID: ''}); // Then validateTaxClaimableValue will return an error. expect(validate.taxClaimableValue).toBeDefined(); // When the taxClaimableValue is greater than the tax rate - const validate2 = validateTaxClaimableValue({taxClaimableValue: '0.69'}, {rate: 67, customUnitRateID: ''}); + const validate2 = validateTaxClaimableValue({taxClaimableValue: '0.72'}, {rate: 70, customUnitRateID: ''}); // Then validateTaxClaimableValue will return an error. expect(validate2.taxClaimableValue).toBeDefined(); // When the taxClaimableValue is less than the tax rate - const validate3 = validateTaxClaimableValue({taxClaimableValue: '0.65'}, {rate: 67, customUnitRateID: ''}); + const validate3 = validateTaxClaimableValue({taxClaimableValue: '0.65'}, {rate: 70, customUnitRateID: ''}); // Then validateTaxClaimableValue will not return an error. expect(validate3.taxClaimableValue).toBeUndefined(); }); From 9fc6280af334f61ae74d298a3412bea68ca42c61 Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 4 Feb 2025 14:18:54 -0800 Subject: [PATCH 055/109] Fix weird indentation --- src/CONST.ts | 1322 +++++++++++++++++++++++++------------------------- 1 file changed, 661 insertions(+), 661 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d72ee5851744..3fd471feb14a 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5325,667 +5325,667 @@ const CONST = { }, }, CURRENCY_TO_DEFAULT_MILEAGE_RATE: JSON.parse(`{ - "AED": { - "rate": 414, - "unit": "km" - }, - "AFN": { - "rate": 8851, - "unit": "km" - }, - "ALL": { - "rate": 10783, - "unit": "km" - }, - "AMD": { - "rate": 45116, - "unit": "km" - }, - "ANG": { - "rate": 203, - "unit": "km" - }, - "AOA": { - "rate": 102929, - "unit": "km" - }, - "ARS": { - "rate": 118428, - "unit": "km" - }, - "AUD": { - "rate": 88, - "unit": "km" - }, - "AWG": { - "rate": 203, - "unit": "km" - }, - "AZN": { - "rate": 192, - "unit": "km" - }, - "BAM": { - "rate": 212, - "unit": "km" - }, - "BBD": { - "rate": 225, - "unit": "km" - }, - "BDT": { - "rate": 13697, - "unit": "km" - }, - "BGN": { - "rate": 211, - "unit": "km" - }, - "BHD": { - "rate": 42, - "unit": "km" - }, - "BIF": { - "rate": 331847, - "unit": "km" - }, - "BMD": { - "rate": 113, - "unit": "km" - }, - "BND": { - "rate": 153, - "unit": "km" - }, - "BOB": { - "rate": 779, - "unit": "km" - }, - "BRL": { - "rate": 660, - "unit": "km" - }, - "BSD": { - "rate": 113, - "unit": "km" - }, - "BTN": { - "rate": 9761, - "unit": "km" - }, - "BWP": { - "rate": 1569, - "unit": "km" - }, - "BYN": { - "rate": 369, - "unit": "km" - }, - "BYR": { - "rate": 2255979, - "unit": "km" - }, - "BZD": { - "rate": 227, - "unit": "km" - }, - "CAD": { - "rate": 72, - "unit": "km" - }, - "CDF": { - "rate": 321167, - "unit": "km" - }, - "CHF": { - "rate": 76, - "unit": "km" - }, - "CLP": { - "rate": 111689, - "unit": "km" - }, - "CNY": { - "rate": 808, - "unit": "km" - }, - "COP": { - "rate": 473791, - "unit": "km" - }, - "CRC": { - "rate": 57190, - "unit": "km" - }, - "CUC": { - "rate": 113, - "unit": "km" - }, - "CUP": { - "rate": 2902, - "unit": "km" - }, - "CVE": { - "rate": 11961, - "unit": "km" - }, - "CZK": { - "rate": 2715, - "unit": "km" - }, - "DJF": { - "rate": 19956, - "unit": "km" - }, - "DKK": { - "rate": 381, - "unit": "km" - }, - "DOP": { - "rate": 6948, - "unit": "km" - }, - "DZD": { - "rate": 15226, - "unit": "km" - }, - "EEK": { - "rate": 1646, - "unit": "km" - }, - "EGP": { - "rate": 5657, - "unit": "km" - }, - "ERN": { - "rate": 1690, - "unit": "km" - }, - "ETB": { - "rate": 14326, - "unit": "km" - }, - "EUR": { - "rate": 30, - "unit": "km" - }, - "FJD": { - "rate": 264, - "unit": "km" - }, - "FKP": { - "rate": 90, - "unit": "km" - }, - "GBP": { - "rate": 45, - "unit": "mi" - }, - "GEL": { - "rate": 323, - "unit": "km" - }, - "GHS": { - "rate": 1724, - "unit": "km" - }, - "GIP": { - "rate": 90, - "unit": "km" - }, - "GMD": { - "rate": 8111, - "unit": "km" - }, - "GNF": { - "rate": 974619, - "unit": "km" - }, - "GTQ": { - "rate": 872, - "unit": "km" - }, - "GYD": { - "rate": 23585, - "unit": "km" - }, - "HKD": { - "rate": 877, - "unit": "km" - }, - "HNL": { - "rate": 2881, - "unit": "km" - }, - "HRK": { - "rate": 814, - "unit": "km" - }, - "HTG": { - "rate": 14734, - "unit": "km" - }, - "HUF": { - "rate": 44127, - "unit": "km" - }, - "IDR": { - "rate": 1830066, - "unit": "km" - }, - "ILS": { - "rate": 540, - "unit": "km" - }, - "INR": { - "rate": 9761, - "unit": "km" - }, - "IQD": { - "rate": 147577, - "unit": "km" - }, - "IRR": { - "rate": 4741290, - "unit": "km" - }, - "ISK": { - "rate": 15772, - "unit": "km" - }, - "JMD": { - "rate": 17738, - "unit": "km" - }, - "JOD": { - "rate": 80, - "unit": "km" - }, - "JPY": { - "rate": 17542, - "unit": "km" - }, - "KES": { - "rate": 14589, - "unit": "km" - }, - "KGS": { - "rate": 9852, - "unit": "km" - }, - "KHR": { - "rate": 453066, - "unit": "km" - }, - "KMF": { - "rate": 53269, - "unit": "km" - }, - "KPW": { - "rate": 101389, - "unit": "km" - }, - "KRW": { - "rate": 162705, - "unit": "km" - }, - "KWD": { - "rate": 35, - "unit": "km" - }, - "KYD": { - "rate": 93, - "unit": "km" - }, - "KZT": { - "rate": 58319, - "unit": "km" - }, - "LAK": { - "rate": 2452802, - "unit": "km" - }, - "LBP": { - "rate": 10093809, - "unit": "km" - }, - "LKR": { - "rate": 33423, - "unit": "km" - }, - "LRD": { - "rate": 22185, - "unit": "km" - }, - "LSL": { - "rate": 2099, - "unit": "km" - }, - "LTL": { - "rate": 364, - "unit": "km" - }, - "LVL": { - "rate": 74, - "unit": "km" - }, - "LYD": { - "rate": 554, - "unit": "km" - }, - "MAD": { - "rate": 1127, - "unit": "km" - }, - "MDL": { - "rate": 2084, - "unit": "km" - }, - "MGA": { - "rate": 529635, - "unit": "km" - }, - "MKD": { - "rate": 6650, - "unit": "km" - }, - "MMK": { - "rate": 236413, - "unit": "km" - }, - "MNT": { - "rate": 382799, - "unit": "km" - }, - "MOP": { - "rate": 904, - "unit": "km" - }, - "MRO": { - "rate": 40234, - "unit": "km" - }, - "MRU": { - "rate": 4506, - "unit": "km" - }, - "MUR": { - "rate": 5226, - "unit": "km" - }, - "MVR": { - "rate": 1735, - "unit": "km" - }, - "MWK": { - "rate": 195485, - "unit": "km" - }, - "MXN": { - "rate": 93, - "unit": "km" - }, - "MYR": { - "rate": 494, - "unit": "km" - }, - "MZN": { - "rate": 7199, - "unit": "km" - }, - "NAD": { - "rate": 2099, - "unit": "km" - }, - "NGN": { - "rate": 174979, - "unit": "km" - }, - "NIO": { - "rate": 4147, - "unit": "km" - }, - "NOK": { - "rate": 350, - "unit": "km" - }, - "NPR": { - "rate": 15617, - "unit": "km" - }, - "NZD": { - "rate": 104, - "unit": "km" - }, - "OMR": { - "rate": 43, - "unit": "km" - }, - "PAB": { - "rate": 113, - "unit": "km" - }, - "PEN": { - "rate": 420, - "unit": "km" - }, - "PGK": { - "rate": 455, - "unit": "km" - }, - "PHP": { - "rate": 6582, - "unit": "km" - }, - "PKR": { - "rate": 31411, - "unit": "km" - }, - "PLN": { - "rate": 89, - "unit": "km" - }, - "PYG": { - "rate": 890772, - "unit": "km" - }, - "QAR": { - "rate": 410, - "unit": "km" - }, - "RON": { - "rate": 538, - "unit": "km" - }, - "RSD": { - "rate": 12656, - "unit": "km" - }, - "RUB": { - "rate": 11182, - "unit": "km" - }, - "RWF": { - "rate": 156589, - "unit": "km" - }, - "SAR": { - "rate": 423, - "unit": "km" - }, - "SBD": { - "rate": 951, - "unit": "km" - }, - "SCR": { - "rate": 1611, - "unit": "km" - }, - "SDG": { - "rate": 67705, - "unit": "km" - }, - "SEK": { - "rate": 250, - "unit": "km" - }, - "SGD": { - "rate": 151, - "unit": "km" - }, - "SHP": { - "rate": 90, - "unit": "km" - }, - "SLL": { - "rate": 2362357, - "unit": "km" - }, - "SLE": { - "rate": 2363, - "unit": "km" - }, - "SOS": { - "rate": 64374, - "unit": "km" - }, - "SRD": { - "rate": 3954, - "unit": "km" - }, - "STD": { - "rate": 2510095, - "unit": "km" - }, - "STN": { - "rate": 2683, - "unit": "km" - }, - "SVC": { - "rate": 987, - "unit": "km" - }, - "SYP": { - "rate": 1464664, - "unit": "km" - }, - "SZL": { - "rate": 2099, - "unit": "km" - }, - "THB": { - "rate": 3801, - "unit": "km" - }, - "TJS": { - "rate": 1228, - "unit": "km" - }, - "TMT": { - "rate": 394, - "unit": "km" - }, - "TND": { - "rate": 360, - "unit": "km" - }, - "TOP": { - "rate": 274, - "unit": "km" - }, - "TRY": { - "rate": 4035, - "unit": "km" - }, - "TTD": { - "rate": 763, - "unit": "km" - }, - "TWD": { - "rate": 3703, - "unit": "km" - }, - "TZS": { - "rate": 286235, - "unit": "km" - }, - "UAH": { - "rate": 4725, - "unit": "km" - }, - "UGX": { - "rate": 416016, - "unit": "km" - }, - "USD": { - "rate": 70, - "unit": "mi" - }, - "UYU": { - "rate": 4888, - "unit": "km" - }, - "UZS": { - "rate": 1462038, - "unit": "km" - }, - "VEB": { - "rate": 709737, - "unit": "km" - }, - "VEF": { - "rate": 27993155, - "unit": "km" - }, - "VES": { - "rate": 6457, - "unit": "km" - }, - "VND": { - "rate": 2825526, - "unit": "km" - }, - "VUV": { - "rate": 13358, - "unit": "km" - }, - "WST": { - "rate": 315, - "unit": "km" - }, - "XAF": { - "rate": 70811, - "unit": "km" - }, - "XCD": { - "rate": 304, - "unit": "km" - }, - "XOF": { - "rate": 70811, - "unit": "km" - }, - "XPF": { - "rate": 12875, - "unit": "km" - }, - "YER": { - "rate": 28003, - "unit": "km" - }, - "ZAR": { - "rate": 484, - "unit": "km" - }, - "ZMK": { - "rate": 591756, - "unit": "km" - }, - "ZMW": { - "rate": 3148, - "unit": "km" - } -}`) as Record, + "AED": { + "rate": 414, + "unit": "km" + }, + "AFN": { + "rate": 8851, + "unit": "km" + }, + "ALL": { + "rate": 10783, + "unit": "km" + }, + "AMD": { + "rate": 45116, + "unit": "km" + }, + "ANG": { + "rate": 203, + "unit": "km" + }, + "AOA": { + "rate": 102929, + "unit": "km" + }, + "ARS": { + "rate": 118428, + "unit": "km" + }, + "AUD": { + "rate": 88, + "unit": "km" + }, + "AWG": { + "rate": 203, + "unit": "km" + }, + "AZN": { + "rate": 192, + "unit": "km" + }, + "BAM": { + "rate": 212, + "unit": "km" + }, + "BBD": { + "rate": 225, + "unit": "km" + }, + "BDT": { + "rate": 13697, + "unit": "km" + }, + "BGN": { + "rate": 211, + "unit": "km" + }, + "BHD": { + "rate": 42, + "unit": "km" + }, + "BIF": { + "rate": 331847, + "unit": "km" + }, + "BMD": { + "rate": 113, + "unit": "km" + }, + "BND": { + "rate": 153, + "unit": "km" + }, + "BOB": { + "rate": 779, + "unit": "km" + }, + "BRL": { + "rate": 660, + "unit": "km" + }, + "BSD": { + "rate": 113, + "unit": "km" + }, + "BTN": { + "rate": 9761, + "unit": "km" + }, + "BWP": { + "rate": 1569, + "unit": "km" + }, + "BYN": { + "rate": 369, + "unit": "km" + }, + "BYR": { + "rate": 2255979, + "unit": "km" + }, + "BZD": { + "rate": 227, + "unit": "km" + }, + "CAD": { + "rate": 72, + "unit": "km" + }, + "CDF": { + "rate": 321167, + "unit": "km" + }, + "CHF": { + "rate": 76, + "unit": "km" + }, + "CLP": { + "rate": 111689, + "unit": "km" + }, + "CNY": { + "rate": 808, + "unit": "km" + }, + "COP": { + "rate": 473791, + "unit": "km" + }, + "CRC": { + "rate": 57190, + "unit": "km" + }, + "CUC": { + "rate": 113, + "unit": "km" + }, + "CUP": { + "rate": 2902, + "unit": "km" + }, + "CVE": { + "rate": 11961, + "unit": "km" + }, + "CZK": { + "rate": 2715, + "unit": "km" + }, + "DJF": { + "rate": 19956, + "unit": "km" + }, + "DKK": { + "rate": 381, + "unit": "km" + }, + "DOP": { + "rate": 6948, + "unit": "km" + }, + "DZD": { + "rate": 15226, + "unit": "km" + }, + "EEK": { + "rate": 1646, + "unit": "km" + }, + "EGP": { + "rate": 5657, + "unit": "km" + }, + "ERN": { + "rate": 1690, + "unit": "km" + }, + "ETB": { + "rate": 14326, + "unit": "km" + }, + "EUR": { + "rate": 30, + "unit": "km" + }, + "FJD": { + "rate": 264, + "unit": "km" + }, + "FKP": { + "rate": 90, + "unit": "km" + }, + "GBP": { + "rate": 45, + "unit": "mi" + }, + "GEL": { + "rate": 323, + "unit": "km" + }, + "GHS": { + "rate": 1724, + "unit": "km" + }, + "GIP": { + "rate": 90, + "unit": "km" + }, + "GMD": { + "rate": 8111, + "unit": "km" + }, + "GNF": { + "rate": 974619, + "unit": "km" + }, + "GTQ": { + "rate": 872, + "unit": "km" + }, + "GYD": { + "rate": 23585, + "unit": "km" + }, + "HKD": { + "rate": 877, + "unit": "km" + }, + "HNL": { + "rate": 2881, + "unit": "km" + }, + "HRK": { + "rate": 814, + "unit": "km" + }, + "HTG": { + "rate": 14734, + "unit": "km" + }, + "HUF": { + "rate": 44127, + "unit": "km" + }, + "IDR": { + "rate": 1830066, + "unit": "km" + }, + "ILS": { + "rate": 540, + "unit": "km" + }, + "INR": { + "rate": 9761, + "unit": "km" + }, + "IQD": { + "rate": 147577, + "unit": "km" + }, + "IRR": { + "rate": 4741290, + "unit": "km" + }, + "ISK": { + "rate": 15772, + "unit": "km" + }, + "JMD": { + "rate": 17738, + "unit": "km" + }, + "JOD": { + "rate": 80, + "unit": "km" + }, + "JPY": { + "rate": 17542, + "unit": "km" + }, + "KES": { + "rate": 14589, + "unit": "km" + }, + "KGS": { + "rate": 9852, + "unit": "km" + }, + "KHR": { + "rate": 453066, + "unit": "km" + }, + "KMF": { + "rate": 53269, + "unit": "km" + }, + "KPW": { + "rate": 101389, + "unit": "km" + }, + "KRW": { + "rate": 162705, + "unit": "km" + }, + "KWD": { + "rate": 35, + "unit": "km" + }, + "KYD": { + "rate": 93, + "unit": "km" + }, + "KZT": { + "rate": 58319, + "unit": "km" + }, + "LAK": { + "rate": 2452802, + "unit": "km" + }, + "LBP": { + "rate": 10093809, + "unit": "km" + }, + "LKR": { + "rate": 33423, + "unit": "km" + }, + "LRD": { + "rate": 22185, + "unit": "km" + }, + "LSL": { + "rate": 2099, + "unit": "km" + }, + "LTL": { + "rate": 364, + "unit": "km" + }, + "LVL": { + "rate": 74, + "unit": "km" + }, + "LYD": { + "rate": 554, + "unit": "km" + }, + "MAD": { + "rate": 1127, + "unit": "km" + }, + "MDL": { + "rate": 2084, + "unit": "km" + }, + "MGA": { + "rate": 529635, + "unit": "km" + }, + "MKD": { + "rate": 6650, + "unit": "km" + }, + "MMK": { + "rate": 236413, + "unit": "km" + }, + "MNT": { + "rate": 382799, + "unit": "km" + }, + "MOP": { + "rate": 904, + "unit": "km" + }, + "MRO": { + "rate": 40234, + "unit": "km" + }, + "MRU": { + "rate": 4506, + "unit": "km" + }, + "MUR": { + "rate": 5226, + "unit": "km" + }, + "MVR": { + "rate": 1735, + "unit": "km" + }, + "MWK": { + "rate": 195485, + "unit": "km" + }, + "MXN": { + "rate": 93, + "unit": "km" + }, + "MYR": { + "rate": 494, + "unit": "km" + }, + "MZN": { + "rate": 7199, + "unit": "km" + }, + "NAD": { + "rate": 2099, + "unit": "km" + }, + "NGN": { + "rate": 174979, + "unit": "km" + }, + "NIO": { + "rate": 4147, + "unit": "km" + }, + "NOK": { + "rate": 350, + "unit": "km" + }, + "NPR": { + "rate": 15617, + "unit": "km" + }, + "NZD": { + "rate": 104, + "unit": "km" + }, + "OMR": { + "rate": 43, + "unit": "km" + }, + "PAB": { + "rate": 113, + "unit": "km" + }, + "PEN": { + "rate": 420, + "unit": "km" + }, + "PGK": { + "rate": 455, + "unit": "km" + }, + "PHP": { + "rate": 6582, + "unit": "km" + }, + "PKR": { + "rate": 31411, + "unit": "km" + }, + "PLN": { + "rate": 89, + "unit": "km" + }, + "PYG": { + "rate": 890772, + "unit": "km" + }, + "QAR": { + "rate": 410, + "unit": "km" + }, + "RON": { + "rate": 538, + "unit": "km" + }, + "RSD": { + "rate": 12656, + "unit": "km" + }, + "RUB": { + "rate": 11182, + "unit": "km" + }, + "RWF": { + "rate": 156589, + "unit": "km" + }, + "SAR": { + "rate": 423, + "unit": "km" + }, + "SBD": { + "rate": 951, + "unit": "km" + }, + "SCR": { + "rate": 1611, + "unit": "km" + }, + "SDG": { + "rate": 67705, + "unit": "km" + }, + "SEK": { + "rate": 250, + "unit": "km" + }, + "SGD": { + "rate": 151, + "unit": "km" + }, + "SHP": { + "rate": 90, + "unit": "km" + }, + "SLL": { + "rate": 2362357, + "unit": "km" + }, + "SLE": { + "rate": 2363, + "unit": "km" + }, + "SOS": { + "rate": 64374, + "unit": "km" + }, + "SRD": { + "rate": 3954, + "unit": "km" + }, + "STD": { + "rate": 2510095, + "unit": "km" + }, + "STN": { + "rate": 2683, + "unit": "km" + }, + "SVC": { + "rate": 987, + "unit": "km" + }, + "SYP": { + "rate": 1464664, + "unit": "km" + }, + "SZL": { + "rate": 2099, + "unit": "km" + }, + "THB": { + "rate": 3801, + "unit": "km" + }, + "TJS": { + "rate": 1228, + "unit": "km" + }, + "TMT": { + "rate": 394, + "unit": "km" + }, + "TND": { + "rate": 360, + "unit": "km" + }, + "TOP": { + "rate": 274, + "unit": "km" + }, + "TRY": { + "rate": 4035, + "unit": "km" + }, + "TTD": { + "rate": 763, + "unit": "km" + }, + "TWD": { + "rate": 3703, + "unit": "km" + }, + "TZS": { + "rate": 286235, + "unit": "km" + }, + "UAH": { + "rate": 4725, + "unit": "km" + }, + "UGX": { + "rate": 416016, + "unit": "km" + }, + "USD": { + "rate": 70, + "unit": "mi" + }, + "UYU": { + "rate": 4888, + "unit": "km" + }, + "UZS": { + "rate": 1462038, + "unit": "km" + }, + "VEB": { + "rate": 709737, + "unit": "km" + }, + "VEF": { + "rate": 27993155, + "unit": "km" + }, + "VES": { + "rate": 6457, + "unit": "km" + }, + "VND": { + "rate": 2825526, + "unit": "km" + }, + "VUV": { + "rate": 13358, + "unit": "km" + }, + "WST": { + "rate": 315, + "unit": "km" + }, + "XAF": { + "rate": 70811, + "unit": "km" + }, + "XCD": { + "rate": 304, + "unit": "km" + }, + "XOF": { + "rate": 70811, + "unit": "km" + }, + "XPF": { + "rate": 12875, + "unit": "km" + }, + "YER": { + "rate": 28003, + "unit": "km" + }, + "ZAR": { + "rate": 484, + "unit": "km" + }, + "ZMK": { + "rate": 591756, + "unit": "km" + }, + "ZMW": { + "rate": 3148, + "unit": "km" + } + }`) as Record, EXIT_SURVEY: { REASONS: { From 940a007348101b22dde535f43ee8699ef594e6a4 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Tue, 4 Feb 2025 14:25:51 -0800 Subject: [PATCH 056/109] Update Configure-Netsuite.md --- .../connections/netsuite/Configure-Netsuite.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md b/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md index 3e464f82d723..2a76e0e8e682 100644 --- a/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md +++ b/docs/articles/expensify-classic/connections/netsuite/Configure-Netsuite.md @@ -59,7 +59,9 @@ This dictates when reimbursable expenses will export, according to your preferre **Journal Entries:** Non-reimbursable expenses will be posted to the Journal Entries posting account selected in your workspace's connection settings. If you centrally manage your company cards through Domains, you can export expenses from each card to a specific account in NetSuite. -- Expensify Card expenses always export as individual, itemized Journal Entries, regardless of Expense Reports or Vendor Bills settings configured for non-reimbursable expenses on the Export tab. +- When [automatic reconciliation](https://help.expensify.com/articles/expensify-classic/expensify-card/Expensify-Card-Reconciliation) is enabled, Expensify Card expenses will always export as individual, itemized Journal Entries, regardless of Expense Reports or Vendor Bills settings configured for non-reimbursable expenses on the Export tab. +- Without automatic reconciliation, Expensify Card expenses will export using the export type configured for non-reimbursable expenses on the Export tab. +- Expensify Card expenses exported as Journal Entries will always export as individual, itemized Journal Entries, regardless of whether the "one journal entry for all items on report" setting is enabled. - Journal entry forms do not contain a customer column, so it is not possible to export customers or projects with this export option - The credit line and header level classifications are pulled from the employee record From f88c5b2d13d34ece9e05fbf755dc5828c6896cdc Mon Sep 17 00:00:00 2001 From: Rocio Perez-Cano Date: Tue, 4 Feb 2025 14:27:01 -0800 Subject: [PATCH 057/109] I missed one test --- tests/unit/ModifiedExpenseMessageTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/ModifiedExpenseMessageTest.ts b/tests/unit/ModifiedExpenseMessageTest.ts index 0d5774b8444c..23daaca79fe8 100644 --- a/tests/unit/ModifiedExpenseMessageTest.ts +++ b/tests/unit/ModifiedExpenseMessageTest.ts @@ -374,7 +374,7 @@ describe('ModifiedExpenseMessage', () => { }; it('then the message says the distance is changed and shows the new and old merchant and amount', () => { - const expectedResult = `changed the distance to ${reportAction.originalMessage.merchant} (previously ${reportAction.originalMessage.oldMerchant}), which updated the amount to $6.70 (previously $0.67)`; + const expectedResult = `changed the distance to ${reportAction.originalMessage.merchant} (previously ${reportAction.originalMessage.oldMerchant}), which updated the amount to $7.00 (previously $0.70)`; const result = ModifiedExpenseMessage.getForReportAction(report.reportID, reportAction); expect(result).toEqual(expectedResult); }); @@ -395,7 +395,7 @@ describe('ModifiedExpenseMessage', () => { }; it('then the message says the rate is changed and shows the new and old merchant and amount', () => { - const expectedResult = `changed the rate to ${reportAction.originalMessage.merchant} (previously ${reportAction.originalMessage.oldMerchant}), which updated the amount to $55.80 (previously $37.76)`; + const expectedResult = `changed the rate to ${reportAction.originalMessage.merchant} (previously ${reportAction.originalMessage.oldMerchant}), which updated the amount to $55.80 (previously $39.45)`; const result = ModifiedExpenseMessage.getForReportAction(report.reportID, reportAction); expect(result).toEqual(expectedResult); }); From 07209b8fcaf09262ebf0aab2a2a3f167653b6565 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Tue, 4 Feb 2025 15:53:23 -0800 Subject: [PATCH 058/109] Update personal-cards.md Update the article title --- .../new-expensify/connect-credit-cards/personal-cards.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles/new-expensify/connect-credit-cards/personal-cards.md b/docs/articles/new-expensify/connect-credit-cards/personal-cards.md index 3b01ccca553d..70fc5cdffb52 100644 --- a/docs/articles/new-expensify/connect-credit-cards/personal-cards.md +++ b/docs/articles/new-expensify/connect-credit-cards/personal-cards.md @@ -1,5 +1,5 @@ --- -title: personal-cards.md +title: Personal Cards description: Learn how to track and manage your personal credit card expenses in Expensify through automatic imports or manual uploads. --- From 9c1503b7f1b5a1af544e4f5853909ae44e6fe724 Mon Sep 17 00:00:00 2001 From: Christina Dobrzynski <51066321+Christinadobrzyn@users.noreply.github.com> Date: Tue, 4 Feb 2025 15:57:22 -0800 Subject: [PATCH 059/109] Rename personal-cards.md to Personal-Cards.md Capitalizing the title of the article --- .../connect-credit-cards/{personal-cards.md => Personal-Cards.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/articles/new-expensify/connect-credit-cards/{personal-cards.md => Personal-Cards.md} (100%) diff --git a/docs/articles/new-expensify/connect-credit-cards/personal-cards.md b/docs/articles/new-expensify/connect-credit-cards/Personal-Cards.md similarity index 100% rename from docs/articles/new-expensify/connect-credit-cards/personal-cards.md rename to docs/articles/new-expensify/connect-credit-cards/Personal-Cards.md From dd95e24c778417ecbdf6f54b96c300eb4e03c476 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 5 Feb 2025 10:02:12 +0700 Subject: [PATCH 060/109] fix: use usePolicy instead of getPolicy --- src/components/DistanceRequest/DistanceRequestFooter.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx index 7bf83f3b9b42..a811face3340 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.tsx +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -9,10 +9,11 @@ import * as Expensicons from '@components/Icon/Expensicons'; import ImageSVG from '@components/ImageSVG'; import type {WayPoint} from '@components/MapView/MapViewTypes'; import useLocalize from '@hooks/useLocalize'; +import usePolicy from '@hooks/usePolicy'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; -import {getPersonalPolicy, getPolicy} from '@libs/PolicyUtils'; +import {getPersonalPolicy} from '@libs/PolicyUtils'; import {getDistanceInMeters, getWaypointIndex, isCustomUnitRateIDForP2P} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -42,7 +43,7 @@ function DistanceRequestFooter({waypoints, transaction, navigateToWaypointEditPa const styles = useThemeStyles(); const {translate} = useLocalize(); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); - const activePolicy = getPolicy(activePolicyID); + const activePolicy = usePolicy(activePolicyID); const [mapboxAccessToken] = useOnyx(ONYXKEYS.MAPBOX_ACCESS_TOKEN); const numberOfWaypoints = Object.keys(waypoints ?? {}).length; From df6d35116da6195d6e8cc8e1d186f8ff9d2bde22 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 5 Feb 2025 10:48:22 +0100 Subject: [PATCH 061/109] rename variable for clarity in report name formatting --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6ea9151cf239..b03d9c8324d5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4496,8 +4496,8 @@ function getReportNameInternal({ if (isInvoiceReport(report)) { const moneyRequestReportName = getMoneyRequestReportName({report, policy, invoiceReceiverPolicy}); - const ODInvoiceName = report?.reportName ?? moneyRequestReportName; - formattedName = isNewDotInvoice(report?.chatReportID) ? moneyRequestReportName : ODInvoiceName; + const oldDotInvoiceName = report?.reportName ?? moneyRequestReportName; + formattedName = isNewDotInvoice(report?.chatReportID) ? moneyRequestReportName : oldDotInvoiceName; } if (isInvoiceRoom(report)) { From f56ae479fd5f2a8981db8f7809201a5eb04e7106 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 12:26:22 +0100 Subject: [PATCH 062/109] fix perf tests and changed files lint --- tests/perf-test/SearchRouter.perf-test.tsx | 5 +++++ tests/unit/SearchAutocompleteParserTest.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index ea260eb4b83d..44309ab5e720 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -69,6 +69,11 @@ jest.mock('@react-navigation/native', () => { }; }); +jest.mock('@libs/runOnLiveMarkdownRuntime', () => { + const runOnLiveMarkdownRuntime = (worklet: (...args: Args) => ReturnValue) => worklet; + return runOnLiveMarkdownRuntime; +}); + const getMockedReports = (length = 100) => createCollection( (item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, diff --git a/tests/unit/SearchAutocompleteParserTest.ts b/tests/unit/SearchAutocompleteParserTest.ts index e9ef145ed9ad..84fbf70b3aeb 100644 --- a/tests/unit/SearchAutocompleteParserTest.ts +++ b/tests/unit/SearchAutocompleteParserTest.ts @@ -1,5 +1,5 @@ import type {SearchQueryJSON} from '@components/Search/types'; -import * as autocompleteParser from '@libs/SearchParser/autocompleteParser'; +import {parse} from '@libs/SearchParser/autocompleteParser'; import parserCommonTests from '../utils/fixtures/searchParsersCommonQueries'; const tests = [ @@ -265,7 +265,7 @@ const tests = [ describe('autocomplete parser', () => { test.each(tests)(`parsing: $query`, ({query, expected}) => { - const result = autocompleteParser.parse(query) as SearchQueryJSON; + const result = parse(query) as SearchQueryJSON; expect(result).toEqual(expected); }); From 3e3221f25848e106cb5b5e14e6ce2145e171ecdf Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 13:34:22 +0100 Subject: [PATCH 063/109] fix names and consts --- src/CONST.ts | 2 +- src/components/Search/SearchAutocompleteInput.tsx | 13 ++----------- src/components/Search/SearchPageHeaderInput.tsx | 5 ++++- src/components/Search/SearchRouter/SearchRouter.tsx | 5 ++++- .../Search/SearchRouter/buildSubstitutionsMap.ts | 2 +- .../SearchRouter/getQueryWithSubstitutions.ts | 8 ++++---- .../SearchRouter/getUpdatedSubstitutionsMap.ts | 8 ++++---- src/components/Search/types.ts | 5 ++++- src/libs/SearchAutocompleteUtils.ts | 4 ++-- src/libs/runOnLiveMarkdownRuntime/index.native.tsx | 2 +- tests/perf-test/SearchRouter.perf-test.tsx | 2 +- 11 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index b687afe4c8fa..9c44e6539071 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6189,7 +6189,7 @@ const CONST = { LOWER_THAN: 'lt', LOWER_THAN_OR_EQUAL_TO: 'lte', }, - SYNTAX_KEY: 'syntax', + SYNTAX_RANGE_NAME: 'syntax', SYNTAX_ROOT_KEYS: { TYPE: 'type', STATUS: 'status', diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 84756b9806e9..a89c68364ad9 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,4 +1,3 @@ -import isEqual from 'lodash/isEqual'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; @@ -100,7 +99,6 @@ function SearchAutocompleteInput( const {isOffline} = useNetwork(); const {activeWorkspaceID} = useActiveWorkspace(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const [map, setMap] = useState({}); const [currencyList] = useOnyx(ONYXKEYS.CURRENCY_LIST); const currencyAutocompleteList = Object.keys(currencyList ?? {}); @@ -122,13 +120,6 @@ function SearchAutocompleteInput( const emailList = Object.keys(loginList ?? {}); const emailListSharedValue = useSharedValue(emailList); - useEffect(() => { - if (isEqual(map, substitutionMap)) { - return; - } - setMap(substitutionMap); - }, [substitutionMap, map]); - const offlineMessage: string = isOffline && shouldShowOfflineMessage ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; useEffect(() => { @@ -167,9 +158,9 @@ function SearchAutocompleteInput( (input: string) => { 'worklet'; - return parseForLiveMarkdown(input, currentUserPersonalDetails.displayName ?? '', map, emailListSharedValue, currencySharedValue, categorySharedValue, tagSharedValue); + return parseForLiveMarkdown(input, currentUserPersonalDetails.displayName ?? '', substitutionMap, emailListSharedValue, currencySharedValue, categorySharedValue, tagSharedValue); }, - [currentUserPersonalDetails.displayName, map, currencySharedValue, categorySharedValue, tagSharedValue, emailListSharedValue], + [currentUserPersonalDetails.displayName, substitutionMap, currencySharedValue, categorySharedValue, tagSharedValue, emailListSharedValue], ); const inputWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth}; diff --git a/src/components/Search/SearchPageHeaderInput.tsx b/src/components/Search/SearchPageHeaderInput.tsx index f763a22d6aeb..d39c58f9edea 100644 --- a/src/components/Search/SearchPageHeaderInput.tsx +++ b/src/components/Search/SearchPageHeaderInput.tsx @@ -1,4 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; +import isEqual from 'lodash/isEqual'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; @@ -123,7 +124,9 @@ function SearchPageHeaderInput({queryJSON, children}: SearchPageHeaderInputProps setAutocompleteQueryValue(updatedUserQuery); const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(userQuery, autocompleteSubstitutions); - setAutocompleteSubstitutions(updatedSubstitutionsMap); + if (!isEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { + setAutocompleteSubstitutions(updatedSubstitutionsMap); + } if (updatedUserQuery) { listRef.current?.updateAndScrollToFocusedIndex(0); diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 51b86812694c..6c6b4dc689d3 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -1,4 +1,5 @@ import {useNavigationState} from '@react-navigation/native'; +import isEqual from 'lodash/isEqual'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {TextInputProps} from 'react-native'; @@ -185,7 +186,9 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps) setAutocompleteQueryValue(updatedUserQuery); const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(userQuery, autocompleteSubstitutions); - setAutocompleteSubstitutions(updatedSubstitutionsMap); + if (!isEqual(autocompleteSubstitutions, updatedSubstitutionsMap)) { + setAutocompleteSubstitutions(updatedSubstitutionsMap); + } if (updatedUserQuery || textInputValue.length > 0) { listRef.current?.updateAndScrollToFocusedIndex(0); diff --git a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts index 0a3c49819b38..1972a487c92f 100644 --- a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts @@ -33,7 +33,7 @@ function buildSubstitutionsMap( ): SubstitutionMap { const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== CONST.SEARCH.SYNTAX_RANGE_NAME); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts index 4262f1df8529..e20577b7df45 100644 --- a/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts +++ b/src/components/Search/SearchRouter/getQueryWithSubstitutions.ts @@ -1,11 +1,11 @@ -import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; +import type {SearchAutocompleteQueryRange, SearchAutocompleteQueryRangeKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; import {sanitizeSearchValue} from '@libs/SearchQueryUtils'; -import type CONST from '@src/CONST'; +import CONST from '@src/CONST'; type SubstitutionMap = Record; -const getSubstitutionMapKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; +const getSubstitutionMapKey = (filterKey: SearchAutocompleteQueryRangeKey, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, this function will return a transformed query where: @@ -22,7 +22,7 @@ const getSubstitutionMapKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH. function getQueryWithSubstitutions(changedQuery: string, substitutions: SubstitutionMap) { const parsed = parse(changedQuery) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsed.ranges.filter((range) => range.key !== 'syntax'); + const searchAutocompleteQueryRanges = parsed.ranges.filter((range) => range.key !== CONST.SEARCH.SYNTAX_RANGE_NAME); if (searchAutocompleteQueryRanges.length === 0) { return changedQuery; diff --git a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts index ac9f8a7a807d..aa37f84acc68 100644 --- a/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/getUpdatedSubstitutionsMap.ts @@ -1,9 +1,9 @@ -import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types'; +import type {SearchAutocompleteQueryRange, SearchAutocompleteQueryRangeKey} from '@components/Search/types'; import {parse} from '@libs/SearchParser/autocompleteParser'; -import type CONST from '@src/CONST'; +import CONST from '@src/CONST'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; -const getSubstitutionsKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY, value: string) => `${filterKey}:${value}`; +const getSubstitutionsKey = (filterKey: SearchAutocompleteQueryRangeKey, value: string) => `${filterKey}:${value}`; /** * Given a plaintext query and a SubstitutionMap object, @@ -19,7 +19,7 @@ const getSubstitutionsKey = (filterKey: SearchFilterKey | typeof CONST.SEARCH.SY function getUpdatedSubstitutionsMap(query: string, substitutions: SubstitutionMap): SubstitutionMap { const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; - const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== 'syntax'); + const searchAutocompleteQueryRanges = parsedQuery.ranges.filter((range) => range.key !== CONST.SEARCH.SYNTAX_RANGE_NAME); if (searchAutocompleteQueryRanges.length === 0) { return {}; diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts index e716be38f9d2..887c70944660 100644 --- a/src/components/Search/types.ts +++ b/src/components/Search/types.ts @@ -98,6 +98,8 @@ type SearchFilterKey = | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS | typeof CONST.SEARCH.SYNTAX_ROOT_KEYS.POLICY_ID; +type SearchAutocompleteQueryRangeKey = SearchFilterKey | typeof CONST.SEARCH.SYNTAX_RANGE_NAME; + type UserFriendlyKey = ValueOf; type QueryFilters = Array<{ @@ -130,7 +132,7 @@ type SearchAutocompleteResult = { }; type SearchAutocompleteQueryRange = { - key: SearchFilterKey | typeof CONST.SEARCH.SYNTAX_KEY; + key: SearchAutocompleteQueryRangeKey; length: number; start: number; value: string; @@ -159,4 +161,5 @@ export type { SearchAutocompleteResult, PaymentData, SearchAutocompleteQueryRange, + SearchAutocompleteQueryRangeKey, }; diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index e458b186b6a8..aca441abd700 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -179,8 +179,8 @@ function parseForLiveMarkdown( .map((range) => { let type = 'mention-user'; - if (range.key === 'syntax') { - type = 'syntax'; + if (range.key === CONST.SEARCH.SYNTAX_RANGE_NAME) { + type = CONST.SEARCH.SYNTAX_RANGE_NAME; } if ((range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) && (userLogins.get().includes(range.value) || range.value === userDisplayName)) { diff --git a/src/libs/runOnLiveMarkdownRuntime/index.native.tsx b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx index 8927fe0a936c..fe7bd2441cf3 100644 --- a/src/libs/runOnLiveMarkdownRuntime/index.native.tsx +++ b/src/libs/runOnLiveMarkdownRuntime/index.native.tsx @@ -1,7 +1,7 @@ import {getWorkletRuntime} from '@expensify/react-native-live-markdown'; import {runOnRuntime} from 'react-native-reanimated'; -function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnType) { return runOnRuntime(getWorkletRuntime(), worklet); } diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index 44309ab5e720..95807a12cd1b 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -122,7 +122,7 @@ function SearchAutocompleteInputWrapper() { value={value} onSearchQueryChange={(searchTerm) => setValue(searchTerm)} isFullWidth={false} - substitutionMap={{}} + substitutionMap={CONST.EMPTY_OBJECT} /> ); From 11b4639e707201e608fdede1faf1a11a3625e254 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 14:01:07 +0100 Subject: [PATCH 064/109] improve readability - extract logic from parser --- src/libs/SearchAutocompleteUtils.ts | 67 +++++++++++++++++++---------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/src/libs/SearchAutocompleteUtils.ts b/src/libs/SearchAutocompleteUtils.ts index aca441abd700..365134546c2b 100644 --- a/src/libs/SearchAutocompleteUtils.ts +++ b/src/libs/SearchAutocompleteUtils.ts @@ -2,7 +2,7 @@ import type {MarkdownRange} from '@expensify/react-native-live-markdown'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {SharedValue} from 'react-native-reanimated/lib/typescript/commonTypes'; import type {SubstitutionMap} from '@components/Search/SearchRouter/getQueryWithSubstitutions'; -import type {SearchAutocompleteResult} from '@components/Search/types'; +import type {SearchAutocompleteQueryRange, SearchAutocompleteResult} from '@components/Search/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, PolicyCategories, PolicyTagLists, RecentlyUsedCategories, RecentlyUsedTags} from '@src/types/onyx'; @@ -135,6 +135,48 @@ function getAutocompleteQueryWithComma(prevQuery: string, newQuery: string) { return newQuery; } +function filterOutRangesWithCorrectValue( + range: SearchAutocompleteQueryRange, + userDisplayName: string, + substitutionMap: SubstitutionMap, + userLogins: SharedValue, + currencyList: SharedValue, + categoryList: SharedValue, + tagList: SharedValue, +) { + 'worklet'; + + const typeList = Object.values(CONST.SEARCH.DATA_TYPES) as string[]; + const expenseTypeList = Object.values(CONST.SEARCH.TRANSACTION_TYPE) as string[]; + const statusList = Object.values({...CONST.SEARCH.STATUS.TRIP, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP}) as string[]; + + switch (range.key) { + case CONST.SEARCH.SYNTAX_FILTER_KEYS.IN: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID: + return substitutionMap[`${range.key}:${range.value}`] !== undefined; + + case CONST.SEARCH.SYNTAX_FILTER_KEYS.TO: + case CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM: + return substitutionMap[`${range.key}:${range.value}`] !== undefined || userLogins.get().includes(range.value); + + case CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY: + return currencyList.get().includes(range.value); + case CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE: + return typeList.includes(range.value); + case CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE: + return expenseTypeList.includes(range.value); + case CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS: + return statusList.includes(range.value); + case CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY: + return categoryList.get().includes(range.value); + case CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG: + return tagList.get().includes(range.value); + default: + return true; + } +} + /** * Parses input string using the autocomplete parser and returns array of * markdown ranges that can be used by RNMarkdownTextInput. @@ -153,29 +195,8 @@ function parseForLiveMarkdown( const parsedAutocomplete = parse(input) as SearchAutocompleteResult; const ranges = parsedAutocomplete.ranges; - - const typeList = Object.values(CONST.SEARCH.DATA_TYPES) as string[]; - const expenseTypeList = Object.values(CONST.SEARCH.TRANSACTION_TYPE) as string[]; - const statusList = Object.values({...CONST.SEARCH.STATUS.TRIP, ...CONST.SEARCH.STATUS.INVOICE, ...CONST.SEARCH.STATUS.CHAT, ...CONST.SEARCH.STATUS.TRIP}) as string[]; - const subMap = map; return ranges - .filter( - (range) => - !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) || - subMap[`${range.key}:${range.value}`] !== undefined, - ) - .filter( - (range) => - !(range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || range.key === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM) || - subMap[`${range.key}:${range.value}`] !== undefined || - userLogins.get().includes(range.value), - ) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY || currencyList.get().includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE || typeList.includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE || expenseTypeList.includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_ROOT_KEYS.STATUS || statusList.includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY || categoryList.get().includes(range.value)) - .filter((range) => range.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG || tagList.get().includes(range.value)) + .filter((range) => filterOutRangesWithCorrectValue(range, userDisplayName, map, userLogins, currencyList, categoryList, tagList)) .map((range) => { let type = 'mention-user'; From 829f608354456e7f2295d7c2679c7da6bb030f74 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 5 Feb 2025 14:24:42 +0100 Subject: [PATCH 065/109] fix type --- src/libs/runOnLiveMarkdownRuntime/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/runOnLiveMarkdownRuntime/index.tsx b/src/libs/runOnLiveMarkdownRuntime/index.tsx index c8e072693323..928e7a34ea3f 100644 --- a/src/libs/runOnLiveMarkdownRuntime/index.tsx +++ b/src/libs/runOnLiveMarkdownRuntime/index.tsx @@ -1,5 +1,5 @@ // Reanimated does not support runOnRuntime() on web -function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnValue) { +function runOnLiveMarkdownRuntime(worklet: (...args: Args) => ReturnType) { return worklet; } From fb818ef3c99ac8f93a8acfdde19a83101bd59fcd Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Wed, 5 Feb 2025 22:33:42 +0700 Subject: [PATCH 066/109] make merchant and comment as optional --- src/libs/actions/IOU.ts | 8 ++++---- src/pages/iou/request/step/IOURequestStepAmount.tsx | 1 - src/pages/iou/request/step/IOURequestStepDistance.tsx | 1 - .../iou/request/step/IOURequestStepScan/index.native.tsx | 4 ---- src/pages/iou/request/step/IOURequestStepScan/index.tsx | 4 ---- tests/actions/IOUTest.ts | 3 --- 6 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d468a965c0b1..3da686fb33cc 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -452,8 +452,8 @@ type TrackExpenseTransactionParams = { amount: number; currency: string; created: string | undefined; - merchant: string; - comment: string; + merchant?: string; + comment?: string; receipt?: Receipt; category?: string; tag?: string; @@ -4688,8 +4688,8 @@ function trackExpense(params: CreateTrackExpenseParams) { amount, currency, created = '', - merchant, - comment, + merchant = '', + comment = '', receipt, category, tag, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index d72bc8f4512d..0289d5cb70f5 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -232,7 +232,6 @@ function IOURequestStepAmount({ currency: currency ?? 'USD', created: transaction?.created, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, - comment: '', }, }); return; diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 11512217f458..22870365fdc1 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -336,7 +336,6 @@ function IOURequestStepDistance({ currency: transaction?.currency ?? 'USD', created: transaction?.created ?? '', merchant: translate('iou.fieldPending'), - comment: '', receipt: {}, billable: false, validWaypoints: getValidWaypoints(waypoints, true), diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index df819a9134b5..d99807bc9759 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -263,8 +263,6 @@ function IOURequestStepScan({ amount: 0, currency: transaction?.currency ?? 'USD', created: transaction?.created, - merchant: '', - comment: '', receipt, }, }); @@ -358,8 +356,6 @@ function IOURequestStepScan({ amount: 0, currency: transaction?.currency ?? 'USD', created: transaction?.created, - merchant: '', - comment: '', receipt, billable: false, gpsPoints: { diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 578b2fb75eff..c2f51c6001b6 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -288,8 +288,6 @@ function IOURequestStepScan({ amount: 0, currency: transaction?.currency ?? 'USD', created: transaction?.created, - merchant: '', - comment: '', receipt, }, }); @@ -384,8 +382,6 @@ function IOURequestStepScan({ amount: 0, currency: transaction?.currency ?? 'USD', created: transaction?.created, - merchant: '', - comment: '', receipt, billable: false, gpsPoints: { diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 610dc0a972d0..7f75d31baec2 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -204,7 +204,6 @@ describe('actions/IOU', () => { currency: fakeTransaction.currency, created: format(new Date(), CONST.DATE.FNS_FORMAT_STRING), merchant: fakeTransaction.merchant, - comment: '', billable: false, validWaypoints: fakeWayPoints, actionableWhisperReportActionID: fakeTransaction?.actionableWhisperReportActionID, @@ -296,7 +295,6 @@ describe('actions/IOU', () => { currency: transactionDraft?.currency ?? fakeTransaction.currency, created: format(new Date(), CONST.DATE.FNS_FORMAT_STRING), merchant: transactionDraft?.merchant ?? fakeTransaction.merchant, - comment: '', category: Object.keys(fakeCategories).at(0) ?? '', validWaypoints: Object.keys(transactionDraft?.comment?.waypoints ?? {}).length ? getValidWaypoints(transactionDraft?.comment?.waypoints, true) : undefined, actionableWhisperReportActionID: transactionDraft?.actionableWhisperReportActionID, @@ -4449,7 +4447,6 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, created: '2024-10-30', merchant: 'KFC', - comment: '', receipt: {}, actionableWhisperReportActionID: '1', linkedTrackedExpenseReportAction: { From ca36e877ad8fdf59b00c36f3274b12b70746da75 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 5 Feb 2025 17:21:36 +0100 Subject: [PATCH 067/109] Introduce temporary focus fix --- src/libs/focusComposerWithDelay/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/focusComposerWithDelay/index.ts b/src/libs/focusComposerWithDelay/index.ts index eef9d21d113d..195f66e3151e 100644 --- a/src/libs/focusComposerWithDelay/index.ts +++ b/src/libs/focusComposerWithDelay/index.ts @@ -32,7 +32,8 @@ function focusComposerWithDelay(textInput: InputType | null): FocusComposerWithD return; } // When the closing modal has a focused text input focus() needs a delay to properly work. - setTimeout(() => textInput.focus(), 0); + // Setting 150ms here is a temporary workaround for the Android HybridApp. It should be reverted once we identify the real root cause of the issue. + setTimeout(() => textInput.focus(), 150); if (forcedSelectionRange) { setTextInputSelection(textInput, forcedSelectionRange); } From 87fbc9eee344b5814634140cd4915b78e2467851 Mon Sep 17 00:00:00 2001 From: Mateusz Rajski Date: Wed, 5 Feb 2025 18:02:14 +0100 Subject: [PATCH 068/109] Add reference to issue --- src/libs/focusComposerWithDelay/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/focusComposerWithDelay/index.ts b/src/libs/focusComposerWithDelay/index.ts index 195f66e3151e..ab251bf8403a 100644 --- a/src/libs/focusComposerWithDelay/index.ts +++ b/src/libs/focusComposerWithDelay/index.ts @@ -32,7 +32,7 @@ function focusComposerWithDelay(textInput: InputType | null): FocusComposerWithD return; } // When the closing modal has a focused text input focus() needs a delay to properly work. - // Setting 150ms here is a temporary workaround for the Android HybridApp. It should be reverted once we identify the real root cause of the issue. + // Setting 150ms here is a temporary workaround for the Android HybridApp. It should be reverted once we identify the real root cause of this issue: https://github.com/Expensify/App/issues/56311. setTimeout(() => textInput.focus(), 150); if (forcedSelectionRange) { setTextInputSelection(textInput, forcedSelectionRange); From cd542fdb142618b3ce0e4782eff731742ec2b574 Mon Sep 17 00:00:00 2001 From: M00rish Date: Wed, 5 Feb 2025 19:53:37 +0100 Subject: [PATCH 069/109] update based on reviews --- src/pages/home/ReportScreen.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 4cf7e0ca6ee3..79538217805b 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -387,11 +387,11 @@ function ReportScreen({route, navigation}: ReportScreenProps) { [linkedAction, report], ); - const previsLinkedActionDeleted = usePrevious(linkedAction ? isLinkedActionDeleted : undefined); + const prevIsLinkedActionDeleted = usePrevious(linkedAction ? isLinkedActionDeleted : undefined); const lastReportActionIDFromRoute = usePrevious(reportActionIDFromRoute); - const [isNavigatingToAction, setIsNavigatingToAction] = useState(false); + const [isNavigatingToDeletedAction, setIsNavigatingToDeletedAction] = useState(false); const isLinkedActionInaccessibleWhisper = useMemo( () => !!linkedAction && isWhisperAction(linkedAction) && !(linkedAction?.whisperedToAccountIDs ?? []).includes(currentUserAccountID), @@ -424,7 +424,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundLinkedAction = - (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && isNavigatingToAction) || + (!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted && isNavigatingToDeletedAction) || (shouldShowSkeleton && !reportMetadata.isLoadingInitialReportActions && !!reportActionIDFromRoute && @@ -741,21 +741,21 @@ function ReportScreen({route, navigation}: ReportScreenProps) { useEffect(() => { // Only handle deletion cases when there's a deleted action if (!isLinkedActionDeleted) { - setIsNavigatingToAction(false); + setIsNavigatingToDeletedAction(false); return; } - // Set navigation state when user clicks a deleted Action link + // we want to do this destinguish between a normal navigation and a delete behavior if (lastReportActionIDFromRoute !== reportActionIDFromRoute) { - setIsNavigatingToAction(true); + setIsNavigatingToDeletedAction(true); return; } // Clear params when Action gets deleted while heighlighted - if (!isNavigatingToAction && previsLinkedActionDeleted === false) { + if (!isNavigatingToDeletedAction && prevIsLinkedActionDeleted === false) { Navigation.setParams({reportActionID: ''}); } - }, [isLinkedActionDeleted, previsLinkedActionDeleted, lastReportActionIDFromRoute, reportActionIDFromRoute, isNavigatingToAction]); + }, [isLinkedActionDeleted, prevIsLinkedActionDeleted, lastReportActionIDFromRoute, reportActionIDFromRoute, isNavigatingToDeletedAction]); // If user redirects to an inaccessible whisper via a deeplink, on a report they have access to, // then we set reportActionID as empty string, so we display them the report and not the "Not found page". From d036c028dd9094c3d1c4d52ad4a424fe9ca15093 Mon Sep 17 00:00:00 2001 From: M00rish Date: Wed, 5 Feb 2025 20:01:50 +0100 Subject: [PATCH 070/109] lint update --- src/pages/home/ReportScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 79538217805b..66d7f3504a5c 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -745,7 +745,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { return; } - // we want to do this destinguish between a normal navigation and a delete behavior + // we want to do this destinguish between normal navigation and delete behavior if (lastReportActionIDFromRoute !== reportActionIDFromRoute) { setIsNavigatingToDeletedAction(true); return; From 94a6d3fa1491cc89ccc33c6bc0e37eaa8772bd0b Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 11:12:33 -0800 Subject: [PATCH 071/109] Use 1P for OS Botify's GPG key --- .../composite/setupGitForOSBotify/action.yml | 14 +++++++------- .../composite/setupGitForOSBotifyApp/action.yml | 9 +++++++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/actions/composite/setupGitForOSBotify/action.yml b/.github/actions/composite/setupGitForOSBotify/action.yml index 456cef93676a..bf0ac5e8b064 100644 --- a/.github/actions/composite/setupGitForOSBotify/action.yml +++ b/.github/actions/composite/setupGitForOSBotify/action.yml @@ -9,9 +9,14 @@ inputs: runs: using: composite steps: - - name: Decrypt OSBotify GPG key - run: cd .github/workflows && gpg --quiet --batch --yes --decrypt --passphrase=${{ inputs.GPG_PASSPHRASE }} --output OSBotify-private-key.asc OSBotify-private-key.asc.gpg + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Load files from 1Password shell: bash + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: op read "op://Mobile-Deploy-CI/OSBotify-private-key.asc/OSBotify-private-key.asc" --force --out-file ./OSBotify-private-key.asc - name: Import OSBotify GPG Key shell: bash @@ -24,8 +29,3 @@ runs: git config --global commit.gpgsign true git config --global user.name OSBotify git config --global user.email infra+osbotify@expensify.com - - - name: Enable debug logs for git - shell: bash - if: runner.debug == '1' - run: echo "GIT_TRACE=true" >> "$GITHUB_ENV" diff --git a/.github/actions/composite/setupGitForOSBotifyApp/action.yml b/.github/actions/composite/setupGitForOSBotifyApp/action.yml index 404ddc55e954..d931183fd2cf 100644 --- a/.github/actions/composite/setupGitForOSBotifyApp/action.yml +++ b/.github/actions/composite/setupGitForOSBotifyApp/action.yml @@ -39,9 +39,14 @@ runs: sparse-checkout: | .github - - name: Decrypt OSBotify GPG key - run: cd .github/workflows && gpg --quiet --batch --yes --decrypt --passphrase=${{ inputs.GPG_PASSPHRASE }} --output OSBotify-private-key.asc OSBotify-private-key.asc.gpg + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Load files from 1Password shell: bash + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: op read "op://Mobile-Deploy-CI/OSBotify-private-key.asc/OSBotify-private-key.asc" --force --out-file ./OSBotify-private-key.asc - name: Import OSBotify GPG Key shell: bash From f8f0e9b51da16a64b36c07c9355a7fb7a96f57b8 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 5 Feb 2025 20:24:30 +0100 Subject: [PATCH 072/109] Add info about submodule.recure option to README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index de5c746a964d..1263b1e66b3a 100644 --- a/README.md +++ b/README.md @@ -532,6 +532,15 @@ The primary difference is that the native code, which runs React Native, is loca The `Mobile-Expensify` directory is a **Git submodule**. This means it points to a specific commit on the `Mobile-Expensify` repository. +If you'd like to fetch the submodule while executing the `git pull` command in `Expensify/App` instead of updating it manually you can run this command in the root of the project: + +``` +git config submodule.recurse true +``` + +> [!WARNING] +> Please, remember that the submodule will get updated automatically only after executing the `git pull` command - if you switch between branches it is still recommended to execute `git submodule update` to make sure you're working on a compatible submodule version! + If you'd like to download the most recent changes from the `main` branch, please use the following command: ```bash git submodule update --remote From 259e04cf8149fb7b34cc2b541be693e1483b4a1b Mon Sep 17 00:00:00 2001 From: M00rish Date: Wed, 5 Feb 2025 21:03:54 +0100 Subject: [PATCH 073/109] fix for reloading notfound does not land on notfound again --- src/pages/home/ReportScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 66d7f3504a5c..c72549f78b9d 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -389,7 +389,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const prevIsLinkedActionDeleted = usePrevious(linkedAction ? isLinkedActionDeleted : undefined); - const lastReportActionIDFromRoute = usePrevious(reportActionIDFromRoute); + const lastReportActionIDFromRoute = usePrevious(!firstRenderRef.current ? reportActionIDFromRoute : undefined); const [isNavigatingToDeletedAction, setIsNavigatingToDeletedAction] = useState(false); From 6f20fd0673dac4e8b6b015c80e44b70a6ead2cf7 Mon Sep 17 00:00:00 2001 From: M00rish Date: Wed, 5 Feb 2025 21:07:29 +0100 Subject: [PATCH 074/109] lint fix --- src/pages/home/ReportScreen.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index c72549f78b9d..b5ab1e187a58 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -389,6 +389,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const prevIsLinkedActionDeleted = usePrevious(linkedAction ? isLinkedActionDeleted : undefined); + // eslint-disable-next-line react-compiler/react-compiler const lastReportActionIDFromRoute = usePrevious(!firstRenderRef.current ? reportActionIDFromRoute : undefined); const [isNavigatingToDeletedAction, setIsNavigatingToDeletedAction] = useState(false); From 7b8c85544260c06005497d326cade67f17d4d0ff Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 15:02:25 -0800 Subject: [PATCH 075/109] Try on AdHoc build --- .github/workflows/testBuild.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index ea62bca794fe..8e42e078ff73 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -242,10 +242,14 @@ jobs: - name: Setup Node uses: ./.github/actions/composite/setupNode - - name: Decrypt Developer ID Certificate - run: cd desktop && gpg --quiet --batch --yes --decrypt --passphrase="$DEVELOPER_ID_SECRET_PASSPHRASE" --output developer_id.p12 developer_id.p12.gpg + - name: Load Desktop credentials from 1Password + id: load-credentials + uses: 1password/load-secrets-action@v2 + with: + export-env: false env: - DEVELOPER_ID_SECRET_PASSPHRASE: ${{ secrets.DEVELOPER_ID_SECRET_PASSPHRASE }} + DESKTOP_CERTIFICATE_BASE64: "op://Mobile-Deploy-CI/Desktop Certificates.p12/CSC_LINK" + DESKTOP_CERTIFICATE_PASSWORD: "op://Mobile-Deploy-CI/Desktop Certificates.p12/CSC_KEY_PASSWORD" - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 @@ -257,8 +261,8 @@ jobs: - name: Build desktop app for testing run: npm run desktop-build-adhoc env: - CSC_LINK: ${{ secrets.CSC_LINK }} - CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + CSC_LINK: ${{ secrets.DESKTOP_CERTIFICATE_BASE64 }} + CSC_KEY_PASSWORD: ${{ secrets.DESKTOP_CERTIFICATE_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} From 256be7ac84f6caa97fd03398ff8b3658101b2503 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 15:08:42 -0800 Subject: [PATCH 076/109] Add 1P token --- .github/workflows/testBuild.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 8e42e078ff73..7cd88e4d9ccd 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -248,6 +248,7 @@ jobs: with: export-env: false env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} DESKTOP_CERTIFICATE_BASE64: "op://Mobile-Deploy-CI/Desktop Certificates.p12/CSC_LINK" DESKTOP_CERTIFICATE_PASSWORD: "op://Mobile-Deploy-CI/Desktop Certificates.p12/CSC_KEY_PASSWORD" From d527000244a0689978c6a937c64c3c6a29df3e2b Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 15:14:08 -0800 Subject: [PATCH 077/109] Use step load instead of secret --- .github/workflows/testBuild.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 7cd88e4d9ccd..80918d65462c 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -262,8 +262,8 @@ jobs: - name: Build desktop app for testing run: npm run desktop-build-adhoc env: - CSC_LINK: ${{ secrets.DESKTOP_CERTIFICATE_BASE64 }} - CSC_KEY_PASSWORD: ${{ secrets.DESKTOP_CERTIFICATE_PASSWORD }} + CSC_LINK: ${{ steps.load-credentials.outputs.DESKTOP_CERTIFICATE_BASE64 }} + CSC_KEY_PASSWORD: ${{ steps.load-credentials.outputs.DESKTOP_CERTIFICATE_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} From cee69984dc793103fff27938a8e0253abe141c8c Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 15:38:42 -0800 Subject: [PATCH 078/109] Remove GPG encrypted files --- .github/workflows/OSBotify-private-key.asc.gpg | Bin 3940 -> 0 bytes desktop/developer_id.p12.gpg | Bin 3300 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .github/workflows/OSBotify-private-key.asc.gpg delete mode 100644 desktop/developer_id.p12.gpg diff --git a/.github/workflows/OSBotify-private-key.asc.gpg b/.github/workflows/OSBotify-private-key.asc.gpg deleted file mode 100644 index 03f06222d0fe956cdb4d3cc00df9b2ff3566da21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3940 zcmV-q51a6e4Fm}T0wnTvM*#)?4ffLO0iQFMt9JY89BP@dCk-creW&cdTFluB9b0WJ z`5Jxzm-8iHfDXd^NMZn%XQEG~HsZ|Z;ISa8KCll~NZ(rmGeQixzP#G^bL%h4 zEf)F@!Cm6&69|TE&bT@2*g0eLG49}RV)=eRBQ#UCQM&IqjOWfgIJ%(!`quL`JX#H1 z#x#56zHFP|551^rEhB&c)y@=-xSGUgTg`IQ@W+*kBt}5jPn`?#^83_6NZvM zdtsv2vj!9TD(SJP;6I>-nsLeg*nVX&`$w`$|Zgphf>F&nNycOHHU1QVnki1V4#UpE6q!H z4)6#qzw6FXv!%py6X8*9JJtwwyg{O5i?`nCQS`y&orz;SYrZ62Ay=&m_rLJ8rz%M@`wjrns3awz_RwAINPTIag2!#Qg+9lW>{{RjL zeIu9=L+XM?P*)8^BQ8PkuF<~T`o8=b7;Mf~P$JpO^}ulO|C?NkPj4x*rhJp*V}){R zSq#}3B(=OBw&arj#vku&tTIaDo+QlK`s5)uDd5(kRzxjr5(Hp^2_Sr&rpv`-s$8pT zc^MHBx7JGGQR=jH!)lKiTUen6YJzK-$;V%E&S?}~+3Pf}jPvcJ+Q)B>!} z|3Q+7&b?oi!yN4Yuf?r4Bhf(`qX`k$)s-~}{J7R62%e5qFks^&Ni~Mj=6JTV7ENG? zq{5Q>nNgqxUF;t;I)z#GjwyhLM4Vv7KIs3wLK{Yfu ze_lTiC2cu#Z;T$_@X<7kG#-*_9f_@})}??-0qgq$Y!h zka2r$c|}eYi})3Q@Z>KL`k|BMZZOa1!cCHMfen8Xe^A6;4a{a;eVoNxr^@{Exk3KB zBWIke_A7QZ?$#v?LOmDP8=eQ`29>E^^g4#v0oQc+l*z0bxN6%VkR47L!lwfIj~72q zE8!L7OokaOxXA>qhBqfC#cZyT8Ku`Y{*PdU(Gbtx(Ps74EhkdQIqI<{;Ocv6OcySX zm?*<2{u+r~j08w3`(1hp!F2v(wnKE@zLu#mCrY~i{QB(pzmKz=4qpc+*9EGA7! z+d!M1qoB^AOx^s<%8!SQ-&^$&#rf zPPQP+Dp7;iL(HKglQ3^1OUi7Tk2w*(0^0EUAn}Qu{#jOM;xV*sGXm6QcYBQ0HHn}G z71D#+9Z3NJhX}&Y*Of&V@@=Y4h2!`ff4LVH=h! zVeP^DVwg+ZeI#lPtBO?eO5oRMgU1BQRSW;^U+IAjItQ?;Jjb?tXo8INvs6aq9MHXP zv`M^QAmk-8czmzrgUNH|z=AX=7xu#v!Cg`2D+qbLZ$=iD|2)mqi9n2ZzyX|Q8Z?bD zKIZy)>_?zC`Sa(^@v~uILjoe*w6atyEJhOBpf-0eoFhwt>X2JU1q)b+B6I()z5K|t zhaHTb-vrRj^ysiqIXQL*r0x2w^NqJuDY5*rpii?N>zG-LS}g``kL|_wKe4o{?WjQy zWCd%OI2gKLXv6v>h~H4xyRaX_7{sWGe~5qW#T#}fvRdLIk`54e5sc=`9Q;(Y3yP{lV4xz@~r&Km}?JngC5Z4w-$ zyS`S4gn*=&82?#28cgHPs91VnVh}w&Pv{j_ohF9yoa;I3LwZJFc&D43!G_BZdC4a= zveya$JV}IpvS855XE<8H#nQIs!IVJVR!922xLQ*E$oV8IKv&PQNCW5kQNFx=>#6T* zw2v-Aj%kz*X2_B<@+p*kZD?`vxHzq05=daM7}i*>B6|4?mKuK~MW=-w&w6%W6N>IX|mAVW^$ z6MgQgmxE9TT8e2{i4M$&vKKWX!0d?~>_B_^*P?6#FhF1S%*q$$tDfkrQI6JNUI6F7 ziabF@=F>bZp9fp*4$DB_6nWp?a)BPS&v|lz8_NwVYj=8cQ3x_ObW>cFuu+Q z#<><}M4#7U#}oF9)1=sipuc?A(14A-);ofM-^b0t#&50h(%>qdKnKdvr zP1iA*{$hEX99eQ;Eg0zN2h4Z)o5@sm7C(7Ob14K)3l+kJVl_o;3IzSp?c~;VXx{lc zugCiz|3>`l=G(=5AIsD6mWz}SjR<0XCtdks7K#suJ>D_~`@EX-EG7N|kB8b;`pGB6 zmrSS!*?2>Egw(qcCm-Bh2~(on6p0&dyhN7>KGA~#cTz`kCt^fZ8Cjh2;+sv-#kJYN zB2u`x_jH=|b9TSDG!T%)&;Apj$Pe@1qJPcLYfno4DK= z!r*NNr}1Gq;Av+fe*RAzI(*N(c0#cTI+{kzq^C?=Z}}+{{H^mYs$KKx`NGCu=gqB> zSJ+S|#>8`PhzQnDnP(Fp*Fgnn?q{7;ul@c<>?B~3YC7N&uw#azYSf9zqmUWr(btQs zOS^fl2VDc6^sN<3iTH2V9ruj>Ub9Hm1e?y^o^n(=7H$SsK|lB86Dv%(`rv1=m@~S!^-$3-pDo--TGRih@HT&#+@% ze|ScY3(_Vc{$RR%pBbr0AW#@ZvU)Li3VQGm>Cr7!1uu9M0a_`;TF4!mJ4W{MlVOSe z2H}PJOq2u_&JQz&k7gbFP&eWKY=FpYgs>biap_-O?^@2?%~i=)AZKQ8wiYvy8*D~= z@cn04MH=GoL@sK$lC+xst@UV{K2;D5hXNt!EiisSi7w$>@soR=Mti}BtA0M4h>unx z^3_14W~&NIx}ICFMnz7F`uw4+uuq4-(+~~#PbqcNLK!@LV5SeSrj*krN9!#o;*fZA z)un`--6qnmDm5u_R>eFg6m1(+0G6`KB>e%9hmSrHKLOy$b%EGhhv2~hl-=FuW(anF z#s02EUTBm$elyuI5mo;veTPT$D&mSD;=^VIa1@P8td(<@dcB!Xef#oi-o$hk-nH-B z`ydt`X4>PCwj0d=%-*3ov40m_QO|xzUY;AJ4*yaJ;oOwCSGoN@6SIDB9Kox*$smAo z8k)%6M~y4pdU(b~6YaXo?xO>Z-zu;AJvH(y;sp{(d; z&P8rYkcL)mG}{FOJ~jmt?gBC;L1AHCbR?*FV*%54G+JVNLv=f9YY#D;0wG3vy5LPu z5!A;<<_$_Y^9#dH8TQjK+4OhL*=hbXI0-m_oF>{;k!Y_IAq2Sq zR6D(swr%6EG#bss!%YOYPxXge*hu9Do&_&bVn)?v^IBO7JX0u1TlkZ^$qr5+L&n1r zT5KP`QQ@t9cT+XhQ=+;GT0CAe&G{KnhLaYt%j7W(w}XnOvkSOee!c zh2{jxRAkwOv!P%@d!6A-AS$%Y)SgGr+i&eyKrs#+yQUFo${e$Gppy1H;*B^9A1xY) y%Z>H(8pCC4E%S6Ea96*e2}NkXPW>OI4tj1Ml9%4&k}@?~qT7!YP<26FZs%5&_MqPY diff --git a/desktop/developer_id.p12.gpg b/desktop/developer_id.p12.gpg deleted file mode 100644 index ad166e3f83344b1234ee146e806bfaaa7ad40cef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3300 zcmV))|4Fm}T0-R9|XT~g52k6r40l{`=wDX8ui6Jf-IV+XYCsS(Ly6ey&J{R{T z_3&%jXgh4{S(Jhvc&d~G@IBR%K|Px>B4@p52%_5IPPY=h;2H9S68*0(tQFDn`4;#| zY4V(76rO!TX$A>f0^Nk*&v{9-dJL!zq9*>XXvO1B#PK^C^|}1wxc!b}f>R8R4!Q?I z0~U1AvV#H!yX2#c$H>9bC(eqQo>1e0WeD4g@)sfAjkJwSkFldGj&M`iK3-C>{R|8W zT+Isg4X2pj05h3PEcI0-dx$%{iSB@a5zpFbvxpesVIyJzhAd{TDy-ORIYl+WC#<*O z!#p~efzi&)yHxod3!q1-a$}NHXpK~8lt5Fe_yy;|fEx72f!jnLrQ-Bz8{=@_0UBV2 zZGp>xfwNDBo{~!}zh{CE=i_YP?E?lO$1mHZU*22Jv8U+eg3Bw|-i27f=2?z`$3$Zx%f_H$wkfXC z)F3dY+-X?K*B>M7Di9x66agddXP+G@Q2~07n231Or|p@fx7=gml+mmzE6sJSD*50I zu;d*l8xWB=v~DHchRr;?FR?Y{0HOYbYkO|FlxZjK-jk3*uA=|2B^0$YhyvM{h>eT< zDD@n9S;i}N;iW(qutL=tvQyZ5!y|wiK^kyHn0r1r4D;@Sh3=dGwD_ZT8x+N}4 z?ujr=HwX|=sulG`KQmrm=Au-|E?0&Jz$1gXNx@x3DYSM(Aw-P@KI^(2gJs{FFk$;> zHGuMzS{0}BCBehi3{6ca^QmE1Fti~6g!jUmC-=x($G_fSnI~#Wxih8zi*kA^FQ6vN zXL$YE%;}@+Cia^Tt^OY1>G3YhGW8eaj8QhPgwBr_*&b9SIYPU#VdAhnD*xDCdy@BB zy7YKx7NTQ^|%g|qY(9kM~#pIzK z0Y#7NVxZ^3{dDvQHKanr$}{#49HuBb(#Lm2S&+YY*|ebTNaJ2}_Q@{mDQe{bx#1=< z!1c*+)jshlL zrOX{lO5hH+1dS_GbONMv#dI4euRu63|ecijpgcb zb5+J~23y&f6{@$vh^Bin4&mx#b^10am2Vsv4W9yOivEncGD6UYyX(M&`y=J^;q-M= zHWF9%9QW42Xui457@uU6vR0~!_JN;Gfz1t-UkjjtfN|I-KOqvyl2Ifwf!A=#V{kqp z`Q7VYl}?{PTp|MlFg<+#AZKbiK5`jW=$jIt_rI)8CGng5TL+f7-PhV5I1@?0bH;+> zQ;J7Xv;T!PtECd9{n2)M7cW#cGUJ{G-ZoEcCmv8uYFrX!j{5+R>fpbiBwjjgaXXXG zn2cxjhngx<5g7jET<^9Pyi*>n%h1xB9D#t8Hd%7OG1{If1GRaGp6SY>U!nQll1yXl zXo9e2dld*XhF2+TlS#$T`7-4SF3qHEKA}d*bi#-D55p9e>A=;wKbB|MbW$_2t(Nbz{RmNN zyECq8^y{gk+TIuJwA{W=uR^oA#+&UXmlPBhM*LQxMji zxyhA36)KiLGe>JD!gHeVOIKcmh;n|ZGt|a}37r!?nk~LD+reWnBZr8^j+2mJfiCI1 z?uujh8ZVD{P77-K(qBA=i|f4-VZbhu%j_wE?j@^o=7DkBlDh4=98| z+tI2GSAIrCM+Us!1LZKe$3be5c^%It#r##5Q~bdPBkFPsF6X{!#xu<(lh#ADNT zm>>HA5-O6IZ>Adn)Z`X^KlFh*HBwLD`G7`Ac&hZLsuCe)zhf-M!pv~d>dBk#klY$~ z*`AmnFVV*{8{_fb!4&4Il$%EkTBz`QDFPN)&kYQFMF``7&Tt#YeY5C0I%f%Ik#KAi zUog`=qkzM#UK3v8FRR$ScxtlxqOFtQ&3F6kMWX^&;?7tefonJ7eYZ1IrmL?z_GSvF zwAZyvi1#wR#M%qFpP2SprXIXT4)r0DvbR*`C_n0^TN&9$!p-5HfvNDfYq@*iW@*gE z9DN5SY8m?eB6gwHZnO|z>6LT6}#%kIoZDbI>@P0+mNfE5 zk^9N?9c+nZC>rHY19z#HD8vsd!P5x5UmG^2qrJ_@2%M^RtM#ZOW4PeKrgao9Ch$QA z8s8cQK{~~xlj<) zjF}XHU=sQG=}Ai*t+3a?-2C>x$@!(gT!!8;YdT+2TU>w%#VwIJsZIUKGn5;BBVrHU zt@K_RM@i~3j^;^Pa^0tVP^r*=bsH_WWwGj|(ipYoT;v;DJ? z(R!W`+ATs`2!WdB%NS{F`e)wv;QfiP=ZSe;-9rX(QwqtA!sB+!RA}aKHwav-xK6nX i4z|=Z@5!(TMLmWQV7!KSK87Ko*& Date: Wed, 5 Feb 2025 15:38:59 -0800 Subject: [PATCH 079/109] Remove all uses of gpg decrytion password --- .github/workflows/README.md | 8 -------- .github/workflows/cherryPick.yml | 1 - .github/workflows/createNewVersion.yml | 5 ----- .github/workflows/deploy.yml | 16 ++++++++++------ .github/workflows/finishReleaseCycle.yml | 3 --- .github/workflows/preDeploy.yml | 1 - 6 files changed, 10 insertions(+), 24 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 45dacacd0b16..525afa3a8cea 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -80,14 +80,6 @@ git fetch origin tag 1.0.1-0 --no-tags --shallow-exclude=1.0.0-0 # This will fet ## Secrets The GitHub workflows require a large list of secrets to deploy, notify and test the code: -1. `LARGE_SECRET_PASSPHRASE` - decrypts secrets stored in various encrypted files stored in GitHub repository. To create updated versions of these encrypted files, refer to steps 1-4 of [this encrypted secrets help page](https://docs.github.com/en/actions/reference/encrypted-secrets#limits-for-secrets) using the `LARGE_SECRET_PASSPHRASE`. - 1. `android/app/my-upload-key.keystore.gpg` - 1. `android/app/android-fastlane-json-key.json.gpg` - 1. `ios/NewApp_AdHoc.mobileprovision` - 1. `ios/NewApp_AdHoc_Notification_Service.mobileprovision` - 1. `ios/NewApp_AppStore.mobileprovision.gpg` - 1. `ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg` - 1. `ios/Certificates.p12.gpg` 1. `SLACK_WEBHOOK` - Sends Slack notifications via Slack WebHook https://expensify.slack.com/services/B01AX48D7MM 1. `OS_BOTIFY_TOKEN` - Personal access token for @OSBotify user in GitHub 1. `CLA_BOTIFY_TOKEN` - Personal access token for @CLABotify user in GitHub diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 5cb0a99730c9..683568e255ef 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -45,7 +45,6 @@ jobs: id: setupGitForOSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} diff --git a/.github/workflows/createNewVersion.yml b/.github/workflows/createNewVersion.yml index 85c928707c6c..6245af07a121 100644 --- a/.github/workflows/createNewVersion.yml +++ b/.github/workflows/createNewVersion.yml @@ -23,9 +23,6 @@ on: value: ${{ jobs.createNewVersion.outputs.NEW_VERSION }} secrets: - LARGE_SECRET_PASSPHRASE: - description: Passphrase used to decrypt GPG key - required: true SLACK_WEBHOOK: description: Webhook used to comment in slack required: true @@ -73,8 +70,6 @@ jobs: - name: Setup git for OSBotify uses: ./.github/actions/composite/setupGitForOSBotify id: setupGitForOSBotify - with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - name: Generate new E/App version id: bumpVersion diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 99c38a2d2e5b..65fc2f2fbe9b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -45,7 +45,6 @@ jobs: uses: ./.github/actions/composite/setupGitForOSBotifyApp id: setupGitForOSBotify with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} @@ -301,10 +300,15 @@ jobs: - name: Setup Node uses: ./.github/actions/composite/setupNode - - name: Decrypt Developer ID Certificate - run: cd desktop && gpg --quiet --batch --yes --decrypt --passphrase="$DEVELOPER_ID_SECRET_PASSPHRASE" --output developer_id.p12 developer_id.p12.gpg + - name: Load Desktop credentials from 1Password + id: load-credentials + uses: 1password/load-secrets-action@v2 + with: + export-env: false env: - DEVELOPER_ID_SECRET_PASSPHRASE: ${{ secrets.DEVELOPER_ID_SECRET_PASSPHRASE }} + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + DESKTOP_CERTIFICATE_BASE64: "op://Mobile-Deploy-CI/Desktop Certificates.p12/CSC_LINK" + DESKTOP_CERTIFICATE_PASSWORD: "op://Mobile-Deploy-CI/Desktop Certificates.p12/CSC_KEY_PASSWORD" - name: Build desktop app run: | @@ -314,8 +318,8 @@ jobs: npm run desktop-build-staging fi env: - CSC_LINK: ${{ secrets.CSC_LINK }} - CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + CSC_LINK: ${{ steps.load-credentials.outputs.DESKTOP_CERTIFICATE_BASE64 }} + CSC_KEY_PASSWORD: ${{ steps.load-credentials.outputs.DESKTOP_CERTIFICATE_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml index 2285eec56065..7a6690c27456 100644 --- a/.github/workflows/finishReleaseCycle.yml +++ b/.github/workflows/finishReleaseCycle.yml @@ -22,7 +22,6 @@ jobs: uses: ./.github/actions/composite/setupGitForOSBotifyApp id: setupGitForOSBotify with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} @@ -87,7 +86,6 @@ jobs: id: setupGitForOSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} @@ -128,7 +126,6 @@ jobs: - name: Setup git for OSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index bfe860e60224..f8723f55866c 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -104,7 +104,6 @@ jobs: - name: Setup Git for OSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: - GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} From 9b44aca6f5fde244ca5d94da9a45c855b1828685 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 15:51:01 -0800 Subject: [PATCH 080/109] Add note about 1p --- .github/workflows/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 525afa3a8cea..73b62556ffd4 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -97,6 +97,11 @@ The GitHub workflows require a large list of secrets to deploy, notify and test 1. `APPLE_DEMO_PASSWORD` - Demo account password used for https://appstoreconnect.apple.com/ 1. `BROWSERSTACK` - Used to access Browserstack's API +We use 1Password for many secrets and in general use two different actions from 1Password to fetch secrets: + +1. `1password/install-cli-action` - This action is used to install 1Password cli `op` and is used to grab **files** using the `op read` command. +1. `1password/load-secrets-action` - This action is used to fetch **strings** from 1Password. + ### Important note about Secrets Secrets are available by default in most workflows. The exception to the rule is callable workflows. If a workflow is triggered by the `workflow_call` event, it will only have access to repo secrets if the workflow that called it passed in the secrets explicitly (for example, using `secrets: inherit`). From dbe1fc620a0f9a4ea8e4fbb08a031e465d815621 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 15:55:12 -0800 Subject: [PATCH 081/109] Remove unused input --- .github/actions/composite/setupGitForOSBotify/action.yml | 5 ----- .github/actions/composite/setupGitForOSBotifyApp/action.yml | 3 --- 2 files changed, 8 deletions(-) diff --git a/.github/actions/composite/setupGitForOSBotify/action.yml b/.github/actions/composite/setupGitForOSBotify/action.yml index bf0ac5e8b064..a655a67f1f44 100644 --- a/.github/actions/composite/setupGitForOSBotify/action.yml +++ b/.github/actions/composite/setupGitForOSBotify/action.yml @@ -1,11 +1,6 @@ name: 'Setup Git for OSBotify' description: 'Setup Git for OSBotify' -inputs: - GPG_PASSPHRASE: - description: 'Passphrase used to decrypt GPG key' - required: true - runs: using: composite steps: diff --git a/.github/actions/composite/setupGitForOSBotifyApp/action.yml b/.github/actions/composite/setupGitForOSBotifyApp/action.yml index d931183fd2cf..1321a3043649 100644 --- a/.github/actions/composite/setupGitForOSBotifyApp/action.yml +++ b/.github/actions/composite/setupGitForOSBotifyApp/action.yml @@ -5,9 +5,6 @@ name: "Setup Git for OSBotify" description: "Setup Git for OSBotify" inputs: - GPG_PASSPHRASE: - description: "Passphrase used to decrypt GPG key" - required: true OS_BOTIFY_APP_ID: description: "Application ID for OS Botify" required: true From 7257636dc7910397c1bb372170f692f4870ce08b Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 16:26:50 -0800 Subject: [PATCH 082/109] Add `OP_SERVICE_ACCOUNT_TOKEN` input instead of secret --- .github/actions/composite/setupGitForOSBotify/action.yml | 7 ++++++- .../actions/composite/setupGitForOSBotifyApp/action.yml | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/actions/composite/setupGitForOSBotify/action.yml b/.github/actions/composite/setupGitForOSBotify/action.yml index a655a67f1f44..0b212f7b40d7 100644 --- a/.github/actions/composite/setupGitForOSBotify/action.yml +++ b/.github/actions/composite/setupGitForOSBotify/action.yml @@ -1,6 +1,11 @@ name: 'Setup Git for OSBotify' description: 'Setup Git for OSBotify' +inputs: + OP_SERVICE_ACCOUNT_TOKEN: + description: "1Password service account token" + required: true + runs: using: composite steps: @@ -10,7 +15,7 @@ runs: - name: Load files from 1Password shell: bash env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + OP_SERVICE_ACCOUNT_TOKEN: ${{ inputs.OP_SERVICE_ACCOUNT_TOKEN }} run: op read "op://Mobile-Deploy-CI/OSBotify-private-key.asc/OSBotify-private-key.asc" --force --out-file ./OSBotify-private-key.asc - name: Import OSBotify GPG Key diff --git a/.github/actions/composite/setupGitForOSBotifyApp/action.yml b/.github/actions/composite/setupGitForOSBotifyApp/action.yml index 1321a3043649..d8e5e8e1e18f 100644 --- a/.github/actions/composite/setupGitForOSBotifyApp/action.yml +++ b/.github/actions/composite/setupGitForOSBotifyApp/action.yml @@ -5,6 +5,9 @@ name: "Setup Git for OSBotify" description: "Setup Git for OSBotify" inputs: + OP_SERVICE_ACCOUNT_TOKEN: + description: "1Password service account token" + required: true OS_BOTIFY_APP_ID: description: "Application ID for OS Botify" required: true @@ -42,7 +45,7 @@ runs: - name: Load files from 1Password shell: bash env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + OP_SERVICE_ACCOUNT_TOKEN: ${{ inputs.OP_SERVICE_ACCOUNT_TOKEN }} run: op read "op://Mobile-Deploy-CI/OSBotify-private-key.asc/OSBotify-private-key.asc" --force --out-file ./OSBotify-private-key.asc - name: Import OSBotify GPG Key From 5c10b746455f73761056ae4057e4d3bffff2997e Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 16:29:45 -0800 Subject: [PATCH 083/109] Add service account to all instances --- .github/workflows/cherryPick.yml | 1 + .github/workflows/createNewVersion.yml | 2 ++ .github/workflows/deploy.yml | 1 + .github/workflows/finishReleaseCycle.yml | 3 +++ .github/workflows/preDeploy.yml | 1 + 5 files changed, 8 insertions(+) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 683568e255ef..425baa37eb15 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -45,6 +45,7 @@ jobs: id: setupGitForOSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} diff --git a/.github/workflows/createNewVersion.yml b/.github/workflows/createNewVersion.yml index 6245af07a121..6c5a18bf4425 100644 --- a/.github/workflows/createNewVersion.yml +++ b/.github/workflows/createNewVersion.yml @@ -70,6 +70,8 @@ jobs: - name: Setup git for OSBotify uses: ./.github/actions/composite/setupGitForOSBotify id: setupGitForOSBotify + with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} - name: Generate new E/App version id: bumpVersion diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 65fc2f2fbe9b..4b4ea2413ae5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -45,6 +45,7 @@ jobs: uses: ./.github/actions/composite/setupGitForOSBotifyApp id: setupGitForOSBotify with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml index 7a6690c27456..ca030e95de1d 100644 --- a/.github/workflows/finishReleaseCycle.yml +++ b/.github/workflows/finishReleaseCycle.yml @@ -22,6 +22,7 @@ jobs: uses: ./.github/actions/composite/setupGitForOSBotifyApp id: setupGitForOSBotify with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} @@ -86,6 +87,7 @@ jobs: id: setupGitForOSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} @@ -126,6 +128,7 @@ jobs: - name: Setup git for OSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index f8723f55866c..10ca10882464 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -104,6 +104,7 @@ jobs: - name: Setup Git for OSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp with: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} From ae548a75024f100dc25ab61d9f70193242369317 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 16:36:56 -0800 Subject: [PATCH 084/109] Add secret to fix lint --- .github/workflows/createNewVersion.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/createNewVersion.yml b/.github/workflows/createNewVersion.yml index 6c5a18bf4425..a933c2f1686f 100644 --- a/.github/workflows/createNewVersion.yml +++ b/.github/workflows/createNewVersion.yml @@ -29,6 +29,9 @@ on: OS_BOTIFY_COMMIT_TOKEN: description: OSBotify personal access token, used to workaround committing to protected branch required: true + OP_SERVICE_ACCOUNT_TOKEN: + description: 1Password service account token + required: true jobs: validateActor: From 6fb4402b5616303a1ad78f3daf308463470bb39c Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 16:53:04 -0800 Subject: [PATCH 085/109] Fix path for gpg key for OSBotify --- .github/actions/composite/setupGitForOSBotify/action.yml | 2 +- .../actions/composite/setupGitForOSBotifyApp/action.yml | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/actions/composite/setupGitForOSBotify/action.yml b/.github/actions/composite/setupGitForOSBotify/action.yml index 0b212f7b40d7..9495ba07b0ac 100644 --- a/.github/actions/composite/setupGitForOSBotify/action.yml +++ b/.github/actions/composite/setupGitForOSBotify/action.yml @@ -20,7 +20,7 @@ runs: - name: Import OSBotify GPG Key shell: bash - run: cd .github/workflows && gpg --import OSBotify-private-key.asc + run: gpg --import OSBotify-private-key.asc - name: Set up git for OSBotify shell: bash diff --git a/.github/actions/composite/setupGitForOSBotifyApp/action.yml b/.github/actions/composite/setupGitForOSBotifyApp/action.yml index d8e5e8e1e18f..45d85663bfa6 100644 --- a/.github/actions/composite/setupGitForOSBotifyApp/action.yml +++ b/.github/actions/composite/setupGitForOSBotifyApp/action.yml @@ -50,7 +50,7 @@ runs: - name: Import OSBotify GPG Key shell: bash - run: cd .github/workflows && gpg --import OSBotify-private-key.asc + run: gpg --import OSBotify-private-key.asc - name: Set up git for OSBotify shell: bash @@ -60,11 +60,6 @@ runs: git config user.name OSBotify git config user.email infra+osbotify@expensify.com - - name: Enable debug logs for git - shell: bash - if: runner.debug == '1' - run: echo "GIT_TRACE=true" >> "$GITHUB_ENV" - - name: Sync clock shell: bash run: sudo sntp -sS time.windows.com From 4e07d44a255a59b5d5e8c4b4ad0945c149527358 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 00:58:47 +0000 Subject: [PATCH 086/109] Update version to 9.0.94-11 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 928d6d648d7e..66e3bec9e4e1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009410 - versionName "9.0.94-10" + versionCode 1009009411 + versionName "9.0.94-11" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 45abb8c07c76..7448d93bd58c 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.10 + 9.0.94.11 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 72dd59cee48d..2817bfa29808 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.10 + 9.0.94.11 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 613b3a038aa3..cf47b6a6284e 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.10 + 9.0.94.11 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index f2df00cbb83b..8968cf04cb8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-10", + "version": "9.0.94-11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-10", + "version": "9.0.94-11", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 9892aba77bd7..f15715195c0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-10", + "version": "9.0.94-11", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From f25555c64a9d1d8ee121155465b646a1eaa70455 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 00:58:47 +0000 Subject: [PATCH 087/109] Update Mobile-Expensify submodule version to 9.0.94-11 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 9e6ead35b763..b8bcc6f027b7 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 9e6ead35b76303685fa83ac22cad83d0955ce3d3 +Subproject commit b8bcc6f027b7a9ebbd57dc9db1e8ba7d2aa31716 From eee408672d936b3864f9eb788869e285f1076aef Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 17:15:46 -0800 Subject: [PATCH 088/109] Tweak which branch we pull for set up git for OSBotify App action --- .github/workflows/cherryPick.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 425baa37eb15..6bb55b7bea1c 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -43,7 +43,7 @@ jobs: - name: Set up git for OSBotify id: setupGitForOSBotify - uses: ./.github/actions/composite/setupGitForOSBotifyApp + uses: ./.github/actions/composite/setupGitForOSBotifyApp@main with: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} From c780f4709eb5ffab5cb6741f17de8c5bd14176d8 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:21:04 +0000 Subject: [PATCH 089/109] Update version to 9.0.94-12 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 66e3bec9e4e1..1546bcdb47b5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009411 - versionName "9.0.94-11" + versionCode 1009009412 + versionName "9.0.94-12" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 7448d93bd58c..2b9dfc39555c 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.11 + 9.0.94.12 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 2817bfa29808..1b3a74c0e2ba 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.11 + 9.0.94.12 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index cf47b6a6284e..81ff15f3d6ae 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.11 + 9.0.94.12 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 8968cf04cb8e..2ad0ad02d64a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-11", + "version": "9.0.94-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-11", + "version": "9.0.94-12", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f15715195c0d..8f4956e7f267 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-11", + "version": "9.0.94-12", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From b2323104861467ef468bc2309ddaee75d0221022 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:21:04 +0000 Subject: [PATCH 090/109] Update Mobile-Expensify submodule version to 9.0.94-12 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index b8bcc6f027b7..aaccdc4e15a4 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit b8bcc6f027b7a9ebbd57dc9db1e8ba7d2aa31716 +Subproject commit aaccdc4e15a41123bc7162b9ca61e9d2a6867f28 From b572b592ac25c19563da3157c306549ff5e64423 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 17:23:01 -0800 Subject: [PATCH 091/109] Run a git fetch in order to fix setupGitForOSBotify --- .github/workflows/cherryPick.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 6bb55b7bea1c..875b5c2e559a 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -41,6 +41,9 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} submodules: true + - name: Run git fetch + run: git fetch + - name: Set up git for OSBotify id: setupGitForOSBotify uses: ./.github/actions/composite/setupGitForOSBotifyApp@main From 70082f65cef83da6795a11b5a57aa88a8c66f19c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:30:12 +0000 Subject: [PATCH 092/109] Update version to 9.0.94-13 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 1546bcdb47b5..6a3045514faf 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009412 - versionName "9.0.94-12" + versionCode 1009009413 + versionName "9.0.94-13" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 2b9dfc39555c..5a72554f4c84 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.12 + 9.0.94.13 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 1b3a74c0e2ba..6c1c5fb659f6 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.12 + 9.0.94.13 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 81ff15f3d6ae..f082ebb95cdf 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.12 + 9.0.94.13 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 2ad0ad02d64a..b1f01488868d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-12", + "version": "9.0.94-13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-12", + "version": "9.0.94-13", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 8f4956e7f267..0757eac96c08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-12", + "version": "9.0.94-13", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 67c8915302e1c68a40f0c0b574c8bf6e7dac53d0 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:30:12 +0000 Subject: [PATCH 093/109] Update Mobile-Expensify submodule version to 9.0.94-13 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index aaccdc4e15a4..9d723f8dfde0 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit aaccdc4e15a41123bc7162b9ca61e9d2a6867f28 +Subproject commit 9d723f8dfde03733f4530892daa08410012c5c4f From 945fbe2c10a92bee34f18dcfa42000d192328406 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 17:39:34 -0800 Subject: [PATCH 094/109] Only fetch the main branch --- .github/workflows/cherryPick.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 875b5c2e559a..3f80963f34a8 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -42,7 +42,7 @@ jobs: submodules: true - name: Run git fetch - run: git fetch + run: git fetch origin main --fetch-depth=1 - name: Set up git for OSBotify id: setupGitForOSBotify From d66055c13793012c42dbeb86a61d009925d33172 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:47:58 +0000 Subject: [PATCH 095/109] Update version to 9.0.94-14 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6a3045514faf..c762416a1ca8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009413 - versionName "9.0.94-13" + versionCode 1009009414 + versionName "9.0.94-14" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 5a72554f4c84..86a66c117e48 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.13 + 9.0.94.14 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 6c1c5fb659f6..bfccaa613f2f 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.13 + 9.0.94.14 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index f082ebb95cdf..0dc358d28619 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.13 + 9.0.94.14 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index b1f01488868d..219aa517fa50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-13", + "version": "9.0.94-14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-13", + "version": "9.0.94-14", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 0757eac96c08..fbae14c6046f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-13", + "version": "9.0.94-14", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 596e37364c5a5c9c8b1c53ef78e95b1aa96766ef Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:47:58 +0000 Subject: [PATCH 096/109] Update Mobile-Expensify submodule version to 9.0.94-14 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 9d723f8dfde0..65db5a2c4de6 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 9d723f8dfde03733f4530892daa08410012c5c4f +Subproject commit 65db5a2c4de6361ed9d23029d5e8b72bdacbd7a4 From 439ac01f8829f7241b6524698304c441960ed891 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 17:49:00 -0800 Subject: [PATCH 097/109] Change to depth flag --- .github/workflows/cherryPick.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 3f80963f34a8..32d6006d82ff 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -42,7 +42,7 @@ jobs: submodules: true - name: Run git fetch - run: git fetch origin main --fetch-depth=1 + run: git fetch origin main --depth=1 - name: Set up git for OSBotify id: setupGitForOSBotify From b08cdaea202353042ab480cb463252c07f079e2d Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:53:42 +0000 Subject: [PATCH 098/109] Update version to 9.0.94-15 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index c762416a1ca8..568b3d83d31e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009414 - versionName "9.0.94-14" + versionCode 1009009415 + versionName "9.0.94-15" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 86a66c117e48..cbcd52c120ab 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.14 + 9.0.94.15 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index bfccaa613f2f..f92abc1c9ade 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.14 + 9.0.94.15 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 0dc358d28619..ba17b9a78a8c 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.14 + 9.0.94.15 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 219aa517fa50..a85d77bdb1a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-14", + "version": "9.0.94-15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-14", + "version": "9.0.94-15", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fbae14c6046f..181223e1f077 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-14", + "version": "9.0.94-15", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 18ad58740e6ab6320b3ec2fa40dbd66fe42b6cd2 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 01:53:42 +0000 Subject: [PATCH 099/109] Update Mobile-Expensify submodule version to 9.0.94-15 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 65db5a2c4de6..649cbeeebcfa 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 65db5a2c4de6361ed9d23029d5e8b72bdacbd7a4 +Subproject commit 649cbeeebcfabc03f9a604582e8c4fe91aa120b9 From db6928638b90df146145d302bd499bbfdb073f56 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 17:59:22 -0800 Subject: [PATCH 100/109] Add owner and repo to composite action --- .github/workflows/cherryPick.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 32d6006d82ff..67bf80bae9c7 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -46,7 +46,7 @@ jobs: - name: Set up git for OSBotify id: setupGitForOSBotify - uses: ./.github/actions/composite/setupGitForOSBotifyApp@main + uses: Expensify/App/.github/actions/composite/setupGitForOSBotifyApp@main with: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} From d9ba4e54023250e8fb3470a418c8a23d4d083804 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:06:47 +0000 Subject: [PATCH 101/109] Update version to 9.0.94-16 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 568b3d83d31e..a589fdabd58a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009415 - versionName "9.0.94-15" + versionCode 1009009416 + versionName "9.0.94-16" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index cbcd52c120ab..6da3b563f906 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.15 + 9.0.94.16 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index f92abc1c9ade..6baba1bffcaa 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.15 + 9.0.94.16 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index ba17b9a78a8c..1e6ac2146eb7 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.15 + 9.0.94.16 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index a85d77bdb1a4..f38c83746457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-15", + "version": "9.0.94-16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-15", + "version": "9.0.94-16", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 181223e1f077..e048ce2ba707 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-15", + "version": "9.0.94-16", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 4425c814307364df1b30c1f390ce33b50709ec62 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:06:47 +0000 Subject: [PATCH 102/109] Update Mobile-Expensify submodule version to 9.0.94-16 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 649cbeeebcfa..6a4777e58822 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 649cbeeebcfabc03f9a604582e8c4fe91aa120b9 +Subproject commit 6a4777e588228cea0fc162d2761b7b7c4216e186 From f093befe3786d06f453dc766d0487da58656a6ab Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:23:45 +0000 Subject: [PATCH 103/109] Update version to 9.0.94-17 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a589fdabd58a..9b4a06132ca5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009416 - versionName "9.0.94-16" + versionCode 1009009417 + versionName "9.0.94-17" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 6da3b563f906..b5756ca6aa93 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.16 + 9.0.94.17 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 6baba1bffcaa..0af077c23fc4 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.16 + 9.0.94.17 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 1e6ac2146eb7..a0f3ab71f46a 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.16 + 9.0.94.17 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index f38c83746457..1b28cec56e55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-16", + "version": "9.0.94-17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-16", + "version": "9.0.94-17", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e048ce2ba707..fb58022b3f6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-16", + "version": "9.0.94-17", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 36278b2c16cd37fe2c1668723413efe3ad666f98 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:23:45 +0000 Subject: [PATCH 104/109] Update Mobile-Expensify submodule version to 9.0.94-17 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 6a4777e58822..54a8c7d6a20b 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 6a4777e588228cea0fc162d2761b7b7c4216e186 +Subproject commit 54a8c7d6a20b9ec5831fb2ab06e3646eaaa5e843 From 795e3af40962f4763b98bebaaecb94f67876474f Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:26:17 +0000 Subject: [PATCH 105/109] Update version to 9.0.94-18 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9b4a06132ca5..7409aa2a2941 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009417 - versionName "9.0.94-17" + versionCode 1009009418 + versionName "9.0.94-18" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index b5756ca6aa93..02ae024cbe6d 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.17 + 9.0.94.18 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 0af077c23fc4..9f5458d69021 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.17 + 9.0.94.18 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index a0f3ab71f46a..bb1bcae53c87 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.17 + 9.0.94.18 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 1b28cec56e55..aec3936d3e8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-17", + "version": "9.0.94-18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-17", + "version": "9.0.94-18", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fb58022b3f6c..c2b1ae80d3b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-17", + "version": "9.0.94-18", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 63adfcf9ef1227fb31006ff6ae1ee90ca1abd664 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:26:17 +0000 Subject: [PATCH 106/109] Update Mobile-Expensify submodule version to 9.0.94-18 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 54a8c7d6a20b..8aab8cd656da 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 54a8c7d6a20b9ec5831fb2ab06e3646eaaa5e843 +Subproject commit 8aab8cd656da1f4914d3b51921b73644f57068f3 From a49c5df4b0b9d76fcd51f3c4524b743128248f54 Mon Sep 17 00:00:00 2001 From: Andrew Gable Date: Wed, 5 Feb 2025 18:36:27 -0800 Subject: [PATCH 107/109] Remove extra git fetch --- .github/workflows/cherryPick.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 67bf80bae9c7..a4ec14ff6825 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -41,9 +41,6 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} submodules: true - - name: Run git fetch - run: git fetch origin main --depth=1 - - name: Set up git for OSBotify id: setupGitForOSBotify uses: Expensify/App/.github/actions/composite/setupGitForOSBotifyApp@main From 91a52b44c20214c996779bb6495bb0aeedde457c Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:45:42 +0000 Subject: [PATCH 108/109] Update version to 9.0.94-19 --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 7409aa2a2941..156718f4c3c2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009009418 - versionName "9.0.94-18" + versionCode 1009009419 + versionName "9.0.94-19" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 02ae024cbe6d..2043ee4a2cb6 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.18 + 9.0.94.19 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 9f5458d69021..6c71db15b8f1 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.18 + 9.0.94.19 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index bb1bcae53c87..73074d2a200e 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.18 + 9.0.94.19 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index aec3936d3e8a..30002a164a28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-18", + "version": "9.0.94-19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-18", + "version": "9.0.94-19", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c2b1ae80d3b3..42e75636878a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-18", + "version": "9.0.94-19", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From a8daefb0570311171c7b5a25f90615e12cb8f3a9 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 6 Feb 2025 02:45:42 +0000 Subject: [PATCH 109/109] Update Mobile-Expensify submodule version to 9.0.94-19 --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 8aab8cd656da..1feafc32db9c 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 8aab8cd656da1f4914d3b51921b73644f57068f3 +Subproject commit 1feafc32db9c2596b02598228a56be57fd8d39d6