From 3df8de2f6aa2b3fe417aa445dd8261dc63e19d64 Mon Sep 17 00:00:00 2001 From: RohanSasne Date: Sun, 25 Feb 2024 15:20:43 +0530 Subject: [PATCH 01/26] Convert to typescript --- ...aryForRefactorRequestConfirmationList.tsx} | 201 +++++++++++++----- 1 file changed, 153 insertions(+), 48 deletions(-) rename src/components/{MoneyTemporaryForRefactorRequestConfirmationList.js => MoneyTemporaryForRefactorRequestConfirmationList.tsx} (88%) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx similarity index 88% rename from src/components/MoneyTemporaryForRefactorRequestConfirmationList.js rename to src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 1c0f58e706a4..371cbb192fd5 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -1,16 +1,20 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; +import type {PersonalDetails, Policy, PolicyCategories, PolicyTags, Session, Transaction} from '@src/types/onyx'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import { withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import type { PolicyTaxRate } from '@src/types/onyx/PolicyTaxRates'; +import type { Participant } from '@src/types/onyx/IOU'; +import type { ValueOf } from 'type-fest'; import _ from 'underscore'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; @@ -27,6 +31,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type Route from '@src/ROUTES' import Button from './Button'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import categoryPropTypes from './categoryPropTypes'; @@ -46,6 +51,7 @@ import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; + const propTypes = { /** Callback to inform parent modal of success */ onConfirm: PropTypes.func, @@ -204,6 +210,110 @@ const defaultProps = { policyTaxRates: {}, }; + +type DropdownOption = { + value: PaymentType; + text: string; + icon: IconAsset; + iconWidth?: number; + iconHeight?: number; + iconDescription?: string; +}; + +type MoneyTemporaryForRefactorRequestConfirmationListProps = { + bankAccountRoute?: Route, + + canModifyParticipants: boolean, + + currentUserPersonalDetails: OnyxEntry, + + + hasMultipleParticipants: boolean, + + hasSmartScanFailed: boolean, + + iouAmount: number, + + iouCategory: string, + + iouComment: string, + + iouCreated: string, + + iouCurrencyCode: string, + + iouIsBillable: boolean, + + iouMerchant: string, + + iouTag: string, + + iouType: ValueOf, + + isDistanceRequest: boolean, + + isEditingSplitBill: boolean, + + isPolicyExpenseChat: boolean, + + isReadOnly: boolean, + + isScanRequest: boolean, + + listStyles: Record | Array>, + + mileageRate: { + /** Unit used to represent distance */ + unit: typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES | typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS; + + /** Rate used to calculate the distance request amount */ + rate: number; + + /** The currency of the rate */ + currency: string; + }; + + onConfirm: () => void, + + onSelectParticipant: (option: string) => void, + + onSendMoney: (paymentMethod: string) => void, + + onToggleBillable: () => void, + + policyCategories: OnyxEntry, + + policyID: string, + + policyTags: OnyxEntry, + + receiptFilename: string, + + receiptPath: string, + + reportID: string, + + session: Session, + + shouldShowSmartScanFields: boolean, + + transaction: Transaction, + + policyTaxRates: PolicyTaxRate, + + reportActionID: string, + + policy: OnyxEntry + + + + + + + +} + + function MoneyTemporaryForRefactorRequestConfirmationList({ bankAccountRoute, canModifyParticipants, @@ -244,7 +354,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ shouldShowSmartScanFields, transaction, policyTaxRates, -}) { +}: MoneyTemporaryForRefactorRequestConfirmationListProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); @@ -255,12 +365,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeSend = iouType === CONST.IOU.TYPE.SEND; const {unit, rate, currency} = mileageRate; - const distance = lodashGet(transaction, 'routes.route0.distance', 0); + const distance = transaction?.routes?.route0?.distance ?? 0; const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories))); - + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || (policyCategories && OptionsListUtils.hasEnabledOptions(Object.values(policyCategories)))); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -272,17 +381,18 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // Fetches the first tag list of the policy const policyTag = PolicyUtils.getTag(policyTags); - const policyTagList = lodashGet(policyTag, 'tags', {}); - const policyTagListName = lodashGet(policyTag, 'name', translate('common.tag')); + const policyTagList = policyTags?.tags ?? {}; + + const policyTagListName = policyTag?.name ?? translate('common.tag'); // A flag for showing the tags field - const shouldShowTags = isPolicyExpenseChat && OptionsListUtils.hasEnabledOptions(_.values(policyTagList)); + const shouldShowTags = isPolicyExpenseChat && (policyTagList && OptionsListUtils.hasEnabledOptions(Object.values(policyTagList))); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy && policy.isTaxTrackingEnabled; + const shouldShowTax = isPolicyExpenseChat && policy?.isTaxTrackingEnabled; // A flag for showing the billable field - const shouldShowBillable = !lodashGet(policy, 'disabledFields.defaultBillable', true); + const shouldShowBillable = !(policy?.disabledFields?.defaultBillable ?? true); const hasRoute = TransactionUtils.hasRoute(transaction); const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate); @@ -294,9 +404,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); - const defaultTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; + const defaultTaxKey = policyTaxRates?.defaultExternalID; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const defaultTaxName = (defaultTaxKey && `${policyTaxRates?.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const taxRateTitle = (transaction?.taxRate?.text) || defaultTaxName; const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -317,8 +429,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isMerchantRequired = isPolicyExpenseChat && !isScanRequest && shouldShowMerchant; - const isCategoryRequired = canUseViolations && lodashGet(policy, 'requiresCategory', false); - const isTagRequired = canUseViolations && lodashGet(policy, 'requiresTag', false); + const isCategoryRequired = canUseViolations && (policy?.requiresCategory ?? false); + const isTagRequired = canUseViolations && (policy?.requiresTag ?? false); useEffect(() => { if ((!isMerchantRequired && isMerchantEmpty) || !merchantError) { @@ -364,7 +476,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ * @returns {Array} */ const getParticipantsWithAmount = useCallback( - (participantsList) => { + (participantsList: Participant[]) => { const amount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode); return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, amount > 0 ? CurrencyUtils.convertToDisplayString(amount, iouCurrencyCode) : ''); }, @@ -383,7 +495,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) { text = translate('iou.request'); if (iouAmount !== 0) { - text = translate('iou.requestAmount', {amount: formattedAmount}); + text = translate('iou.requestAmount', {amount: Number(formattedAmount)}); } } else { const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.requestAmount'; @@ -397,7 +509,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ]; }, [isTypeSplit, isTypeRequest, iouType, iouAmount, receiptPath, formattedAmount, isDistanceRequestWithPendingRoute, translate]); - const selectedParticipants = useMemo(() => _.filter(pickedParticipants, (participant) => participant.selected), [pickedParticipants]); + const selectedParticipants = useMemo(() => pickedParticipants.filter(participant => participant.selected), [pickedParticipants]); const personalDetailsOfPayee = useMemo(() => payeePersonalDetails || currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); const userCanModifyParticipants = useRef(!isReadOnly && canModifyParticipants && hasMultipleParticipants); useEffect(() => { @@ -407,17 +519,18 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const optionSelectorSections = useMemo(() => { const sections = []; - const unselectedParticipants = _.filter(pickedParticipants, (participant) => !participant.selected); + const unselectedParticipants = pickedParticipants.filter(participant => !participant.selected); if (hasMultipleParticipants) { const formattedSelectedParticipants = getParticipantsWithAmount(selectedParticipants); let formattedParticipantsList = _.union(formattedSelectedParticipants, unselectedParticipants); if (!userCanModifyParticipants.current) { - formattedParticipantsList = _.map(formattedParticipantsList, (participant) => ({ + formattedParticipantsList = formattedParticipantsList.map(participant => ({ ...participant, isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID), })); } + const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode, true); const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( @@ -441,10 +554,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, ); } else { - const formattedSelectedParticipants = _.map(selectedParticipants, (participant) => ({ + const formattedSelectedParticipants = selectedParticipants.map(participant => ({ ...participant, isDisabled: !participant.isPolicyExpenseChat && ReportUtils.isOptimisticPersonalDetail(participant.accountID), - })); + })); sections.push({ title: translate('common.to'), data: formattedSelectedParticipants, @@ -493,7 +606,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ * @param {Object} option */ const selectParticipant = useCallback( - (option) => { + (option: string) => { // Return early if selected option is currently logged in user. if (option.accountID === accountID) { return; @@ -517,11 +630,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } }; - /** - * @param {String} paymentMethod - */ + const confirm = useCallback( - (paymentMethod) => { + (paymentMethod?: PaymentMethodType) => { if (_.isEmpty(selectedParticipants)) { return; } @@ -608,7 +719,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ confirm(value)} + onPress={(event, value) => confirm(value)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} @@ -790,7 +901,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} @@ -837,15 +948,14 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, ]; - const primaryFields = _.map( - _.filter(classifiedFields, (classifiedField) => classifiedField.shouldShow && !classifiedField.isSupplementary), - (primaryField) => primaryField.item, - ); + const primaryFields = classifiedFields + .filter(classifiedField => classifiedField.shouldShow && !classifiedField.isSupplementary) + .map(primaryField => primaryField.item); + +const supplementaryFields = classifiedFields + .filter(classifiedField => classifiedField.shouldShow && classifiedField.isSupplementary) + .map(supplementaryField => supplementaryField.item); - const supplementaryFields = _.map( - _.filter(classifiedFields, (classifiedField) => classifiedField.shouldShow && classifiedField.isSupplementary), - (supplementaryField) => supplementaryField.item, - ); const {image: receiptImage, thumbnail: receiptThumbnail} = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction, receiptPath, receiptFilename) : {}; return ( @@ -916,13 +1026,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); } -MoneyTemporaryForRefactorRequestConfirmationList.propTypes = propTypes; -MoneyTemporaryForRefactorRequestConfirmationList.defaultProps = defaultProps; MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyTemporaryForRefactorRequestConfirmationList'; -export default compose( - withCurrentUserPersonalDetails, - withOnyx({ +export default withOnyx({ session: { key: ONYXKEYS.SESSION, }, @@ -942,5 +1048,4 @@ export default compose( policyTaxRates: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, }, - }), -)(MoneyTemporaryForRefactorRequestConfirmationList); + })(MoneyTemporaryForRefactorRequestConfirmationList); From a8e69fdd471b6d4f3ae5f890fda0a434ccdd0196 Mon Sep 17 00:00:00 2001 From: RohanSasne Date: Tue, 27 Feb 2024 00:45:52 +0530 Subject: [PATCH 02/26] Fix RBR error --- ...eyTemporaryForRefactorRequestConfirmationList.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index d76b7499d36b..d7bd650279bc 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -295,7 +295,7 @@ type MoneyTemporaryForRefactorRequestConfirmationListProps = { transaction: Transaction, - policyTaxRates: PolicyTaxRate, + policyTaxRates: Policy, reportActionID: string, @@ -380,7 +380,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowTags = useMemo(() => isPolicyExpenseChat && OptionsListUtils.hasEnabledTags(policyTagLists), [isPolicyExpenseChat, policyTagLists]); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy && lodashGet(policy, 'tax.trackingEnabled', policy.isTaxTrackingEnabled); + const shouldShowTax = isPolicyExpenseChat && policy && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled); // A flag for showing the billable field const shouldShowBillable = !(policy?.disabledFields?.defaultBillable ?? true); @@ -755,7 +755,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? translate('common.error.enterAmount') : ''} /> ), @@ -818,7 +818,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={merchantError ? translate('common.error.fieldRequired') : ''} rightLabel={isMerchantRequired ? translate('common.required') : ''} /> @@ -842,7 +842,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> ), @@ -872,7 +872,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ shouldShow: shouldShowCategories, isSupplementary: !isCategoryRequired, }, - ..._.map(policyTagLists, ({name}, index) => ({ + ...policyTagLists.map(({ name }, index) => ({ item: ( Date: Tue, 27 Feb 2024 01:56:35 +0530 Subject: [PATCH 03/26] Migrate to typescript --- ...raryForRefactorRequestConfirmationList.tsx | 229 ++++++++---------- 1 file changed, 104 insertions(+), 125 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 858b338d652d..8d996fcaad03 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -1,20 +1,17 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; +import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {Fragment, useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; -import type {PersonalDetails, Policy, PolicyCategories, PolicyTags, Session, Transaction} from '@src/types/onyx'; import {View} from 'react-native'; -import { withOnyx} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import type { PolicyTaxRate } from '@src/types/onyx/PolicyTaxRates'; -import type { Participant } from '@src/types/onyx/IOU'; -import type { ValueOf } from 'type-fest'; import _ from 'underscore'; -import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; @@ -32,7 +29,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type Route from '@src/ROUTES' +import type * as OnyxTypes from '@src/types/onyx'; import Button from './Button'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import categoryPropTypes from './categoryPropTypes'; @@ -51,7 +48,6 @@ import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; - const propTypes = { /** Callback to inform parent modal of success */ onConfirm: PropTypes.func, @@ -206,109 +202,91 @@ const defaultProps = { isPolicyExpenseChat: false, }; +type MoneyTemporaryForRefactorRequestConfirmationListOnyxProps = { + session: OnyxEntry; -type DropdownOption = { - value: PaymentType; - text: string; - icon: IconAsset; - iconWidth?: number; - iconHeight?: number; - iconDescription?: string; -}; - -type MoneyTemporaryForRefactorRequestConfirmationListProps = { - bankAccountRoute?: Route, - - canModifyParticipants: boolean, - - currentUserPersonalDetails: OnyxEntry, - + policyCategories: OnyxEntry; - hasMultipleParticipants: boolean, + policyTags: OnyxEntry; - hasSmartScanFailed: boolean, - - iouAmount: number, - - iouCategory: string, - - iouComment: string, - - iouCreated: string, - - iouCurrencyCode: string, + policy: OnyxEntry; +}; - iouIsBillable: boolean, +type MoneyTemporaryForRefactorRequestConfirmationListProps = { + onConfirm: () => void; - iouMerchant: string, + onSendMoney: () => void; - iouTag: string, + onSelectParticipant: () => void; - iouType: ValueOf, + hasMultipleParticipants: boolean; - isDistanceRequest: boolean, + iouAmount: number; - isEditingSplitBill: boolean, + iouComment: string; - isPolicyExpenseChat: boolean, + iouCurrencyCode: string; - isReadOnly: boolean, + iouType: string; - isScanRequest: boolean, + iouCreated: string; - listStyles: Record | Array>, + iouMerchant: string; - mileageRate: { - /** Unit used to represent distance */ - unit: typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES | typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS; - - /** Rate used to calculate the distance request amount */ - rate: number; - - /** The currency of the rate */ - currency: string; - }; + iouCategory: string; - onConfirm: () => void, + iouIsBillable: boolean; - onSelectParticipant: (option: string) => void, + onToggleBillable: () => void; - onSendMoney: (paymentMethod: string) => void, + selectedParticipants: optionPropTypes[]; - onToggleBillable: () => void, + payeePersonalDetails: optionPropTypes; - policyCategories: OnyxEntry, + canModifyParticipants: boolean; - policyID: string, + isReadOnly: boolean; - policyTags: OnyxEntry, + isScanRequest: boolean; - receiptFilename: string, + bankAccountRoute: string; - receiptPath: string, + session: OnyxTypes.Session; - reportID: string, + policyCategories: OnyxTypes.PolicyCategories; - session: Session, + policyTags: OnyxTypes.PolicyTags; - shouldShowSmartScanFields: boolean, + policy: OnyxTypes.Policy; - transaction: Transaction, + receiptPath: string; - policyTaxRates: Policy, + receiptFilename: string; - reportActionID: string, + listStyles: object | object[]; - policy: OnyxEntry + transactionID: string; + mileageRate: { + unit: string; + rate: number; + currency: string; + }; + isDistanceRequest: boolean; + isEditingSplitBill: boolean; + shouldShowSmartScanFields: boolean; + isPolicyExpenseChat: boolean; + transaction: OnyxTypes.Transaction; -} + policyID: string; +}; +type MoneyTemporaryForRefactorRequestConfirmationList = MoneyTemporaryForRefactorRequestConfirmationListOnyxProps & MoneyTemporaryForRefactorRequestConfirmationListProps; function MoneyTemporaryForRefactorRequestConfirmationList({ bankAccountRoute, @@ -348,9 +326,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, - policyTaxRates, -}: MoneyTemporaryForRefactorRequestConfirmationListProps) { - +}: MoneyTemporaryForRefactorRequestConfirmationList) { const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); @@ -361,12 +337,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeSend = iouType === CONST.IOU.TYPE.SEND; const {unit, rate, currency} = mileageRate; - const distance = transaction?.routes?.route0?.distance ?? 0; + const distance = lodashGet(transaction, 'routes.route0.distance', 0); const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; const taxRates = lodashGet(policy, 'taxRates', {}); // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || (policyCategories && OptionsListUtils.hasEnabledOptions(Object.values(policyCategories)))); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories))); + // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -382,10 +359,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowTags = useMemo(() => isPolicyExpenseChat && OptionsListUtils.hasEnabledTags(policyTagLists), [isPolicyExpenseChat, policyTagLists]); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled); + const shouldShowTax = isPolicyExpenseChat && policy && lodashGet(policy, 'tax.trackingEnabled', policy.isTaxTrackingEnabled); // A flag for showing the billable field - const shouldShowBillable = !(policy?.disabledFields?.defaultBillable ?? true); + const shouldShowBillable = !lodashGet(policy, 'disabledFields.defaultBillable', true); const hasRoute = TransactionUtils.hasRoute(transaction); const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate); @@ -397,7 +374,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); - const defaultTaxKey = taxRates.defaultExternalID; const defaultTaxName = (defaultTaxKey && `${taxRates.taxes[defaultTaxKey].name} (${taxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; @@ -421,8 +397,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isMerchantRequired = isPolicyExpenseChat && !isScanRequest && shouldShowMerchant; - const isCategoryRequired = canUseViolations && (policy?.requiresCategory ?? false); - const isTagRequired = canUseViolations && (policy?.requiresTag ?? false); + const isCategoryRequired = canUseViolations && lodashGet(policy, 'requiresCategory', false); + const isTagRequired = canUseViolations && lodashGet(policy, 'requiresTag', false); useEffect(() => { if ((!isMerchantRequired && isMerchantEmpty) || !merchantError) { @@ -468,7 +444,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ * @returns {Array} */ const getParticipantsWithAmount = useCallback( - (participantsList: Participant[]) => { + (participantsList) => { const amount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode); return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, amount > 0 ? CurrencyUtils.convertToDisplayString(amount, iouCurrencyCode) : ''); }, @@ -487,7 +463,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) { text = translate('iou.request'); if (iouAmount !== 0) { - text = translate('iou.requestAmount', {amount: Number(formattedAmount)}); + text = translate('iou.requestAmount', {amount: formattedAmount}); } } else { const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.requestAmount'; @@ -501,7 +477,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ]; }, [isTypeSplit, isTypeRequest, iouType, iouAmount, receiptPath, formattedAmount, isDistanceRequestWithPendingRoute, translate]); - const selectedParticipants = useMemo(() => pickedParticipants.filter(participant => participant.selected), [pickedParticipants]); + const selectedParticipants = useMemo(() => _.filter(pickedParticipants, (participant) => participant.selected), [pickedParticipants]); const personalDetailsOfPayee = useMemo(() => payeePersonalDetails || currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); const userCanModifyParticipants = useRef(!isReadOnly && canModifyParticipants && hasMultipleParticipants); useEffect(() => { @@ -511,18 +487,17 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const optionSelectorSections = useMemo(() => { const sections = []; - const unselectedParticipants = pickedParticipants.filter(participant => !participant.selected); + const unselectedParticipants = _.filter(pickedParticipants, (participant) => !participant.selected); if (hasMultipleParticipants) { const formattedSelectedParticipants = getParticipantsWithAmount(selectedParticipants); let formattedParticipantsList = _.union(formattedSelectedParticipants, unselectedParticipants); if (!userCanModifyParticipants.current) { - formattedParticipantsList = formattedParticipantsList.map(participant => ({ + formattedParticipantsList = _.map(formattedParticipantsList, (participant) => ({ ...participant, isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID), })); } - const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode, true); const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( @@ -546,10 +521,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, ); } else { - const formattedSelectedParticipants = selectedParticipants.map(participant => ({ + const formattedSelectedParticipants = _.map(selectedParticipants, (participant) => ({ ...participant, isDisabled: !participant.isPolicyExpenseChat && ReportUtils.isOptimisticPersonalDetail(participant.accountID), - })); + })); sections.push({ title: translate('common.to'), data: formattedSelectedParticipants, @@ -598,7 +573,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ * @param {Object} option */ const selectParticipant = useCallback( - (option: string) => { + (option) => { // Return early if selected option is currently logged in user. if (option.accountID === accountID) { return; @@ -622,9 +597,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } }; - + /** + * @param {String} paymentMethod + */ const confirm = useCallback( - (paymentMethod?: PaymentMethodType) => { + (paymentMethod) => { if (_.isEmpty(selectedParticipants)) { return; } @@ -711,7 +688,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ confirm(value)} + onPress={(_event, value) => confirm(value)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} @@ -756,7 +733,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? translate('common.error.enterAmount') : ''} /> ), @@ -819,7 +796,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} error={merchantError ? translate('common.error.fieldRequired') : ''} rightLabel={isMerchantRequired ? translate('common.required') : ''} /> @@ -843,7 +820,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> ), @@ -873,7 +850,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ shouldShow: shouldShowCategories, isSupplementary: !isCategoryRequired, }, - ...policyTagLists.map(({ name }, index) => ({ + ..._.map(policyTagLists, ({name}, index) => ({ item: ( classifiedField.shouldShow && !classifiedField.isSupplementary) - .map(primaryField => primaryField.item); - -const supplementaryFields = classifiedFields - .filter(classifiedField => classifiedField.shouldShow && classifiedField.isSupplementary) - .map(supplementaryField => supplementaryField.item); + const primaryFields = _.map( + _.filter(classifiedFields, (classifiedField) => classifiedField.shouldShow && !classifiedField.isSupplementary), + (primaryField) => primaryField.item, + ); + const supplementaryFields = _.map( + _.filter(classifiedFields, (classifiedField) => classifiedField.shouldShow && classifiedField.isSupplementary), + (supplementaryField) => supplementaryField.item, + ); const {image: receiptImage, thumbnail: receiptThumbnail} = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction, receiptPath, receiptFilename) : {}; return ( @@ -1023,24 +1001,25 @@ const supplementaryFields = classifiedFields ); } +MoneyTemporaryForRefactorRequestConfirmationList.propTypes = propTypes; +MoneyTemporaryForRefactorRequestConfirmationList.defaultProps = defaultProps; MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyTemporaryForRefactorRequestConfirmationList'; -export default withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - policyCategories: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - }, - policyTags: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - }, - mileageRate: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - selector: DistanceRequestUtils.getDefaultMileageRate, - }, - policy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, - }), -)(MoneyTemporaryForRefactorRequestConfirmationList); +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, + policyCategories: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + }, + policyTags: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + }, + mileageRate: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: DistanceRequestUtils.getDefaultMileageRate, + }, + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + }, +})(MoneyTemporaryForRefactorRequestConfirmationList); From 1029becfd23a5455c56045783c7212620486909a Mon Sep 17 00:00:00 2001 From: RohanSasne Date: Tue, 27 Feb 2024 02:18:35 +0530 Subject: [PATCH 04/26] Working with onyx --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 8d996fcaad03..d505586f9571 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -245,6 +245,8 @@ type MoneyTemporaryForRefactorRequestConfirmationListProps = { canModifyParticipants: boolean; + hasSmartScanFailed: boolean; + isReadOnly: boolean; isScanRequest: boolean; @@ -284,6 +286,10 @@ type MoneyTemporaryForRefactorRequestConfirmationListProps = { transaction: OnyxTypes.Transaction; policyID: string; + + reportID: string; + + reportActionID: string; }; type MoneyTemporaryForRefactorRequestConfirmationList = MoneyTemporaryForRefactorRequestConfirmationListOnyxProps & MoneyTemporaryForRefactorRequestConfirmationListProps; From 66a4e318399f1040dfa517fa936f32f660299e6a Mon Sep 17 00:00:00 2001 From: RohanSasne Date: Tue, 27 Feb 2024 02:50:47 +0530 Subject: [PATCH 05/26] fix props --- ...TemporaryForRefactorRequestConfirmationList.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index d505586f9571..982883a349e1 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -270,7 +270,7 @@ type MoneyTemporaryForRefactorRequestConfirmationListProps = { transactionID: string; mileageRate: { - unit: string; + unit: typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES | typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS; rate: number; currency: string; }; @@ -292,7 +292,7 @@ type MoneyTemporaryForRefactorRequestConfirmationListProps = { reportActionID: string; }; -type MoneyTemporaryForRefactorRequestConfirmationList = MoneyTemporaryForRefactorRequestConfirmationListOnyxProps & MoneyTemporaryForRefactorRequestConfirmationListProps; +type MoneyTemporaryForRefactorRequestConfirmation = MoneyTemporaryForRefactorRequestConfirmationListOnyxProps & MoneyTemporaryForRefactorRequestConfirmationListProps; function MoneyTemporaryForRefactorRequestConfirmationList({ bankAccountRoute, @@ -332,7 +332,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, -}: MoneyTemporaryForRefactorRequestConfirmationList) { +}: MoneyTemporaryForRefactorRequestConfirmation) { const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); @@ -345,10 +345,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const {unit, rate, currency} = mileageRate; const distance = lodashGet(transaction, 'routes.route0.distance', 0); const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; - const taxRates = lodashGet(policy, 'taxRates', {}); + const taxRates = policy?.taxRates ?? {}; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(_.values(policyCategories))); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || (OptionsListUtils.hasEnabledOptions(Object.values(policyCategories)))); + // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -1007,8 +1008,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); } -MoneyTemporaryForRefactorRequestConfirmationList.propTypes = propTypes; -MoneyTemporaryForRefactorRequestConfirmationList.defaultProps = defaultProps; + MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyTemporaryForRefactorRequestConfirmationList'; export default withOnyx({ From aae1a41d56ef7f89ba1569620f0a611d98923a72 Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 29 Feb 2024 04:42:17 +0530 Subject: [PATCH 06/26] Migrate to typescript --- ...raryForRefactorRequestConfirmationList.tsx | 533 +++++++----------- 1 file changed, 217 insertions(+), 316 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 161f1ff27897..c6248165b797 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -1,338 +1,231 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {Fragment, useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import _ from 'underscore'; +import {withOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Log from '@libs/Log'; +import playSound, {SOUNDS} from '@libs/Sound'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import playSound, {SOUNDS} from '@libs/Sound'; import * as TransactionUtils from '@libs/TransactionUtils'; -import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; -import Button from './Button'; +import type {Participant} from '@src/types/onyx/IOU'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import type {MileageRate} from '@src/types/onyx/Policy'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; -import categoryPropTypes from './categoryPropTypes'; import ConfirmedRoute from './ConfirmedRoute'; import FormHelpMessage from './FormHelpMessage'; -import * as Expensicons from './Icon/Expensicons'; import Image from './Image'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; -import optionPropTypes from './optionPropTypes'; import OptionsSelector from './OptionsSelector'; import ReceiptEmptyState from './ReceiptEmptyState'; import SettlementButton from './SettlementButton'; +import ShowMoreButton from './ShowMoreButton'; import Switch from './Switch'; -import tagPropTypes from './tagPropTypes'; import Text from './Text'; -import transactionPropTypes from './transactionPropTypes'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; - -const propTypes = { - /** Callback to inform parent modal of success */ - onConfirm: PropTypes.func, - - /** Callback to parent modal to send money */ - onSendMoney: PropTypes.func, - - /** Callback to inform a participant is selected */ - onSelectParticipant: PropTypes.func, - - /** Should we request a single or multiple participant selection from user */ - hasMultipleParticipants: PropTypes.bool.isRequired, - - /** IOU amount */ - iouAmount: PropTypes.number.isRequired, - - /** IOU comment */ - iouComment: PropTypes.string, - - /** IOU currency */ - iouCurrencyCode: PropTypes.string, - - /** IOU type */ - iouType: PropTypes.string, - - /** IOU date */ - iouCreated: PropTypes.string, - - /** IOU merchant */ - iouMerchant: PropTypes.string, - - /** IOU category */ - iouCategory: PropTypes.string, - - /** IOU isBillable */ - iouIsBillable: PropTypes.bool, - - /** Callback to toggle the billable state */ - onToggleBillable: PropTypes.func, - - /** Selected participants from MoneyRequestModal with login / accountID */ - selectedParticipants: PropTypes.arrayOf(optionPropTypes).isRequired, - - /** Payee of the money request with login */ - payeePersonalDetails: optionPropTypes, - - /** Can the participants be modified or not */ - canModifyParticipants: PropTypes.bool, - - /** Should the list be read only, and not editable? */ - isReadOnly: PropTypes.bool, - - /** Whether the money request is a scan request */ - isScanRequest: PropTypes.bool, - - /** Depending on expense report or personal IOU report, respective bank account route */ - bankAccountRoute: PropTypes.string, - - ...withCurrentUserPersonalDetailsPropTypes, - - /** Current user session */ - session: PropTypes.shape({ - email: PropTypes.string.isRequired, - }), - - /** The policyID of the request */ - policyID: PropTypes.string, - - /** The reportID of the request */ - reportID: PropTypes.string, +import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; +import Button from './Button'; - /** File path of the receipt */ - receiptPath: PropTypes.string, +type DropdownOption = { + text: string; + value: DeepValueOf; +}; - /** File name of the receipt */ - receiptFilename: PropTypes.string, +type Option = Partial; - /** List styles for OptionsSelector */ - listStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), +type CategorySection = { + title: string | undefined; + shouldShow: boolean; + indexOffset: number; + data: Option[]; +}; - /** ID of the transaction that represents the money request */ - transactionID: PropTypes.string, +type MoneyRequestConfirmationListOnyxProps = { + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + iou: OnyxEntry; /** Unit and rate used for if the money request is a distance request */ - mileageRate: PropTypes.shape({ - /** Unit used to represent distance */ - unit: PropTypes.oneOf([CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS]), - - /** Rate used to calculate the distance request amount */ - rate: PropTypes.number, - - /** The currency of the rate */ - currency: PropTypes.string, - }), + mileageRate: OnyxEntry; - /** Whether the money request is a distance request */ - isDistanceRequest: PropTypes.bool, - - /** Whether we're editing a split bill */ - isEditingSplitBill: PropTypes.bool, - - /** Whether we should show the amount, date, and merchant fields. */ - shouldShowSmartScanFields: PropTypes.bool, - - /** A flag for verifying that the current report is a sub-report of a workspace chat */ - isPolicyExpenseChat: PropTypes.bool, - - /* Onyx Props */ /** Collection of categories attached to a policy */ - policyCategories: PropTypes.objectOf(categoryPropTypes), - - /** Collection of tags attached to a policy */ - policyTags: tagPropTypes, - - /* Onyx Props */ - /** The policy of the report */ - policy: policyPropTypes.policy, - - /** Transaction that represents the money request */ - transaction: transactionPropTypes, -}; - -const defaultProps = { - onConfirm: () => {}, - onSendMoney: () => {}, - onSelectParticipant: () => {}, - iouType: CONST.IOU.TYPE.REQUEST, - iouCategory: '', - iouIsBillable: false, - onToggleBillable: () => {}, - payeePersonalDetails: null, - canModifyParticipants: false, - isReadOnly: false, - bankAccountRoute: '', - session: { - email: null, - }, - policyID: '', - reportID: '', - ...withCurrentUserPersonalDetailsDefaultProps, - receiptPath: '', - receiptFilename: '', - listStyles: [], - policy: {}, - policyCategories: {}, - policyTags: {}, - transactionID: '', - transaction: {}, - mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'}, - isDistanceRequest: false, - shouldShowSmartScanFields: true, - isPolicyExpenseChat: false, -}; - -type MoneyTemporaryForRefactorRequestConfirmationListOnyxProps = { - session: OnyxEntry; - policyCategories: OnyxEntry; + /** Collection of tags attached to a policy */ policyTags: OnyxEntry; + /** The policy of root parent report */ policy: OnyxEntry; -}; - -type MoneyTemporaryForRefactorRequestConfirmationListProps = { - onConfirm: () => void; - - onSendMoney: () => void; - - onSelectParticipant: () => void; - hasMultipleParticipants: boolean; + /** The session of the logged in user */ + session: OnyxEntry; +}; - iouAmount: number; +type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & + WithCurrentUserPersonalDetailsProps & { + /** Callback to inform parent modal of success */ + onConfirm?: (selectedParticipants: Participant[]) => void; - iouComment: string; + /** Callback to parent modal to send money */ + onSendMoney?: (paymentMethod: PaymentMethodType) => void; - iouCurrencyCode: string; + /** Callback to inform a participant is selected */ + onSelectParticipant?: (option: Participant) => void; - iouType: string; + /** Should we request a single or multiple participant selection from user */ + hasMultipleParticipants: boolean; - iouCreated: string; + /** IOU amount */ + iouAmount: number; - iouMerchant: string; + /** IOU comment */ + iouComment?: string; - iouCategory: string; + /** IOU currency */ + iouCurrencyCode?: string; - iouIsBillable: boolean; + /** IOU type */ + iouType?: ValueOf; - onToggleBillable: () => void; + /** IOU date */ + iouCreated?: string; - selectedParticipants: optionPropTypes[]; + /** IOU merchant */ + iouMerchant?: string; - payeePersonalDetails: optionPropTypes; + /** IOU Category */ + iouCategory?: string; - canModifyParticipants: boolean; + /** IOU Tag */ + iouTag?: string; - hasSmartScanFailed: boolean; + /** IOU isBillable */ + iouIsBillable?: boolean; - isReadOnly: boolean; + /** Callback to toggle the billable state */ + onToggleBillable?: () => void; - isScanRequest: boolean; + /** Selected participants from MoneyRequestModal with login / accountID */ + pickedParticipants: Participant[]; - bankAccountRoute: string; + /** Payee of the money request with login */ + payeePersonalDetails?: OnyxEntry; - session: OnyxTypes.Session; + /** Can the participants be modified or not */ + canModifyParticipants?: boolean; - policyCategories: OnyxTypes.PolicyCategories; + /** Should the list be read only, and not editable? */ + isReadOnly?: boolean; - policyTags: OnyxTypes.PolicyTags; + /** Depending on expense report or personal IOU report, respective bank account route */ + bankAccountRoute?: Route; - policy: OnyxTypes.Policy; + /** The policyID of the request */ + policyID?: string; - receiptPath: string; + /** The reportID of the request */ + reportID: string; - receiptFilename: string; + /** File path of the receipt */ + receiptPath?: string; - listStyles: object | object[]; + /** File name of the receipt */ + receiptFilename?: string; - transactionID: string; + /** List styles for OptionsSelector */ + listStyles?: StyleProp; - mileageRate: { - unit: typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES | typeof CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS; - rate: number; - currency: string; - }; + /** ID of the transaction that represents the money request */ + transactionID?: string; - isDistanceRequest: boolean; + /** Whether the money request is a distance request */ + isDistanceRequest?: boolean; - isEditingSplitBill: boolean; + /** Whether the money request is a scan request */ + isScanRequest?: boolean; - shouldShowSmartScanFields: boolean; + /** Whether we're editing a split bill */ + isEditingSplitBill?: boolean; - isPolicyExpenseChat: boolean; + /** Whether we should show the amount, date, and merchant fields. */ + shouldShowSmartScanFields?: boolean; - transaction: OnyxTypes.Transaction; + /** A flag for verifying that the current report is a sub-report of a workspace chat */ + isPolicyExpenseChat?: boolean; - policyID: string; + /** Whether there is smartscan failed */ + hasSmartScanFailed?: boolean; - reportID: string; + /** ID of the report action */ + reportActionID: string; - reportActionID: string; -}; - -type MoneyTemporaryForRefactorRequestConfirmation = MoneyTemporaryForRefactorRequestConfirmationListOnyxProps & MoneyTemporaryForRefactorRequestConfirmationListProps; + /** Transaction object */ + transaction: OnyxTypes.Transaction; + }; function MoneyTemporaryForRefactorRequestConfirmationList({ + onConfirm = () => {}, + onSendMoney = () => {}, + onSelectParticipant = () => {}, + iouType = CONST.IOU.TYPE.REQUEST, + iouCategory = '', + iouTag = '', + iouIsBillable = false, + onToggleBillable = () => {}, + payeePersonalDetails, + canModifyParticipants = false, + isReadOnly = false, bankAccountRoute, - canModifyParticipants, - currentUserPersonalDetails, - hasMultipleParticipants, - hasSmartScanFailed, + policyID, + reportID, + receiptPath, + receiptFilename, + transactionID, + mileageRate, + isDistanceRequest = false, + isScanRequest = false, + shouldShowSmartScanFields = true, + isPolicyExpenseChat = false, + transaction, iouAmount, - iouCategory, - iouComment, - iouCreated, + policyTags, + policyCategories, + policy, iouCurrencyCode, - iouIsBillable, - iouMerchant, - iouType, - isDistanceRequest, isEditingSplitBill, - isPolicyExpenseChat, - isReadOnly, - isScanRequest, - listStyles, - mileageRate, - onConfirm, - onSelectParticipant, - onSendMoney, - onToggleBillable, - payeePersonalDetails, - policy, - policyCategories, - policyID, - policyTags, - receiptFilename, - receiptPath, + hasSmartScanFailed, + iouMerchant, + currentUserPersonalDetails, + hasMultipleParticipants, + pickedParticipants, + session, + iou, reportActionID, - reportID, - selectedParticipants: pickedParticipants, - session: {accountID}, - shouldShowSmartScanFields, - transaction, -}: MoneyTemporaryForRefactorRequestConfirmation) { + iouCreated, + listStyles, + iouComment, +}: MoneyRequestConfirmationListProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); @@ -343,12 +236,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeSend = iouType === CONST.IOU.TYPE.SEND; const {unit, rate, currency} = mileageRate; - const distance = lodashGet(transaction, 'routes.route0.distance', 0); + const distance = transaction?.routes?.route0.distance ?? 0; const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; - const taxRates = policy?.taxRates ?? {}; + const taxRates = policy?.taxRates ?? null; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || (OptionsListUtils.hasEnabledOptions(Object.values(policyCategories)))); + const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); // A flag and a toggler for showing the rest of the form fields @@ -366,10 +259,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowTags = useMemo(() => isPolicyExpenseChat && OptionsListUtils.hasEnabledTags(policyTagLists), [isPolicyExpenseChat, policyTagLists]); // A flag for showing tax rate - const shouldShowTax = isPolicyExpenseChat && policy && lodashGet(policy, 'tax.trackingEnabled', policy.isTaxTrackingEnabled); + const shouldShowTax = isPolicyExpenseChat && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled); // A flag for showing the billable field - const shouldShowBillable = !lodashGet(policy, 'disabledFields.defaultBillable', true); + const shouldShowBillable = !policy?.disabledFields?.defaultBillable ?? true; const hasRoute = TransactionUtils.hasRoute(transaction); const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate); @@ -379,11 +272,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : iouAmount, isDistanceRequest ? currency : iouCurrencyCode, ); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); - const defaultTaxKey = taxRates.defaultExternalID; + const defaultTaxKey = taxRates?.defaultExternalID; const defaultTaxName = (defaultTaxKey && `${taxRates.taxes[defaultTaxKey].name} (${taxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const taxRateTitle = (transaction?.taxRate?.text) || defaultTaxName; const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -398,14 +292,14 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return false; } - return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); + return (!!hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)); }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isMerchantRequired = isPolicyExpenseChat && !isScanRequest && shouldShowMerchant; - const isCategoryRequired = canUseViolations && lodashGet(policy, 'requiresCategory', false); - const isTagRequired = canUseViolations && lodashGet(policy, 'requiresTag', false); + const isCategoryRequired = canUseViolations && (policy?.requiresCategory ?? false); + const isTagRequired = canUseViolations && (policy?.requiresTag ?? false); useEffect(() => { if ((!isMerchantRequired && isMerchantEmpty) || !merchantError) { @@ -442,18 +336,19 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate); - IOU.setMoneyRequestAmount_temporaryForRefactor(transaction.transactionID, amount, currency); + IOU.setMoneyRequestAmount_temporaryForRefactor(transaction?.transactionID, amount, currency); }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency]); /** * Returns the participants with amount - * @param {Array} participants - * @returns {Array} */ const getParticipantsWithAmount = useCallback( - (participantsList) => { - const amount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode); - return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, amount > 0 ? CurrencyUtils.convertToDisplayString(amount, iouCurrencyCode) : ''); + (participantsList: Participant[]) => { + const calculatedIouAmount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode ?? ''); + return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( + participantsList, + calculatedIouAmount > 0 ? CurrencyUtils.convertToDisplayString(calculatedIouAmount, iouCurrencyCode) : '', + ); }, [iouAmount, iouCurrencyCode], ); @@ -463,10 +358,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ setDidConfirm(false); } - const splitOrRequestOptions = useMemo(() => { + const splitOrRequestOptions: DropdownOption[] = useMemo(() => { let text; if (isTypeSplit && iouAmount === 0) { text = translate('iou.split'); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) { text = translate('iou.request'); if (iouAmount !== 0) { @@ -484,17 +380,17 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ]; }, [isTypeSplit, isTypeRequest, iouType, iouAmount, receiptPath, formattedAmount, isDistanceRequestWithPendingRoute, translate]); - const selectedParticipants = useMemo(() => _.filter(pickedParticipants, (participant) => participant.selected), [pickedParticipants]); - const personalDetailsOfPayee = useMemo(() => payeePersonalDetails || currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); + const selectedParticipants = useMemo(() => pickedParticipants.filter((participant) => participant.selected), [pickedParticipants]); + const personalDetailsOfPayee = useMemo(() => payeePersonalDetails ?? currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); const userCanModifyParticipants = useRef(!isReadOnly && canModifyParticipants && hasMultipleParticipants); useEffect(() => { userCanModifyParticipants.current = !isReadOnly && canModifyParticipants && hasMultipleParticipants; }, [isReadOnly, canModifyParticipants, hasMultipleParticipants]); const shouldDisablePaidBySection = userCanModifyParticipants.current; - const optionSelectorSections = useMemo(() => { + const optionSelectorSections: CategorySection[] = useMemo(() => { const sections = []; - const unselectedParticipants = _.filter(pickedParticipants, (participant) => !participant.selected); + cosnt unselectedParticipants = pickedParticipants.filter((participant) => !participant.selected); if (hasMultipleParticipants) { const formattedSelectedParticipants = getParticipantsWithAmount(selectedParticipants); let formattedParticipantsList = _.union(formattedSelectedParticipants, unselectedParticipants); @@ -502,11 +398,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (!userCanModifyParticipants.current) { formattedParticipantsList = _.map(formattedParticipantsList, (participant) => ({ ...participant, - isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID), + isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), })); } - const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode, true); + const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode ?? '', true); const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( personalDetailsOfPayee, iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', @@ -557,7 +453,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (!hasMultipleParticipants) { return []; } - return [...selectedParticipants, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetailsOfPayee)]; + return [ + ...selectedParticipants, + OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetailsOfPayee, CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode)) + ]; }, [selectedParticipants, hasMultipleParticipants, personalDetailsOfPayee]); useEffect(() => { @@ -576,31 +475,27 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ IOU.setMoneyRequestMerchant(transaction.transactionID, distanceMerchant, true); }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction]); - /** - * @param {Object} option - */ const selectParticipant = useCallback( - (option) => { + (option: Participant) => { // Return early if selected option is currently logged in user. - if (option.accountID === accountID) { + if (option.accountID === session?.accountID) { return; } onSelectParticipant(option); }, - [accountID, onSelectParticipant], + [session?.accountID, onSelectParticipant], ); /** * Navigate to report details or profile of selected user - * @param {Object} option */ - const navigateToReportOrUserDetail = (option) => { - const activeRoute = Navigation.getActiveRouteWithoutParams(); + const navigateToReportOrUserDetail = (option: Participant | OnyxTypes.Report) => { + if ('accountID' in option && option.accountID) { + const activeRoute = Navigation.getActiveRouteWithoutParams(); - if (option.accountID) { Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, activeRoute)); - } else if (option.reportID) { - Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(option.reportID, activeRoute)); + } else if ('reportID' in option && option.reportID) { + Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(option.reportID)); } }; @@ -608,8 +503,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ * @param {String} paymentMethod */ const confirm = useCallback( - (paymentMethod) => { - if (_.isEmpty(selectedParticipants)) { + (paymentMethod: PaymentMethodType | undefined) => { + if (selectedParticipants.length === 0) { return; } if ((isMerchantRequired && isMerchantEmpty) || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction))) { @@ -695,7 +590,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ confirm(value)} + onPress={(_, value) => confirm(value as PaymentMethodType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} @@ -740,7 +635,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? translate('common.error.enterAmount') : ''} /> ), @@ -803,7 +698,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={merchantError ? translate('common.error.fieldRequired') : ''} rightLabel={isMerchantRequired ? translate('common.required') : ''} /> @@ -827,7 +722,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> ), @@ -882,10 +777,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} @@ -899,10 +794,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} @@ -939,7 +834,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ (supplementaryField) => supplementaryField.item, ); - const {image: receiptImage, thumbnail: receiptThumbnail} = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction, receiptPath, receiptFilename) : {}; + const receiptData = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename) : null; return ( )} - {receiptImage || receiptThumbnail ? ( + {receiptData?.image ?? receiptData?.thumbnail ? ( ) : ( // The empty receipt component should only show for IOU Requests of a paid policy ("Team" or "Corporate") @@ -1011,21 +906,27 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyTemporaryForRefactorRequestConfirmationList'; -export default withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - policyCategories: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - }, - policyTags: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - }, - mileageRate: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - selector: DistanceRequestUtils.getDefaultMileageRate, - }, - policy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, -})(MoneyTemporaryForRefactorRequestConfirmationList); + +export default withCurrentUserPersonalDetails( + withOnyx({ + policyCategories: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + }, + policyTags: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + }, + mileageRate: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: DistanceRequestUtils.getDefaultMileageRate, + }, + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + }, + iou: { + key: ONYXKEYS.IOU, + }, + session: { + key: ONYXKEYS.SESSION, + }, + })(MoneyTemporaryForRefactorRequestConfirmationList), +); \ No newline at end of file From 2a59f2b2bc0b6e3dedef55f078580cc050ca756f Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 7 Mar 2024 01:23:45 +0530 Subject: [PATCH 07/26] migrate to typescript --- ...raryForRefactorRequestConfirmationList.tsx | 464 +++++++++--------- src/libs/DistanceRequestUtils.ts | 2 + 2 files changed, 221 insertions(+), 245 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index cfabc8f2b0b6..600e2ccc44ec 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -1,254 +1,258 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; -import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; +import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; -import DistanceRequestUtils from '@libs/DistanceRequestUtils'; +import type {StyleProp, ViewStyle} from 'react-native'; +import DistanceRequestUtils, { DefaultMileageRate } from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Log from '@libs/Log'; -import playSound, {SOUNDS} from '@libs/Sound'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import playSound, {SOUNDS} from '@libs/Sound'; import * as TransactionUtils from '@libs/TransactionUtils'; +import {policyPropTypes} from '@pages/workspace/withPolicy'; +import type {DropdownOption} from './ButtonWithDropdownMenu/types'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; -import type * as OnyxTypes from '@src/types/onyx'; -import type {Participant} from '@src/types/onyx/IOU'; -import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import type {MileageRate} from '@src/types/onyx/Policy'; -import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import Button from './Button'; +import type {AllRoutes} from '@src/ROUTES'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; +import categoryPropTypes from './categoryPropTypes'; import ConfirmedRoute from './ConfirmedRoute'; import ConfirmModal from './ConfirmModal'; import FormHelpMessage from './FormHelpMessage'; +import * as Expensicons from './Icon/Expensicons'; import Image from './Image'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; +import optionPropTypes from './optionPropTypes'; import OptionsSelector from './OptionsSelector'; import PDFThumbnail from './PDFThumbnail'; import ReceiptEmptyState from './ReceiptEmptyState'; import SettlementButton from './SettlementButton'; -import ShowMoreButton from './ShowMoreButton'; import Switch from './Switch'; +import tagPropTypes from './tagPropTypes'; import Text from './Text'; -import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersonalDetails'; -import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails'; -import Button from './Button'; - -type DropdownOption = { - text: string; - value: DeepValueOf; -}; - -type Option = Partial; - -type CategorySection = { - title: string | undefined; - shouldShow: boolean; - indexOffset: number; - data: Option[]; -}; +import transactionPropTypes from './transactionPropTypes'; +import type {OnyxEntry} from 'react-native-onyx'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Participant} from '@src/types/onyx/IOU'; +import type {ValueOf} from 'type-fest'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; type MoneyRequestConfirmationListOnyxProps = { - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: OnyxEntry; - - /** Unit and rate used for if the money request is a distance request */ - mileageRate: OnyxEntry; - /** Collection of categories attached to a policy */ policyCategories: OnyxEntry; /** Collection of tags attached to a policy */ policyTags: OnyxEntry; - /** The policy of root parent report */ + /** The policy of the report */ policy: OnyxEntry; + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + iou: OnyxEntry; + /** The session of the logged in user */ session: OnyxEntry; + + /** Unit and rate used for if the money request is a distance request */ + mileageRate: OnyxEntry; }; -type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & - WithCurrentUserPersonalDetailsProps & { - /** Callback to inform parent modal of success */ - onConfirm?: (selectedParticipants: Participant[]) => void; +type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & { + /** Callback to inform parent modal of success */ + onConfirm?: (selectedParticipants: Participant[]) => void; - /** Callback to parent modal to send money */ - onSendMoney?: (paymentMethod: PaymentMethodType) => void; + /** Callback to parent modal to send money */ + onSendMoney?: (paymentMethod: PaymentMethodType | undefined) => void; - /** Callback to inform a participant is selected */ - onSelectParticipant?: (option: Participant) => void; + /** Callback to inform a participant is selected */ + onSelectParticipant?: (option: Participant) => void; - /** Should we request a single or multiple participant selection from user */ - hasMultipleParticipants: boolean; + /** Should we request a single or multiple participant selection from user */ + hasMultipleParticipants: boolean; - /** IOU amount */ - iouAmount: number; + /** IOU amount */ + iouAmount: number; - /** IOU comment */ - iouComment?: string; + /** IOU comment */ + iouComment?: string; - /** IOU currency */ - iouCurrencyCode?: string; + /** IOU currency */ + iouCurrencyCode?: string; - /** IOU type */ - iouType?: ValueOf; + /** IOU type */ + iouType?: ValueOf; - /** IOU date */ - iouCreated?: string; + /** IOU date */ + iouCreated?: string; - /** IOU merchant */ - iouMerchant?: string; + /** IOU merchant */ + iouMerchant?: string; - /** IOU Category */ - iouCategory?: string; + /** IOU Category */ + iouCategory?: string; - /** IOU Tag */ - iouTag?: string; + /** IOU Tag */ + iouTag?: string; - /** IOU isBillable */ - iouIsBillable?: boolean; + /** IOU isBillable */ + iouIsBillable?: boolean; - /** Callback to toggle the billable state */ - onToggleBillable?: () => void; + /** Callback to toggle the billable state */ + onToggleBillable?: (isOn: boolean) => void; - /** Selected participants from MoneyRequestModal with login / accountID */ - pickedParticipants: Participant[]; + /** Selected participants from MoneyRequestModal with login / accountID */ + selectedParticipants: Participant[]; - /** Payee of the money request with login */ - payeePersonalDetails?: OnyxEntry; + /** Payee of the money request with login */ + payeePersonalDetails?: OnyxTypes.PersonalDetails; - /** Can the participants be modified or not */ - canModifyParticipants?: boolean; + /** Can the participants be modified or not */ + canModifyParticipants?: boolean; - /** Should the list be read only, and not editable? */ - isReadOnly?: boolean; + /** Should the list be read only, and not editable? */ + isReadOnly?: boolean; - /** Depending on expense report or personal IOU report, respective bank account route */ - bankAccountRoute?: Route; + /** Depending on expense report or personal IOU report, respective bank account route */ + bankAccountRoute?: AllRoutes; - /** The policyID of the request */ - policyID?: string; + /** The policyID of the request */ + policyID?: string; - /** The reportID of the request */ - reportID: string; + /** The reportID of the request */ + reportID?: string; - /** File path of the receipt */ - receiptPath?: string; + /** File path of the receipt */ + receiptPath?: string; - /** File name of the receipt */ - receiptFilename?: string; + /** File name of the receipt */ + receiptFilename?: string; - /** List styles for OptionsSelector */ - listStyles?: StyleProp; + /** List styles for OptionsSelector */ + listStyles?: StyleProp; - /** ID of the transaction that represents the money request */ - transactionID?: string; + /** ID of the transaction that represents the money request */ + transactionID?: string; - /** Whether the money request is a distance request */ - isDistanceRequest?: boolean; + /** Transaction that represents the money request */ + transaction?: OnyxEntry; - /** Whether the money request is a scan request */ - isScanRequest?: boolean; + /** Whether the money request is a distance request */ + isDistanceRequest?: boolean; - /** Whether we're editing a split bill */ - isEditingSplitBill?: boolean; + /** Whether the money request is a scan request */ + isScanRequest?: boolean; - /** Whether we should show the amount, date, and merchant fields. */ - shouldShowSmartScanFields?: boolean; + /** Whether we're editing a split bill */ + isEditingSplitBill?: boolean; - /** A flag for verifying that the current report is a sub-report of a workspace chat */ - isPolicyExpenseChat?: boolean; + /** Whether we should show the amount, date, and merchant fields. */ + shouldShowSmartScanFields?: boolean; - /** Whether there is smartscan failed */ - hasSmartScanFailed?: boolean; + /** A flag for verifying that the current report is a sub-report of a workspace chat */ + isPolicyExpenseChat?: boolean; - /** ID of the report action */ - reportActionID: string; + /** Whether smart scan failed */ + hasSmartScanFailed?: boolean; + + reportActionID?: string; +}; - /** Transaction object */ - transaction: OnyxTypes.Transaction; - }; function MoneyTemporaryForRefactorRequestConfirmationList({ - onConfirm = () => {}, - onSendMoney = () => {}, - onSelectParticipant = () => {}, + transaction, + onSendMoney, + onConfirm, + onSelectParticipant, iouType = CONST.IOU.TYPE.REQUEST, - iouCategory = '', - iouTag = '', - iouIsBillable = false, - onToggleBillable = () => {}, - payeePersonalDetails, - canModifyParticipants = false, - isReadOnly = false, - bankAccountRoute, - policyID, - reportID, - receiptPath, - receiptFilename, - transactionID, - mileageRate, - isDistanceRequest = false, isScanRequest = false, - shouldShowSmartScanFields = true, - isPolicyExpenseChat = false, - transaction, iouAmount, - policyTags, policyCategories, + mileageRate, + isDistanceRequest = false, policy, - iouCurrencyCode, + isPolicyExpenseChat = false, + iouCategory = '', + shouldShowSmartScanFields = true, isEditingSplitBill, - hasSmartScanFailed, + policyTags, + iouCurrencyCode, iouMerchant, - currentUserPersonalDetails, hasMultipleParticipants, - pickedParticipants, + selectedParticipants: pickedParticipants, + payeePersonalDetails: payeePersonalDetails, + iou = { + id: '', + amount: 0, + currency: CONST.CURRENCY.USD, + comment: '', + merchant: '', + category: '', + tag: '', + billable: false, + created: '', + participants: [], + receiptPath: '', + }, + canModifyParticipants: canModifyParticipants = false, session, - iou, - reportActionID, - iouCreated, - listStyles, + isReadOnly = false, + bankAccountRoute = '', + policyID = '', + reportID = '', + receiptPath = '', iouComment, + receiptFilename = '', + listStyles, + iouCreated, + iouIsBillable = false, + onToggleBillable, + iouTag = '', + transactionID = '', + hasSmartScanFailed, + reportActionID, }: MoneyRequestConfirmationListProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const {canUseViolations} = usePermissions(); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const isTypeRequest = iouType === CONST.IOU.TYPE.REQUEST; const isTypeSplit = iouType === CONST.IOU.TYPE.SPLIT; const isTypeSend = iouType === CONST.IOU.TYPE.SEND; - const {unit, rate, currency} = mileageRate; + const {unit, rate, currency} = mileageRate ?? { + unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + rate: 0, + currency: 'USD', + }; const distance = transaction?.routes?.route0.distance ?? 0; const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; - const taxRates = policy?.taxRates ?? null; + const taxRates = policy?.taxRates; // A flag for showing the categories field - const shouldShowCategories = isPolicyExpenseChat && (iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); - + const shouldShowCategories = isPolicyExpenseChat && (!!iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -268,22 +272,21 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowTax = isPolicyExpenseChat && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled); // A flag for showing the billable field - const shouldShowBillable = !policy?.disabledFields?.defaultBillable ?? true; + const shouldShowBillable = !(policy?.disabledFields?.defaultBillable ?? true); - const hasRoute = TransactionUtils.hasRoute(transaction); + const hasRoute = transaction ? TransactionUtils.hasRoute(transaction) : false; const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate); const formattedAmount = isDistanceRequestWithPendingRoute ? '' : CurrencyUtils.convertToDisplayString( - shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : iouAmount, + shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0) : iouAmount, isDistanceRequest ? currency : iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); const defaultTaxKey = taxRates?.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${taxRates.taxes[defaultTaxKey].name} (${taxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const taxRateTitle = (transaction?.taxRate?.text) || defaultTaxName; + const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; + const taxRateTitle = transaction?.taxRate?.text || defaultTaxName; const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -296,7 +299,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); const navigateBack = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(iouType, transaction.transactionID, reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(iouType, transactionID ?? '', reportID)); }; const shouldDisplayFieldError = useMemo(() => { @@ -304,14 +307,14 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return false; } - return (!!hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)); + return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isMerchantRequired = isPolicyExpenseChat && !isScanRequest && shouldShowMerchant; - const isCategoryRequired = canUseViolations && (policy?.requiresCategory ?? false); - const isTagRequired = canUseViolations && (policy?.requiresTag ?? false); + const isCategoryRequired = canUseViolations && lodashGet(policy, 'requiresCategory', false); + const isTagRequired = canUseViolations && lodashGet(policy, 'requiresTag', false); useEffect(() => { if ((!isMerchantRequired && isMerchantEmpty) || !merchantError) { @@ -347,8 +350,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return; } - const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate); - IOU.setMoneyRequestAmount_temporaryForRefactor(transaction?.transactionID, amount, currency); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0) ; + IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID ?? '', amount, currency ?? ''); }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency]); /** @@ -356,11 +359,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ */ const getParticipantsWithAmount = useCallback( (participantsList: Participant[]) => { - const calculatedIouAmount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode ?? ''); - return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( - participantsList, - calculatedIouAmount > 0 ? CurrencyUtils.convertToDisplayString(calculatedIouAmount, iouCurrencyCode) : '', - ); + const amount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode ?? 'USD'); + return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, amount > 0 ? CurrencyUtils.convertToDisplayString(amount, iouCurrencyCode) : ''); }, [iouAmount, iouCurrencyCode], ); @@ -370,11 +370,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ setDidConfirm(false); } - const splitOrRequestOptions: DropdownOption[] = useMemo(() => { + const splitOrRequestOptions: Array> = useMemo(() => { let text; if (isTypeSplit && iouAmount === 0) { text = translate('iou.split'); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute) { text = translate('iou.request'); if (iouAmount !== 0) { @@ -382,7 +381,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } } else { const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.requestAmount'; - text = translate(translationKey, {amount: formattedAmount}); + text = translate(translationKey, {amount: Number(formattedAmount)}); } return [ { @@ -393,16 +392,16 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, [isTypeSplit, isTypeRequest, iouType, iouAmount, receiptPath, formattedAmount, isDistanceRequestWithPendingRoute, translate]); const selectedParticipants = useMemo(() => pickedParticipants.filter((participant) => participant.selected), [pickedParticipants]); - const personalDetailsOfPayee = useMemo(() => payeePersonalDetails ?? currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); + const personalDetailsOfPayee = useMemo(() => payeePersonalDetails || currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); const userCanModifyParticipants = useRef(!isReadOnly && canModifyParticipants && hasMultipleParticipants); useEffect(() => { userCanModifyParticipants.current = !isReadOnly && canModifyParticipants && hasMultipleParticipants; }, [isReadOnly, canModifyParticipants, hasMultipleParticipants]); const shouldDisablePaidBySection = userCanModifyParticipants.current; - const optionSelectorSections: CategorySection[] = useMemo(() => { + const optionSelectorSections = useMemo(() => { const sections = []; - cosnt unselectedParticipants = pickedParticipants.filter((participant) => !participant.selected); + const unselectedParticipants = _.filter(pickedParticipants, (participant) => !participant.selected); if (hasMultipleParticipants) { const formattedSelectedParticipants = getParticipantsWithAmount(selectedParticipants); let formattedParticipantsList = _.union(formattedSelectedParticipants, unselectedParticipants); @@ -438,7 +437,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } else { const formattedSelectedParticipants = _.map(selectedParticipants, (participant) => ({ ...participant, - isDisabled: !participant.isPolicyExpenseChat && ReportUtils.isOptimisticPersonalDetail(participant.accountID), + isDisabled: !participant.isPolicyExpenseChat && ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), })); sections.push({ title: translate('common.to'), @@ -465,10 +464,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (!hasMultipleParticipants) { return []; } - return [ - ...selectedParticipants, - OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetailsOfPayee, CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode)) - ]; + return [...selectedParticipants, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( + personalDetailsOfPayee, + iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', + )]; }, [selectedParticipants, hasMultipleParticipants, personalDetailsOfPayee]); useEffect(() => { @@ -481,33 +480,36 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ When the user completes the initial steps of the IOU flow offline and then goes online on the confirmation page. In this scenario, the route will be fetched from the server, and the waypoints will no longer be pending. */ - IOU.setMoneyRequestPendingFields(transaction.transactionID, {waypoints: isDistanceRequestWithPendingRoute ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : null}); + IOU.setMoneyRequestPendingFields(transactionID, {waypoints: isDistanceRequestWithPendingRoute ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : null}); - const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate, toLocaleDigit); - IOU.setMoneyRequestMerchant(transaction.transactionID, distanceMerchant, true); + const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate ?? 0, currency ?? 'USD', translate, toLocaleDigit); + IOU.setMoneyRequestMerchant(transactionID, distanceMerchant, true); }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction]); + /** + */ const selectParticipant = useCallback( (option: Participant) => { // Return early if selected option is currently logged in user. if (option.accountID === session?.accountID) { return; } - onSelectParticipant(option); + onSelectParticipant?.(option); }, [session?.accountID, onSelectParticipant], ); /** * Navigate to report details or profile of selected user + * @param {Object} option */ - const navigateToReportOrUserDetail = (option: Participant | OnyxTypes.Report) => { - if ('accountID' in option && option.accountID) { - const activeRoute = Navigation.getActiveRouteWithoutParams(); + const navigateToReportOrUserDetail = (option: ReportUtils.OptionData) => { + const activeRoute = Navigation.getActiveRouteWithoutParams(); + if (option.accountID) { Navigation.navigate(ROUTES.PROFILE.getRoute(option.accountID, activeRoute)); - } else if ('reportID' in option && option.reportID) { - Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(option.reportID)); + } else if (option.reportID) { + Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(option.reportID, activeRoute)); } }; @@ -516,7 +518,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ */ const confirm = useCallback( (paymentMethod: PaymentMethodType | undefined) => { - if (selectedParticipants.length === 0) { + if (_.isEmpty(selectedParticipants)) { return; } if ((isMerchantRequired && isMerchantEmpty) || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction))) { @@ -532,7 +534,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ setDidConfirm(true); Log.info(`[IOU] Sending money via: ${paymentMethod}`); - onSendMoney(paymentMethod); + onSendMoney?.(paymentMethod); } else { // validate the amount for distance requests const decimals = CurrencyUtils.getCurrencyDecimals(iouCurrencyCode); @@ -541,7 +543,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return; } - if (isEditingSplitBill && TransactionUtils.areRequiredFieldsEmpty(transaction)) { + if (isEditingSplitBill && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)) { setDidConfirmSplit(true); setFormError('iou.error.genericSmartscanFailureMessage'); return; @@ -549,7 +551,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ playSound(SOUNDS.DONE); setDidConfirm(true); - onConfirm(selectedParticipants); + onConfirm?.(selectedParticipants); } }, [ @@ -603,7 +605,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ success pressOnEnter isDisabled={shouldDisableButton} - onPress={(_, value) => confirm(value as PaymentMethodType)} + onPress={(_event, value) => confirm(value as PaymentMethodType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} @@ -640,16 +642,16 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return; } if (isEditingSplitBill) { - Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID, CONST.EDIT_REQUEST_FIELD.AMOUNT)); + Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.AMOUNT)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams())); }} style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction) ? translate('common.error.enterAmount') : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? translate('common.error.enterAmount') : ''} /> ), shouldShow: shouldShowSmartScanFields, @@ -665,7 +667,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.description')} onPress={() => { Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), ); }} style={[styles.moneyRequestMenuItem]} @@ -711,7 +713,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} error={merchantError ? translate('common.error.fieldRequired') : ''} rightLabel={isMerchantRequired ? translate('common.required') : ''} /> @@ -735,7 +737,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> ), @@ -752,7 +754,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), ) } style={[styles.moneyRequestMenuItem]} @@ -775,7 +777,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.CREATE, iouType, index, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.CREATE, iouType, index, transaction?.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), ) } style={[styles.moneyRequestMenuItem]} @@ -807,10 +809,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} @@ -847,8 +849,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ (supplementaryField) => supplementaryField.item, ); - const receiptData = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename) : null; - const { image: receiptImage, thumbnail: receiptThumbnail, @@ -902,29 +902,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ )} - {receiptData?.image ?? receiptData?.thumbnail ? ( - - ) : ( - // The empty receipt component should only show for IOU Requests of a paid policy ("Team" or "Corporate") - PolicyUtils.isPaidGroupPolicy(policy) && - !isDistanceRequest && - iouType === CONST.IOU.TYPE.REQUEST && ( - - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), - ) - } - /> - ) - )} {receiptImage || receiptThumbnail ? receiptThumbnailContent : // The empty receipt component should only show for IOU Requests of a paid policy ("Team" or "Corporate") @@ -970,29 +947,26 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } -MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyTemporaryForRefactorRequestConfirmationList'; - - -export default withCurrentUserPersonalDetails( - withOnyx({ - policyCategories: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, - }, - policyTags: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, - }, - mileageRate: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - selector: DistanceRequestUtils.getDefaultMileageRate, - }, - policy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, - iou: { - key: ONYXKEYS.IOU, - }, - session: { - key: ONYXKEYS.SESSION, - }, - })(MoneyTemporaryForRefactorRequestConfirmationList), -); \ No newline at end of file +MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyRequestConfirmationList'; + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, + policyCategories: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + }, + policyTags: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + }, + mileageRate: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: DistanceRequestUtils.getDefaultMileageRate, + }, + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + }, + iou: { + key: ONYXKEYS.IOU, + }, +})(MoneyTemporaryForRefactorRequestConfirmationList); \ No newline at end of file diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index a42cb6a8f756..7a98351bb4e6 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -125,3 +125,5 @@ function getDistanceRequestAmount(distance: number, unit: Unit, rate: number): n } export default {getDefaultMileageRate, getDistanceMerchant, getDistanceRequestAmount}; + +export type {DefaultMileageRate}; \ No newline at end of file From 4e06aac89868862de833391217c015ab943d3c02 Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 7 Mar 2024 02:10:05 +0530 Subject: [PATCH 08/26] Migrate to typescript --- .../ButtonWithDropdownMenu/types.ts | 4 +- ...raryForRefactorRequestConfirmationList.tsx | 83 +++++++++---------- src/libs/TransactionUtils.ts | 18 ++-- 3 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/components/ButtonWithDropdownMenu/types.ts b/src/components/ButtonWithDropdownMenu/types.ts index 9975c10c13c3..1ad4f9f66a1b 100644 --- a/src/components/ButtonWithDropdownMenu/types.ts +++ b/src/components/ButtonWithDropdownMenu/types.ts @@ -13,7 +13,7 @@ type WorkspaceMemberBulkActionType = DeepValueOf = { value: TValueType; text: string; - icon: IconAsset; + icon?: IconAsset; iconWidth?: number; iconHeight?: number; iconDescription?: string; @@ -56,7 +56,7 @@ type ButtonWithDropdownMenuProps = { anchorAlignment?: AnchorAlignment; /* ref for the button */ - buttonRef: RefObject; + buttonRef?: RefObject; /** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */ enterKeyEventListenerPriority?: number; diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 600e2ccc44ec..fa8d97fa1ec3 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -1,8 +1,6 @@ import {useIsFocused} from '@react-navigation/native'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -35,28 +33,23 @@ import ROUTES from '@src/ROUTES'; import Button from './Button'; import type {AllRoutes} from '@src/ROUTES'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; -import categoryPropTypes from './categoryPropTypes'; import ConfirmedRoute from './ConfirmedRoute'; import ConfirmModal from './ConfirmModal'; import FormHelpMessage from './FormHelpMessage'; import * as Expensicons from './Icon/Expensicons'; import Image from './Image'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; -import optionPropTypes from './optionPropTypes'; import OptionsSelector from './OptionsSelector'; import PDFThumbnail from './PDFThumbnail'; import ReceiptEmptyState from './ReceiptEmptyState'; import SettlementButton from './SettlementButton'; import Switch from './Switch'; -import tagPropTypes from './tagPropTypes'; import Text from './Text'; -import transactionPropTypes from './transactionPropTypes'; import type {OnyxEntry} from 'react-native-onyx'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {ValueOf} from 'type-fest'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; type MoneyRequestConfirmationListOnyxProps = { /** Collection of categories attached to a policy */ @@ -299,7 +292,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const [isAttachmentInvalid, setIsAttachmentInvalid] = useState(false); const navigateBack = () => { - Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(iouType, transactionID ?? '', reportID)); + Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(iouType, transaction?.transactionID ?? '', reportID)); }; const shouldDisplayFieldError = useMemo(() => { @@ -307,14 +300,14 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return false; } - return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); + return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)); }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isMerchantRequired = isPolicyExpenseChat && !isScanRequest && shouldShowMerchant; - const isCategoryRequired = canUseViolations && lodashGet(policy, 'requiresCategory', false); - const isTagRequired = canUseViolations && lodashGet(policy, 'requiresTag', false); + const isCategoryRequired = canUseViolations && (policy?.requiresCategory ?? false); + const isTagRequired = canUseViolations && (policy?.requiresTag ?? false); useEffect(() => { if ((!isMerchantRequired && isMerchantEmpty) || !merchantError) { @@ -350,8 +343,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return; } - const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0) ; - IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID ?? '', amount, currency ?? ''); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0); + IOU.setMoneyRequestAmount_temporaryForRefactor(transaction?.transactionID ?? '', amount, currency ?? ''); }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency]); /** @@ -381,7 +374,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } } else { const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.requestAmount'; - text = translate(translationKey, {amount: Number(formattedAmount)}); + text = translate(translationKey, {amount: formattedAmount}); } return [ { @@ -480,10 +473,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ When the user completes the initial steps of the IOU flow offline and then goes online on the confirmation page. In this scenario, the route will be fetched from the server, and the waypoints will no longer be pending. */ - IOU.setMoneyRequestPendingFields(transactionID, {waypoints: isDistanceRequestWithPendingRoute ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : null}); + IOU.setMoneyRequestPendingFields(transaction?.transactionID ?? '', {waypoints: isDistanceRequestWithPendingRoute ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : null}); const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate ?? 0, currency ?? 'USD', translate, toLocaleDigit); - IOU.setMoneyRequestMerchant(transactionID, distanceMerchant, true); + IOU.setMoneyRequestMerchant(transaction?.transactionID ?? '', distanceMerchant, true); }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction]); /** @@ -521,7 +514,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (_.isEmpty(selectedParticipants)) { return; } - if ((isMerchantRequired && isMerchantEmpty) || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction))) { + if ((isMerchantRequired && isMerchantEmpty) || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null))) { setMerchantError(true); return; } @@ -645,12 +638,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID ?? '', CONST.EDIT_REQUEST_FIELD.AMOUNT)); return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams())); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams())); }} style={[styles.moneyRequestMenuItem, styles.mt2]} titleStyle={styles.moneyRequestConfirmationAmount} disabled={didConfirm} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isAmountMissing(transaction ?? null) ? translate('common.error.enterAmount') : ''} /> ), @@ -667,7 +660,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.description')} onPress={() => { Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), ); }} style={[styles.moneyRequestMenuItem]} @@ -689,7 +682,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.distance')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()))} disabled={didConfirm || !isTypeRequest} interactive={!isReadOnly} /> @@ -708,12 +701,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => { Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), ); }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={merchantError ? translate('common.error.fieldRequired') : ''} rightLabel={isMerchantRequired ? translate('common.required') : ''} /> @@ -732,12 +725,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => { Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_DATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), ); }} disabled={didConfirm} interactive={!isReadOnly} - brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + brickRoadIndicator={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> ), @@ -754,7 +747,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), ) } style={[styles.moneyRequestMenuItem]} @@ -772,12 +765,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.CREATE, iouType, index, transaction?.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.CREATE, iouType, index, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), ) } style={[styles.moneyRequestMenuItem]} @@ -798,7 +791,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={taxRates?.name} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()))} disabled={didConfirm} interactive={!isReadOnly} /> @@ -809,13 +802,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()))} disabled={didConfirm} interactive={!isReadOnly} /> @@ -830,7 +823,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ onToggleBillable?.(isOn)} /> ), @@ -839,21 +832,21 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, ]; - const primaryFields = _.map( - _.filter(classifiedFields, (classifiedField) => classifiedField.shouldShow && !classifiedField.isSupplementary), - (primaryField) => primaryField.item, - ); + const primaryFields = classifiedFields + .filter(classifiedField => classifiedField.shouldShow && !classifiedField.isSupplementary) + .map(primaryField => primaryField.item); + + + const supplementaryFields = classifiedFields + .filter(classifiedField => classifiedField.shouldShow && classifiedField.isSupplementary) + .map(supplementaryField => supplementaryField.item); - const supplementaryFields = _.map( - _.filter(classifiedFields, (classifiedField) => classifiedField.shouldShow && classifiedField.isSupplementary), - (supplementaryField) => supplementaryField.item, - ); const { image: receiptImage, thumbnail: receiptThumbnail, isLocalFile, - } = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction, receiptPath, receiptFilename) : {}; + } = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename) : {}; const receiptThumbnailContent = useMemo( () => @@ -899,7 +892,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ > {isDistanceRequest && ( - + )} {receiptImage || receiptThumbnail @@ -911,7 +904,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), ) } /> @@ -947,7 +940,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } -MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyRequestConfirmationList'; +MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyTemporaryForRefactorRequestConfirmationList'; export default withOnyx({ session: { diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 8a98fe0f2cdc..c1b7573a8188 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -140,11 +140,11 @@ function hasReceipt(transaction: Transaction | undefined | null): boolean { return !!transaction?.receipt?.state || hasEReceipt(transaction); } -function isMerchantMissing(transaction: Transaction) { - if (transaction.modifiedMerchant && transaction.modifiedMerchant !== '') { +function isMerchantMissing(transaction: OnyxEntry) { + if (transaction?.modifiedMerchant && transaction.modifiedMerchant !== '') { return transaction.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; } - const isMerchantEmpty = transaction.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction.merchant === ''; + const isMerchantEmpty = transaction?.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT || transaction?.merchant === ''; return isMerchantEmpty; } @@ -156,18 +156,18 @@ function isPartialMerchant(merchant: string): boolean { return merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; } -function isAmountMissing(transaction: Transaction) { - return transaction.amount === 0 && (!transaction.modifiedAmount || transaction.modifiedAmount === 0); +function isAmountMissing(transaction: OnyxEntry) { + return transaction?.amount === 0 && (!transaction.modifiedAmount || transaction.modifiedAmount === 0); } -function isCreatedMissing(transaction: Transaction) { - return transaction.created === '' && (!transaction.created || transaction.modifiedCreated === ''); +function isCreatedMissing(transaction: OnyxEntry | undefined) { + return transaction?.created === '' && (!transaction.created || transaction.modifiedCreated === ''); } -function areRequiredFieldsEmpty(transaction: Transaction): boolean { +function areRequiredFieldsEmpty(transaction: OnyxEntry): boolean { const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`] ?? null; const isFromExpenseReport = parentReport?.type === CONST.REPORT.TYPE.EXPENSE; - const isSplitPolicyExpenseChat = !!transaction.comment?.splits?.some((participant) => allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`]?.isOwnPolicyExpenseChat); + const isSplitPolicyExpenseChat = !!transaction?.comment?.splits?.some((participant) => allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`]?.isOwnPolicyExpenseChat); const isMerchantRequired = isFromExpenseReport || isSplitPolicyExpenseChat; return (isMerchantRequired && isMerchantMissing(transaction)) || isAmountMissing(transaction) || isCreatedMissing(transaction); } From 4b2e38e4ffd8aecf04fbe6fad349334883a40071 Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 7 Mar 2024 02:33:48 +0530 Subject: [PATCH 09/26] Migrate to typescript --- ...raryForRefactorRequestConfirmationList.tsx | 120 ++++++++++++------ src/libs/DistanceRequestUtils.ts | 2 +- src/libs/OptionsListUtils.ts | 2 +- 3 files changed, 81 insertions(+), 43 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index fa8d97fa1ec3..e78b8aeeaa68 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -3,17 +3,19 @@ import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; import {View} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import _ from 'underscore'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; -import type {StyleProp, ViewStyle} from 'react-native'; -import DistanceRequestUtils, { DefaultMileageRate } from '@libs/DistanceRequestUtils'; +import DistanceRequestUtils, {DefaultMileageRate} from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Log from '@libs/Log'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; @@ -25,14 +27,17 @@ import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import * as TransactionUtils from '@libs/TransactionUtils'; import {policyPropTypes} from '@pages/workspace/withPolicy'; -import type {DropdownOption} from './ButtonWithDropdownMenu/types'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import Button from './Button'; import type {AllRoutes} from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Participant} from '@src/types/onyx/IOU'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import Button from './Button'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; +import type {DropdownOption} from './ButtonWithDropdownMenu/types'; import ConfirmedRoute from './ConfirmedRoute'; import ConfirmModal from './ConfirmModal'; import FormHelpMessage from './FormHelpMessage'; @@ -45,11 +50,6 @@ import ReceiptEmptyState from './ReceiptEmptyState'; import SettlementButton from './SettlementButton'; import Switch from './Switch'; import Text from './Text'; -import type {OnyxEntry} from 'react-native-onyx'; -import type * as OnyxTypes from '@src/types/onyx'; -import type {Participant} from '@src/types/onyx/IOU'; -import type {ValueOf} from 'type-fest'; -import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; type MoneyRequestConfirmationListOnyxProps = { /** Collection of categories attached to a policy */ @@ -171,7 +171,6 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & reportActionID?: string; }; - function MoneyTemporaryForRefactorRequestConfirmationList({ transaction, onSendMoney, @@ -394,13 +393,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const optionSelectorSections = useMemo(() => { const sections = []; - const unselectedParticipants = _.filter(pickedParticipants, (participant) => !participant.selected); + const unselectedParticipants = pickedParticipants.filter((participant) => !participant.selected); if (hasMultipleParticipants) { const formattedSelectedParticipants = getParticipantsWithAmount(selectedParticipants); - let formattedParticipantsList = _.union(formattedSelectedParticipants, unselectedParticipants); + let formattedParticipantsList = [...new Set([...formattedSelectedParticipants, ...unselectedParticipants])]; - if (!userCanModifyParticipants.current) { - formattedParticipantsList = _.map(formattedParticipantsList, (participant) => ({ + if (!canModifyParticipants) { + formattedParticipantsList = formattedParticipantsList.map((participant) => ({ ...participant, isDisabled: ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), })); @@ -457,10 +456,15 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (!hasMultipleParticipants) { return []; } - return [...selectedParticipants, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( - personalDetailsOfPayee, - iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', - )]; + + const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode ?? '', true); + return [ + ...selectedParticipants, + OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( + personalDetailsOfPayee, + iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', + ), + ]; }, [selectedParticipants, hasMultipleParticipants, personalDetailsOfPayee]); useEffect(() => { @@ -607,13 +611,14 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return ( <> - {!_.isEmpty(formError) && ( + {formError && ( )} + {button} ); @@ -660,7 +665,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.description')} onPress={() => { Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ); }} style={[styles.moneyRequestMenuItem]} @@ -682,7 +693,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.distance')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams())) + } disabled={didConfirm || !isTypeRequest} interactive={!isReadOnly} /> @@ -701,7 +714,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => { Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ); }} disabled={didConfirm} @@ -747,7 +766,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ) } style={[styles.moneyRequestMenuItem]} @@ -770,7 +795,14 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ numberOfLinesTitle={2} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.CREATE, iouType, index, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_TAG.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + index, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ) } style={[styles.moneyRequestMenuItem]} @@ -791,7 +823,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={taxRates?.name} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams())) + } disabled={didConfirm} interactive={!isReadOnly} /> @@ -808,7 +842,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={taxRates?.name} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams())) + } disabled={didConfirm} interactive={!isReadOnly} /> @@ -832,27 +868,23 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, ]; - const primaryFields = classifiedFields - .filter(classifiedField => classifiedField.shouldShow && !classifiedField.isSupplementary) - .map(primaryField => primaryField.item); - + const primaryFields = classifiedFields.filter((classifiedField) => classifiedField.shouldShow && !classifiedField.isSupplementary).map((primaryField) => primaryField.item); const supplementaryFields = classifiedFields - .filter(classifiedField => classifiedField.shouldShow && classifiedField.isSupplementary) - .map(supplementaryField => supplementaryField.item); - + .filter((classifiedField) => classifiedField.shouldShow && classifiedField.isSupplementary) + .map((supplementaryField) => supplementaryField.item); const { image: receiptImage, thumbnail: receiptThumbnail, isLocalFile, - } = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename) : {}; + } = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction ?? null, receiptPath, receiptFilename) : ({} as ReceiptUtils.ThumbnailAndImageURI); const receiptThumbnailContent = useMemo( () => isLocalFile && Str.isPDF(receiptFilename) ? ( ), [receiptFilename, receiptImage, styles, receiptThumbnail, isLocalFile, isAttachmentInvalid], ); return ( + // @ts-expect-error This component is deprecated and will not be migrated to TypeScript (context: https://expensify.slack.com/archives/C01GTK53T8Q/p1709232289899589?thread_ts=1709156803.359359&cid=C01GTK53T8Q) Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ) } /> @@ -939,7 +978,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); } - MoneyTemporaryForRefactorRequestConfirmationList.displayName = 'MoneyTemporaryForRefactorRequestConfirmationList'; export default withOnyx({ @@ -962,4 +1000,4 @@ export default withOnyx, personalDetails: On /** * Build the IOUConfirmation options for showing the payee personalDetail */ -function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails, amountText: string): PayeePersonalDetails { +function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails | EmptyObject, amountText: string): PayeePersonalDetails { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login ?? ''); return { text: PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, formattedLogin), From 352b991de4b4c3d7b9605f8cf5ab1cba900f264b Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 7 Mar 2024 03:06:36 +0530 Subject: [PATCH 10/26] Lint fix --- ...raryForRefactorRequestConfirmationList.tsx | 35 +++++-------------- src/components/PopoverMenu.tsx | 2 +- src/libs/OptionsListUtils.ts | 1 + 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 0b50b1ca8c8b..25b6ee579af6 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -7,15 +7,14 @@ import type {StyleProp, ViewStyle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import _ from 'underscore'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; -import DistanceRequestUtils, {DefaultMileageRate} from '@libs/DistanceRequestUtils'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; +import type {DefaultMileageRate} from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Log from '@libs/Log'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; @@ -26,7 +25,6 @@ import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import * as TransactionUtils from '@libs/TransactionUtils'; -import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -192,21 +190,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ iouMerchant, hasMultipleParticipants, selectedParticipants: pickedParticipants, - payeePersonalDetails: payeePersonalDetails, - iou = { - id: '', - amount: 0, - currency: CONST.CURRENCY.USD, - comment: '', - merchant: '', - category: '', - tag: '', - billable: false, - created: '', - participants: [], - receiptPath: '', - }, - canModifyParticipants: canModifyParticipants = false, + payeePersonalDetails, + canModifyParticipants = false, session, isReadOnly = false, bankAccountRoute = '', @@ -219,15 +204,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ iouCreated, iouIsBillable = false, onToggleBillable, - iouTag = '', - transactionID = '', hasSmartScanFailed, reportActionID, }: MoneyRequestConfirmationListProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); - const {canUseViolations} = usePermissions(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const {canUseP2PDistanceRequests, canUseViolations} = usePermissions(); const isTypeRequest = iouType === CONST.IOU.TYPE.REQUEST; @@ -428,7 +410,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, ); } else { - const formattedSelectedParticipants = _.map(selectedParticipants, (participant) => ({ + const formattedSelectedParticipants = selectedParticipants.map((participant) => ({ ...participant, isDisabled: !participant.isPolicyExpenseChat && ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? -1), })); @@ -451,6 +433,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ translate, shouldDisablePaidBySection, userCanModifyParticipants, + canModifyParticipants, ]); const selectedOptions = useMemo(() => { @@ -482,7 +465,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate ?? 0, currency ?? 'USD', translate, toLocaleDigit); IOU.setMoneyRequestMerchant(transaction?.transactionID ?? '', distanceMerchant, true); - }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction]); + }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction, iouAmount, iouCurrencyCode]); /** */ @@ -516,7 +499,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ */ const confirm = useCallback( (paymentMethod: PaymentMethodType | undefined) => { - if (_.isEmpty(selectedParticipants)) { + if (selectedParticipants.length === 0) { return; } if ((isMerchantRequired && isMerchantEmpty) || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction ?? null))) { @@ -787,7 +770,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ shouldShow: shouldShowCategories, isSupplementary: !isCategoryRequired, }, - ..._.map(policyTagLists, ({name}, index) => ({ + ...policyTagLists.map(({name}, index) => ({ item: ( Date: Thu, 7 Mar 2024 03:25:34 +0530 Subject: [PATCH 11/26] Lint fix --- ...raryForRefactorRequestConfirmationList.tsx | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 25b6ee579af6..7b0da116b196 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -59,9 +59,6 @@ type MoneyRequestConfirmationListOnyxProps = { /** The policy of the report */ policy: OnyxEntry; - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: OnyxEntry; - /** The session of the logged in user */ session: OnyxEntry; @@ -103,9 +100,6 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & /** IOU Category */ iouCategory?: string; - /** IOU Tag */ - iouTag?: string; - /** IOU isBillable */ iouIsBillable?: boolean; @@ -260,8 +254,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); const defaultTaxKey = taxRates?.defaultExternalID; - const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; - const taxRateTitle = transaction?.taxRate?.text || defaultTaxName; + const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) ?? ''; + const taxRateTitle = transaction?.taxRate?.text ?? defaultTaxName; const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -282,7 +276,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return false; } - return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)); + return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) ?? (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)); }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; @@ -367,7 +361,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, [isTypeSplit, isTypeRequest, iouType, iouAmount, receiptPath, formattedAmount, isDistanceRequestWithPendingRoute, translate]); const selectedParticipants = useMemo(() => pickedParticipants.filter((participant) => participant.selected), [pickedParticipants]); - const personalDetailsOfPayee = useMemo(() => payeePersonalDetails || currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); + const personalDetailsOfPayee = useMemo(() => payeePersonalDetails ?? currentUserPersonalDetails, [payeePersonalDetails, currentUserPersonalDetails]); const userCanModifyParticipants = useRef(!isReadOnly && canModifyParticipants && hasMultipleParticipants); useEffect(() => { userCanModifyParticipants.current = !isReadOnly && canModifyParticipants && hasMultipleParticipants; @@ -432,7 +426,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ personalDetailsOfPayee, translate, shouldDisablePaidBySection, - userCanModifyParticipants, canModifyParticipants, ]); @@ -449,7 +442,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', ), ]; - }, [selectedParticipants, hasMultipleParticipants, personalDetailsOfPayee]); + }, [selectedParticipants, hasMultipleParticipants, personalDetailsOfPayee, iouAmount, iouCurrencyCode]); useEffect(() => { if (!isDistanceRequest) { @@ -465,7 +458,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate ?? 0, currency ?? 'USD', translate, toLocaleDigit); IOU.setMoneyRequestMerchant(transaction?.transactionID ?? '', distanceMerchant, true); - }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction, iouAmount, iouCurrencyCode]); + }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction]); /** */ @@ -482,7 +475,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ /** * Navigate to report details or profile of selected user - * @param {Object} option */ const navigateToReportOrUserDetail = (option: ReportUtils.OptionData) => { const activeRoute = Navigation.getActiveRouteWithoutParams(); @@ -586,7 +578,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ success pressOnEnter isDisabled={shouldDisableButton} - onPress={(_event, value) => confirm(value as PaymentMethodType)} + onPress={(event, value) => confirm(value as PaymentMethodType)} options={splitOrRequestOptions} buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} enterKeyEventListenerPriority={1} @@ -723,7 +715,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, - iou: { - key: ONYXKEYS.IOU, - }, })(MoneyTemporaryForRefactorRequestConfirmationList); From 9076b8ce2fa63f9f78d656d560fecebf44bb02ae Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 14 Mar 2024 00:37:09 +0530 Subject: [PATCH 12/26] prettier --- src/components/PopoverMenu.tsx | 1 - src/libs/OptionsListUtils.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 1da7e899d3b2..4ee070e19893 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -16,7 +16,6 @@ import PopoverWithMeasuredContent from './PopoverWithMeasuredContent'; import Text from './Text'; type PopoverMenuItem = MenuItemProps & { - /** Text label */ text: string; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 6027579211cc..3de3d1622405 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1775,7 +1775,7 @@ function getShareLogOptions(reports: OnyxCollection, personalDetails: On /** * Build the IOUConfirmation options for showing the payee personalDetail */ -function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails, amountText: string): PayeePersonalDetails { +function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: PersonalDetails | EmptyObject, amountText?: string): PayeePersonalDetails { const formattedLogin = LocalePhoneNumber.formatPhoneNumber(personalDetail.login ?? ''); return { text: PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, formattedLogin), From 771d177346a6a43095639bd0f73bd0da3e4dd565 Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 14 Mar 2024 01:07:51 +0530 Subject: [PATCH 13/26] Migrate to typescript --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index c93d16119c6a..30fed2d7df3b 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -136,9 +136,6 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & /** List styles for OptionsSelector */ listStyles?: StyleProp; - /** ID of the transaction that represents the money request */ - transactionID?: string; - /** Transaction that represents the money request */ transaction?: OnyxEntry; @@ -164,7 +161,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & }; function MoneyTemporaryForRefactorRequestConfirmationList({ - transaction, + transaction=null, onSendMoney, onConfirm, onSelectParticipant, @@ -462,7 +459,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // Auto select the category if there is only one enabled category and it is required useEffect(() => { - const enabledCategories = _.filter(policyCategories, (category) => category.enabled); + const enabledCategories = policyCategories.filter((category: policyCategory) => category.enabled); if (iouCategory || !shouldShowCategories || enabledCategories.length !== 1 || !isCategoryRequired) { return; } @@ -481,7 +478,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ updatedTagsString = IOUUtils.insertTagIntoTransactionTagsString(updatedTagsString, enabledTags[0] ? enabledTags[0].name : '', index); }); if (updatedTagsString !== TransactionUtils.getTag(transaction) && updatedTagsString) { - IOU.setMoneyRequestTag(transaction.transactionID, updatedTagsString); + IOU.setMoneyRequestTag(transaction?.transactionID ?? '', updatedTagsString); } }, [policyTagLists, transaction, policyTags, isTagRequired, canUseViolations]); From b761e9f776c46f6ffc7de9a0fc66691c09d6631f Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Thu, 14 Mar 2024 01:33:03 +0530 Subject: [PATCH 14/26] Migrate to typescript --- ...raryForRefactorRequestConfirmationList.tsx | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 30fed2d7df3b..600623aba286 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -161,7 +161,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & }; function MoneyTemporaryForRefactorRequestConfirmationList({ - transaction=null, + transaction = null, onSendMoney, onConfirm, onSelectParticipant, @@ -430,16 +430,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (!hasMultipleParticipants) { return []; } - - const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode ?? '', true); - return [ - ...selectedParticipants, - OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail( - personalDetailsOfPayee, - iouAmount > 0 ? CurrencyUtils.convertToDisplayString(myIOUAmount, iouCurrencyCode) : '', - ), - ]; - }, [selectedParticipants, hasMultipleParticipants, personalDetailsOfPayee, iouAmount, iouCurrencyCode]); + return [...selectedParticipants, OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetailsOfPayee)]; + }, [selectedParticipants, hasMultipleParticipants, personalDetailsOfPayee]); useEffect(() => { if (!isDistanceRequest) { @@ -459,19 +451,19 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // Auto select the category if there is only one enabled category and it is required useEffect(() => { - const enabledCategories = policyCategories.filter((category: policyCategory) => category.enabled); + const enabledCategories = Object.values(policyCategories ?? {}).filter((category) => category.enabled); if (iouCategory || !shouldShowCategories || enabledCategories.length !== 1 || !isCategoryRequired) { return; } - IOU.setMoneyRequestCategory(transaction.transactionID, enabledCategories[0].name); + IOU.setMoneyRequestCategory(transaction?.transactionID ?? '', enabledCategories[0].name); }, [iouCategory, shouldShowCategories, policyCategories, transaction, isCategoryRequired]); // Auto select the tag if there is only one enabled tag and it is required useEffect(() => { let updatedTagsString = TransactionUtils.getTag(transaction); policyTagLists.forEach((tagList, index) => { - const enabledTags = _.filter(tagList.tags, (tag) => tag.enabled); - const isTagListRequired = isUndefined(tagList.required) ? false : tagList.required && canUseViolations; + const enabledTags = Object.values(tagList.tags).filter((tag) => tag.enabled); + const isTagListRequired = tagList.required === undefined ? false : tagList.required && canUseViolations; if (!isTagListRequired || enabledTags.length !== 1 || TransactionUtils.getTag(transaction, index)) { return; } @@ -609,7 +601,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return ( <> - {formError && ( + {!!formError && ( From 17b23615440e855c5601e4d78456405b205b13e4 Mon Sep 17 00:00:00 2001 From: Gandalf Date: Mon, 18 Mar 2024 04:29:07 +0530 Subject: [PATCH 15/26] Apply suggestions from code review Co-authored-by: Vinh Hoang --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index fad46d47962c..515a64b03c8d 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -238,9 +238,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowTax = isPolicyExpenseChat && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled); // A flag for showing the billable field - const shouldShowBillable = !(policy?.disabledFields?.defaultBillable ?? true); + const shouldShowBillable = policy?.disabledFields?.defaultBillable === false; - const hasRoute = transaction ? TransactionUtils.hasRoute(transaction) : false; + const hasRoute = TransactionUtils.hasRoute(transaction); const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate); const formattedAmount = isDistanceRequestWithPendingRoute ? '' @@ -279,8 +279,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isMerchantRequired = isPolicyExpenseChat && !isScanRequest && shouldShowMerchant; - const isCategoryRequired = canUseViolations && (policy?.requiresCategory ?? false); - const isTagRequired = canUseViolations && (policy?.requiresTag ?? false); + const isCategoryRequired = canUseViolations && !!policy?.requiresCategory; + const isTagRequired = canUseViolations && !!policy?.requiresTag; useEffect(() => { if ((!isMerchantRequired && isMerchantEmpty) || !merchantError) { From 3cd8027689f3bd1e2ee3149906854d831826f697 Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Wed, 20 Mar 2024 18:17:31 +0530 Subject: [PATCH 16/26] Fix null assertions --- ...emporaryForRefactorRequestConfirmationList.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 627a3b6ed934..cccc0b1ca6a7 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -252,7 +252,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const defaultTaxKey = taxRates?.defaultExternalID; const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) ?? ''; - const taxRateTitle = transaction?.taxRate?.text ?? defaultTaxName; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing is not working when a left hand side value is '' + const taxRateTitle = transaction?.taxRate?.text || defaultTaxName; const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -268,12 +269,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(iouType, transaction?.transactionID ?? '', reportID)); }; - const shouldDisplayFieldError = useMemo(() => { + const shouldDisplayFieldError: boolean = useMemo(() => { if (!isEditingSplitBill) { return false; } - return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction ?? null)) ?? (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction ?? null)); + return (!!hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; @@ -325,7 +326,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ */ const getParticipantsWithAmount = useCallback( (participantsList: Participant[]) => { - const amount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode ?? 'USD'); + const amount = IOUUtils.calculateAmount(participantsList.length, iouAmount, iouCurrencyCode ?? ''); return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, amount > 0 ? CurrencyUtils.convertToDisplayString(amount, iouCurrencyCode) : ''); }, [iouAmount, iouCurrencyCode], @@ -729,7 +730,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ Date: Thu, 21 Mar 2024 22:02:42 +0530 Subject: [PATCH 17/26] introduce isSelfDM prop --- src/types/onyx/IOU.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 6bbcb174a617..7e1827f73954 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -21,6 +21,7 @@ type Participant = { phoneNumber?: string; text?: string; isSelected?: boolean; + isSelfDM?: boolean; }; type Split = { From c57e69f4c718da0ed634d2e431faf5219b035d63 Mon Sep 17 00:00:00 2001 From: Rohan Sasne Date: Fri, 22 Mar 2024 20:41:06 +0530 Subject: [PATCH 18/26] Fix Lint --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index b740e0aa0834..5de5ef4590ba 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -694,7 +694,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ) } // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing From a1420c46cab6766abf42a1a31f4ca0a3b0a25276 Mon Sep 17 00:00:00 2001 From: GandalfGwaihir Date: Wed, 27 Mar 2024 05:48:03 +0530 Subject: [PATCH 19/26] fix lint --- ...raryForRefactorRequestConfirmationList.tsx | 46 ++++++++++--------- src/components/ReceiptImage.tsx | 2 +- src/types/onyx/IOU.ts | 1 - 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 58a217b0eae9..0ee1dc2594d5 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -895,6 +895,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ () => isLocalFile && Str.isPDF(receiptFilename) ? ( )} - {receiptImage || receiptThumbnail - ? receiptThumbnailContent - : // The empty receipt component should only show for IOU Requests of a paid policy ("Team" or "Corporate") - PolicyUtils.isPaidGroupPolicy(policy) && - !isDistanceRequest && - iouType === CONST.IOU.TYPE.REQUEST && ( - - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute( - CONST.IOU.ACTION.CREATE, - iouType, - transaction?.transactionID ?? '', - reportID, - Navigation.getActiveRouteWithoutParams(), - ), - ) - } - /> - )} + { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + receiptImage || receiptThumbnail + ? receiptThumbnailContent + : // The empty receipt component should only show for IOU Requests of a paid policy ("Team" or "Corporate") + PolicyUtils.isPaidGroupPolicy(policy) && + !isDistanceRequest && + iouType === CONST.IOU.TYPE.REQUEST && ( + + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) + } + /> + ) + } {primaryFields} {!shouldShowAllFields && ( diff --git a/src/components/ReceiptImage.tsx b/src/components/ReceiptImage.tsx index 08892f11b021..f4aa2de090f7 100644 --- a/src/components/ReceiptImage.tsx +++ b/src/components/ReceiptImage.tsx @@ -46,7 +46,7 @@ type ReceiptImageProps = ( isEReceipt?: boolean; isThumbnail?: boolean; source: string; - isPDFThumbnail: string; + isPDFThumbnail?: string; } ) & { /** Whether we should display the receipt with ThumbnailImage component */ diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 7e1827f73954..6bbcb174a617 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -21,7 +21,6 @@ type Participant = { phoneNumber?: string; text?: string; isSelected?: boolean; - isSelfDM?: boolean; }; type Split = { From 9f73b2a9666ac7e5befbcb7196199c3b488266a4 Mon Sep 17 00:00:00 2001 From: GandalfGwaihir Date: Wed, 27 Mar 2024 05:49:29 +0530 Subject: [PATCH 20/26] introduce isSelfDM in ONyx --- src/types/onyx/IOU.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 6bbcb174a617..7e1827f73954 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -21,6 +21,7 @@ type Participant = { phoneNumber?: string; text?: string; isSelected?: boolean; + isSelfDM?: boolean; }; type Split = { From 79aca7395729b86fbb9816115cf0dfd606c9f9a6 Mon Sep 17 00:00:00 2001 From: GandalfGwaihir Date: Wed, 10 Apr 2024 00:17:11 +0530 Subject: [PATCH 21/26] Fix taxRatetitle --- ...raryForRefactorRequestConfirmationList.tsx | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 8ef908925da6..8b73747a797f 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -17,7 +17,6 @@ import * as CurrencyUtils from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import type {DefaultMileageRate} from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; -import * as Localize from '@libs/Localize'; import Log from '@libs/Log'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -259,10 +258,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ isDistanceRequest ? currency : iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); - const defaultTaxKey = taxRates?.defaultExternalID; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const defaultTaxName = (defaultTaxKey && `${taxRates?.taxes[defaultTaxKey].name} (${taxRates?.taxes[defaultTaxKey].value}) • ${Localize.translateLocal('common.default')}`) || ''; - const taxRateTitle = transaction?.taxRate?.text ?? defaultTaxName; + const taxRateTitle = taxRates && transaction ? TransactionUtils.getDefaultTaxName(taxRates, transaction) : ''; const previousTransactionAmount = usePrevious(transaction?.amount); @@ -857,7 +853,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ) } disabled={didConfirm} @@ -878,7 +880,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ titleStyle={styles.flex1} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()), + ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transaction?.transactionID ?? '', + reportID, + Navigation.getActiveRouteWithoutParams(), + ), ) } disabled={didConfirm} From 0575d3e530be142cd4f6160c7e131ca14f89bbec Mon Sep 17 00:00:00 2001 From: GandalfGwaihir Date: Thu, 11 Apr 2024 02:45:50 +0530 Subject: [PATCH 22/26] resolve merge conflict market --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 8184f5a3c987..ed7a60af8c50 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -209,13 +209,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); -<<<<<<< HEAD:src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {canUseP2PDistanceRequests, canUseViolations} = usePermissions(); -======= const {canUseViolations} = usePermissions(); ->>>>>>> main:src/components/MoneyTemporaryForRefactorRequestConfirmationList.js const isTypeRequest = iouType === CONST.IOU.TYPE.REQUEST; const isTypeSplit = iouType === CONST.IOU.TYPE.SPLIT; const isTypeSend = iouType === CONST.IOU.TYPE.SEND; From 7941b66ed684b84959f90492981b0448a66f85c6 Mon Sep 17 00:00:00 2001 From: GandalfGwaihir Date: Thu, 11 Apr 2024 17:28:41 +0530 Subject: [PATCH 23/26] Fix unrelated typefails --- .../iou/request/step/IOURequestStepConfirmation.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 897af48f0573..8a43014bd31c 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -2,7 +2,7 @@ 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 type {ValueOf} from 'type-fest'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -422,11 +422,9 @@ function IOURequestStepConfirmation({ /** * Checks if user has a GOLD wallet then creates a paid IOU report on the fly - * - * @param {String} paymentMethodType */ const sendMoney = useCallback( - (paymentMethodType: ValueOf) => { + (paymentMethod: PaymentMethodType | undefined) => { const currency = transaction?.currency; const trimmedComment = transaction?.comment?.comment ? transaction.comment.comment.trim() : ''; @@ -437,12 +435,12 @@ function IOURequestStepConfirmation({ return; } - if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { + if (paymentMethod === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { IOU.sendMoneyElsewhere(report, transaction.amount, currency, trimmedComment, currentUserPersonalDetails.accountID, participant); return; } - if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { + if (paymentMethod === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { IOU.sendMoneyWithWallet(report, transaction.amount, currency, trimmedComment, currentUserPersonalDetails.accountID, participant); } }, @@ -490,12 +488,11 @@ function IOURequestStepConfirmation({ /> {isLoading && } - {/* @ts-expect-error TODO: Remove this once MoneyRequestConfirmationList (https://github.com/Expensify/App/issues/36130) is migrated to TypeScript. */} Date: Thu, 11 Apr 2024 17:33:53 +0530 Subject: [PATCH 24/26] Fix unrelated typefails --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e49c2f25ccc4..8183c6f036de 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -418,7 +418,7 @@ type OptionData = { notificationPreference?: NotificationPreference | null; isDisabled?: boolean | null; name?: string | null; - isSelfDM?: boolean | null; + isSelfDM?: boolean; reportID?: string; enabled?: boolean; data?: Partial; From 130ad9a9a3ee404c5b300c0c82af7b27d5bc671d Mon Sep 17 00:00:00 2001 From: GandalfGwaihir Date: Thu, 11 Apr 2024 17:34:40 +0530 Subject: [PATCH 25/26] Fix unrelated typefails --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 8a43014bd31c..2396fde76df8 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -97,7 +97,7 @@ function IOURequestStepConfirmation({ transaction?.participants?.map((participant) => { const participantAccountID = participant.accountID ?? 0; return participantAccountID ? OptionsListUtils.getParticipantsOption(participant, personalDetails) : OptionsListUtils.getReportOption(participant); - }), + }) ?? [], [transaction?.participants, personalDetails], ); const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]); From ae5242f07eee12ce307efee04d385e784f63184e Mon Sep 17 00:00:00 2001 From: GandalfGwaihir Date: Thu, 11 Apr 2024 17:46:44 +0530 Subject: [PATCH 26/26] Fix unrelated typefails --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 4 ++-- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index ed7a60af8c50..21815f00253b 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -31,7 +31,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {AllRoutes} from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; @@ -121,7 +121,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & isReadOnly?: boolean; /** Depending on expense report or personal IOU report, respective bank account route */ - bankAccountRoute?: AllRoutes; + bankAccountRoute?: Route; /** The policyID of the request */ policyID?: string; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 2396fde76df8..43e2c99f243d 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -2,7 +2,6 @@ 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 type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -30,6 +29,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Policy, PolicyCategories, PolicyTagList} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; +import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type {Receipt} from '@src/types/onyx/Transaction'; import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound';