From 532dcacf98ff02b8999695dfc469a06fb4151926 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:01:20 +0100 Subject: [PATCH 01/14] refactor all base pages to tsx --- ...e.js => IOURequestRedirectToStartPage.tsx} | 23 +++------- ...stStartPage.js => IOURequestStartPage.tsx} | 0 ...StepAmount.js => IOURequestStepAmount.tsx} | 0 ...Distance.js => IOURequestStepDistance.tsx} | 45 +++++++------------ 4 files changed, 21 insertions(+), 47 deletions(-) rename src/pages/iou/request/{IOURequestRedirectToStartPage.js => IOURequestRedirectToStartPage.tsx} (73%) rename src/pages/iou/request/{IOURequestStartPage.js => IOURequestStartPage.tsx} (100%) rename src/pages/iou/request/step/{IOURequestStepAmount.js => IOURequestStepAmount.tsx} (100%) rename src/pages/iou/request/step/{IOURequestStepDistance.js => IOURequestStepDistance.tsx} (92%) diff --git a/src/pages/iou/request/IOURequestRedirectToStartPage.js b/src/pages/iou/request/IOURequestRedirectToStartPage.tsx similarity index 73% rename from src/pages/iou/request/IOURequestRedirectToStartPage.js rename to src/pages/iou/request/IOURequestRedirectToStartPage.tsx index 2da235743705..c34fa6065028 100644 --- a/src/pages/iou/request/IOURequestRedirectToStartPage.js +++ b/src/pages/iou/request/IOURequestRedirectToStartPage.tsx @@ -1,34 +1,22 @@ import PropTypes from 'prop-types'; import React, {useEffect} from 'react'; -import _ from 'underscore'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import ScreenWrapper from '@components/ScreenWrapper'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import type { WithWritableReportOrNotFoundProps } from './step/withWritableReportOrNotFound'; -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.oneOf(_.values(CONST.IOU.TYPE)).isRequired, - - /** The type of IOU Request, i.e. manual, scan, distance */ - iouRequestType: PropTypes.oneOf(_.values(CONST.IOU.REQUEST_TYPE)).isRequired, - }), - }).isRequired, -}; +type IOURequestRedirectToStartPageProps = WithWritableReportOrNotFoundProps; function IOURequestRedirectToStartPage({ route: { params: {iouType, iouRequestType}, }, -}) { - const isIouTypeValid = _.values(CONST.IOU.TYPE).includes(iouType); - const isIouRequestTypeValid = _.values(CONST.IOU.REQUEST_TYPE).includes(iouRequestType); +}: IOURequestRedirectToStartPageProps) { + const isIouTypeValid = Object.values(CONST.IOU.TYPE).includes(iouType); + const isIouRequestTypeValid = Object.values(CONST.IOU.REQUEST_TYPE).includes(iouRequestType); useEffect(() => { if (!isIouTypeValid || !isIouRequestTypeValid) { @@ -64,6 +52,5 @@ function IOURequestRedirectToStartPage({ } IOURequestRedirectToStartPage.displayName = 'IOURequestRedirectToStartPage'; -IOURequestRedirectToStartPage.propTypes = propTypes; export default IOURequestRedirectToStartPage; diff --git a/src/pages/iou/request/IOURequestStartPage.js b/src/pages/iou/request/IOURequestStartPage.tsx similarity index 100% rename from src/pages/iou/request/IOURequestStartPage.js rename to src/pages/iou/request/IOURequestStartPage.tsx diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.tsx similarity index 100% rename from src/pages/iou/request/step/IOURequestStepAmount.js rename to src/pages/iou/request/step/IOURequestStepAmount.tsx diff --git a/src/pages/iou/request/step/IOURequestStepDistance.js b/src/pages/iou/request/step/IOURequestStepDistance.tsx similarity index 92% rename from src/pages/iou/request/step/IOURequestStepDistance.js rename to src/pages/iou/request/step/IOURequestStepDistance.tsx index dad610cbc636..d725cdfe152f 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.js +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -1,7 +1,8 @@ import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import { withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import Button from '@components/Button'; import DistanceRequestFooter from '@components/DistanceRequest/DistanceRequestFooter'; @@ -17,7 +18,6 @@ import compose from '@libs/compose'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as TransactionUtils from '@libs/TransactionUtils'; -import reportPropTypes from '@pages/reportPropTypes'; import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; import * as MapboxToken from '@userActions/MapboxToken'; @@ -25,50 +25,39 @@ import * as Transaction from '@userActions/Transaction'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; +import type * as OnyxTypes from '@src/types/onyx'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; +import type { WithWritableReportOrNotFoundProps } from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: IOURequestStepRoutePropTypes.isRequired, - - /* Onyx Props */ - /** The report that the transaction belongs to */ - report: reportPropTypes, - - /** The transaction object being modified in Onyx */ - transaction: transactionPropTypes, - - /** backup version of the original transaction */ - transactionBackup: transactionPropTypes, -}; +type IOURequestStepDistanceOnyxProps = { + transactionBackup?: OnyxEntry, +} -const defaultProps = { - report: {}, - transaction: {}, - transactionBackup: {}, +type IOURequestStepDistanceProps = WithWritableReportOrNotFoundProps & IOURequestStepDistanceOnyxProps & { + report?: OnyxEntry, + transaction?: OnyxEntry, }; function IOURequestStepDistance({ - report, route: { params: {action, iouType, reportID, transactionID, backTo}, }, + report, transaction, transactionBackup, -}) { +}: IOURequestStepDistanceProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); const {translate} = useLocalize(); const [optimisticWaypoints, setOptimisticWaypoints] = useState(null); const waypoints = useMemo(() => optimisticWaypoints || lodashGet(transaction, 'comment.waypoints', {waypoint0: {}, waypoint1: {}}), [optimisticWaypoints, transaction]); - const waypointsList = _.keys(waypoints); + const waypointsList = Object.keys(waypoints); const previousWaypoints = usePrevious(waypoints); - const numberOfWaypoints = _.size(waypoints); - const numberOfPreviousWaypoints = _.size(previousWaypoints); + const numberOfWaypoints = Object.keys(waypoints).length; + const numberOfPreviousWaypoints = Object.keys(previousWaypoints).length; const scrollViewRef = useRef(null); const isLoadingRoute = lodashGet(transaction, 'comment.isLoading', false); const isLoading = lodashGet(transaction, 'isLoading', false); @@ -82,7 +71,7 @@ function IOURequestStepDistance({ const [shouldShowAtLeastTwoDifferentWaypointsError, setShouldShowAtLeastTwoDifferentWaypointsError] = useState(false); const nonEmptyWaypointsCount = useMemo(() => _.filter(_.keys(waypoints), (key) => !_.isEmpty(waypoints[key])).length, [waypoints]); const duplicateWaypointsError = useMemo(() => nonEmptyWaypointsCount >= 2 && _.size(validatedWaypoints) !== nonEmptyWaypointsCount, [nonEmptyWaypointsCount, validatedWaypoints]); - const atLeastTwoDifferentWaypointsError = useMemo(() => _.size(validatedWaypoints) < 2, [validatedWaypoints]); + const atLeastTwoDifferentWaypointsError = useMemo(() => Object.keys(validatedWaypoints) < 2, [validatedWaypoints]); const isEditing = action === CONST.IOU.ACTION.EDIT; const isCreatingNewRequest = Navigation.getActiveRoute().includes('start'); @@ -284,8 +273,6 @@ function IOURequestStepDistance({ } IOURequestStepDistance.displayName = 'IOURequestStepDistance'; -IOURequestStepDistance.propTypes = propTypes; -IOURequestStepDistance.defaultProps = defaultProps; export default compose( withWritableReportOrNotFound, From 9f68f82a9901ddad5012226fa009f3128adc6226 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:09:11 +0100 Subject: [PATCH 02/14] add correct props for IOURequestStartPage --- src/pages/iou/request/IOURequestStartPage.tsx | 46 ++++++------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index cb078fac133c..3852e04dfe04 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -1,9 +1,9 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import { withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import DragAndDropProvider from '@components/DragAndDrop/Provider'; @@ -11,7 +11,6 @@ import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TabSelector from '@components/TabSelector/TabSelector'; -import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import usePrevious from '@hooks/usePrevious'; @@ -23,53 +22,34 @@ import Navigation from '@libs/Navigation/Navigation'; import OnyxTabNavigator, {TopTab} from '@libs/Navigation/OnyxTabNavigator'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import reportPropTypes from '@pages/reportPropTypes'; +import type * as OnyxTypes from '@src/types/onyx'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import IOURequestStepAmount from './step/IOURequestStepAmount'; import IOURequestStepDistance from './step/IOURequestStepDistance'; -import IOURequestStepRoutePropTypes from './step/IOURequestStepRoutePropTypes'; import IOURequestStepScan from './step/IOURequestStepScan'; +import type { WithWritableReportOrNotFoundProps } from './step/withWritableReportOrNotFound'; -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: IOURequestStepRoutePropTypes.isRequired, - - /* Onyx Props */ - /** The report that holds the transaction */ - report: reportPropTypes, - - /** The policy tied to the report */ - policy: PropTypes.shape({ - /** Type of the policy */ - type: PropTypes.string, - }), - - /** The tab to select by default (whatever the user visited last) */ - selectedTab: PropTypes.oneOf(_.values(CONST.TAB_REQUEST)), - - /** The transaction being modified */ - transaction: transactionPropTypes, +type IOURequestStartPageOnyxProps = { + report?: OnyxEntry, + policy?: OnyxEntry, + selectedTab?: typeof CONST.TAB_REQUEST[keyof typeof CONST.TAB_REQUEST], + transaction?: OnyxEntry, }; -const defaultProps = { - report: {}, - policy: {}, - selectedTab: CONST.TAB_REQUEST.SCAN, - transaction: {}, -}; +type IOURequestStartPageProps = WithWritableReportOrNotFoundProps & IOURequestStartPageOnyxProps; function IOURequestStartPage({ - report, - policy, route, route: { params: {iouType, reportID}, }, + report, + policy, selectedTab, transaction, -}) { +}: IOURequestStartPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const navigation = useNavigation(); From 6f86eeea9e050d705c899ee1764f6db485b294e0 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:31:16 +0200 Subject: [PATCH 03/14] refactor IOURequest pages --- .../DistanceRequestRenderItem.tsx | 2 +- src/libs/Navigation/OnyxTabNavigator.tsx | 5 +- src/libs/Navigation/types.ts | 27 +++- src/libs/TransactionUtils.ts | 7 +- src/libs/actions/IOU.ts | 56 ++++--- .../request/IOURequestRedirectToStartPage.tsx | 4 +- src/pages/iou/request/IOURequestStartPage.tsx | 56 +++---- .../iou/request/step/IOURequestStepAmount.tsx | 80 ++++------ .../request/step/IOURequestStepDistance.tsx | 151 ++++++++++-------- .../step/withWritableReportOrNotFound.tsx | 8 +- src/types/onyx/Transaction.ts | 3 +- 11 files changed, 220 insertions(+), 179 deletions(-) diff --git a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx index 57e4fb0b530e..f4b11c159326 100644 --- a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx +++ b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx @@ -17,7 +17,7 @@ type DistanceRequestProps = { onSecondaryInteraction?: () => void; /** Function to get the index of the item */ - getIndex?: () => number; + getIndex?: () => number | undefined; /** Whether the item is active */ isActive?: boolean; diff --git a/src/libs/Navigation/OnyxTabNavigator.tsx b/src/libs/Navigation/OnyxTabNavigator.tsx index deab975e067b..91c25dc82084 100644 --- a/src/libs/Navigation/OnyxTabNavigator.tsx +++ b/src/libs/Navigation/OnyxTabNavigator.tsx @@ -9,6 +9,7 @@ import Tab from '@userActions/Tab'; import ONYXKEYS from '@src/ONYXKEYS'; import type {SelectedTabRequest} from '@src/types/onyx'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; +import type { IOURequestType } from '@libs/actions/IOU'; import {defaultScreenOptions} from './OnyxTabNavigatorConfig'; type OnyxTabNavigatorOnyxProps = { @@ -24,7 +25,7 @@ type OnyxTabNavigatorProps = OnyxTabNavigatorOnyxProps & selectedTab?: SelectedTabRequest; /** A function triggered when a tab has been selected */ - onTabSelected?: (newIouType: string) => void; + onTabSelected?: (newIouType: IOURequestType) => void; tabBar: (props: TabSelectorProps) => React.ReactNode; @@ -52,7 +53,7 @@ function OnyxTabNavigator({id, selectedTab, children, onTabSelected = () => {}, const index = state.index; const routeNames = state.routeNames; Tab.setSelectedTab(id, routeNames[index] as SelectedTabRequest); - onTabSelected(routeNames[index]); + onTabSelected(routeNames[index] as IOURequestType); }, ...(screenListeners ?? {}), }} diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 10ca31816b10..8e147604b8e1 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -17,6 +17,7 @@ import type NAVIGATORS from '@src/NAVIGATORS'; import type {HybridAppRoute, Route as Routes} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type EXIT_SURVEY_REASON_FORM_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; +import type { IOURequestType } from '@libs/actions/IOU'; type NavigationRef = NavigationContainerRefWithCurrent; @@ -330,13 +331,6 @@ type RoomInviteNavigatorParamList = { }; type MoneyRequestNavigatorParamList = { - [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: { - action: ValueOf; - iouType: ValueOf; - transactionID: string; - reportID: string; - backTo: string; - }; [SCREENS.MONEY_REQUEST.PARTICIPANTS]: { iouType: string; reportID: string; @@ -419,7 +413,7 @@ type MoneyRequestNavigatorParamList = { threadReportID: number; }; [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: { - action: string; + action: ValueOf; iouType: ValueOf; transactionID: string; reportID: string; @@ -429,6 +423,23 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; + [SCREENS.MONEY_REQUEST.CREATE]: { + iouType: ValueOf; + transactionID: string; + reportID: string; + }; + [SCREENS.MONEY_REQUEST.START]: { + iouType: ValueOf; + reportID: string; + iouRequestType: IOURequestType; + }; + [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: { + iouType: ValueOf; + reportID: string; + transactionID: string; + backTo: Routes; + action: ValueOf; + }; }; type NewTaskNavigatorParamList = { diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 96c5ba8c78ec..a7af69d6961b 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -12,6 +12,7 @@ import DateUtils from './DateUtils'; import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; import {getCleanedTagName} from './PolicyUtils'; +import type { IOURequestType } from './actions/IOU'; let allTransactions: OnyxCollection = {}; @@ -45,16 +46,16 @@ function isDistanceRequest(transaction: OnyxEntry): boolean { return type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && customUnitName === CONST.CUSTOM_UNITS.NAME_DISTANCE; } -function isScanRequest(transaction: Transaction): boolean { +function isScanRequest(transaction: OnyxEntry): boolean { // This is used during the request creation flow before the transaction has been saved to the server if (lodashHas(transaction, 'iouRequestType')) { - return transaction.iouRequestType === CONST.IOU.REQUEST_TYPE.SCAN; + return transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.SCAN; } return Boolean(transaction?.receipt?.source); } -function getRequestType(transaction: Transaction): ValueOf { +function getRequestType(transaction: OnyxEntry): IOURequestType { if (isDistanceRequest(transaction)) { return CONST.IOU.REQUEST_TYPE.DISTANCE; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 540b9143ba6e..f39190dcf8f6 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2172,15 +2172,24 @@ function updateMoneyRequestTaxRate( API.write('UpdateMoneyRequestTaxRate', params, onyxData); } +type UpdateMoneyRequestDistanceParams = { + transactionID: string; + transactionThreadReportID: string; + waypoints: WaypointCollection; + policy?: OnyxEntry; + policyTagList?: OnyxEntry; + policyCategories?: OnyxEntry; +} + /** Updates the waypoints of a distance money request */ -function updateMoneyRequestDistance( - transactionID: string, - transactionThreadReportID: string, - waypoints: WaypointCollection, - policy: OnyxEntry, - policyTagList: OnyxEntry, - policyCategories: OnyxEntry, -) { +function updateMoneyRequestDistance({ + transactionID, + transactionThreadReportID, + waypoints, + policy = {} as OnyxTypes.Policy, + policyTagList = {}, + policyCategories = {}, +}: UpdateMoneyRequestDistanceParams) { const transactionChanges: TransactionChanges = { waypoints, }; @@ -3780,21 +3789,31 @@ function editMoneyRequest( } } +type UpdateMoneyRequestAmountAndCurrencyParams = { + transactionID: string; + transactionThreadReportID: string; + currency: string; + amount: number; + policy?: OnyxEntry; + policyTagList?: OnyxEntry; + policyCategories?: OnyxEntry; +}; + /** Updates the amount and currency fields of a money request */ -function updateMoneyRequestAmountAndCurrency( - transactionID: string, - transactionThreadReportID: string, - currency: string, - amount: number, - policy: OnyxEntry, - policyTagList: OnyxEntry, - policyCategories: OnyxEntry, -) { +function updateMoneyRequestAmountAndCurrency({ + transactionID, + transactionThreadReportID, + currency, + amount, + policy, + policyTagList, + policyCategories, +}: UpdateMoneyRequestAmountAndCurrencyParams) { const transactionChanges = { amount, currency, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy ?? null, policyTagList ?? null, policyCategories ?? null, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY, params, onyxData); } @@ -5433,6 +5452,7 @@ function savePreferredPaymentMethod(policyID: string, paymentMethod: PaymentMeth Onyx.merge(`${ONYXKEYS.NVP_LAST_PAYMENT_METHOD}`, {[policyID]: paymentMethod}); } +export type {IOURequestType} export { setMoneyRequestParticipants, createDistanceRequest, diff --git a/src/pages/iou/request/IOURequestRedirectToStartPage.tsx b/src/pages/iou/request/IOURequestRedirectToStartPage.tsx index c34fa6065028..ea459392481e 100644 --- a/src/pages/iou/request/IOURequestRedirectToStartPage.tsx +++ b/src/pages/iou/request/IOURequestRedirectToStartPage.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React, {useEffect} from 'react'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -6,9 +5,10 @@ import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import type { WithWritableReportOrNotFoundProps } from './step/withWritableReportOrNotFound'; -type IOURequestRedirectToStartPageProps = WithWritableReportOrNotFoundProps; +type IOURequestRedirectToStartPageProps = WithWritableReportOrNotFoundProps; function IOURequestRedirectToStartPage({ route: { diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 3852e04dfe04..9f14ad1d989b 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -1,10 +1,9 @@ +import _ from 'lodash'; import {useFocusEffect, useNavigation} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import { withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import DragAndDropProvider from '@components/DragAndDrop/Provider'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; @@ -22,31 +21,36 @@ import Navigation from '@libs/Navigation/Navigation'; import OnyxTabNavigator, {TopTab} from '@libs/Navigation/OnyxTabNavigator'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; -import type * as OnyxTypes from '@src/types/onyx'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type { Policy, Report, SelectedTabRequest, Transaction } from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; +import type { IOURequestType } from '@userActions/IOU'; import IOURequestStepAmount from './step/IOURequestStepAmount'; import IOURequestStepDistance from './step/IOURequestStepDistance'; import IOURequestStepScan from './step/IOURequestStepScan'; import type { WithWritableReportOrNotFoundProps } from './step/withWritableReportOrNotFound'; type IOURequestStartPageOnyxProps = { - report?: OnyxEntry, - policy?: OnyxEntry, - selectedTab?: typeof CONST.TAB_REQUEST[keyof typeof CONST.TAB_REQUEST], - transaction?: OnyxEntry, + report: OnyxEntry; + + policy: OnyxEntry; + + selectedTab: OnyxEntry; + + transaction: OnyxEntry; }; -type IOURequestStartPageProps = WithWritableReportOrNotFoundProps & IOURequestStartPageOnyxProps; +type IOURequestStartPageProps = IOURequestStartPageOnyxProps & WithWritableReportOrNotFoundProps; function IOURequestStartPage({ + report, + policy, route, route: { params: {iouType, reportID}, }, - report, - policy, selectedTab, transaction, }: IOURequestStartPageProps) { @@ -63,11 +67,11 @@ function IOURequestStartPage({ const transactionRequestType = useRef(TransactionUtils.getRequestType(transaction)); const previousIOURequestType = usePrevious(transactionRequestType.current); const {canUseP2PDistanceRequests} = usePermissions(); - const isFromGlobalCreate = _.isEmpty(report.reportID); + const isFromGlobalCreate = _.isEmpty(report?.reportID); useFocusEffect( useCallback(() => { - const handler = (event) => { + const handler = (event: KeyboardEvent) => { if (event.code !== CONST.KEYBOARD_SHORTCUTS.TAB.shortcutKey) { return; } @@ -82,7 +86,7 @@ function IOURequestStartPage({ // Clear out the temporary money request if the reportID in the URL has changed from the transaction's reportID useEffect(() => { - if (transaction.reportID === reportID) { + if (transaction?.reportID === reportID) { return; } IOU.initMoneyRequest(reportID, policy, isFromGlobalCreate, transactionRequestType.current); @@ -90,30 +94,30 @@ function IOURequestStartPage({ const isExpenseChat = ReportUtils.isPolicyExpenseChat(report); const isExpenseReport = ReportUtils.isExpenseReport(report); - const shouldDisplayDistanceRequest = iouType !== CONST.IOU.TYPE.TRACK_EXPENSE && (canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate); + const shouldDisplayDistanceRequest = iouType !== CONST.IOU.TYPE.TRACK_EXPENSE && (canUseP2PDistanceRequests ?? isExpenseChat ?? isExpenseReport ?? isFromGlobalCreate); // Allow the user to create the request if we are creating the request in global menu or the report can create the request - const isAllowedToCreateRequest = _.isEmpty(report.reportID) || ReportUtils.canCreateRequest(report, policy, iouType); + const isAllowedToCreateRequest = _.isEmpty(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType); const navigateBack = () => { Navigation.dismissModal(); }; const resetIOUTypeIfChanged = useCallback( - (newIouType) => { + (newIouType: IOURequestType) => { if (newIouType === previousIOURequestType) { return; } - if (iouType === CONST.IOU.TYPE.SPLIT && transaction.isFromGlobalCreate) { - IOU.updateMoneyRequestTypeParams(navigation.getState().routes, CONST.IOU.TYPE.REQUEST, newIouType); + if (iouType === CONST.IOU.TYPE.SPLIT && transaction?.isFromGlobalCreate) { + IOU.updateMoneyRequestTypeParams(navigation.getState()?.routes ?? [], CONST.IOU.TYPE.REQUEST, newIouType); } IOU.initMoneyRequest(reportID, policy, isFromGlobalCreate, newIouType); transactionRequestType.current = newIouType; }, - [policy, previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation, transaction.isFromGlobalCreate], + [policy, previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation, transaction?.isFromGlobalCreate], ); - if (!transaction.transactionID) { + if (!transaction?.transactionID) { // The draft transaction is initialized only after the component is mounted, // which will lead to briefly displaying the Not Found page without this loader. return ; @@ -141,7 +145,7 @@ function IOURequestStartPage({ {iouType !== CONST.IOU.TYPE.SEND ? ( @@ -161,20 +165,18 @@ function IOURequestStartPage({ } IOURequestStartPage.displayName = 'IOURequestStartPage'; -IOURequestStartPage.propTypes = propTypes; -IOURequestStartPage.defaultProps = defaultProps; -export default withOnyx({ +export default withOnyx({ report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route?.params?.reportID}`, }, policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${lodashGet(report, 'policyID')}`, + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, }, selectedTab: { key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.IOU_REQUEST_TYPE}`, }, transaction: { - key: ({route}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${lodashGet(route, 'params.transactionID', '0')}`, + key: ({route}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${route?.params.transactionID ?? 0}`, }, })(IOURequestStartPage); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index b392ee310205..8d25a39086b9 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -1,9 +1,8 @@ import {useFocusEffect} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; import lodashIsEmpty from 'lodash/isEmpty'; import React, {useCallback, useEffect, useRef} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import transactionPropTypes from '@components/transactionPropTypes'; +import type {OnyxEntry} from 'react-native-onyx'; +import { withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import * as TransactionEdit from '@libs/actions/TransactionEdit'; import compose from '@libs/compose'; @@ -13,39 +12,25 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import {getRequestType} from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; -import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; +import type { Transaction } from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; +import type { BaseTextInputRef } from '@components/TextInput/BaseTextInput/types'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; -import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import type { WithWritableReportOrNotFoundProps } from './withWritableReportOrNotFound'; -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: IOURequestStepRoutePropTypes.isRequired, +type IOURequestStepAmountOnyxProps = { + splitDraftTransaction: OnyxEntry; - /* Onyx Props */ - /** The report that the transaction belongs to */ - report: reportPropTypes, - - /** The transaction object being modified in Onyx */ - transaction: transactionPropTypes, - - /** The draft transaction that holds data to be persisted on the current transaction */ - splitDraftTransaction: transactionPropTypes, - - /** The draft transaction object being modified in Onyx */ - draftTransaction: transactionPropTypes, + draftTransaction: OnyxEntry; }; -const defaultProps = { - report: {}, - transaction: {}, - splitDraftTransaction: {}, - draftTransaction: {}, +type IOURequestStepAmountProps = IOURequestStepAmountOnyxProps & WithWritableReportOrNotFoundProps & { + transaction: OnyxEntry; }; function IOURequestStepAmount({ @@ -56,22 +41,22 @@ function IOURequestStepAmount({ transaction, splitDraftTransaction, draftTransaction, -}) { +}: IOURequestStepAmountProps) { const {translate} = useLocalize(); - const textInput = useRef(null); - const focusTimeoutRef = useRef(null); + const textInput = useRef(null); + const focusTimeoutRef = useRef(null); const isSaveButtonPressed = useRef(false); - const originalCurrency = useRef(null); + const originalCurrency = useRef(null); const iouRequestType = getRequestType(transaction); const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isEditingSplitBill = isEditing && isSplitBill; - const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction); - const {currency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction); + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction) ?? { amount: 0 }; + const {currency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? { currency: '' }; useFocusEffect( useCallback(() => { - focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); + focusTimeoutRef.current = setTimeout(() => textInput.current?.focus(), CONST.ANIMATED_TRANSITION); return () => { if (!focusTimeoutRef.current) { return; @@ -92,10 +77,10 @@ function IOURequestStepAmount({ if (isSaveButtonPressed.current) { return; } - TransactionEdit.removeBackupTransaction(transaction.transactionID || ''); + TransactionEdit.removeBackupTransaction(transaction?.transactionID ?? ''); }; } - if (transaction.originalCurrency) { + if (transaction?.originalCurrency) { originalCurrency.current = transaction.originalCurrency; } else { originalCurrency.current = currency; @@ -105,7 +90,7 @@ function IOURequestStepAmount({ if (isSaveButtonPressed.current) { return; } - IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current, true); + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current ?? '', true); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -118,10 +103,14 @@ function IOURequestStepAmount({ Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CURRENCY.getRoute(action, iouType, transactionID, reportID, backTo ? 'confirm' : '', Navigation.getActiveRouteWithoutParams())); }; + type AmountParams = { + amount: string; + }; + /** - * @param {Number} amount + * @param amount */ - const navigateToNextPage = ({amount}) => { + const navigateToNextPage = ({amount}: AmountParams) => { isSaveButtonPressed.current = true; const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); @@ -135,7 +124,7 @@ function IOURequestStepAmount({ // If a reportID exists in the report object, it's because the user started this flow from using the + button in the composer // inside a report. 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 (report.reportID) { + if (report?.reportID) { IOU.setMoneyRequestParticipantsFromReport(transactionID, report); Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID)); return; @@ -146,7 +135,7 @@ function IOURequestStepAmount({ Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID)); }; - const saveAmountAndCurrency = ({amount}) => { + const saveAmountAndCurrency = ({amount}: AmountParams) => { if (!isEditing) { navigateToNextPage({amount}); return; @@ -166,7 +155,7 @@ function IOURequestStepAmount({ return; } - IOU.updateMoneyRequestAmountAndCurrency(transactionID, reportID, currency, newAmount); + IOU.updateMoneyRequestAmountAndCurrency({transactionID, transactionThreadReportID: reportID, currency, amount: newAmount}); Navigation.dismissModal(); }; @@ -191,23 +180,20 @@ function IOURequestStepAmount({ ); } -IOURequestStepAmount.propTypes = propTypes; -IOURequestStepAmount.defaultProps = defaultProps; IOURequestStepAmount.displayName = 'IOURequestStepAmount'; export default compose( - withWritableReportOrNotFound, withFullTransactionOrNotFound, - withOnyx({ + withOnyx({ splitDraftTransaction: { key: ({route}) => { - const transactionID = lodashGet(route, 'params.transactionID', 0); + const transactionID = route.params.transactionID ?? 0; return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; }, }, draftTransaction: { key: ({route}) => { - const transactionID = lodashGet(route, 'params.transactionID', 0); + const transactionID = route.params.transactionID ?? 0; return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; }, }, diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 7b3c49e80cf5..0378102ef649 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -1,15 +1,12 @@ -import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import { withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type { OnyxEntry } from 'react-native-onyx'; +import {withOnyx } from 'react-native-onyx'; import Button from '@components/Button'; import DistanceRequestFooter from '@components/DistanceRequest/DistanceRequestFooter'; import DistanceRequestRenderItem from '@components/DistanceRequest/DistanceRequestRenderItem'; import DotIndicatorMessage from '@components/DotIndicatorMessage'; import DraggableList from '@components/DraggableList'; -import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; @@ -18,33 +15,37 @@ import compose from '@libs/compose'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as TransactionUtils from '@libs/TransactionUtils'; -import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; import * as MapboxToken from '@userActions/MapboxToken'; -import * as Transaction from '@userActions/Transaction'; +import * as TransactionAction from '@userActions/Transaction'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; +import { isEmpty, isEqual } from 'lodash'; +// eslint-disable-next-line no-restricted-imports +import type { ScrollView as RNScrollView } from 'react-native'; +import type { WaypointCollection } from '@src/types/onyx/Transaction'; +import type { RenderItemParams } from 'react-native-draggable-flatlist/lib/typescript/types'; +import type { Errors } from '@src/types/onyx/OnyxCommon'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type { WithWritableReportOrNotFoundProps } from './withWritableReportOrNotFound'; -import withWritableReportOrNotFound from './withWritableReportOrNotFound'; type IOURequestStepDistanceOnyxProps = { - transactionBackup?: OnyxEntry, -} + transactionBackup: OnyxEntry; +}; -type IOURequestStepDistanceProps = WithWritableReportOrNotFoundProps & IOURequestStepDistanceOnyxProps & { - report?: OnyxEntry, - transaction?: OnyxEntry, +type IOURequestStepDistanceProps = IOURequestStepDistanceOnyxProps & WithWritableReportOrNotFoundProps & { + transaction: OnyxEntry; }; function IOURequestStepDistance({ + report, route: { params: {action, iouType, reportID, transactionID, backTo}, }, - report, transaction, transactionBackup, }: IOURequestStepDistanceProps) { @@ -52,26 +53,26 @@ function IOURequestStepDistance({ const {isOffline} = useNetwork(); const {translate} = useLocalize(); - const [optimisticWaypoints, setOptimisticWaypoints] = useState(null); - const waypoints = useMemo(() => optimisticWaypoints || lodashGet(transaction, 'comment.waypoints', {waypoint0: {}, waypoint1: {}}), [optimisticWaypoints, transaction]); + const [optimisticWaypoints, setOptimisticWaypoints] = useState(null); + const waypoints = useMemo(() => optimisticWaypoints ?? transaction?.comment?.waypoints ?? { waypoint0: {}, waypoint1: {} }, [optimisticWaypoints, transaction]); const waypointsList = Object.keys(waypoints); const previousWaypoints = usePrevious(waypoints); const numberOfWaypoints = Object.keys(waypoints).length; const numberOfPreviousWaypoints = Object.keys(previousWaypoints).length; - const scrollViewRef = useRef(null); - const isLoadingRoute = lodashGet(transaction, 'comment.isLoading', false); - const isLoading = lodashGet(transaction, 'isLoading', false); - const hasRouteError = !!lodashGet(transaction, 'errorFields.route'); + const scrollViewRef = useRef(null); + const isLoadingRoute = transaction?.comment?.isLoading ?? false; + const isLoading = transaction?.isLoading ?? false; + const hasRouteError = !!transaction?.errorFields?.route; const hasRoute = TransactionUtils.hasRoute(transaction); const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); const previousValidatedWaypoints = usePrevious(validatedWaypoints); - const haveValidatedWaypointsChanged = !_.isEqual(previousValidatedWaypoints, validatedWaypoints); + const haveValidatedWaypointsChanged = isEqual(previousValidatedWaypoints, validatedWaypoints); const isRouteAbsentWithoutErrors = !hasRoute && !hasRouteError; - const shouldFetchRoute = (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && _.size(validatedWaypoints) > 1; + const shouldFetchRoute = (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1; const [shouldShowAtLeastTwoDifferentWaypointsError, setShouldShowAtLeastTwoDifferentWaypointsError] = useState(false); - const nonEmptyWaypointsCount = useMemo(() => _.filter(_.keys(waypoints), (key) => !_.isEmpty(waypoints[key])).length, [waypoints]); - const duplicateWaypointsError = useMemo(() => nonEmptyWaypointsCount >= 2 && _.size(validatedWaypoints) !== nonEmptyWaypointsCount, [nonEmptyWaypointsCount, validatedWaypoints]); - const atLeastTwoDifferentWaypointsError = useMemo(() => Object.keys(validatedWaypoints) < 2, [validatedWaypoints]); + const nonEmptyWaypointsCount = useMemo(() => Object.keys(waypoints).filter((key) => isEmpty(waypoints[key])).length, [waypoints]); + const duplicateWaypointsError = useMemo(() => nonEmptyWaypointsCount >= 2 && Object.keys(validatedWaypoints).length !== nonEmptyWaypointsCount, [nonEmptyWaypointsCount, validatedWaypoints]); + const atLeastTwoDifferentWaypointsError = useMemo(() => Object.keys(validatedWaypoints).length < 2, [validatedWaypoints]); const isEditing = action === CONST.IOU.ACTION.EDIT; const isCreatingNewRequest = !(backTo || isEditing); @@ -84,14 +85,14 @@ function IOURequestStepDistance({ if (isOffline || !shouldFetchRoute) { return; } - Transaction.getRoute(transactionID, validatedWaypoints, action === CONST.IOU.ACTION.CREATE); + TransactionAction.getRoute(transactionID, validatedWaypoints, action === CONST.IOU.ACTION.CREATE); }, [shouldFetchRoute, transactionID, validatedWaypoints, isOffline, action]); useEffect(() => { if (numberOfWaypoints <= numberOfPreviousWaypoints) { return; } - scrollViewRef.current.scrollToEnd({animated: true}); + scrollViewRef.current?.scrollToEnd({animated: true}); }, [numberOfPreviousWaypoints, numberOfWaypoints]); useEffect(() => { @@ -107,10 +108,11 @@ function IOURequestStepDistance({ /** * Takes the user to the page for editing a specific waypoint - * @param {Number} index of the waypoint to edit + * @param index of the waypoint to edit */ - const navigateToWaypointEditPage = (index) => { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_WAYPOINT.getRoute(action, CONST.IOU.TYPE.REQUEST, transactionID, report.reportID, index, Navigation.getActiveRouteWithoutParams())); + // eslint-disable-next-line react-hooks/exhaustive-deps + const navigateToWaypointEditPage = (index: number) => { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_WAYPOINT.getRoute(action, CONST.IOU.TYPE.REQUEST, transactionID, report?.reportID ?? '', index.toString(), Navigation.getActiveRouteWithoutParams())); }; const navigateToNextStep = useCallback(() => { @@ -122,7 +124,7 @@ function IOURequestStepDistance({ // If a reportID exists in the report object, it's because the user started this flow from using the + button in the composer // inside a report. 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 (report.reportID) { + if (report?.reportID) { IOU.setMoneyRequestParticipantsFromReport(transactionID, report); Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID)); return; @@ -139,36 +141,40 @@ function IOURequestStepDistance({ return ErrorUtils.getLatestErrorField(transaction, 'route'); } if (duplicateWaypointsError) { - return {0: translate('iou.error.duplicateWaypointsErrorMessage')}; + return {duplicateWaypointsError: translate('iou.error.duplicateWaypointsErrorMessage')} as Errors; } if (atLeastTwoDifferentWaypointsError) { - return {0: 'iou.error.atLeastTwoDifferentWaypoints'}; + return {atLeastTwoDifferentWaypointsError: 'iou.error.atLeastTwoDifferentWaypoints'} as Errors; } + return {}; + }; + + type DataParams = { + data: string[] }; const updateWaypoints = useCallback( - ({data}) => { - if (_.isEqual(waypointsList, data)) { + ({data}: DataParams) => { + if (isEqual(waypointsList, data)) { return; } - const newWaypoints = {}; + const newWaypoints: WaypointCollection = {}; let emptyWaypointIndex = -1; - _.each(data, (waypoint, index) => { - newWaypoints[`waypoint${index}`] = lodashGet(waypoints, waypoint, {}); + data.forEach((waypoint, index) => { + newWaypoints[`waypoint${index}`] = waypoints.waypoint ?? {}; // Find waypoint that BECOMES empty after dragging - if (_.isEmpty(newWaypoints[`waypoint${index}`]) && !_.isEmpty(lodashGet(waypoints, `waypoint${index}`, {}))) { + if (isEmpty(newWaypoints[`waypoint${index}`]) && !isEmpty(waypoints[`waypoint${index}`] ?? {})) { emptyWaypointIndex = index; } }); setOptimisticWaypoints(newWaypoints); - // eslint-disable-next-line rulesdir/no-thenable-actions-in-views Promise.all([ - Transaction.removeWaypoint(transaction, emptyWaypointIndex.toString(), action === CONST.IOU.ACTION.CREATE), - Transaction.updateWaypoints(transactionID, newWaypoints, action === CONST.IOU.ACTION.CREATE), + TransactionAction.removeWaypoint(transaction, emptyWaypointIndex.toString(), action === CONST.IOU.ACTION.CREATE), + TransactionAction.updateWaypoints(transactionID, newWaypoints, action === CONST.IOU.ACTION.CREATE), ]).then(() => { - setOptimisticWaypoints(undefined); + setOptimisticWaypoints(null); }); }, [transactionID, transaction, waypoints, waypointsList, action], @@ -183,15 +189,19 @@ function IOURequestStepDistance({ if (isEditing) { // If nothing was changed, simply go to transaction thread // We compare only addresses because numbers are rounded while backup - const oldWaypoints = lodashGet(transactionBackup, 'comment.waypoints', {}); - const oldAddresses = _.mapObject(oldWaypoints, (waypoint) => _.pick(waypoint, 'address')); - const addresses = _.mapObject(waypoints, (waypoint) => _.pick(waypoint, 'address')); - if (_.isEqual(oldAddresses, addresses)) { - Navigation.dismissModal(report.reportID); + const oldWaypoints = transactionBackup?.comment.waypoints ?? {}; + const oldAddresses = Object.fromEntries(Object.entries(oldWaypoints).map( + ([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}] + )); + const addresses = Object.fromEntries(Object.entries(waypoints).map( + ([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}] + )); + if (isEqual(oldAddresses, addresses)) { + Navigation.dismissModal(); return; } - IOU.updateMoneyRequestDistance(transaction.transactionID, report.reportID, waypoints); - Navigation.dismissModal(report.reportID); + IOU.updateMoneyRequestDistance({transactionID: transaction?.transactionID ?? '', transactionThreadReportID: report?.reportID ?? '', waypoints}); + Navigation.dismissModal(); return; } @@ -206,10 +216,25 @@ function IOURequestStepDistance({ navigateToNextStep, transactionBackup, waypoints, - transaction.transactionID, - report.reportID, + transaction?.transactionID, + report?.reportID, ]); + const renderItem = useCallback( + ({item, drag, isActive, getIndex}: RenderItemParams) => ( + + ), + [isLoadingRoute, navigateToWaypointEditPage, waypoints], + ); + return ( item} shouldUsePortal onDragEnd={updateWaypoints} - scrollEventThrottle={variables.distanceScrollEventThrottle} ref={scrollViewRef} - renderItem={({item, drag, isActive, getIndex}) => ( - - )} + renderItem={renderItem} ListFooterComponent={ @@ -275,11 +288,13 @@ function IOURequestStepDistance({ IOURequestStepDistance.displayName = 'IOURequestStepDistance'; export default compose( - withWritableReportOrNotFound, withFullTransactionOrNotFound, - withOnyx({ + withOnyx({ transactionBackup: { - key: (props) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${props.transactionID}`, + key: ({route}) => { + const transactionID = route.params.transactionID ?? 0; + return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; + }, }, }), )(IOURequestStepDistance); diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index 00ebba2b56c4..eca42bc14a8c 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -21,7 +21,11 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_WAYPOINT | typeof SCREENS.MONEY_REQUEST.STEP_DESCRIPTION | typeof SCREENS.MONEY_REQUEST.STEP_TAX_RATE - | typeof SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT; + | typeof SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT + | typeof SCREENS.MONEY_REQUEST.STEP_AMOUNT + | typeof SCREENS.MONEY_REQUEST.STEP_DISTANCE + | typeof SCREENS.MONEY_REQUEST.CREATE + | typeof SCREENS.MONEY_REQUEST.START; type Route = RouteProp; @@ -53,7 +57,7 @@ export default function , WithWritableReportOrNotFoundOnyxProps>({ report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params?.reportID ?? '0'}`, + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID ?? '0'}`, }, })(forwardRef(WithWritableReportOrNotFound)); } diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 1750fa61e514..5f08da7afa4a 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -2,6 +2,7 @@ import type {KeysOfUnion, ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type ONYXKEYS from '@src/ONYXKEYS'; import type CollectionDataSet from '@src/types/utils/CollectionDataSet'; +import type { IOURequestType } from '@libs/actions/IOU'; import type {Participant, Split} from './IOU'; import type * as OnyxCommon from './OnyxCommon'; import type RecentWaypoint from './RecentWaypoint'; @@ -133,7 +134,7 @@ type Transaction = OnyxCommon.OnyxValueWithOfflineFeedback< filename?: string; /** Used during the creation flow before the transaction is saved to the server */ - iouRequestType?: ValueOf; + iouRequestType?: IOURequestType; /** The original merchant name */ merchant: string; From 8e333dca6c01b5c9d8a24ce4c8eca89e96974a7b Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:31:53 +0200 Subject: [PATCH 04/14] fix prettier --- src/libs/Navigation/OnyxTabNavigator.tsx | 2 +- src/libs/Navigation/types.ts | 2 +- src/libs/TransactionUtils.ts | 2 +- src/libs/actions/IOU.ts | 14 ++++-- .../request/IOURequestRedirectToStartPage.tsx | 2 +- src/pages/iou/request/IOURequestStartPage.tsx | 12 ++--- .../iou/request/step/IOURequestStepAmount.tsx | 19 ++++---- .../request/step/IOURequestStepDistance.tsx | 48 ++++++++++--------- src/types/onyx/Transaction.ts | 2 +- 9 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/libs/Navigation/OnyxTabNavigator.tsx b/src/libs/Navigation/OnyxTabNavigator.tsx index 91c25dc82084..734d8ee2a373 100644 --- a/src/libs/Navigation/OnyxTabNavigator.tsx +++ b/src/libs/Navigation/OnyxTabNavigator.tsx @@ -5,11 +5,11 @@ import React from 'react'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {TabSelectorProps} from '@components/TabSelector/TabSelector'; +import type {IOURequestType} from '@libs/actions/IOU'; import Tab from '@userActions/Tab'; import ONYXKEYS from '@src/ONYXKEYS'; import type {SelectedTabRequest} from '@src/types/onyx'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import type { IOURequestType } from '@libs/actions/IOU'; import {defaultScreenOptions} from './OnyxTabNavigatorConfig'; type OnyxTabNavigatorOnyxProps = { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 8e147604b8e1..095a06a67675 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -11,13 +11,13 @@ import type { Route, } from '@react-navigation/native'; import type {ValueOf} from 'type-fest'; +import type {IOURequestType} from '@libs/actions/IOU'; import type CONST from '@src/CONST'; import type {Country} from '@src/CONST'; import type NAVIGATORS from '@src/NAVIGATORS'; import type {HybridAppRoute, Route as Routes} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type EXIT_SURVEY_REASON_FORM_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; -import type { IOURequestType } from '@libs/actions/IOU'; type NavigationRef = NavigationContainerRefWithCurrent; diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index a7af69d6961b..efd5a08e5bd3 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -7,12 +7,12 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {RecentWaypoint, Report, TaxRate, TaxRates, TaxRatesWithDefault, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Comment, Receipt, TransactionChanges, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import type {IOURequestType} from './actions/IOU'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; import {getCleanedTagName} from './PolicyUtils'; -import type { IOURequestType } from './actions/IOU'; let allTransactions: OnyxCollection = {}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f39190dcf8f6..f5a78ddb0eed 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2179,7 +2179,7 @@ type UpdateMoneyRequestDistanceParams = { policy?: OnyxEntry; policyTagList?: OnyxEntry; policyCategories?: OnyxEntry; -} +}; /** Updates the waypoints of a distance money request */ function updateMoneyRequestDistance({ @@ -3813,7 +3813,15 @@ function updateMoneyRequestAmountAndCurrency({ amount, currency, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy ?? null, policyTagList ?? null, policyCategories ?? null, true); + const {params, onyxData} = getUpdateMoneyRequestParams( + transactionID, + transactionThreadReportID, + transactionChanges, + policy ?? null, + policyTagList ?? null, + policyCategories ?? null, + true, + ); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY, params, onyxData); } @@ -5452,7 +5460,7 @@ function savePreferredPaymentMethod(policyID: string, paymentMethod: PaymentMeth Onyx.merge(`${ONYXKEYS.NVP_LAST_PAYMENT_METHOD}`, {[policyID]: paymentMethod}); } -export type {IOURequestType} +export type {IOURequestType}; export { setMoneyRequestParticipants, createDistanceRequest, diff --git a/src/pages/iou/request/IOURequestRedirectToStartPage.tsx b/src/pages/iou/request/IOURequestRedirectToStartPage.tsx index ea459392481e..033b3b3e2edd 100644 --- a/src/pages/iou/request/IOURequestRedirectToStartPage.tsx +++ b/src/pages/iou/request/IOURequestRedirectToStartPage.tsx @@ -6,7 +6,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type { WithWritableReportOrNotFoundProps } from './step/withWritableReportOrNotFound'; +import type {WithWritableReportOrNotFoundProps} from './step/withWritableReportOrNotFound'; type IOURequestRedirectToStartPageProps = WithWritableReportOrNotFoundProps; diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 9f14ad1d989b..cb062d22dde3 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -1,9 +1,9 @@ -import _ from 'lodash'; import {useFocusEffect, useNavigation} from '@react-navigation/native'; +import _ from 'lodash'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import { withOnyx} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import DragAndDropProvider from '@components/DragAndDrop/Provider'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; @@ -22,15 +22,15 @@ import OnyxTabNavigator, {TopTab} from '@libs/Navigation/OnyxTabNavigator'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; +import type {IOURequestType} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type { Policy, Report, SelectedTabRequest, Transaction } from '@src/types/onyx'; import type SCREENS from '@src/SCREENS'; -import type { IOURequestType } from '@userActions/IOU'; +import type {Policy, Report, SelectedTabRequest, Transaction} from '@src/types/onyx'; import IOURequestStepAmount from './step/IOURequestStepAmount'; import IOURequestStepDistance from './step/IOURequestStepDistance'; import IOURequestStepScan from './step/IOURequestStepScan'; -import type { WithWritableReportOrNotFoundProps } from './step/withWritableReportOrNotFound'; +import type {WithWritableReportOrNotFoundProps} from './step/withWritableReportOrNotFound'; type IOURequestStartPageOnyxProps = { report: OnyxEntry; @@ -38,7 +38,7 @@ type IOURequestStartPageOnyxProps = { policy: OnyxEntry; selectedTab: OnyxEntry; - + transaction: OnyxEntry; }; diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 8d25a39086b9..893da88c468c 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -2,7 +2,8 @@ import {useFocusEffect} from '@react-navigation/native'; import lodashIsEmpty from 'lodash/isEmpty'; import React, {useCallback, useEffect, useRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; -import { withOnyx} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import * as TransactionEdit from '@libs/actions/TransactionEdit'; import compose from '@libs/compose'; @@ -16,12 +17,11 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type { Transaction } from '@src/types/onyx'; import type SCREENS from '@src/SCREENS'; -import type { BaseTextInputRef } from '@components/TextInput/BaseTextInput/types'; +import type {Transaction} from '@src/types/onyx'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; -import type { WithWritableReportOrNotFoundProps } from './withWritableReportOrNotFound'; +import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type IOURequestStepAmountOnyxProps = { splitDraftTransaction: OnyxEntry; @@ -29,9 +29,10 @@ type IOURequestStepAmountOnyxProps = { draftTransaction: OnyxEntry; }; -type IOURequestStepAmountProps = IOURequestStepAmountOnyxProps & WithWritableReportOrNotFoundProps & { - transaction: OnyxEntry; -}; +type IOURequestStepAmountProps = IOURequestStepAmountOnyxProps & + WithWritableReportOrNotFoundProps & { + transaction: OnyxEntry; + }; function IOURequestStepAmount({ report, @@ -51,8 +52,8 @@ function IOURequestStepAmount({ const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isEditingSplitBill = isEditing && isSplitBill; - const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction) ?? { amount: 0 }; - const {currency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? { currency: '' }; + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction) ?? {amount: 0}; + const {currency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? {currency: ''}; useFocusEffect( useCallback(() => { diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 0378102ef649..09eb1898743c 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -1,7 +1,11 @@ +import {isEmpty, isEqual} from 'lodash'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; -import type { OnyxEntry } from 'react-native-onyx'; -import {withOnyx } from 'react-native-onyx'; +// eslint-disable-next-line no-restricted-imports +import type {ScrollView as RNScrollView} from 'react-native'; +import type {RenderItemParams} from 'react-native-draggable-flatlist/lib/typescript/types'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import DistanceRequestFooter from '@components/DistanceRequest/DistanceRequestFooter'; import DistanceRequestRenderItem from '@components/DistanceRequest/DistanceRequestRenderItem'; @@ -21,25 +25,22 @@ import * as TransactionAction from '@userActions/Transaction'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type * as OnyxTypes from '@src/types/onyx'; import type SCREENS from '@src/SCREENS'; -import { isEmpty, isEqual } from 'lodash'; -// eslint-disable-next-line no-restricted-imports -import type { ScrollView as RNScrollView } from 'react-native'; -import type { WaypointCollection } from '@src/types/onyx/Transaction'; -import type { RenderItemParams } from 'react-native-draggable-flatlist/lib/typescript/types'; -import type { Errors } from '@src/types/onyx/OnyxCommon'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; +import type {WaypointCollection} from '@src/types/onyx/Transaction'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; -import type { WithWritableReportOrNotFoundProps } from './withWritableReportOrNotFound'; +import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type IOURequestStepDistanceOnyxProps = { transactionBackup: OnyxEntry; }; -type IOURequestStepDistanceProps = IOURequestStepDistanceOnyxProps & WithWritableReportOrNotFoundProps & { - transaction: OnyxEntry; -}; +type IOURequestStepDistanceProps = IOURequestStepDistanceOnyxProps & + WithWritableReportOrNotFoundProps & { + transaction: OnyxEntry; + }; function IOURequestStepDistance({ report, @@ -54,7 +55,7 @@ function IOURequestStepDistance({ const {translate} = useLocalize(); const [optimisticWaypoints, setOptimisticWaypoints] = useState(null); - const waypoints = useMemo(() => optimisticWaypoints ?? transaction?.comment?.waypoints ?? { waypoint0: {}, waypoint1: {} }, [optimisticWaypoints, transaction]); + const waypoints = useMemo(() => optimisticWaypoints ?? transaction?.comment?.waypoints ?? {waypoint0: {}, waypoint1: {}}, [optimisticWaypoints, transaction]); const waypointsList = Object.keys(waypoints); const previousWaypoints = usePrevious(waypoints); const numberOfWaypoints = Object.keys(waypoints).length; @@ -71,7 +72,10 @@ function IOURequestStepDistance({ const shouldFetchRoute = (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1; const [shouldShowAtLeastTwoDifferentWaypointsError, setShouldShowAtLeastTwoDifferentWaypointsError] = useState(false); const nonEmptyWaypointsCount = useMemo(() => Object.keys(waypoints).filter((key) => isEmpty(waypoints[key])).length, [waypoints]); - const duplicateWaypointsError = useMemo(() => nonEmptyWaypointsCount >= 2 && Object.keys(validatedWaypoints).length !== nonEmptyWaypointsCount, [nonEmptyWaypointsCount, validatedWaypoints]); + const duplicateWaypointsError = useMemo( + () => nonEmptyWaypointsCount >= 2 && Object.keys(validatedWaypoints).length !== nonEmptyWaypointsCount, + [nonEmptyWaypointsCount, validatedWaypoints], + ); const atLeastTwoDifferentWaypointsError = useMemo(() => Object.keys(validatedWaypoints).length < 2, [validatedWaypoints]); const isEditing = action === CONST.IOU.ACTION.EDIT; const isCreatingNewRequest = !(backTo || isEditing); @@ -112,7 +116,9 @@ function IOURequestStepDistance({ */ // eslint-disable-next-line react-hooks/exhaustive-deps const navigateToWaypointEditPage = (index: number) => { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_WAYPOINT.getRoute(action, CONST.IOU.TYPE.REQUEST, transactionID, report?.reportID ?? '', index.toString(), Navigation.getActiveRouteWithoutParams())); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_WAYPOINT.getRoute(action, CONST.IOU.TYPE.REQUEST, transactionID, report?.reportID ?? '', index.toString(), Navigation.getActiveRouteWithoutParams()), + ); }; const navigateToNextStep = useCallback(() => { @@ -150,7 +156,7 @@ function IOURequestStepDistance({ }; type DataParams = { - data: string[] + data: string[]; }; const updateWaypoints = useCallback( @@ -190,12 +196,8 @@ function IOURequestStepDistance({ // If nothing was changed, simply go to transaction thread // We compare only addresses because numbers are rounded while backup const oldWaypoints = transactionBackup?.comment.waypoints ?? {}; - const oldAddresses = Object.fromEntries(Object.entries(oldWaypoints).map( - ([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}] - )); - const addresses = Object.fromEntries(Object.entries(waypoints).map( - ([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}] - )); + const oldAddresses = Object.fromEntries(Object.entries(oldWaypoints).map(([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}])); + const addresses = Object.fromEntries(Object.entries(waypoints).map(([key, waypoint]) => [key, 'address' in waypoint ? waypoint.address : {}])); if (isEqual(oldAddresses, addresses)) { Navigation.dismissModal(); return; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 5f08da7afa4a..281b6b4228ce 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -1,8 +1,8 @@ import type {KeysOfUnion, ValueOf} from 'type-fest'; +import type {IOURequestType} from '@libs/actions/IOU'; import type CONST from '@src/CONST'; import type ONYXKEYS from '@src/ONYXKEYS'; import type CollectionDataSet from '@src/types/utils/CollectionDataSet'; -import type { IOURequestType } from '@libs/actions/IOU'; import type {Participant, Split} from './IOU'; import type * as OnyxCommon from './OnyxCommon'; import type RecentWaypoint from './RecentWaypoint'; From 9e9ee8f8054406d1cf579a10f07f11f83074923f Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:42:01 +0200 Subject: [PATCH 05/14] add prop comments --- src/pages/iou/request/IOURequestStartPage.tsx | 6 +++++- src/pages/iou/request/step/IOURequestStepAmount.tsx | 3 +++ src/pages/iou/request/step/IOURequestStepDistance.tsx | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index cb062d22dde3..2327e6e61817 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -32,13 +32,17 @@ import IOURequestStepDistance from './step/IOURequestStepDistance'; import IOURequestStepScan from './step/IOURequestStepScan'; import type {WithWritableReportOrNotFoundProps} from './step/withWritableReportOrNotFound'; -type IOURequestStartPageOnyxProps = { +type IOURequestStartPageOnyxProps = + /** The report that holds the transaction */{ report: OnyxEntry; + /** The policy tied to the report */ policy: OnyxEntry; + /** The tab to select by default (whatever the user visited last) */ selectedTab: OnyxEntry; + /** The transaction being modified */ transaction: OnyxEntry; }; diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 893da88c468c..5e21616e7750 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -24,13 +24,16 @@ import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type IOURequestStepAmountOnyxProps = { + /** The draft transaction that holds data to be persisted on the current transaction */ splitDraftTransaction: OnyxEntry; + /** The draft transaction object being modified in Onyx */ draftTransaction: OnyxEntry; }; type IOURequestStepAmountProps = IOURequestStepAmountOnyxProps & WithWritableReportOrNotFoundProps & { + /** The transaction object being modified in Onyx */ transaction: OnyxEntry; }; diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 09eb1898743c..84bf8594dce2 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -34,11 +34,13 @@ import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type IOURequestStepDistanceOnyxProps = { + /** backup version of the original transaction */ transactionBackup: OnyxEntry; }; type IOURequestStepDistanceProps = IOURequestStepDistanceOnyxProps & WithWritableReportOrNotFoundProps & { + /** The transaction object being modified in Onyx */ transaction: OnyxEntry; }; From 59899dc0e259ebdb0b4d69cb7226ff09d6041c86 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:15:10 +0200 Subject: [PATCH 06/14] fix some types --- src/libs/Navigation/types.ts | 1 + src/pages/iou/request/IOURequestStartPage.tsx | 5 ++--- src/pages/iou/request/step/IOURequestStepAmount.tsx | 2 +- src/pages/iou/request/step/IOURequestStepDistance.tsx | 2 +- src/pages/iou/request/step/withFullTransactionOrNotFound.tsx | 3 +++ 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 9f56a2203021..49301cdfdb1c 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -435,6 +435,7 @@ type MoneyRequestNavigatorParamList = { iouType: ValueOf; reportID: string; iouRequestType: IOURequestType; + transactionID: string; }; [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: { iouType: ValueOf; diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 2327e6e61817..e17d6012e186 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -32,8 +32,8 @@ import IOURequestStepDistance from './step/IOURequestStepDistance'; import IOURequestStepScan from './step/IOURequestStepScan'; import type {WithWritableReportOrNotFoundProps} from './step/withWritableReportOrNotFound'; -type IOURequestStartPageOnyxProps = - /** The report that holds the transaction */{ +type IOURequestStartPageOnyxProps = { + /** The report that holds the transaction */ report: OnyxEntry; /** The policy tied to the report */ @@ -149,7 +149,6 @@ function IOURequestStartPage({ {iouType !== CONST.IOU.TYPE.SEND ? ( diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 5e21616e7750..607d0689e93f 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -187,7 +187,6 @@ function IOURequestStepAmount({ IOURequestStepAmount.displayName = 'IOURequestStepAmount'; export default compose( - withFullTransactionOrNotFound, withOnyx({ splitDraftTransaction: { key: ({route}) => { @@ -202,4 +201,5 @@ export default compose( }, }, }), + withFullTransactionOrNotFound, )(IOURequestStepAmount); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 84bf8594dce2..5a42f5713127 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -292,7 +292,6 @@ function IOURequestStepDistance({ IOURequestStepDistance.displayName = 'IOURequestStepDistance'; export default compose( - withFullTransactionOrNotFound, withOnyx({ transactionBackup: { key: ({route}) => { @@ -301,4 +300,5 @@ export default compose( }, }, }), + withFullTransactionOrNotFound, )(IOURequestStepDistance); diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx index 5f8a981ab3bb..67cf90b8c5de 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx @@ -18,6 +18,9 @@ type WithFullTransactionOrNotFoundOnyxProps = { }; type MoneyRequestRouteName = + | typeof SCREENS.MONEY_REQUEST.CREATE + | typeof SCREENS.MONEY_REQUEST.STEP_DISTANCE + | typeof SCREENS.MONEY_REQUEST.STEP_AMOUNT | typeof SCREENS.MONEY_REQUEST.STEP_WAYPOINT | typeof SCREENS.MONEY_REQUEST.STEP_DESCRIPTION | typeof SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT From e5d00bf41eed89a85e38746c747fae65c94383ec Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:50:50 +0200 Subject: [PATCH 07/14] fix types --- src/libs/Navigation/types.ts | 9 +++++++-- src/pages/iou/request/IOURequestStartPage.tsx | 2 +- src/pages/iou/request/step/IOURequestStepAmount.tsx | 10 ++++++---- src/pages/iou/request/step/IOURequestStepDistance.tsx | 4 +++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 49301cdfdb1c..364b711edffa 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -428,14 +428,19 @@ type MoneyRequestNavigatorParamList = { }; [SCREENS.MONEY_REQUEST.CREATE]: { iouType: ValueOf; - transactionID: string; reportID: string; + transactionID: string; + + // These are not used in the screen, but are needed for the navigation + // for IOURequestStepDistance and IOURequestStepAmount components + backTo: never; + action: never; }; [SCREENS.MONEY_REQUEST.START]: { iouType: ValueOf; reportID: string; - iouRequestType: IOURequestType; transactionID: string; + iouRequestType: IOURequestType; }; [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: { iouType: ValueOf; diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index e17d6012e186..5d0ed14408ad 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -98,7 +98,7 @@ function IOURequestStartPage({ const isExpenseChat = ReportUtils.isPolicyExpenseChat(report); const isExpenseReport = ReportUtils.isExpenseReport(report); - const shouldDisplayDistanceRequest = iouType !== CONST.IOU.TYPE.TRACK_EXPENSE && (canUseP2PDistanceRequests ?? isExpenseChat ?? isExpenseReport ?? isFromGlobalCreate); + const shouldDisplayDistanceRequest = iouType !== CONST.IOU.TYPE.TRACK_EXPENSE && (!!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate); // Allow the user to create the request if we are creating the request in global menu or the report can create the request const isAllowedToCreateRequest = _.isEmpty(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 607d0689e93f..27816a933bf0 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -22,6 +22,7 @@ import type {Transaction} from '@src/types/onyx'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; +import withWritableReportOrNotFound from './withWritableReportOrNotFound'; type IOURequestStepAmountOnyxProps = { /** The draft transaction that holds data to be persisted on the current transaction */ @@ -32,7 +33,7 @@ type IOURequestStepAmountOnyxProps = { }; type IOURequestStepAmountProps = IOURequestStepAmountOnyxProps & - WithWritableReportOrNotFoundProps & { + WithWritableReportOrNotFoundProps & { /** The transaction object being modified in Onyx */ transaction: OnyxEntry; }; @@ -56,7 +57,7 @@ function IOURequestStepAmount({ const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isEditingSplitBill = isEditing && isSplitBill; const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction) ?? {amount: 0}; - const {currency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? {currency: ''}; + const {currency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; useFocusEffect( useCallback(() => { @@ -168,11 +169,11 @@ function IOURequestStepAmount({ headerTitle={translate('iou.amount')} onBackButtonPress={navigateBack} testID={IOURequestStepAmount.displayName} - shouldShowWrapper={Boolean(backTo || isEditing)} + shouldShowWrapper={Boolean(backTo) || isEditing} includeSafeAreaPaddingBottom > (textInput.current = e)} @@ -202,4 +203,5 @@ export default compose( }, }), withFullTransactionOrNotFound, + withWritableReportOrNotFound, )(IOURequestStepAmount); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 5a42f5713127..429cfd938d39 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -32,6 +32,7 @@ import type {WaypointCollection} from '@src/types/onyx/Transaction'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; +import withWritableReportOrNotFound from './withWritableReportOrNotFound'; type IOURequestStepDistanceOnyxProps = { /** backup version of the original transaction */ @@ -39,7 +40,7 @@ type IOURequestStepDistanceOnyxProps = { }; type IOURequestStepDistanceProps = IOURequestStepDistanceOnyxProps & - WithWritableReportOrNotFoundProps & { + WithWritableReportOrNotFoundProps & { /** The transaction object being modified in Onyx */ transaction: OnyxEntry; }; @@ -301,4 +302,5 @@ export default compose( }, }), withFullTransactionOrNotFound, + withWritableReportOrNotFound, )(IOURequestStepDistance); From 12194120bdd32973fab9ce4756968a9df27a0119 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 9 Apr 2024 12:52:46 +0200 Subject: [PATCH 08/14] address review --- src/ROUTES.ts | 2 +- src/pages/iou/request/IOURequestStartPage.tsx | 4 +- .../iou/request/step/IOURequestStepAmount.tsx | 48 +++++++++---------- .../request/step/IOURequestStepDistance.tsx | 37 +++++++------- 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f334da7a2407..dfe44c846444 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -389,7 +389,7 @@ const ROUTES = { }, MONEY_REQUEST_STEP_WAYPOINT: { route: ':action/:iouType/waypoint/:transactionID/:reportID/:pageIndex', - getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, pageIndex = '', backTo = '') => + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID?: string, pageIndex = '', backTo = '') => getUrlWithBackToParam(`${action}/${iouType}/waypoint/${transactionID}/${reportID}/${pageIndex}`, backTo), }, // This URL is used as a redirect to one of the create tabs below. This is so that we can message users with a link diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 5d0ed14408ad..00dcf894dfa1 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -1,5 +1,4 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native'; -import _ from 'lodash'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -27,6 +26,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {Policy, Report, SelectedTabRequest, Transaction} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import IOURequestStepAmount from './step/IOURequestStepAmount'; import IOURequestStepDistance from './step/IOURequestStepDistance'; import IOURequestStepScan from './step/IOURequestStepScan'; @@ -71,7 +71,7 @@ function IOURequestStartPage({ const transactionRequestType = useRef(TransactionUtils.getRequestType(transaction)); const previousIOURequestType = usePrevious(transactionRequestType.current); const {canUseP2PDistanceRequests} = usePermissions(); - const isFromGlobalCreate = _.isEmpty(report?.reportID); + const isFromGlobalCreate = isEmptyObject(report?.reportID); useFocusEffect( useCallback(() => { diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 27816a933bf0..b29c2413a503 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -6,7 +6,6 @@ import {withOnyx} from 'react-native-onyx'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import * as TransactionEdit from '@libs/actions/TransactionEdit'; -import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; @@ -24,6 +23,10 @@ import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +type AmountParams = { + amount: string; +}; + type IOURequestStepAmountOnyxProps = { /** The draft transaction that holds data to be persisted on the current transaction */ splitDraftTransaction: OnyxEntry; @@ -108,13 +111,6 @@ function IOURequestStepAmount({ Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CURRENCY.getRoute(action, iouType, transactionID, reportID, backTo ? 'confirm' : '', Navigation.getActiveRouteWithoutParams())); }; - type AmountParams = { - amount: string; - }; - - /** - * @param amount - */ const navigateToNextPage = ({amount}: AmountParams) => { isSaveButtonPressed.current = true; const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); @@ -169,11 +165,11 @@ function IOURequestStepAmount({ headerTitle={translate('iou.amount')} onBackButtonPress={navigateBack} testID={IOURequestStepAmount.displayName} - shouldShowWrapper={Boolean(backTo) || isEditing} + shouldShowWrapper={!!backTo || isEditing} includeSafeAreaPaddingBottom > (textInput.current = e)} @@ -187,21 +183,21 @@ function IOURequestStepAmount({ IOURequestStepAmount.displayName = 'IOURequestStepAmount'; -export default compose( - withOnyx({ - splitDraftTransaction: { - key: ({route}) => { - const transactionID = route.params.transactionID ?? 0; - return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; +export default withWritableReportOrNotFound( + withFullTransactionOrNotFound( + withOnyx({ + splitDraftTransaction: { + key: ({route}) => { + const transactionID = route.params.transactionID ?? 0; + return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; + }, }, - }, - draftTransaction: { - key: ({route}) => { - const transactionID = route.params.transactionID ?? 0; - return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; + draftTransaction: { + key: ({route}) => { + const transactionID = route.params.transactionID ?? 0; + return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; + }, }, - }, - }), - withFullTransactionOrNotFound, - withWritableReportOrNotFound, -)(IOURequestStepAmount); + })(IOURequestStepAmount), + ), +); diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 429cfd938d39..8ad13893c786 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -15,7 +15,6 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -117,12 +116,14 @@ function IOURequestStepDistance({ * Takes the user to the page for editing a specific waypoint * @param index of the waypoint to edit */ - // eslint-disable-next-line react-hooks/exhaustive-deps - const navigateToWaypointEditPage = (index: number) => { - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_WAYPOINT.getRoute(action, CONST.IOU.TYPE.REQUEST, transactionID, report?.reportID ?? '', index.toString(), Navigation.getActiveRouteWithoutParams()), - ); - }; + const navigateToWaypointEditPage = useCallback( + (index: number) => { + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_WAYPOINT.getRoute(action, CONST.IOU.TYPE.REQUEST, transactionID, report?.reportID, index.toString(), Navigation.getActiveRouteWithoutParams()), + ); + }, + [action, transactionID, report?.reportID], + ); const navigateToNextStep = useCallback(() => { if (backTo) { @@ -292,15 +293,15 @@ function IOURequestStepDistance({ IOURequestStepDistance.displayName = 'IOURequestStepDistance'; -export default compose( - withOnyx({ - transactionBackup: { - key: ({route}) => { - const transactionID = route.params.transactionID ?? 0; - return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; +export default withWritableReportOrNotFound( + withFullTransactionOrNotFound( + withOnyx({ + transactionBackup: { + key: ({route}) => { + const transactionID = route.params.transactionID ?? 0; + return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; + }, }, - }, - }), - withFullTransactionOrNotFound, - withWritableReportOrNotFound, -)(IOURequestStepDistance); + })(IOURequestStepDistance), + ), +); From 7a599c2290964e998f2c466a4debca62de566baf Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 9 Apr 2024 12:53:45 +0200 Subject: [PATCH 09/14] adress review --- src/pages/iou/request/IOURequestStartPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 00dcf894dfa1..e7d235229fa8 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -101,7 +101,7 @@ function IOURequestStartPage({ const shouldDisplayDistanceRequest = iouType !== CONST.IOU.TYPE.TRACK_EXPENSE && (!!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate); // Allow the user to create the request if we are creating the request in global menu or the report can create the request - const isAllowedToCreateRequest = _.isEmpty(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType); + const isAllowedToCreateRequest = isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType); const navigateBack = () => { Navigation.dismissModal(); From 019637240064a06ba0230fdaa75522f81dc24e1f Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:12:49 +0200 Subject: [PATCH 10/14] fix issues on distance page --- src/pages/iou/request/step/IOURequestStepDistance.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 8ad13893c786..88468556bc8c 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -69,11 +69,11 @@ function IOURequestStepDistance({ const hasRoute = TransactionUtils.hasRoute(transaction); const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); const previousValidatedWaypoints = usePrevious(validatedWaypoints); - const haveValidatedWaypointsChanged = isEqual(previousValidatedWaypoints, validatedWaypoints); + const haveValidatedWaypointsChanged = !isEqual(previousValidatedWaypoints, validatedWaypoints); const isRouteAbsentWithoutErrors = !hasRoute && !hasRouteError; const shouldFetchRoute = (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1; const [shouldShowAtLeastTwoDifferentWaypointsError, setShouldShowAtLeastTwoDifferentWaypointsError] = useState(false); - const nonEmptyWaypointsCount = useMemo(() => Object.keys(waypoints).filter((key) => isEmpty(waypoints[key])).length, [waypoints]); + const nonEmptyWaypointsCount = useMemo(() => Object.keys(waypoints).filter((key) => !isEmpty(waypoints[key])).length, [waypoints]); const duplicateWaypointsError = useMemo( () => nonEmptyWaypointsCount >= 2 && Object.keys(validatedWaypoints).length !== nonEmptyWaypointsCount, [nonEmptyWaypointsCount, validatedWaypoints], From 9902923869871a3ae1b59445a9b107ed7452a177 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:27:32 +0200 Subject: [PATCH 11/14] address review --- src/pages/iou/request/step/IOURequestStepAmount.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index b29c2413a503..1936a132c665 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import lodashIsEmpty from 'lodash/isEmpty'; import React, {useCallback, useEffect, useRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -18,6 +17,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Transaction} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; @@ -59,7 +59,7 @@ function IOURequestStepAmount({ const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isEditingSplitBill = isEditing && isSplitBill; - const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction) ?? {amount: 0}; + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction) ?? {amount: 0}; const {currency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; useFocusEffect( @@ -79,7 +79,7 @@ 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.createBackupTransaction(isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction); + TransactionEdit.createBackupTransaction(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction); return () => { if (isSaveButtonPressed.current) { From d0f50ad40648ff264557d34dfc74c3ced8c7ed65 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:57:50 +0200 Subject: [PATCH 12/14] cast boolean --- src/pages/iou/request/IOURequestStartPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 7d29ca7e6468..6c69598893c5 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -98,7 +98,7 @@ function IOURequestStartPage({ const isExpenseChat = ReportUtils.isPolicyExpenseChat(report); const isExpenseReport = ReportUtils.isExpenseReport(report); - const shouldDisplayDistanceRequest = canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate; + const shouldDisplayDistanceRequest = !!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate; // Allow the user to create the request if we are creating the request in global menu or the report can create the request const isAllowedToCreateRequest = isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType); From 5fa6e389986301297686b79d0925a8a017971455 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 11 Apr 2024 17:48:39 +0200 Subject: [PATCH 13/14] fix types --- .../iou/request/step/IOURequestStepDistance.tsx | 2 +- .../step/IOURequestStepScan/index.native.tsx | 12 +----------- .../iou/request/step/IOURequestStepScan/index.tsx | 14 ++------------ .../iou/request/step/IOURequestStepScan/types.ts | 10 +++++++++- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 4e91c86ce47f..13d6f9360a84 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -128,7 +128,7 @@ function IOURequestStepDistance({ if (transactionWasSaved.current) { return; } - TransactionEdit.restoreOriginalTransactionFromBackup(lodashGet(transaction, 'transactionID', ''), action === CONST.IOU.ACTION.CREATE); + TransactionEdit.restoreOriginalTransactionFromBackup(transaction?.transactionID ?? '', action === CONST.IOU.ACTION.CREATE); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index e084a3db7422..c1b360f89e48 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -3,7 +3,6 @@ import React, {useCallback, useRef, useState} from 'react'; import {ActivityIndicator, Alert, AppState, InteractionManager, View} from 'react-native'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import {withOnyx} from 'react-native-onyx'; -import type {OnyxEntry} from 'react-native-onyx'; import {RESULTS} from 'react-native-permissions'; import Animated, {runOnJS, useAnimatedStyle, useSharedValue, withDelay, withSequence, withSpring, withTiming} from 'react-native-reanimated'; import type {Camera, PhotoFile, Point} from 'react-native-vision-camera'; @@ -26,23 +25,14 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; -import type {WithWritableReportOrNotFoundProps} from '@pages/iou/request/step/withWritableReportOrNotFound'; 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'; -import type SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; import CameraPermission from './CameraPermission'; import NavigationAwareCamera from './NavigationAwareCamera'; -import type IOURequestStepOnyxProps from './types'; - -type IOURequestStepScanProps = IOURequestStepOnyxProps & - WithWritableReportOrNotFoundProps & { - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - transaction: OnyxEntry; - }; +import type {IOURequestStepOnyxProps, IOURequestStepScanProps} from './types'; function IOURequestStepScan({ report, diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index b9c4f866d493..995e52c67fc0 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -1,6 +1,5 @@ import React, {useCallback, useContext, useEffect, useReducer, useRef, useState} from 'react'; import {ActivityIndicator, PanResponder, PixelRatio, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import type Webcam from 'react-webcam'; import Hand from '@assets/images/hand.svg'; import ReceiptUpload from '@assets/images/receipt-upload.svg'; @@ -26,23 +25,14 @@ import Navigation from '@libs/Navigation/Navigation'; import ReceiptDropUI from '@pages/iou/ReceiptDropUI'; import StepScreenDragAndDropWrapper from '@pages/iou/request/step/StepScreenDragAndDropWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; -import type {WithWritableReportOrNotFoundProps} from '@pages/iou/request/step/withWritableReportOrNotFound'; 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 ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import NavigationAwareCamera from './NavigationAwareCamera'; -import type IOURequestStepOnyxProps from './types'; - -type IOURequestStepScanProps = IOURequestStepOnyxProps & - WithWritableReportOrNotFoundProps & { - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - transaction: OnyxEntry; - }; +import type {IOURequestStepScanProps} from './types'; function IOURequestStepScan({ report, @@ -50,7 +40,7 @@ function IOURequestStepScan({ params: {action, iouType, reportID, transactionID, backTo}, }, transaction, -}: IOURequestStepScanProps) { +}: Omit) { const theme = useTheme(); const styles = useThemeStyles(); diff --git a/src/pages/iou/request/step/IOURequestStepScan/types.ts b/src/pages/iou/request/step/IOURequestStepScan/types.ts index adf3e5c81748..60af94aca12e 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/types.ts +++ b/src/pages/iou/request/step/IOURequestStepScan/types.ts @@ -1,8 +1,16 @@ import type {OnyxEntry} from 'react-native-onyx'; +import type {WithWritableReportOrNotFoundProps} from '@pages/iou/request/step/withWritableReportOrNotFound'; +import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; type IOURequestStepOnyxProps = { user: OnyxEntry; }; -export default IOURequestStepOnyxProps; +type IOURequestStepScanProps = IOURequestStepOnyxProps & + WithWritableReportOrNotFoundProps & { + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + transaction: OnyxEntry; + }; + +export type {IOURequestStepOnyxProps, IOURequestStepScanProps}; From 24e637dfeb096e87c1d6494cb49255bbe4b8e7fe Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:14:53 +0200 Subject: [PATCH 14/14] fix issue --- src/pages/iou/request/step/IOURequestStepDistance.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 13d6f9360a84..fae07ad2249c 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -197,7 +197,7 @@ function IOURequestStepDistance({ const newWaypoints: WaypointCollection = {}; let emptyWaypointIndex = -1; data.forEach((waypoint, index) => { - newWaypoints[`waypoint${index}`] = waypoints.waypoint ?? {}; + newWaypoints[`waypoint${index}`] = waypoints[waypoint] ?? {}; // Find waypoint that BECOMES empty after dragging if (isEmpty(newWaypoints[`waypoint${index}`]) && !isEmpty(waypoints[`waypoint${index}`] ?? {})) { emptyWaypointIndex = index;