From 75656213396603f69e7d93ddc569cedb4ac71181 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 5 Feb 2024 20:29:51 +0530 Subject: [PATCH 001/283] Remove MoneyRequestParticipantsPage.js and copy any changes since Nov 27 into IOURequestStepParticipants.js. Signed-off-by: Krishna Gupta --- .../AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/linkingConfig/config.ts | 1 - src/libs/Navigation/types.ts | 4 - .../MoneyRequestParticipantsPage.js | 174 ------------------ 4 files changed, 180 deletions(-) delete mode 100644 src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 4606f867c3fc..a19ffb184cf1 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -95,7 +95,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType, [SCREENS.MONEY_REQUEST.ROOT]: () => require('../../../pages/iou/MoneyRequestSelectorPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, [SCREENS.MONEY_REQUEST.DATE]: () => require('../../../pages/iou/MoneyRequestDatePage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index f1c9c316fe93..2bae3ec1f73f 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -400,7 +400,6 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.AMOUNT]: ROUTES.MONEY_REQUEST_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: ROUTES.MONEY_REQUEST_STEP_TAX_RATE.route, - [SCREENS.MONEY_REQUEST.PARTICIPANTS]: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, [SCREENS.MONEY_REQUEST.CONFIRMATION]: ROUTES.MONEY_REQUEST_CONFIRMATION.route, [SCREENS.MONEY_REQUEST.DATE]: ROUTES.MONEY_REQUEST_DATE.route, [SCREENS.MONEY_REQUEST.CURRENCY]: ROUTES.MONEY_REQUEST_CURRENCY.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 3c4cf17853f1..c1b31cc3a864 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -195,10 +195,6 @@ type RoomInviteNavigatorParamList = { type MoneyRequestNavigatorParamList = { [SCREENS.MONEY_REQUEST.ROOT]: undefined; [SCREENS.MONEY_REQUEST.AMOUNT]: undefined; - [SCREENS.MONEY_REQUEST.PARTICIPANTS]: { - iouType: string; - reportID: string; - }; [SCREENS.MONEY_REQUEST.CONFIRMATION]: { iouType: string; reportID: string; diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js deleted file mode 100644 index 216154be9cd4..000000000000 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ /dev/null @@ -1,174 +0,0 @@ -import _ from 'lodash'; -import lodashGet from 'lodash/get'; -import lodashSize from 'lodash/size'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import transactionPropTypes from '@components/transactionPropTypes'; -import useInitialValue from '@hooks/useInitialValue'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; -import Navigation from '@libs/Navigation/Navigation'; -import * as TransactionUtils from '@libs/TransactionUtils'; -import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import MoneyRequestParticipantsSelector from './MoneyRequestParticipantsSelector'; - -const propTypes = { - /** React Navigation route */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, - - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ - selectedTab: PropTypes.oneOf(_.values(CONST.TAB_REQUEST)), - - /** Transaction that stores the distance request data */ - transaction: transactionPropTypes, -}; - -const defaultProps = { - iou: iouDefaultProps, - transaction: {}, - selectedTab: undefined, -}; - -function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const prevMoneyRequestId = useRef(iou.id); - const iouType = useInitialValue(() => lodashGet(route, 'params.iouType', '')); - const reportID = useInitialValue(() => lodashGet(route, 'params.reportID', '')); - const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, selectedTab); - const isSendRequest = iouType === CONST.IOU.TYPE.SEND; - const isScanRequest = MoneyRequestUtils.isScanRequest(selectedTab); - const isSplitRequest = iou.id === CONST.IOU.TYPE.SPLIT; - const waypoints = lodashGet(transaction, 'comment.waypoints', {}); - const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); - const isInvalidWaypoint = lodashSize(validatedWaypoints) < 2; - const headerTitle = useMemo(() => { - if (isDistanceRequest) { - return translate('common.distance'); - } - - if (isSendRequest) { - return translate('common.send'); - } - - if (isScanRequest) { - return translate('tabSelector.scan'); - } - - if (iou.isSplitRequest) { - return translate('iou.split'); - } - - return translate('tabSelector.manual'); - }, [iou, isDistanceRequest, translate, isScanRequest, isSendRequest]); - - const navigateToConfirmationStep = (moneyRequestType) => { - IOU.setMoneyRequestId(moneyRequestType); - IOU.resetMoneyRequestCategory(); - IOU.resetMoneyRequestTag(); - Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID)); - }; - - const navigateBack = useCallback((forceFallback = false) => { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), forceFallback); - // eslint-disable-next-line react-hooks/exhaustive-deps -- no deps as we use only initial values - }, []); - - useEffect(() => { - const isInvalidDistanceRequest = !isDistanceRequest || isInvalidWaypoint; - - // ID in Onyx could change by initiating a new request in a separate browser tab or completing a request - if (prevMoneyRequestId.current !== iou.id) { - // The ID is cleared on completing a request. In that case, we will do nothing - if (iou.id && isInvalidDistanceRequest && !isSplitRequest) { - navigateBack(true); - } - return; - } - - // Reset the money request Onyx if the ID in Onyx does not match the ID from params - const moneyRequestId = `${iouType}${reportID}`; - const shouldReset = iou.id !== moneyRequestId && !_.isEmpty(reportID); - if (shouldReset) { - IOU.resetMoneyRequestInfo(moneyRequestId); - } - if (isInvalidDistanceRequest && ((iou.amount === 0 && !iou.receiptPath) || shouldReset)) { - navigateBack(true); - } - - return () => { - prevMoneyRequestId.current = iou.id; - }; - }, [iou.amount, iou.id, iou.receiptPath, isDistanceRequest, isSplitRequest, iouType, reportID, navigateBack, isInvalidWaypoint]); - - return ( - - {({safeAreaPaddingBottomStyle}) => ( - - - navigateToConfirmationStep(iouType)} - navigateToSplit={() => navigateToConfirmationStep(CONST.IOU.TYPE.SPLIT)} - safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} - iouType={iouType} - isDistanceRequest={isDistanceRequest} - isScanRequest={isScanRequest} - /> - - )} - - ); -} - -MoneyRequestParticipantsPage.displayName = 'MoneyRequestParticipantsPage'; -MoneyRequestParticipantsPage.propTypes = propTypes; -MoneyRequestParticipantsPage.defaultProps = defaultProps; - -export default compose( - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - transaction: { - key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(iou, 'transactionID', 0)}`, - }, - }), -)(MoneyRequestParticipantsPage); From db861f619867edcfad892e05d4c48507223097d1 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Tue, 6 Feb 2024 12:08:45 +0530 Subject: [PATCH 002/283] remove MoneyRequestParticipantsPage.js. Signed-off-by: Krishna Gupta --- .../MoneyRequestParticipantsPage.js | 173 ------------------ 1 file changed, 173 deletions(-) delete mode 100644 src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js deleted file mode 100644 index ea57d88579ae..000000000000 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ /dev/null @@ -1,173 +0,0 @@ -import _ from 'lodash'; -import lodashGet from 'lodash/get'; -import lodashSize from 'lodash/size'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import transactionPropTypes from '@components/transactionPropTypes'; -import useInitialValue from '@hooks/useInitialValue'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; -import Navigation from '@libs/Navigation/Navigation'; -import * as TransactionUtils from '@libs/TransactionUtils'; -import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import MoneyRequestParticipantsSelector from './MoneyRequestParticipantsSelector'; - -const propTypes = { - /** React Navigation route */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - }), - }).isRequired, - - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ - selectedTab: PropTypes.oneOf(_.values(CONST.TAB_REQUEST)), - - /** Transaction that stores the distance request data */ - transaction: transactionPropTypes, -}; - -const defaultProps = { - iou: iouDefaultProps, - transaction: {}, - selectedTab: undefined, -}; - -function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const prevMoneyRequestId = useRef(iou.id); - const iouType = useInitialValue(() => lodashGet(route, 'params.iouType', '')); - const reportID = useInitialValue(() => lodashGet(route, 'params.reportID', '')); - const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, selectedTab); - const isSendRequest = iouType === CONST.IOU.TYPE.SEND; - const isScanRequest = MoneyRequestUtils.isScanRequest(selectedTab); - const isSplitRequest = iou.id === CONST.IOU.TYPE.SPLIT; - const waypoints = lodashGet(transaction, 'comment.waypoints', {}); - const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); - const isInvalidWaypoint = lodashSize(validatedWaypoints) < 2; - const headerTitle = useMemo(() => { - if (isDistanceRequest) { - return translate('common.distance'); - } - - if (isSendRequest) { - return translate('common.send'); - } - - if (isScanRequest) { - return translate('tabSelector.scan'); - } - - if (iou.isSplitRequest) { - return translate('iou.split'); - } - - return translate('tabSelector.manual'); - }, [iou, isDistanceRequest, translate, isScanRequest, isSendRequest]); - - const navigateToConfirmationStep = (moneyRequestType) => { - IOU.setMoneyRequestId(moneyRequestType); - IOU.resetMoneyRequestCategory(); - Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(moneyRequestType, reportID)); - }; - - const navigateBack = useCallback((forceFallback = false) => { - Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), forceFallback); - // eslint-disable-next-line react-hooks/exhaustive-deps -- no deps as we use only initial values - }, []); - - useEffect(() => { - const isInvalidDistanceRequest = !isDistanceRequest || isInvalidWaypoint; - - // ID in Onyx could change by initiating a new request in a separate browser tab or completing a request - if (prevMoneyRequestId.current !== iou.id) { - // The ID is cleared on completing a request. In that case, we will do nothing - if (iou.id && isInvalidDistanceRequest && !isSplitRequest) { - navigateBack(true); - } - return; - } - - // Reset the money request Onyx if the ID in Onyx does not match the ID from params - const moneyRequestId = `${iouType}${reportID}`; - const shouldReset = iou.id !== moneyRequestId && !_.isEmpty(reportID); - if (shouldReset) { - IOU.resetMoneyRequestInfo(moneyRequestId); - } - if (isInvalidDistanceRequest && ((iou.amount === 0 && !iou.receiptPath) || shouldReset)) { - navigateBack(true); - } - - return () => { - prevMoneyRequestId.current = iou.id; - }; - }, [iou.amount, iou.id, iou.receiptPath, isDistanceRequest, isSplitRequest, iouType, reportID, navigateBack, isInvalidWaypoint]); - - return ( - - {({safeAreaPaddingBottomStyle}) => ( - - - navigateToConfirmationStep(iouType)} - navigateToSplit={() => navigateToConfirmationStep(CONST.IOU.TYPE.SPLIT)} - safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle} - iouType={iouType} - isDistanceRequest={isDistanceRequest} - isScanRequest={isScanRequest} - /> - - )} - - ); -} - -MoneyRequestParticipantsPage.displayName = 'MoneyRequestParticipantsPage'; -MoneyRequestParticipantsPage.propTypes = propTypes; -MoneyRequestParticipantsPage.defaultProps = defaultProps; - -export default compose( - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - transaction: { - key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${lodashGet(iou, 'transactionID', 0)}`, - }, - }), -)(MoneyRequestParticipantsPage); From 1db16d9aa254f4bf0eaa2a067446196a1f61a28e Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 26 Feb 2024 17:14:29 +0100 Subject: [PATCH 003/283] feat: create new rate field --- .../MoneyRequestConfirmationList.js | 12 ++++++ ...oraryForRefactorRequestConfirmationList.js | 20 ++++++++++ src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/DistanceRequestUtils.ts | 39 ++++++++++++++++--- 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index df2781d3ea89..7bb4bb868c99 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -730,6 +730,18 @@ function MoneyRequestConfirmationList(props) { interactive={!props.isReadOnly} /> )} + {props.isDistanceRequest && ( + Navigation.navigate(ROUTES.MONEY_REQUEST_DISTANCE.getRoute(props.iouType, props.reportID))} + disabled={didConfirm || !isTypeRequest} + interactive={!props.isReadOnly} + /> + )} {shouldShowMerchant && ( {}} + // onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + disabled={didConfirm || !isTypeRequest} + interactive={!isReadOnly} + /> + ), + // TODO: hide when betas ready + shouldShow: isDistanceRequest, + isSupplementary: true, + }, { item: ( Date: Mon, 26 Feb 2024 19:20:47 +0100 Subject: [PATCH 004/283] feat: create rate selection page --- src/ROUTES.ts | 5 ++ src/SCREENS.ts | 1 + ...oraryForRefactorRequestConfirmationList.js | 23 +++---- .../AppNavigator/ModalStackNavigators.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + .../iou/request/step/IOURequestStepRate.tsx | 65 +++++++++++++++++++ 6 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 src/pages/iou/request/step/IOURequestStepRate.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a8786bda3ffb..c01fdac72aef 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -369,6 +369,11 @@ const ROUTES = { getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}`, backTo), }, + MONEY_REQUEST_STEP_RATE: { + route: ':action/:iouType/rate/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action}/${iouType}/rate/${transactionID}/${reportID}`, backTo), + }, MONEY_REQUEST_STEP_MERCHANT: { route: ':action/:iouType/merchant/:transactionID/:reportID', getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 520895c89c98..ddaf52a03a59 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -143,6 +143,7 @@ const SCREENS = { STEP_DATE: 'Money_Request_Step_Date', STEP_DESCRIPTION: 'Money_Request_Step_Description', STEP_DISTANCE: 'Money_Request_Step_Distance', + STEP_RATE: 'Money_Request_Step_Rate', STEP_MERCHANT: 'Money_Request_Step_Merchant', STEP_PARTICIPANTS: 'Money_Request_Step_Participants', STEP_SCAN: 'Money_Request_Step_Scan', diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 6fb0e8bc0f08..f2fcb3ac96eb 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -1,10 +1,10 @@ -import {useIsFocused} from '@react-navigation/native'; -import {format} from 'date-fns'; +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 {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; +import React, { Fragment, useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'; +import { View } from 'react-native'; +import { withOnyx } from 'react-native-onyx'; import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; @@ -21,9 +21,9 @@ 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 playSound, { SOUNDS } from '@libs/Sound'; import * as TransactionUtils from '@libs/TransactionUtils'; -import {policyPropTypes} from '@pages/workspace/withPolicy'; +import { policyPropTypes } from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -44,7 +44,8 @@ import Switch from './Switch'; import tagPropTypes from './tagPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails, { withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes } from './withCurrentUserPersonalDetails'; + const propTypes = { /** Callback to inform parent modal of success */ @@ -701,9 +702,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.rate')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - // TODO: Add the onPress function - onPress={() => {}} - // onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_RATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())); + }} disabled={didConfirm || !isTypeRequest} interactive={!isReadOnly} /> diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 2be262aa5f0f..941900c3bf2c 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -89,6 +89,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/request/step/IOURequestStepDate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: () => require('../../../pages/iou/request/step/IOURequestStepDescription').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: () => require('../../../pages/iou/request/step/IOURequestStepDistance').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.STEP_RATE]: () => require('../../../pages/iou/request/step/IOURequestStepRate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_MERCHANT]: () => require('../../../pages/iou/request/step/IOURequestStepMerchant').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: () => require('../../../pages/iou/request/step/IOURequestStepParticipants').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_SCAN]: () => require('../../../pages/iou/request/step/IOURequestStepScan').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 48d649cc4dd9..9163c0b9bfe5 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -416,6 +416,7 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.STEP_DATE]: ROUTES.MONEY_REQUEST_STEP_DATE.route, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.route, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: ROUTES.MONEY_REQUEST_STEP_DISTANCE.route, + [SCREENS.MONEY_REQUEST.STEP_RATE]: ROUTES.MONEY_REQUEST_STEP_RATE.route, [SCREENS.MONEY_REQUEST.HOLD]: ROUTES.MONEY_REQUEST_HOLD_REASON.route, [SCREENS.MONEY_REQUEST.STEP_MERCHANT]: ROUTES.MONEY_REQUEST_STEP_MERCHANT.route, [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.route, diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx new file mode 100644 index 000000000000..347e627624e9 --- /dev/null +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import SelectionList from '@components/SelectionList'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; +import type {Route} from '@src/ROUTES'; + +type Props = { + // eslint-disable-next-line react/no-unused-prop-types + lastSelectedDistanceRate: string; + /** The route object passed to this screen */ + route: { + /** The params passed to this screen */ + params: { + /** The route to go back to */ + backTo: Route; + }; + }; +}; + +const mockRates = [ + {text: 'Default Rate', alternateText: '0.656/mile', keyForList: 'DefaultRate'}, + {text: 'Custom Rate', alternateText: '0.700/mile', keyForList: 'CustomRate'}, +]; + +function IOURequestStepRate({ + route: { + params: {backTo}, + }, +}: Props) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + Navigation.goBack(backTo)} + shouldShowWrapper={Boolean(backTo)} + testID="rate" + > + {translate('themePage.chooseThemeBelowOrSync')} + + {}} + initiallyFocusedOptionKey="DefaultRate" + /> + + ); +} + +IOURequestStepRate.displayName = 'IOURequestStepRate'; + +// export default withOnyx({ +// lastSelectedDistanceRate: { +// key: 'ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATE', +// }, +// })(IOURequestStepRate); + +export default IOURequestStepRate; From b3e1f48577d3cd8e1b4c041ce661ae684507fdd0 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 29 Feb 2024 10:07:54 +0100 Subject: [PATCH 005/283] feat: add translation for the rate page --- src/languages/en.ts | 1 + src/languages/es.ts | 2 ++ src/pages/iou/request/step/IOURequestStepRate.tsx | 6 +++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 920705883b02..d904a52c1b7a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -696,6 +696,7 @@ export default { set: 'set', changed: 'changed', removed: 'removed', + chooseARate: 'Choose a rate to use below', }, notificationPreferencesPage: { header: 'Notification preferences', diff --git a/src/languages/es.ts b/src/languages/es.ts index 1b858f707851..842f357639c8 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -691,6 +691,8 @@ export default { set: 'estableció', changed: 'cambió', removed: 'eliminó', + // TODO: check if this is the correct translation + chooseARate: 'Elige una tarifa para utilizar a continuación', }, notificationPreferencesPage: { header: 'Preferencias de avisos', diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 347e627624e9..d82238f2c7ea 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -23,8 +23,8 @@ type Props = { }; const mockRates = [ - {text: 'Default Rate', alternateText: '0.656/mile', keyForList: 'DefaultRate'}, - {text: 'Custom Rate', alternateText: '0.700/mile', keyForList: 'CustomRate'}, + {text: 'Default Rate', alternateText: '$0.656 / mile', keyForList: 'DefaultRate'}, + {text: 'Custom Rate', alternateText: '$0.700 / mile', keyForList: 'CustomRate'}, ]; function IOURequestStepRate({ @@ -42,7 +42,7 @@ function IOURequestStepRate({ shouldShowWrapper={Boolean(backTo)} testID="rate" > - {translate('themePage.chooseThemeBelowOrSync')} + {translate('iou.chooseARate')} Date: Thu, 29 Feb 2024 17:59:45 +0100 Subject: [PATCH 006/283] feat: wip - display rates for policy --- ...oraryForRefactorRequestConfirmationList.js | 4 +- src/libs/DistanceRequestUtils.ts | 119 +++++++++++++++--- .../iou/request/step/IOURequestStepRate.tsx | 49 +++++--- src/types/onyx/Policy.ts | 4 +- 4 files changed, 139 insertions(+), 37 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 46e4cb3d6733..267662f07157 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -681,7 +681,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (policy, key) => { + if (!policy || !key || !policy.name) { + return; + } + + policies[key] = policy; + }, +}); + /** * Retrieves the default mileage rate based on a given policy. * @@ -67,22 +81,21 @@ function convertDistanceUnit(distanceInMeters: number, unit: Unit): number { */ function getRoundedDistanceInUnits(distanceInMeters: number, unit: Unit): string { const convertedDistance = convertDistanceUnit(distanceInMeters, unit); - return convertedDistance.toFixed(2); + // TODO: add logic for currencies for which we need to round to 4 decimals + return convertedDistance.toFixed(3); } /** -* @param hasRoute Whether the route exists for the distance request -* @param distanceInMeters Distance traveled -* @param unit Unit that should be used to display the distance -* @param rate Expensable amount allowed per unit -* @param currency The currency associated with the rate -* @param translate Translate function -* @param toLocaleDigit Function to convert to localized digit -* @returns A string that describes the distance traveled and the rate used for expense calculation -*/ + * @param hasRoute Whether the route exists for the distance request + * @param unit Unit that should be used to display the distance + * @param rate Expensable amount allowed per unit + * @param currency The currency associated with the rate + * @param translate Translate function + * @param toLocaleDigit Function to convert to localized digit + * @returns A string that describes the distance traveled and the rate used for expense calculation + */ function getRateForDisplay( hasRoute: boolean, - distanceInMeters: number, unit: Unit, rate: number, currency: string, @@ -101,6 +114,28 @@ function getRateForDisplay( return `${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`; } +// TODO: I wonder if it would be better to rfactor these functions to pass params in an object +/** + * @param hasRoute Whether the route exists for the distance request + * @param distanceInMeters Distance traveled + * @param unit Unit that should be used to display the distance + * @param rate Expensable amount allowed per unit + * @param translate Translate function + * @returns A string that describes the distance traveled + */ +function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit: Unit, translate: LocaleContextProps['translate']): string { + if (!hasRoute) { + return translate('iou.routePending'); + } + + const distanceInUnits = getRoundedDistanceInUnits(distanceInMeters, unit); + const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers'); + const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); + const unitString = distanceInUnits === '1' ? singularDistanceUnit : distanceUnit; + + return `${distanceInUnits} ${unitString}`; +} + /** * @param hasRoute Whether the route exists for the distance request * @param distanceInMeters Distance traveled @@ -124,13 +159,52 @@ function getDistanceMerchant( return translate('iou.routePending'); } - const distanceInUnits = getRoundedDistanceInUnits(distanceInMeters, unit); - const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers'); - const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); - const unitString = distanceInUnits === '1' ? singularDistanceUnit : distanceUnit; - const ratePerUnit = getRateForDisplay(hasRoute, distanceInMeters, unit, rate, currency, translate, toLocaleDigit); + const distanceInUnits = getDistanceForDisplay(hasRoute, distanceInMeters, unit, translate); + const ratePerUnit = getRateForDisplay(hasRoute, unit, rate, currency, translate, toLocaleDigit); + + return `${distanceInUnits} @ ${ratePerUnit}`; +} + +/** + * Retrieves the mileage rates for given policy. + * + * @param policyID - The policy ID from which to extract the mileage rates. + * + * @returns An array of mileage rates or an empty array if not found. + */ +function getMileageRates(policyID?: string): MileageRate[] | [] { + if (!policyID) { + return []; + } + + const mileageRates: MileageRate[] = []; + + const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? null; + + if (!policy || !policy?.customUnits) { + return mileageRates; + } + + const distanceUnit = Object.values(policy.customUnits).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + if (!distanceUnit?.rates) { + return mileageRates; + } + + const rates = Object.values(distanceUnit.rates); + + for (const rate of rates) { + if (rate.enabled) { + mileageRates.push({ + rate: rate.rate ?? 0, + name: rate.name, + currency: rate.currency ?? 'USD', + unit: distanceUnit.attributes.unit, + customUnitRateID: rate.customUnitRateID, + }); + } + } - return `${distanceInUnits} ${unitString} @ ${ratePerUnit}`; + return mileageRates; } /** @@ -147,4 +221,11 @@ function getDistanceRequestAmount(distance: number, unit: Unit, rate: number): n return Math.round(roundedDistance * rate); } -export default {getDefaultMileageRate, getDistanceMerchant, getDistanceRequestAmount, getRateForDisplay}; +export default { + getDefaultMileageRate, + getDistanceMerchant, + getDistanceRequestAmount, + getRateForDisplay, + getMileageRates, + getDistanceForDisplay, +}; diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index d82238f2c7ea..3b7889de9651 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -1,17 +1,27 @@ import React from 'react'; import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import compose from '@libs/compose'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; +import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; +import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; +import type {Policy} from '@src/types/onyx'; type Props = { // eslint-disable-next-line react/no-unused-prop-types lastSelectedDistanceRate: string; + + /** Policy details */ + policy: OnyxEntry; + /** The route object passed to this screen */ route: { /** The params passed to this screen */ @@ -22,18 +32,22 @@ type Props = { }; }; -const mockRates = [ - {text: 'Default Rate', alternateText: '$0.656 / mile', keyForList: 'DefaultRate'}, - {text: 'Custom Rate', alternateText: '$0.700 / mile', keyForList: 'CustomRate'}, -]; - function IOURequestStepRate({ + policy, route: { params: {backTo}, }, }: Props) { const styles = useThemeStyles(); - const {translate} = useLocalize(); + const {translate, toLocaleDigit} = useLocalize(); + const rates = DistanceRequestUtils.getMileageRates(policy?.id); + + const data = rates.map((rate) => ({ + text: rate.name ?? '', + alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), + keyForList: rate.name ?? '', + value: rate.customUnitRateID, + })); return ( {translate('iou.chooseARate')} {}} - initiallyFocusedOptionKey="DefaultRate" + // TODO: change for lastSelectedDistanceRates + initiallyFocusedOptionKey="Default Rate" /> ); @@ -56,10 +71,14 @@ function IOURequestStepRate({ IOURequestStepRate.displayName = 'IOURequestStepRate'; -// export default withOnyx({ -// lastSelectedDistanceRate: { -// key: 'ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATE', -// }, -// })(IOURequestStepRate); - -export default IOURequestStepRate; +export default compose( + withWritableReportOrNotFound, + withOnyx({ + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + }, + // lastSelectedDistanceRates: { + // key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, + // }, + }), +)(IOURequestStepRate); diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 1eece2d3a1e0..8e408951a84f 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -19,8 +19,10 @@ type Attributes = { type MileageRate = { unit: Unit; - rate?: number; currency: string; + customUnitRateID?: string; + rate?: number; + name?: string; }; type CustomUnit = OnyxCommon.OnyxValueWithOfflineFeedback<{ From cde1d19d37cdd0203ca6023612dfe12ea0f46537 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 1 Mar 2024 16:12:00 +0100 Subject: [PATCH 007/283] feat: wip - get rate for p2p --- ...oraryForRefactorRequestConfirmationList.js | 52 ++++++++++++++++--- src/libs/DistanceRequestUtils.ts | 13 +++-- ...yForRefactorRequestParticipantsSelector.js | 4 +- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 267662f07157..5a7019e63428 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -239,6 +239,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, + lastSelectedDistanceRate = {}, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -250,6 +251,12 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeSend = iouType === CONST.IOU.TYPE.SEND; const {unit, rate, currency} = mileageRate; + + // const rate = transaction.comment.customUnit.customUnitRateID === '_FAKE_P2P_ID_' + + // const {unit, rate, currency} = lastSelectedDistanceRate || mileageRate; + + const distance = lodashGet(transaction, 'routes.route0.distance', 0); const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; const taxRates = lodashGet(policy, 'taxRates', {}); @@ -266,6 +273,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowDate = shouldShowSmartScanFields || isDistanceRequest; const shouldShowMerchant = shouldShowSmartScanFields && !isDistanceRequest; + console.log({transaction}); + const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); // A flag for showing the tags field @@ -622,6 +631,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); }, [isReadOnly, iouType, selectedParticipants.length, confirm, bankAccountRoute, iouCurrencyCode, policyID, splitOrRequestOptions, formError, styles.ph1, styles.mb2]); + // TODO: change for a value from usePermissions [will be added in this PR https://github.com/Expensify/App/pull/37185] + // change for true for development + const canUseP2PDistanceRequests = true; + // An intermediate structure that helps us classify the fields as "primary" and "supplementary". // The primary fields are always shown to the user, while an extra action is needed to reveal the supplementary ones. const classifiedFields = [ @@ -681,7 +694,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ), - shouldShow: isDistanceRequest, + shouldShow: isDistanceRequest && !canUseP2PDistanceRequests, isSupplementary: true, }, { item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + disabled={didConfirm || !isTypeRequest} + interactive={!isReadOnly} + /> + ), + shouldShow: isDistanceRequest && canUseP2PDistanceRequests, + isSupplementary: true, + }, + { + item: ( + { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_RATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_RATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ); }} disabled={didConfirm || !isTypeRequest} - interactive={!isReadOnly} + interactive={!isReadOnly && isPolicyExpenseChat} /> ), - // TODO: hide when betas ready - shouldShow: isDistanceRequest, + shouldShow: isDistanceRequest && canUseP2PDistanceRequests, isSupplementary: true, }, { @@ -950,12 +982,16 @@ export default compose( policyTags: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, }, + // TODO: add NVP_LAST_SELECTED_DISTANCE_RATES + // lastSelectedDistanceRates: { + // key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, + // }, mileageRate: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, selector: DistanceRequestUtils.getDefaultMileageRate, }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - }, + } }), )(MoneyTemporaryForRefactorRequestConfirmationList); diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index a605ace8da87..191edd93bfb9 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -12,6 +12,8 @@ type DefaultMileageRate = { rate?: number; currency?: string; unit: Unit; + name?: string; + customUnitRateID?: string; }; const policies: OnyxCollection = {}; @@ -55,6 +57,8 @@ function getDefaultMileageRate(policy: OnyxEntry): DefaultMileageRate | rate: distanceRate.rate, currency: distanceRate.currency, unit: distanceUnit.attributes.unit, + name: distanceRate.name, + customUnitRateID: distanceRate.customUnitRateID, }; } @@ -91,6 +95,7 @@ function getRoundedDistanceInUnits(distanceInMeters: number, unit: Unit): string return convertedDistance.toFixed(3); } +// TODO: I wonder if it would be better to refactor these functions to pass params in an object /** * @param hasRoute Whether the route exists for the distance request * @param unit Unit that should be used to display the distance @@ -120,7 +125,7 @@ function getRateForDisplay( return `${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`; } -// TODO: I wonder if it would be better to rfactor these functions to pass params in an object +// TODO: this function will be added in https://github.com/Expensify/App/pull/37185, remove it to avoid conflicts /** * @param hasRoute Whether the route exists for the distance request * @param distanceInMeters Distance traveled @@ -129,8 +134,8 @@ function getRateForDisplay( * @param translate Translate function * @returns A string that describes the distance traveled */ -function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit: Unit, translate: LocaleContextProps['translate']): string { - if (!hasRoute) { +function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit: Unit, rate: number, translate: LocaleContextProps['translate']): string { + if (!hasRoute || !rate) { return translate('iou.routePending'); } @@ -165,7 +170,7 @@ function getDistanceMerchant( return translate('iou.routePending'); } - const distanceInUnits = getDistanceForDisplay(hasRoute, distanceInMeters, unit, translate); + const distanceInUnits = getDistanceForDisplay(hasRoute, distanceInMeters, unit, rate, translate); const ratePerUnit = getRateForDisplay(hasRoute, unit, rate, currency, translate, toLocaleDigit); return `${distanceInUnits} @ ${ratePerUnit}`; diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 2865316b7fd5..b42b94afc686 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -121,7 +121,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ iouType === CONST.IOU.TYPE.REQUEST, // We don't want to include any P2P options like personal details or reports that are not workspace chats for certain features. - iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, + true, false, {}, [], @@ -131,7 +131,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ // We don't want the user to be able to invite individuals when they are in the "Distance request" flow for now. // This functionality is being built here: https://github.com/Expensify/App/issues/23291 - iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, + true, false, ); From c3cb80bf507de6c4922b28e170893cdcaa126ebb Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 1 Mar 2024 17:38:59 +0100 Subject: [PATCH 008/283] feat: wip - new rate field logic --- src/CONST.ts | 7 ++++ ...oraryForRefactorRequestConfirmationList.js | 40 +++++++++---------- src/libs/DistanceRequestUtils.ts | 39 +++++++++--------- src/libs/actions/IOU.ts | 13 ++++++ .../iou/request/step/IOURequestStepRate.tsx | 12 +++++- 5 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 8d4eaac44a38..2bd047f6bf51 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1398,6 +1398,13 @@ const CONST = { RATE_DECIMALS: 3, }, + // TODO: remove this mock when https://github.com/Expensify/App/issues/36982 is done + CURRENCY_TO_DEFAULT_MILEAGE_RATE: { + USD: { unit: "mile", rate: 0.5 }, + EUR: { unit: "kilometer", rate: 0.8 }, + GBP: { unit: "mile", rate: 0.45 }, + }, + TERMS: { CFPB_PREPAID: 'cfpb.gov/prepaid', CFPB_COMPLAINT: 'cfpb.gov/complaint', diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 5a7019e63428..9c2d77239015 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -1,10 +1,10 @@ -import { useIsFocused } from '@react-navigation/native'; -import { format } from 'date-fns'; +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 { View } from 'react-native'; -import { withOnyx } from 'react-native-onyx'; +import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; @@ -21,9 +21,9 @@ 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 playSound, {SOUNDS} from '@libs/Sound'; import * as TransactionUtils from '@libs/TransactionUtils'; -import { policyPropTypes } from '@pages/workspace/withPolicy'; +import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -44,8 +44,7 @@ import Switch from './Switch'; import tagPropTypes from './tagPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; -import withCurrentUserPersonalDetails, { withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes } from './withCurrentUserPersonalDetails'; - +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; const propTypes = { /** Callback to inform parent modal of success */ @@ -239,7 +238,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, - lastSelectedDistanceRate = {}, + mileageRates, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -250,12 +249,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeSplit = iouType === CONST.IOU.TYPE.SPLIT; const isTypeSend = iouType === CONST.IOU.TYPE.SEND; + // TODO: uncomment after Splits and P2P are enabled https://github.com/Expensify/App/pull/37185, mileageRate prop should be be removed + // const mileageRate = transaction.comment.customUnit.customUnitRateID === '_FAKE_P2P_ID_' ? DistanceRequestUtils.getRateForP2P(policy.outputCurrency) : mileageRates[transaction.comment.customUnit.customUnitRateID]; const {unit, rate, currency} = mileageRate; - // const rate = transaction.comment.customUnit.customUnitRateID === '_FAKE_P2P_ID_' - - // const {unit, rate, currency} = lastSelectedDistanceRate || mileageRate; - + // will hardcoded rates will have a currency property? If not we have to get currency other way const distance = lodashGet(transaction, 'routes.route0.distance', 0); const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; @@ -273,8 +271,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const shouldShowDate = shouldShowSmartScanFields || isDistanceRequest; const shouldShowMerchant = shouldShowSmartScanFields && !isDistanceRequest; - console.log({transaction}); - const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); // A flag for showing the tags field @@ -633,7 +629,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // TODO: change for a value from usePermissions [will be added in this PR https://github.com/Expensify/App/pull/37185] // change for true for development - const canUseP2PDistanceRequests = true; + const canUseP2PDistanceRequests = false; // An intermediate structure that helps us classify the fields as "primary" and "supplementary". // The primary fields are always shown to the user, while an extra action is needed to reveal the supplementary ones. @@ -982,16 +978,16 @@ export default compose( policyTags: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, }, - // TODO: add NVP_LAST_SELECTED_DISTANCE_RATES - // lastSelectedDistanceRates: { - // key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, - // }, mileageRate: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, selector: DistanceRequestUtils.getDefaultMileageRate, }, + mileageRates: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: (policy) => DistanceRequestUtils.getMileageRates(policy ? policy.id : ''), + }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - } + }, }), )(MoneyTemporaryForRefactorRequestConfirmationList); diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 191edd93bfb9..dd95612af3e6 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -1,4 +1,4 @@ -import type {OnyxEntry, OnyxCollection} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import CONST from '@src/CONST'; @@ -183,13 +183,13 @@ function getDistanceMerchant( * * @returns An array of mileage rates or an empty array if not found. */ -function getMileageRates(policyID?: string): DefaultMileageRate[] | [] { +function getMileageRates(policyID?: string): Record { + const mileageRates = {}; + if (!policyID) { - return []; + return mileageRates; } - const mileageRates: DefaultMileageRate[] = []; - const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? null; if (!policy || !policy?.customUnits) { @@ -201,23 +201,25 @@ function getMileageRates(policyID?: string): DefaultMileageRate[] | [] { return mileageRates; } - const rates = Object.values(distanceUnit.rates); - - for (const rate of rates) { - if (rate.enabled) { - mileageRates.push({ - rate: rate.rate ?? 0, - name: rate.name, - currency: rate.currency ?? 'USD', - unit: distanceUnit.attributes.unit, - customUnitRateID: rate.customUnitRateID, - }); - } - } + Object.entries(distanceUnit.rates).forEach(([rateID, rate]) => { + // TODO: fix TS error + mileageRates[rateID] = { + rate: rate.rate, + currency: rate.currency, + unit: distanceUnit.attributes.unit, + name: rate.name, + customUnitRateID: rate.customUnitRateID, + }; + }); return mileageRates; } +// TODO: probably will need to be changed +function getRateForP2P(currency) { + return CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currency] ?? CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE.USD; +} + /** * Calculates the request amount based on distance, unit, and rate. * @@ -239,4 +241,5 @@ export default { getRateForDisplay, getMileageRates, getDistanceForDisplay, + getRateForP2P, }; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 5f9657755b02..91dd95b12592 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -355,6 +355,17 @@ function setMoneyRequestReceipt(transactionID: string, source: string, filename: }); } +/** Set the last selected distance rate for policy */ +// TODO: probably need to be changed +function setLastSelectedDistanceRates(policyID: string, rateID: string) { + Onyx.merge('lastSelectedDistanceRates', {[policyID]: rateID}); +} + +/** Update transaction distance rate */ +function updateDistanceRequestRate(transactionID: string, rateID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {comment: {customUnit: {customUnitRateID: rateID}}}); +} + /** Reset money request info from the store with its initial value */ function resetMoneyRequestInfo(id = '') { // Disabling this line since currentDate can be an empty string @@ -4327,4 +4338,6 @@ export { cancelPayment, navigateToStartStepIfScanFileCannotBeRead, savePreferredPaymentMethod, + setLastSelectedDistanceRates, + updateDistanceRequestRate, }; diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 3b7889de9651..a2cf7a0af1c8 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -9,6 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as IOU from '@libs/actions/IOU'; import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -42,13 +43,20 @@ function IOURequestStepRate({ const {translate, toLocaleDigit} = useLocalize(); const rates = DistanceRequestUtils.getMileageRates(policy?.id); - const data = rates.map((rate) => ({ + const data = Object.values(rates).map((rate) => ({ text: rate.name ?? '', alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), keyForList: rate.name ?? '', value: rate.customUnitRateID, })); + const selectDistanceRate = (customUnitRateID) => { + IOU.setLastSelectedDistanceRates(policy?.id ?? '', customUnitRateID); + // TODO: get a proper transaction ID + IOU.updateDistanceRequestRate('1', customUnitRateID); + Navigation.goBack(backTo); + } + return ( {}} + onSelectRow={({value}) => selectDistanceRate(value)} // TODO: change for lastSelectedDistanceRates initiallyFocusedOptionKey="Default Rate" /> From 5bf399af20bdb7816bedf1ed40ee37fa83e3fffa Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 1 Mar 2024 17:51:11 +0100 Subject: [PATCH 009/283] fix: revert filtering changes --- .../MoneyTemporaryForRefactorRequestParticipantsSelector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index b42b94afc686..2865316b7fd5 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -121,7 +121,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ iouType === CONST.IOU.TYPE.REQUEST, // We don't want to include any P2P options like personal details or reports that are not workspace chats for certain features. - true, + iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, false, {}, [], @@ -131,7 +131,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ // We don't want the user to be able to invite individuals when they are in the "Distance request" flow for now. // This functionality is being built here: https://github.com/Expensify/App/issues/23291 - true, + iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE, false, ); From ef7b30e6a95bc95ed2ead63f27e25636398f7c6b Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 13 Mar 2024 10:19:52 +0100 Subject: [PATCH 010/283] refactor: use canUseP2PDistanceRequests from usePermissions --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 182aa5464772..7333d405363b 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -664,10 +664,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ); }, [isReadOnly, iouType, selectedParticipants.length, confirm, bankAccountRoute, iouCurrencyCode, policyID, splitOrRequestOptions, formError, styles.ph1, styles.mb2]); - // TODO: change for a value from usePermissions [will be added in this PR https://github.com/Expensify/App/pull/37185] - // change for true for development - const canUseP2PDistanceRequests = false; - // An intermediate structure that helps us classify the fields as "primary" and "supplementary". // The primary fields are always shown to the user, while an extra action is needed to reveal the supplementary ones. const classifiedFields = [ From 6f3afddef1d036658a48b38ca8f0caf6c696c85f Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 13 Mar 2024 17:28:24 +0100 Subject: [PATCH 011/283] fix: remove mock rates --- src/CONST.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 9b2502cb17d1..d4fbd0ff6ef3 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1431,13 +1431,6 @@ const CONST = { FAKE_P2P_ID: '_FAKE_P2P_ID_', }, - // TODO: remove this mock when https://github.com/Expensify/App/issues/36982 is done - CURRENCY_TO_DEFAULT_MILEAGE_RATE: { - USD: { unit: "mile", rate: 0.5 }, - EUR: { unit: "kilometer", rate: 0.8 }, - GBP: { unit: "mile", rate: 0.45 }, - }, - TERMS: { CFPB_PREPAID: 'cfpb.gov/prepaid', CFPB_COMPLAINT: 'cfpb.gov/complaint', From 50174f38629f23ef3363e51613755b05e30ec3d0 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 13 Mar 2024 17:29:24 +0100 Subject: [PATCH 012/283] feat: create function for getting personal policy --- src/libs/PolicyUtils.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 4689fd03ebd0..6a3829b10934 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1,5 +1,6 @@ import Str from 'expensify-common/lib/str'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -12,6 +13,14 @@ import Navigation from './Navigation/Navigation'; type MemberEmailsToAccountIDs = Record; type UnitRate = {rate: number}; +let allPolicies: OnyxCollection; + +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + waitForCollectionCallback: true, + callback: (value) => (allPolicies = value), +}); + /** * Filter out the active policies, which will exclude policies with pending deletion * These are policies that we can use to create reports with in NewDot. @@ -265,6 +274,10 @@ function goBackFromInvalidPolicy() { Navigation.navigateWithSwitchPolicyID({route: ROUTES.ALL_SETTINGS}); } +function getPersonalPolicy() { + return Object.values(allPolicies ?? {}).find((policy) => policy?.type === CONST.POLICY.TYPE.PERSONAL); +} + export { getActivePolicies, hasAccountingConnections, @@ -295,6 +308,7 @@ export { getPathWithoutPolicyID, getPolicyMembersByIdWithoutCurrentUser, goBackFromInvalidPolicy, + getPersonalPolicy }; export type {MemberEmailsToAccountIDs}; From eb3aff8ee97f1f055d22376ea7004ba575ff1cb9 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 14 Mar 2024 19:15:31 +0100 Subject: [PATCH 013/283] feat: add rate field to MoneyRequestView --- .../ReportActionItem/MoneyRequestView.tsx | 68 +++++++++++++++---- src/libs/DistanceRequestUtils.ts | 17 ++--- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 70c65d1d66ce..bcaa7f830925 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -21,6 +21,7 @@ import type {ViolationField} from '@hooks/useViolations'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CardUtils from '@libs/CardUtils'; import * as CurrencyUtils from '@libs/CurrencyUtils'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; @@ -89,8 +90,8 @@ function MoneyRequestView({ const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {isSmallScreenWidth} = useWindowDimensions(); - const {translate} = useLocalize(); - const {canUseViolations} = usePermissions(); + const {translate, toLocaleDigit} = useLocalize(); + const {canUseViolations, canUseP2PDistanceRequests} = usePermissions(); const parentReportAction = parentReportActions?.[report.parentReportActionID ?? ''] ?? null; const moneyRequestReport = parentReport; const { @@ -151,6 +152,20 @@ function MoneyRequestView({ let amountDescription = `${translate('iou.amount')}`; + const hasRoute = TransactionUtils.hasRoute(transaction); + const distance = transaction?.routes?.route0?.distance ?? 0; + const rateID = transaction?.comment.customUnit?.customUnitRateID ?? '0'; + + const rates = DistanceRequestUtils.getMileageRates(policy?.id); + const {unit, currency, rate} = rates[rateID as string] ?? { + unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + currency: CONST.CURRENCY.USD, + rate: 0, + }; + + const rateToDisplay = DistanceRequestUtils.getRateForDisplay(hasRoute, unit, rate, currency, translate, toLocaleDigit); + const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate); + const saveBillable = useCallback( (newBillable: boolean) => { // If the value hasn't changed, don't request to save changes on the server and just close the modal @@ -236,6 +251,44 @@ function MoneyRequestView({ [transactionAmount, isSettled, isCancelled, isPolicyExpenseChat, isEmptyMerchant, transactionDate, hasErrors, canUseViolations, hasViolations, translate, getViolationsForField], ); + const distanceRequestFields = canUseP2PDistanceRequests ? ( + <> + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} + /> + + + {}} + /> + + + ) : ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} + /> + + ) + return ( @@ -320,16 +373,7 @@ function MoneyRequestView({ /> {isDistanceRequest ? ( - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} - /> - + distanceRequestFields ) : ( { - const mileageRates = {}; + const mileageRates: Record = {}; if (!policyID) { return mileageRates; @@ -202,7 +201,6 @@ function getMileageRates(policyID?: string): Record } Object.entries(distanceUnit.rates).forEach(([rateID, rate]) => { - // TODO: fix TS error mileageRates[rateID] = { rate: rate.rate, currency: rate.currency, @@ -215,8 +213,7 @@ function getMileageRates(policyID?: string): Record return mileageRates; } -// TODO: probably will need to be changed -function getRateForP2P(currency) { +function getRateForP2P(currency: string) { return CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currency] ?? CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE.USD; } From 7dc206cf66ae8f96ddea9883b16334618b4a5379 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 18 Mar 2024 19:57:19 -0300 Subject: [PATCH 014/283] feat: change customRateID when participant changes, other improvements --- ...oraryForRefactorRequestConfirmationList.js | 25 ++++--- .../ReportActionItem/MoneyRequestView.tsx | 72 +++++++++---------- src/libs/DistanceRequestUtils.ts | 3 +- src/libs/TransactionUtils.ts | 9 +++ src/libs/actions/IOU.ts | 6 +- ...yForRefactorRequestParticipantsSelector.js | 2 +- .../step/IOURequestStepParticipants.js | 35 ++++++++- .../iou/request/step/IOURequestStepRate.tsx | 28 +++++--- 8 files changed, 116 insertions(+), 64 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 93831606416e..ce5668924271 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -224,7 +224,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ isReadOnly, isScanRequest, listStyles, - mileageRate, + mileageRates, onConfirm, onSelectParticipant, onSendMoney, @@ -242,7 +242,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, - mileageRates, }) { const theme = useTheme(); const styles = useThemeStyles(); @@ -254,14 +253,19 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeSend = iouType === CONST.IOU.TYPE.SEND; const canEditDistance = isTypeRequest || (canUseP2PDistanceRequests && isTypeSplit); - // TODO: uncomment after Splits and P2P are enabled https://github.com/Expensify/App/pull/37185, mileageRate prop should be be removed - // const mileageRate = transaction.comment.customUnit.customUnitRateID === '_FAKE_P2P_ID_' ? DistanceRequestUtils.getRateForP2P(policy.outputCurrency) : mileageRates[transaction.comment.customUnit.customUnitRateID]; - const {unit, rate, currency} = mileageRate; + const personalPolicy = policyID === CONST.POLICY.ID_FAKE ? PolicyUtils.getPersonalPolicy() : policy; + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) + ? DistanceRequestUtils.getRateForP2P(personalPolicy.outputCurrency) + : mileageRates[transaction.comment.customUnit.customUnitRateID]; - // will hardcoded rates will have a currency property? If not we have to get currency other way + const {unit, rate} = mileageRate || { + unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * 100, + }; + + const currency = personalPolicy.outputCurrency; 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 @@ -292,7 +296,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const formattedAmount = isDistanceRequestWithPendingRoute ? '' : CurrencyUtils.convertToDisplayString( - shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : iouAmount, + isDistanceRequest && iouAmount === 0 ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : iouAmount, isDistanceRequest ? currency : iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); @@ -359,13 +363,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, [isFocused, transaction, shouldDisplayFieldError, hasSmartScanFailed, didConfirmSplit, isMerchantRequired, merchantError]); useEffect(() => { - if (!shouldCalculateDistanceAmount) { + if (!isDistanceRequest) { return; } const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate); IOU.setMoneyRequestAmount_temporaryForRefactor(transaction.transactionID, amount, currency); - }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency]); + }, [isDistanceRequest, distance, rate, unit, transaction, currency]); /** * Returns the participants with amount @@ -757,7 +761,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ item: ( - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} - /> - - - {}} - /> - - - ) : ( - - Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} - /> - - ) + <> + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} + /> + + + {}} + /> + + + ) : ( + + Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} + /> + + ); return ( diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index e954ce4d266d..28714a92ed84 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -91,8 +91,7 @@ function convertDistanceUnit(distanceInMeters: number, unit: Unit): number { */ function getRoundedDistanceInUnits(distanceInMeters: number, unit: Unit): string { const convertedDistance = convertDistanceUnit(distanceInMeters, unit); - // TODO: add logic for currencies for which we need to round to 4 decimals - return convertedDistance.toFixed(3); + return convertedDistance.toFixed(2); } // TODO: I wonder if it would be better to refactor these functions to pass params in an object diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index bc94c8fee8fc..f694199fbeb4 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -584,6 +584,14 @@ function getEnabledTaxRateCount(options: TaxRates) { return Object.values(options).filter((option: TaxRate) => !option.isDisabled).length; } +/** + * Check if the customUnitRateID has a value default for P2P distance requests + */ + +function isCustomUnitRateIDForP2P(transaction: Transaction): boolean { + return transaction?.comment?.customUnit?.customUnitRateID === CONST.CUSTOM_UNITS.FAKE_P2P_ID; +} + export { buildOptimisticTransaction, calculateTaxAmount, @@ -633,6 +641,7 @@ export { waypointHasValidAddress, getRecentTransactions, hasViolation, + isCustomUnitRateIDForP2P, }; export type {TransactionChanges}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 477fb99900b8..31cd49269e2c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -253,8 +253,9 @@ function initMoneyRequest(reportID: string, policy: OnyxEntry, waypoint1: {}, }; const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; + const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; - if (ReportUtils.isPolicyExpenseChat(report)) { + if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; } comment.customUnit = {customUnitRateID}; @@ -373,9 +374,8 @@ function setMoneyRequestReceipt(transactionID: string, source: string, filename: } /** Set the last selected distance rate for policy */ -// TODO: probably need to be changed function setLastSelectedDistanceRates(policyID: string, rateID: string) { - Onyx.merge('lastSelectedDistanceRates', {[policyID]: rateID}); + Onyx.merge(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, {[policyID]: rateID}); } /** Update transaction distance rate */ diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index cdc3e72f98f4..7bdcafef61e6 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -190,7 +190,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({ const addSingleParticipant = (option) => { onParticipantsAdded([ { - ..._.pick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText'), + ..._.pick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText', 'policyID'), selected: true, }, ]); diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 5ca465d8fb78..8c92961a28c4 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -1,16 +1,21 @@ import {useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; +import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import StepScreenWrapper from './StepScreenWrapper'; @@ -24,6 +29,9 @@ const propTypes = { /* Onyx Props */ /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, + + // eslint-disable-next-line + lastSelectedDistanceRates: PropTypes.object, }; const defaultProps = { @@ -36,6 +44,7 @@ function IOURequestStepParticipants({ }, transaction, transaction: {participants = []}, + lastSelectedDistanceRates = {}, }) { const {translate} = useLocalize(); const navigation = useNavigation(); @@ -100,6 +109,22 @@ function IOURequestStepParticipants({ } IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); + + // change customUnitRateID when choosing + if (val[0].isPolicyExpenseChat && TransactionUtils.isCustomUnitRateIDForP2P(transaction)) { + let customUnitRateID = ''; + if (val[0].policyID && lastSelectedDistanceRates[val[0].policyID]) { + customUnitRateID = lastSelectedDistanceRates[val[0].policyID]; + } else { + const policy = ReportUtils.getPolicy(val[0].policyID); + const defaultRate = DistanceRequestUtils.getDefaultMileageRate(policy); + customUnitRateID = defaultRate ? defaultRate.customUnitRateID : ''; + } + IOU.updateDistanceRequestRate(transactionID, customUnitRateID); + } else if (!TransactionUtils.isCustomUnitRateIDForP2P(transaction)) { + IOU.updateDistanceRequestRate(transactionID, CONST.CUSTOM_UNITS.FAKE_P2P_ID); + } + numberOfParticipants.current = val.length; // When multiple participants are selected, the reportID is generated at the end of the confirmation step. @@ -163,4 +188,12 @@ IOURequestStepParticipants.displayName = 'IOURequestStepParticipants'; IOURequestStepParticipants.propTypes = propTypes; IOURequestStepParticipants.defaultProps = defaultProps; -export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepParticipants); +export default compose( + withWritableReportOrNotFound, + withFullTransactionOrNotFound, + withOnyx({ + lastSelectedDistanceRates: { + key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, + }, + }), +)(IOURequestStepParticipants); diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index a2cf7a0af1c8..10513f7a44ca 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -6,19 +6,21 @@ import RadioListItem from '@components/SelectionList/RadioListItem'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as IOU from '@libs/actions/IOU'; import compose from '@libs/compose'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as IOU from '@libs/actions/IOU'; import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import type {Policy} from '@src/types/onyx'; + type Props = { - // eslint-disable-next-line react/no-unused-prop-types - lastSelectedDistanceRate: string; + /** Object of last selected rates for the policies */ + lastSelectedDistanceRates: Record; /** Policy details */ policy: OnyxEntry; @@ -38,11 +40,14 @@ function IOURequestStepRate({ route: { params: {backTo}, }, + lastSelectedDistanceRates = {}, }: Props) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const rates = DistanceRequestUtils.getMileageRates(policy?.id); + const lastSelectedRate = lastSelectedDistanceRates[policy?.id ?? '0'] ?? '0'; + const data = Object.values(rates).map((rate) => ({ text: rate.name ?? '', alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), @@ -50,9 +55,10 @@ function IOURequestStepRate({ value: rate.customUnitRateID, })); - const selectDistanceRate = (customUnitRateID) => { + const initiallyFocusedOption = rates[lastSelectedRate]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; + + function selectDistanceRate(customUnitRateID = '0') { IOU.setLastSelectedDistanceRates(policy?.id ?? '', customUnitRateID); - // TODO: get a proper transaction ID IOU.updateDistanceRequestRate('1', customUnitRateID); Navigation.goBack(backTo); } @@ -70,8 +76,7 @@ function IOURequestStepRate({ sections={[{data}]} ListItem={RadioListItem} onSelectRow={({value}) => selectDistanceRate(value)} - // TODO: change for lastSelectedDistanceRates - initiallyFocusedOptionKey="Default Rate" + initiallyFocusedOptionKey={initiallyFocusedOption} /> ); @@ -82,11 +87,14 @@ IOURequestStepRate.displayName = 'IOURequestStepRate'; export default compose( withWritableReportOrNotFound, withOnyx({ + // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS policy: { + // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, - // lastSelectedDistanceRates: { - // key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, - // }, + // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS + lastSelectedDistanceRates: { + key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, + }, }), )(IOURequestStepRate); From a0227e07854c76bbc9039d5df2971de4c5294a5f Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 19 Mar 2024 09:42:20 -0300 Subject: [PATCH 015/283] fix: run prettier --- src/pages/iou/request/step/IOURequestStepRate.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 10513f7a44ca..583fa8c055c7 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -17,7 +17,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import type {Policy} from '@src/types/onyx'; - type Props = { /** Object of last selected rates for the policies */ lastSelectedDistanceRates: Record; From eb350eec27ba9858929864cdc29aada4453b88e1 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 19 Mar 2024 09:53:29 -0300 Subject: [PATCH 016/283] fix: lint fix --- src/pages/iou/request/step/IOURequestStepParticipants.js | 2 +- src/pages/iou/request/step/IOURequestStepRate.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 8c92961a28c4..a66387709f61 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -137,7 +137,7 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. selectedReportID.current = lodashGet(val, '[0].reportID', reportID); }, - [reportID, transactionID, iouType, participants, updateRouteParams], + [iouType, participants, transactionID, transaction, reportID, updateRouteParams, lastSelectedDistanceRates], ); const goToNextStep = useCallback( diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 583fa8c055c7..de570ca3c67f 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -10,12 +10,12 @@ import * as IOU from '@libs/actions/IOU'; import compose from '@libs/compose'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; -import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; -import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import type {Policy} from '@src/types/onyx'; +import StepScreenWrapper from './StepScreenWrapper'; +import withWritableReportOrNotFound from './withWritableReportOrNotFound'; type Props = { /** Object of last selected rates for the policies */ From 4b823f46eeb833902249b682b7b211721df433c5 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Sat, 23 Mar 2024 21:50:16 +0800 Subject: [PATCH 017/283] only add the saved user on first render --- src/pages/workspace/WorkspaceInvitePage.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index 00d7eaa33f6b..fe7d82658e82 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -1,6 +1,6 @@ import {useNavigation} from '@react-navigation/native'; import type {StackNavigationProp, StackScreenProps} from '@react-navigation/stack'; -import React, {useEffect, useMemo, useState} from 'react'; +import React, {useEffect, useMemo, useRef, useState} from 'react'; import type {SectionListData} from 'react-native'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -67,6 +67,7 @@ function WorkspaceInvitePage({ const [personalDetails, setPersonalDetails] = useState([]); const [usersToInvite, setUsersToInvite] = useState([]); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); + const firstRenderRef = useRef(true); const navigation = useNavigation>(); const openWorkspaceInvitePage = () => { const policyMemberEmailsToAccountIDs = PolicyUtils.getMemberAccountIDsForWorkspace(policyMembers, personalDetailsProp); @@ -120,12 +121,16 @@ function WorkspaceInvitePage({ }); const newSelectedOptions: MemberForList[] = []; - Object.keys(invitedEmailsToAccountIDsDraft ?? {}).forEach((login) => { - if (!(login in detailsMap)) { - return; - } - newSelectedOptions.push({...detailsMap[login], isSelected: true}); - }); + if (firstRenderRef.current) { + // We only want to add the saved selected user on first render + firstRenderRef.current = false; + Object.keys(invitedEmailsToAccountIDsDraft ?? {}).forEach((login) => { + if (!(login in detailsMap)) { + return; + } + newSelectedOptions.push({...detailsMap[login], isSelected: true}); + }); + } selectedOptions.forEach((option) => { newSelectedOptions.push(option.login && option.login in detailsMap ? {...detailsMap[option.login], isSelected: true} : option); }); From c112ca3c34c240a9f1afe4f69467f20610e2d6a3 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Wed, 27 Mar 2024 20:51:59 +0530 Subject: [PATCH 018/283] remove MoneyRequestParticipantsPage.js. Signed-off-by: Krishna Gupta --- .../MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js deleted file mode 100644 index e69de29bb2d1..000000000000 From 33196ae4a73f078f9f3074f3b08a7b256081f049 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Wed, 27 Mar 2024 21:32:06 +0530 Subject: [PATCH 019/283] minor import update. Signed-off-by: Krishna Gupta --- src/pages/iou/request/step/IOURequestStepParticipants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 5ca465d8fb78..5b65a281a2f5 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -1,7 +1,7 @@ import {useNavigation} from '@react-navigation/native'; +import _ from 'lodash'; import lodashGet from 'lodash/get'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; -import _ from 'underscore'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; From 0682afbf2bd04faf5f56b44aae122d797a5983e4 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 13:56:02 +0100 Subject: [PATCH 020/283] fix: add missing screen --- src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index ef1fc3c2dfb0..7fead4088bf1 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -81,6 +81,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/iou/request/step/IOURequestStepDate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: () => require('../../../../pages/iou/request/step/IOURequestStepDescription').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: () => require('../../../../pages/iou/request/step/IOURequestStepDistance').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.STEP_RATE]: () => require('../../../pages/iou/request/step/IOURequestStepRate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_MERCHANT]: () => require('../../../../pages/iou/request/step/IOURequestStepMerchant').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: () => require('../../../../pages/iou/request/step/IOURequestStepParticipants').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_SCAN]: () => require('../../../../pages/iou/request/step/IOURequestStepScan').default as React.ComponentType, From 6105b5d917e8172779de97cca9947694ee3dcb4c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 17:05:33 +0100 Subject: [PATCH 021/283] fix: show check mark on lastSelectedDistanceRate --- src/pages/iou/request/step/IOURequestStepRate.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index de570ca3c67f..c4e638a48c2f 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -52,11 +52,12 @@ function IOURequestStepRate({ alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), keyForList: rate.name ?? '', value: rate.customUnitRateID, + isSelected: lastSelectedRate === rate.customUnitRateID, })); const initiallyFocusedOption = rates[lastSelectedRate]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; - function selectDistanceRate(customUnitRateID = '0') { + function selectDistanceRate(customUnitRateID: string) { IOU.setLastSelectedDistanceRates(policy?.id ?? '', customUnitRateID); IOU.updateDistanceRequestRate('1', customUnitRateID); Navigation.goBack(backTo); @@ -74,7 +75,7 @@ function IOURequestStepRate({ selectDistanceRate(value)} + onSelectRow={({value}) => selectDistanceRate(value ?? '')} initiallyFocusedOptionKey={initiallyFocusedOption} /> @@ -96,4 +97,5 @@ export default compose( key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, }, }), + // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS )(IOURequestStepRate); From 063a8b6464615d1d3e4bc252e86bef7ef5d5cdb1 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 17:15:46 +0100 Subject: [PATCH 022/283] fix: fix distance edit route --- src/components/ReportActionItem/MoneyRequestView.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 8a2ad321cedc..9011e8193077 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -260,7 +260,9 @@ function MoneyRequestView({ interactive={canEditDistance} shouldShowRightIcon={canEditDistance} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DISTANCE))} + onPress={() => + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction?.transactionID ?? '', report.reportID)) + } /> @@ -285,9 +287,7 @@ function MoneyRequestView({ shouldShowRightIcon={canEditDistance} titleStyle={styles.flex1} onPress={() => - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction?.transactionID ?? '', report.reportID), - ) + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction?.transactionID ?? '', report.reportID)) } /> From 7fe595a9f274790a987949ff4fcfb960647983f9 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 18:46:11 +0100 Subject: [PATCH 023/283] fix: path fix --- src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 7fead4088bf1..75a9806e4ee8 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -81,7 +81,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/iou/request/step/IOURequestStepDate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: () => require('../../../../pages/iou/request/step/IOURequestStepDescription').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: () => require('../../../../pages/iou/request/step/IOURequestStepDistance').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.STEP_RATE]: () => require('../../../pages/iou/request/step/IOURequestStepRate').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.STEP_RATE]: () => require('../../../../pages/iou/request/step/IOURequestStepRate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_MERCHANT]: () => require('../../../../pages/iou/request/step/IOURequestStepMerchant').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: () => require('../../../../pages/iou/request/step/IOURequestStepParticipants').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_SCAN]: () => require('../../../../pages/iou/request/step/IOURequestStepScan').default as React.ComponentType, From 9e68814a1f476f205be5bb0a3188723ca38b568d Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 19:08:20 +0100 Subject: [PATCH 024/283] fix: minor fix --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 394db0bddbe3..5dd94c331e80 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -254,7 +254,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeTrackExpense = iouType === CONST.IOU.TYPE.TRACK_EXPENSE; const canEditDistance = isTypeRequest || (canUseP2PDistanceRequests && isTypeSplit); - const personalPolicy = policyID === CONST.POLICY.ID_FAKE ? PolicyUtils.getPersonalPolicy() : policy; + // if there is no policyID that means that the transaction was started from Global Create and the participant is not a policy + const personalPolicy = policyID === CONST.POLICY.ID_FAKE || !policyID ? PolicyUtils.getPersonalPolicy() : policy; + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(personalPolicy.outputCurrency) : mileageRates[transaction.comment.customUnit.customUnitRateID]; From 68eecf0cb83aae10fd3c18ef735bd243986d3107 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 19:17:55 +0100 Subject: [PATCH 025/283] fix: enable fields for split, fix route --- ...neyTemporaryForRefactorRequestConfirmationList.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 5dd94c331e80..e4bc65b9840d 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -757,13 +757,17 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ item: ( Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} - disabled={didConfirm || !isTypeRequest} + onPress={() => + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ) + } + disabled={didConfirm} interactive={!isReadOnly} /> ), @@ -784,7 +788,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ROUTES.MONEY_REQUEST_STEP_RATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()), ); }} - disabled={didConfirm || !isTypeRequest} + disabled={didConfirm} interactive={!isReadOnly && isPolicyExpenseChat} /> ), From 35baf304a5f41f213f653836ec10378b173fbfd8 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 19:29:58 +0100 Subject: [PATCH 026/283] fix: minor fix --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index e4bc65b9840d..5a28b8be1ef5 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -266,7 +266,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * 100, }; - const currency = personalPolicy.outputCurrency; + const currency = mileageRate.currency || personalPolicy.outputCurrency; const distance = lodashGet(transaction, 'routes.route0.distance', 0); const taxRates = lodashGet(policy, 'taxRates', {}); From 9803669d05f0ad55c5d47ec6215c0f09df88bd95 Mon Sep 17 00:00:00 2001 From: Ren Jones <153645623+ren-jones@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:35:34 -0500 Subject: [PATCH 027/283] Create Enable-and-set-up-expense-violations.md New article for enabling and setting up expense violations --- .../Enable-and-set-up-expense-violations.md | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 docs/articles/expensify-classic/workspaces/Enable-and-set-up-expense-violations.md diff --git a/docs/articles/expensify-classic/workspaces/Enable-and-set-up-expense-violations.md b/docs/articles/expensify-classic/workspaces/Enable-and-set-up-expense-violations.md new file mode 100644 index 000000000000..b6cea251785b --- /dev/null +++ b/docs/articles/expensify-classic/workspaces/Enable-and-set-up-expense-violations.md @@ -0,0 +1,111 @@ +--- +title: Enable and set up expense violations +description: Set up rules for expenses and enable violations +--- +
+ +Expensify automatically detects expense errors or discrepancies as violations that must be corrected. You can also set rules for a workspace that will trigger a violation if the rule is not met. These rules can be set for categories, tags, and even for specific domain groups. + +When reviewing submitted expense reports, approvers will see violations highlighted with an exclamation mark. There are two types of violations: +- **Yellow**: Automated highlights that require attention but may not require corrective action. For example, if a receipt was SmartScanned and then the amount was modified, a yellow violation will be added to call out the change for review. +- **Red**: Violations directly tied to your workspace settings. These violations must be addressed before the report can be submitted and reimbursed. + +You can hover over the icon to see a brief description, and you can find more detailed information below the list of expenses. + +{% include info.html %} +If your workspace has automations set to automatically submit reports for approval, the report that contains violations will not be submitted automatically until the violations are corrected. (However, if a comment is added to an expense, it will override the violation as the member is providing a reason for submission *unless* domain workspace rules are set to be strictly enforced, as detailed near the bottom of this article.) +{% include end-info.html %} + +# Enable or disable expense violations + +1. Hover over Settings, then click **Workspaces**. +2. Click the **Group** tab on the left. +3. Click the desired workspace name. +4. Click the **Expenses** tab on the left. +5. Click the “Enable violations” toggle. +6. If desired, enter the expense rules that will be used to create violations: + - **Max expense age (days)**: How old an expense can be + - **Max expense amount**: How much a single expense can cost + - **Receipt required amount**: How much a single expense can cost before a receipt is required + +{% include info.html %} +Expensify includes certain system mandatory violations that can't be disabled, even if your policy has violations turned off. +{% include end-info.html %} + +# Set category rules + +Admin on a Control workspace can enable specific rules for each category, including setting expense caps for specific categories, requiring receipts, and more. These rules can allow you to have a default expense limit of $2,500 but to only allow a daily entertainment limit of $150 per person. You can also choose to not require receipts for mileage or per diem expenses. + +To set up category rules, +1. Hover over Settings, then click **Workspaces**. +2. Click the **Group** tab on the left. +3. Click the desired workspace name. +4. Click the **Categories** tab on the left. +5. Click **Edit** to the right of the category. +6. Enter your category rules, as desired: + - **GL Code and Payroll Code**: You can add general ledger (GL) or payroll codes to the category for accounting. GL codes populate automatically if you have an accounting integration connected with Expensify. + - **Max Amount**: You can set specific expense caps for the expense category. Use the Limit Type dropdown to determine if the amount is set per individual expense or per day, then enter the maximum amount into this field. + - **Receipts**: You can determine whether receipts are required for the category. For example, many companies disable receipt requirements for toll expenses. + - **Description**: You can determine whether a description is required for expenses under this category. + - **Description Hint**: You can add a hint in the description field to prompt the expense creator on what they should enter into the description field for expenses under this category. + - **Approver**: You can set a specific approver for expenses labeled with this category. + +If users are in violation of these rules, the violations will be shown in red on the report. + +{% include info.html %} +If Scheduled Submit is enabled on a workspace, expenses with category violations will not be auto-submitted unless the expense has a comment added. +{% include end-info.html %} + +# Make categories required + +This means all expenses must be coded with a Category. + +1. Hover over Settings, then click **Workspaces**. +2. Click the **Group** tab on the left. +3. Click the desired workspace name. +4. Click the **Categories** tab on the left. +5. Enable the “People must categorize expenses” toggle. + +Each Workspace Member will now be required to select a category for their expense. If they do not select a category, the report will receive a violation, which can prevent submission if Scheduled Submit is enabled. + +# Make tags required + +1. Hover over Settings, then click **Workspaces**. +2. Click the **Group** tab on the left. +3. Click the desired workspace name. +4. Click the **Tags** tab on the left. +5. Enable the “People must tag expenses” toggle. + +Each Workspace Member will now be required to select a tag for their expense before they’re able to submit it. + +# Require strict compliance by domain group + +You can require strict compliance to require members of a specific domain group to submit reports that meet **all** workspace rules before they can submit their expense report—even if they add a note. Every rule and regulation on the workspace must be met before a report can be submitted. + +{% include info.html %} +This will prevent members from submitting any reports where a manager has granted them a manual exception for any of the workspace rules. +{% include end-info.html %} + +To enable strict domain group compliance for reports, + +1. Hover over Settings, then click **Domains**. +2. Click the **Groups** tab on the left. +3. Click **Edit** to the right of the desired workspace name. +4. Enable the “Strictly enforce expense workspace rules” toggle. + +# FAQs + +**Why can’t my employees see the categories on their expenses?** + +The employee may have their default workspace set as their personal workspace. Look under the details section on top right of the report to ensure it is being reported under the correct workspace. + +**Will the account numbers from our accounting system (QuickBooks Online, Sage Intacct, etc.) show in the category list for employees?** + +The general ledger (GL) account numbers are visible only for Workspace Admins in the workspace settings when they are part of a control workspace. This information is not visible to other members of the workspace. However, if you wish to have this information available to your employees when they are categorizing their expenses, you can edit the account name in your accounting software to include the GL number (for example, Accounts Payable - 12345). + +**What causes a category violation?** + +- An expense is categorized with a category that is not included in the workspace's categories. This may happen if the employee creates an expense under the wrong workspace, which will cause a "category out of workspace" violation. +- If the workspace categories are being imported from an accounting integration and they’ve been updated in the accounting system but not in Expensify, this can cause an old category to still be in use on an open report which would throw a violation on submission. Simply reselect a proper category to clear violation. + +
From e9a15d7d7790bed485cc18ce40cddbcd45ac1d32 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 28 Mar 2024 22:19:08 +0100 Subject: [PATCH 028/283] fix: change copy on rate selection page --- src/languages/en.ts | 3 ++- src/languages/es.ts | 3 ++- src/languages/types.ts | 3 +++ src/pages/iou/request/step/IOURequestStepRate.tsx | 8 +++++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index de5d4d80ea48..f45e4bd1c555 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -49,6 +49,7 @@ import type { PayerPaidAmountParams, PayerPaidParams, PayerSettledParams, + ReimbursementRateUnit, RemovedTheRequestParams, RenamedRoomActionParams, ReportArchiveReasonsClosedParams, @@ -726,7 +727,7 @@ export default { set: 'set', changed: 'changed', removed: 'removed', - chooseARate: 'Choose a rate to use below', + chooseARate: ({unit}: ReimbursementRateUnit) => `Select a workspace reimbursement rate per ${unit}`, }, notificationPreferencesPage: { header: 'Notification preferences', diff --git a/src/languages/es.ts b/src/languages/es.ts index c00ec12216fd..7fc558f9673b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -48,6 +48,7 @@ import type { PayerPaidAmountParams, PayerPaidParams, PayerSettledParams, + ReimbursementRateUnit, RemovedTheRequestParams, RenamedRoomActionParams, ReportArchiveReasonsClosedParams, @@ -724,7 +725,7 @@ export default { set: 'estableció', changed: 'cambió', removed: 'eliminó', - chooseARate: 'Elige una tarifa para utilizar a continuación', + chooseARate: ({unit}: ReimbursementRateUnit) => `Seleccione una tasa de reembolso del espacio de trabajo por ${unit}`, }, notificationPreferencesPage: { header: 'Preferencias de avisos', diff --git a/src/languages/types.ts b/src/languages/types.ts index c365363f84af..86f4b4709181 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -297,6 +297,8 @@ type HeldRequestParams = {comment: string}; type DistanceRateOperationsParams = {count: number}; +type ReimbursementRateUnit = {unit: string}; + export type { AdminCanceledRequestParams, ApprovedAmountParams, @@ -400,4 +402,5 @@ export type { ZipCodeExampleFormatParams, LogSizeParams, HeldRequestParams, + ReimbursementRateUnit, }; diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index c4e638a48c2f..8395dbc60ffa 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -45,16 +45,18 @@ function IOURequestStepRate({ const {translate, toLocaleDigit} = useLocalize(); const rates = DistanceRequestUtils.getMileageRates(policy?.id); - const lastSelectedRate = lastSelectedDistanceRates[policy?.id ?? '0'] ?? '0'; + const lastSelectedRate = lastSelectedDistanceRates[policy?.id ?? '0'] ?? rates[0]?.customUnitRateID; const data = Object.values(rates).map((rate) => ({ text: rate.name ?? '', alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), keyForList: rate.name ?? '', value: rate.customUnitRateID, - isSelected: lastSelectedRate === rate.customUnitRateID, + isSelected: lastSelectedRate ? lastSelectedRate === rate.customUnitRateID : Boolean(rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE), })); + const unit = Object.values(rates)[0]?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); + const initiallyFocusedOption = rates[lastSelectedRate]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; function selectDistanceRate(customUnitRateID: string) { @@ -70,7 +72,7 @@ function IOURequestStepRate({ shouldShowWrapper={Boolean(backTo)} testID="rate" > - {translate('iou.chooseARate')} + {translate('iou.chooseARate', {unit})} Date: Fri, 29 Mar 2024 14:04:01 +0100 Subject: [PATCH 029/283] fix: minor fix --- src/pages/iou/request/step/IOURequestStepParticipants.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index a66387709f61..7511b2f8d06e 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -30,8 +30,8 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, - // eslint-disable-next-line - lastSelectedDistanceRates: PropTypes.object, + // eslint-disable-next-line react/require-default-props + lastSelectedDistanceRates: PropTypes.shape({}), }; const defaultProps = { From 4217a9838ef4002f76ec3d917f956e2ca5562ce1 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 29 Mar 2024 16:31:48 +0100 Subject: [PATCH 030/283] fix: remove a comment --- src/libs/DistanceRequestUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index a676e2617f72..0aa5a0c4b3a5 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -94,7 +94,6 @@ function getRoundedDistanceInUnits(distanceInMeters: number, unit: Unit): string return convertedDistance.toFixed(2); } -// TODO: I wonder if it would be better to refactor these functions to pass params in an object /** * @param hasRoute Whether the route exists for the distance request * @param unit Unit that should be used to display the distance From 4e11125e6862353dfc095b831501988a43d5cc1c Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 30 Mar 2024 19:00:19 +0300 Subject: [PATCH 031/283] fixed optimistic submitted report action --- src/libs/ReportUtils.ts | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9fa28535a7a7..cc52ce559d9b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3283,8 +3283,40 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa * @param paymentType - IOU paymentMethodType. Can be oneOf(Elsewhere, Expensify) * @param isSettlingUp - Whether we are settling up an IOU */ -function getIOUReportActionMessage(iouReportID: string, type: string, total: number, comment: string, currency: string, paymentType = '', isSettlingUp = false): [Message] { +function getIOUReportActionMessage(iouReportID: string, type: string, total: number, comment: string, currency: string, paymentType = '', isSettlingUp = false): Message[] { const report = getReport(iouReportID); + + if (type === CONST.REPORT.ACTIONS.TYPE.SUBMITTED) { + const policy = getPolicy(report?.policyID); + const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.ownerAccountID ?? 0); + const ownerDisplayName = ownerPersonalDetails?.displayName + ? `${ownerPersonalDetails.displayName} (${ownerPersonalDetails.login})` + : ownerPersonalDetails?.login ?? Localize.translateLocal('common.hidden'); + + return [ + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'strong', + text: 'You', + }, + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'normal', + text: ' submitted this report', + }, + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'normal', + text: ' to ', + }, + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'strong', + text: ownerDisplayName, + }, + ]; + } + const amount = type === CONST.IOU.REPORT_ACTION_TYPE.PAY ? CurrencyUtils.convertToDisplayString(getMoneyRequestSpendBreakdown(!isEmptyObject(report) ? report : null).totalDisplaySpend, currency) @@ -3306,9 +3338,6 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num case CONST.REPORT.ACTIONS.TYPE.APPROVED: iouMessage = `approved ${amount}`; break; - case CONST.REPORT.ACTIONS.TYPE.SUBMITTED: - iouMessage = `submitted ${amount}`; - break; case CONST.IOU.REPORT_ACTION_TYPE.CREATE: iouMessage = `requested ${amount}${comment && ` for ${comment}`}`; break; From deb453cda65cc673b73114401e27bd5db2d10403 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 30 Mar 2024 19:16:22 +0300 Subject: [PATCH 032/283] fixed for no display name case --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cc52ce559d9b..8194667baf75 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3290,8 +3290,8 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num const policy = getPolicy(report?.policyID); const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.ownerAccountID ?? 0); const ownerDisplayName = ownerPersonalDetails?.displayName - ? `${ownerPersonalDetails.displayName} (${ownerPersonalDetails.login})` - : ownerPersonalDetails?.login ?? Localize.translateLocal('common.hidden'); + ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` + : Localize.translateLocal('common.hidden'); return [ { From 22ca301945140408ecf14281b5b540cbbe51c37f Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 30 Mar 2024 19:49:26 +0300 Subject: [PATCH 033/283] changed to policy submitsTo --- 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 8194667baf75..79fa3a775073 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3288,7 +3288,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num if (type === CONST.REPORT.ACTIONS.TYPE.SUBMITTED) { const policy = getPolicy(report?.policyID); - const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.ownerAccountID ?? 0); + const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); const ownerDisplayName = ownerPersonalDetails?.displayName ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` : Localize.translateLocal('common.hidden'); From fa1a0f8712bf35f0754adcfd91941d216a6f6c14 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 30 Mar 2024 20:14:06 +0300 Subject: [PATCH 034/283] Add submitted to oneself case --- src/libs/ReportUtils.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 79fa3a775073..7bea7466eae1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3289,10 +3289,14 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num if (type === CONST.REPORT.ACTIONS.TYPE.SUBMITTED) { const policy = getPolicy(report?.policyID); const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); - const ownerDisplayName = ownerPersonalDetails?.displayName - ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` - : Localize.translateLocal('common.hidden'); - + let ownerDisplayName: string; + if (ownerPersonalDetails?.accountID === currentUserAccountID) { + ownerDisplayName = 'yourself'; + } else { + ownerDisplayName = ownerPersonalDetails?.displayName + ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` + : 'Hidden'; + } return [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, From cfc0c8a167cdeb188f9abf0d1f8c07ab0a882e85 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Sat, 30 Mar 2024 20:45:48 +0300 Subject: [PATCH 035/283] changed variable name --- src/libs/ReportUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7bea7466eae1..fbcf6b038f51 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3289,11 +3289,11 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num if (type === CONST.REPORT.ACTIONS.TYPE.SUBMITTED) { const policy = getPolicy(report?.policyID); const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); - let ownerDisplayName: string; + let submittedToDisplayName: string; if (ownerPersonalDetails?.accountID === currentUserAccountID) { - ownerDisplayName = 'yourself'; + submittedToDisplayName = 'yourself'; } else { - ownerDisplayName = ownerPersonalDetails?.displayName + submittedToDisplayName = ownerPersonalDetails?.displayName ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` : 'Hidden'; } @@ -3316,7 +3316,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num { type: CONST.REPORT.MESSAGE.TYPE.TEXT, style: 'strong', - text: ownerDisplayName, + text: submittedToDisplayName, }, ]; } From da62d52007a7adf7ccf7cfe232a272eee955484b Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 1 Apr 2024 09:04:06 +0530 Subject: [PATCH 036/283] remove ModalStackNavigators file. Signed-off-by: Krishna Gupta --- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx deleted file mode 100644 index e69de29bb2d1..000000000000 From 6d606568df619d19c149aca65ead7cfa6fe53fc3 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 1 Apr 2024 09:05:52 +0530 Subject: [PATCH 037/283] remove all instances of SCREENS.MONEY_REQUEST.PARTICIPANTS. Signed-off-by: Krishna Gupta --- .../Navigation/AppNavigator/ModalStackNavigators/index.tsx | 1 - src/libs/Navigation/types.ts | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 6583097ac9b8..01ae75903ff2 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -87,7 +87,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/iou/request/step/IOURequestStepTag').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: () => require('../../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType, [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, [SCREENS.MONEY_REQUEST.HOLD]: () => require('../../../../pages/iou/HoldReasonPage').default as React.ComponentType, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 69686790ac78..84917cfdeb17 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -341,10 +341,6 @@ type MoneyRequestNavigatorParamList = { reportID: string; currency: string; }; - [SCREENS.MONEY_REQUEST.PARTICIPANTS]: { - iouType: string; - reportID: string; - }; [SCREENS.MONEY_REQUEST.CONFIRMATION]: { iouType: string; reportID: string; From ab1542f6d37f1668f12c3076ca8793198c634b05 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 1 Apr 2024 09:20:59 +0530 Subject: [PATCH 038/283] remove redundant code. Signed-off-by: Krishna Gupta --- src/ROUTES.ts | 4 ---- src/SCREENS.ts | 1 - src/libs/actions/IOU.ts | 4 ++-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 7b28a2672ba6..b19325cd9ca5 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -281,10 +281,6 @@ const ROUTES = { route: ':iouType/new/amount/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/amount/${reportID}` as const, }, - MONEY_REQUEST_PARTICIPANTS: { - route: ':iouType/new/participants/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}` as const, - }, MONEY_REQUEST_CONFIRMATION: { route: ':iouType/new/confirmation/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/confirmation/${reportID}` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f5891b042e49..5a56f8496587 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -150,7 +150,6 @@ const SCREENS = { STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount', STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate', AMOUNT: 'Money_Request_Amount', - PARTICIPANTS: 'Money_Request_Participants', CONFIRMATION: 'Money_Request_Confirmation', CURRENCY: 'Money_Request_Currency', WAYPOINT: 'Money_Request_Waypoint', diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8582e9e3f3de..e6d27d6570ff 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5080,9 +5080,9 @@ function navigateToNextPage(iou: OnyxEntry, iouType: string, repo setMoneyRequestParticipants(participants); } Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); - return; + // return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType)); + // Navigation.navigate(ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType)); } /** From 9ce0d6b846d2bf737fc652ace9ca9545dced0d5b Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 2 Apr 2024 12:33:57 +0300 Subject: [PATCH 039/283] implemented it into separate function --- src/libs/ReportUtils.ts | 68 ++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0dcf3ab50d7c..ec5165f3f99a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3275,6 +3275,41 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa return expenseReport; } +function getIOUSubmittedMessage(report: OnyxEntry) { + const policy = getPolicy(report?.policyID); + const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); + let submittedToDisplayName: string; + if (ownerPersonalDetails?.accountID === currentUserAccountID) { + submittedToDisplayName = 'yourself'; + } else { + submittedToDisplayName = ownerPersonalDetails?.displayName + ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` + : ''; + } + return [ + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'strong', + text: 'You', + }, + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'normal', + text: ' submitted this report', + }, + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'normal', + text: ' to ', + }, + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + style: 'strong', + text: submittedToDisplayName, + }, + ]; +} + /** * @param iouReportID - the report ID of the IOU report the action belongs to * @param type - IOUReportAction type. Can be oneOf(create, decline, cancel, pay, split) @@ -3288,38 +3323,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num const report = getReport(iouReportID); if (type === CONST.REPORT.ACTIONS.TYPE.SUBMITTED) { - const policy = getPolicy(report?.policyID); - const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); - let submittedToDisplayName: string; - if (ownerPersonalDetails?.accountID === currentUserAccountID) { - submittedToDisplayName = 'yourself'; - } else { - submittedToDisplayName = ownerPersonalDetails?.displayName - ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` - : 'Hidden'; - } - return [ - { - type: CONST.REPORT.MESSAGE.TYPE.TEXT, - style: 'strong', - text: 'You', - }, - { - type: CONST.REPORT.MESSAGE.TYPE.TEXT, - style: 'normal', - text: ' submitted this report', - }, - { - type: CONST.REPORT.MESSAGE.TYPE.TEXT, - style: 'normal', - text: ' to ', - }, - { - type: CONST.REPORT.MESSAGE.TYPE.TEXT, - style: 'strong', - text: submittedToDisplayName, - }, - ]; + return getIOUSubmittedMessage(!isEmptyObject(report) ? report : null); } const amount = From 8103f5a8ab00445cc378b259e821e21fea758d64 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 2 Apr 2024 13:03:30 +0300 Subject: [PATCH 040/283] avoided double terniary --- src/libs/ReportUtils.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ec5165f3f99a..3c3817659bde 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3281,11 +3281,12 @@ function getIOUSubmittedMessage(report: OnyxEntry) { let submittedToDisplayName: string; if (ownerPersonalDetails?.accountID === currentUserAccountID) { submittedToDisplayName = 'yourself'; + } else if (ownerPersonalDetails?.displayName) { + submittedToDisplayName = `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}`; } else { - submittedToDisplayName = ownerPersonalDetails?.displayName - ? `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}` - : ''; + submittedToDisplayName = ''; } + return [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, From 989ed819c63871a4bebcfac011e719bdf300b1e5 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 2 Apr 2024 13:49:20 +0300 Subject: [PATCH 041/283] minor fix --- src/libs/ReportUtils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3c3817659bde..dda853389bb7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3281,10 +3281,8 @@ function getIOUSubmittedMessage(report: OnyxEntry) { let submittedToDisplayName: string; if (ownerPersonalDetails?.accountID === currentUserAccountID) { submittedToDisplayName = 'yourself'; - } else if (ownerPersonalDetails?.displayName) { - submittedToDisplayName = `${ownerPersonalDetails.displayName}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}`; } else { - submittedToDisplayName = ''; + submittedToDisplayName = `${ownerPersonalDetails.displayName ?? ''}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}`; } return [ From 7fe8f611f8b94b7ff2d5366ef8639f9fbf729250 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 3 Apr 2024 17:53:14 +0300 Subject: [PATCH 042/283] re-shuffled code implementation --- src/libs/ReportUtils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index dda853389bb7..11aff4c3c1d2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3278,11 +3278,9 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa function getIOUSubmittedMessage(report: OnyxEntry) { const policy = getPolicy(report?.policyID); const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); - let submittedToDisplayName: string; + let submittedToDisplayName = `${ownerPersonalDetails.displayName ?? ''}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}`; if (ownerPersonalDetails?.accountID === currentUserAccountID) { submittedToDisplayName = 'yourself'; - } else { - submittedToDisplayName = `${ownerPersonalDetails.displayName ?? ''}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}`; } return [ From c8ca8645593763137ded3a5068f72b76a174b040 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Thu, 4 Apr 2024 12:26:45 +0530 Subject: [PATCH 043/283] add type for SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS. Signed-off-by: Krishna Gupta --- src/libs/Navigation/types.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a83c0831797a..96e169ae0924 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -343,6 +343,12 @@ type MoneyRequestNavigatorParamList = { reportID: string; backTo: string; }; + [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: { + iouType: string; + transactionID: string; + reportID: string; + backTo: string; + }; [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: { action: ValueOf; iouType: string; From c7b8854b701d3a6c04a5d5cbf30c816c00a5651c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 4 Apr 2024 21:48:45 +0200 Subject: [PATCH 044/283] fix: display distance and rate properly --- src/CONST.ts | 3 ++- src/components/ReportActionItem/MoneyRequestView.tsx | 9 ++++----- src/libs/TransactionUtils.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index d33cf174cf48..e1d055d89028 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3,11 +3,12 @@ import dateAdd from 'date-fns/add'; import dateSubtract from 'date-fns/sub'; import Config from 'react-native-config'; import * as KeyCommand from 'react-native-key-command'; +import type {Unit} from '@src/types/onyx/Policy'; import * as Url from './libs/Url'; import SCREENS from './SCREENS'; type RateAndUnit = { - unit: string; + unit: Unit; rate: number; }; type CurrencyDefaultMileageRate = Record; diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 6f184c10f5d6..597b0494928d 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -176,11 +176,10 @@ function MoneyRequestView({ const rateID = transaction?.comment.customUnit?.customUnitRateID ?? '0'; const rates = DistanceRequestUtils.getMileageRates(policy?.id); - const {unit, currency, rate} = rates[rateID as string] ?? { - unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, - currency: CONST.CURRENCY.USD, - rate: 0, - }; + const currency = policy ? policy.outputCurrency : PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; + + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(currency) : rates[rateID as string]; + const {unit, rate} = mileageRate; const rateToDisplay = DistanceRequestUtils.getRateForDisplay(hasRoute, unit, rate, currency, translate, toLocaleDigit); const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate); diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index be6822b936c4..2b5f5aff4a1b 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -624,7 +624,7 @@ function getEnabledTaxRateCount(options: TaxRates) { * Check if the customUnitRateID has a value default for P2P distance requests */ -function isCustomUnitRateIDForP2P(transaction: Transaction): boolean { +function isCustomUnitRateIDForP2P(transaction: OnyxEntry): boolean { return transaction?.comment?.customUnit?.customUnitRateID === CONST.CUSTOM_UNITS.FAKE_P2P_ID; } From b16107aae43d7f37a835fe0cac32122b0fcf9814 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 4 Apr 2024 22:07:21 +0200 Subject: [PATCH 045/283] fix: minor fixes --- src/CONST.ts | 2 +- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index e1d055d89028..3ce64396a538 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3,9 +3,9 @@ import dateAdd from 'date-fns/add'; import dateSubtract from 'date-fns/sub'; import Config from 'react-native-config'; import * as KeyCommand from 'react-native-key-command'; -import type {Unit} from '@src/types/onyx/Policy'; import * as Url from './libs/Url'; import SCREENS from './SCREENS'; +import type {Unit} from './types/onyx/Policy'; type RateAndUnit = { unit: Unit; diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 4de7d52e9e81..91274c119625 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -790,7 +790,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ /> ), shouldShow: isDistanceRequest && canUseP2PDistanceRequests, - isSupplementary: true, + isSupplementary: false, }, { item: ( @@ -811,7 +811,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ /> ), shouldShow: isDistanceRequest && canUseP2PDistanceRequests, - isSupplementary: true, + isSupplementary: false, }, { item: ( From 53e0ca31da0f54d1e5f4ff08ed000bfdae48bbd7 Mon Sep 17 00:00:00 2001 From: bartektomczyk Date: Fri, 5 Apr 2024 12:52:45 +0200 Subject: [PATCH 046/283] feat: added new method for handliung onboarding, build new messages structure for onboarding --- src/CONST.ts | 240 +++++++++++++++++++++++++++++++++++++ src/libs/API/types.ts | 1 + src/libs/actions/Report.ts | 104 ++++++++++++++++ 3 files changed, 345 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index 6d1195ff5c79..b05e71fe7f19 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3541,6 +3541,246 @@ const CONST = { "Expensify is best known for expense and corporate card management, but we do a lot more than that. Let me know what you're interested in and I'll help get you started.", }, + ONBOARDING_MESSAGES: { + [onboardingChoices.TRACK]: { + message: 'Here are some essential tasks to keep your business spend in shape for tax season.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Create a workspace', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more. ', + message: + 'Here’s how to create a workspace:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Workspaces > New workspace.\n' + + '\n' + + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + }, + { + title: 'Track an expense', + subtitle: 'Track an expense in any currency, in just a few clicks.', + message: + 'Here’s how to track an expense:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Track expense.\n' + + '3. Enter an amount or scan a receipt.\n' + + '4. Click Track.\n' + + '\n' + + 'And you’re done! Yep, it’s that easy.', + }, + ], + }, + [onboardingChoices.EMPLOYER]: { + message: 'Getting paid back is as easy as sending a message. Let’s go over the basics.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Submit an expense', + subtitle: 'Submit an expense by entering an amount or scanning a receipt.', + message: + 'Here’s how to submit an expense:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Submit expense.\n' + + '3. Enter an amount or scan a receipt.\n' + + '4. Add your reimburser to the request.\n' + + '\n' + + 'Then, send your request and wait for that sweet “Cha-ching!” when it’s complete.', + }, + { + title: 'Enable your wallet', + subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', + message: + 'Here’s how to set up your wallet:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Wallet > Enable wallet.\n' + + '3. Connect your bank account.\n' + + '\n' + + 'Once that’s done, you can request money from anyone and get paid back right into your personal bank account.', + }, + ], + }, + [onboardingChoices.MANAGE_TEAM]: { + message: 'Here are some important tasks to help get your team’s expenses under control.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Create a workspace', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more. ', + message: + 'Here’s how to create a workspace:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Workspaces > New workspace.\n' + + '\n' + + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + }, + { + title: 'Meet your setup specialist', + subtitle: '', + message: + 'Meet your setup specialist, {guideName}, who can answer any questions as you get started with Expensify. Yes, a real human!\n' + + '\n' + + 'Chat with {guideName} in your [admins room]({adminsRoomID}) or [schedule a call]({guideCalendarLink}) today', + }, + { + title: 'Set up categories', + subtitle: 'Set up categories so your team can code expenses for easy reporting.', + message: + 'Here’s how to set up categories:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Go to Workspaces > [your workspace].\n' + + '3. Click Categories.\n' + + '4. Enable and disable default categories.\n' + + '5. Click Add categories to make your own.\n' + + '\n' + + 'For more controls like requiring a category for every expense, click Settings. ', + }, + { + title: 'Add expense approvals', + subtitle: 'Add expense approvals to review your team’s spend and keep it under control.', + message: + 'Here’s how to add expense approvals:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Go to Workspaces > [your workspace].\n' + + '3. Click More features.\n' + + '4. Enable Workflows.\n' + + '5. In Workflows, enable Add approvals.\n' + + '\n' + + 'You’ll be set as the expense approver. You can change this to any admin once you invite your team. ', + }, + { + title: 'Invite your team', + subtitle: 'Invite your team to Expensify so they can start tracking expenses today.', + message: + 'Here’s how to invite your team:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Go to Workspaces > [your workspace].\n' + + '3. Click Members > Invite member.\n' + + '4. Enter emails or phone numbers. \n' + + '5. Add an invite message if you want.\n' + + '\n' + + 'That’s it! Happy expensing :)', + }, + ], + }, + [onboardingChoices.PERSONAL_SPEND]: { + message: 'Here’s how to track your spend in a few clicks.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Track an expense', + subtitle: 'Track an expense in any currency, whether you have a receipt or not.', + message: + 'Here’s how to track an expense:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Track expense.\n' + + '3. Enter an amount or scan a receipt.\n' + + '4. Click Track.\n' + + '\n' + + 'And you’re done! Yep, it’s that easy.', + }, + ], + }, + [onboardingChoices.CHAT_SPLIT]: { + message: 'Splitting bills with friends is as easy as sending a message. Here’s how.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Start a chat', + subtitle: 'Start a chat with a friend or group using their email or phone number.', + message: + 'Here’s how to start a chat:\n' + + '\n' + + '1. rClick the green + button.\n' + + '2. Choose Start chat.\n' + + '3. Enter emails or phone numbers.\n' + + '\n' + + 'If any of your friends aren’t using Expensify already, they’ll be invited automatically. \n' + + '\n' + + 'Every chat will also turn into an email or text that they can respond to directly.', + }, + { + title: 'Split an expense', + subtitle: 'Split an expense right in your chat with one or more friends.', + message: + 'Here’s how to request money:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Split expense.\n' + + '3. Scan a receipt or enter an amount.\n' + + '4. Add your friend(s) to the request.\n' + + '\n' + + 'Feel free to add more details if you want, or just send it off. Let’s get you paid back!', + }, + { + title: 'Enable your wallet', + subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', + message: + 'Here’s how to enable your wallet:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Wallet > Enable wallet.\n' + + '3. Add your bank account.\n' + + '\n' + + 'Once that’s done, you can request money from anyone and get paid right into your personal bank account.', + }, + ], + }, + [onboardingChoices.LOOKING_AROUND]: { + message: + '# Welcome to Expensify!\n' + + "Hi there, I'm Concierge. Chat with me here for anything you need.\n" + + '\n' + + "Expensify is best known for expense and corporate card management, but we do a lot more than that. Let me know what you're interested in and I'll help get you started.", + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [], + }, + }, + REPORT_FIELD_TITLE_FIELD_ID: 'text_title', MOBILE_PAGINATION_SIZE: 15, diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 3cff726a530c..90e03188f3a3 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -134,6 +134,7 @@ const WRITE_COMMANDS = { REOPEN_TASK: 'ReopenTask', COMPLETE_TASK: 'CompleteTask', COMPLETE_ENGAGEMENT_MODAL: 'CompleteEngagementModal', + COMPLETE_GUIDED_SETUP: 'CompleteGuidedSetup', SET_NAME_VALUE_PAIR: 'SetNameValuePair', SET_REPORT_FIELD: 'Report_SetFields', SET_REPORT_NAME: 'RenameReport', diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 179ee87862ff..60cb81c9165a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2712,6 +2712,109 @@ function getReportPrivateNote(reportID: string | undefined) { API.read(READ_COMMANDS.GET_REPORT_PRIVATE_NOTE, parameters, {optimisticData, successData, failureData}); } +function completeOnboarding(properties: { + data: ValueOf; + firstName: string; + lastName: string; + targetEmail: ValueOf; + engagementChoice: ValueOf; +}) { + const {data, engagementChoice, targetEmail, firstName, lastName} = properties; + const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; + const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); + const targetChatReportID = targetChatReport?.reportID ?? ''; + const targetChatPolicyID = targetChatReport?.policyID ?? ''; + + // Text message + const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined); + const textCommentAction: OptimisticAddCommentReportAction = textComment.reportAction; + const textCommentText = textComment.commentText; + + const textMessage: AddCommentOrAttachementParams & {type: string} = { + reportID: targetChatReportID, + reportActionID: textCommentAction.reportActionID, + reportComment: textCommentText, + type: 'message', + }; + + // Video message + const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined); + const videoCommentAction: OptimisticAddCommentReportAction = videoComment.reportAction; + const videoCommentText = videoComment.commentText; + + const videoMessage: AddCommentOrAttachementParams & {type: string} & typeof data.video = { + reportID: targetChatReportID, + reportActionID: videoCommentAction.reportActionID, + reportComment: videoCommentText, + type: 'video', + ...data.video, + }; + + // tasks + const tasks = data.tasks.reduce((acc, curr) => { + const currTask = ReportUtils.buildOptimisticTaskReport(actorAccountID, undefined, targetChatReportID, curr.title, undefined, targetChatPolicyID); + const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(targetEmail); + const taskAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(currTask.reportID, curr.title, 0, `task for ${curr.title}`, targetChatReportID); + + // subtitle message + const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(curr.subtitle, undefined, actorAccountID); + const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; + const subtitleCommentText = subtitleComment.commentText; + + const subtitleMessage: AddCommentOrAttachementParams = { + reportID: currTask.reportID, + reportActionID: subtitleCommentAction.reportActionID, + reportComment: subtitleCommentText, + }; + + // instruction message + const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(curr.message, undefined, actorAccountID); + const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; + const instructionCommentText = instructionComment.commentText; + + const instructionMessage: AddCommentOrAttachementParams = { + reportID: currTask.reportID, + reportActionID: instructionCommentAction.reportActionID, + reportComment: instructionCommentText, + }; + + return [ + ...acc, + { + parentReportActionID: taskAddCommentReport.reportAction.reportActionID, + parentReportID: currTask.parentReportID, + taskReportID: currTask.reportID, + createdTaskReportActionID: taskCreatedAction.reportActionID, + title: currTask.reportName, + description: currTask.description, + assigneeChatReportID: '', + type: 'task', + task: 'trackExpense', + }, + { + type: 'message', + ...subtitleMessage, + }, + { + type: 'message', + ...instructionMessage, + }, + ]; + }, []); + + const parameters = { + engagementChoice, + firstName, + lastName, + data: JSON.stringify([textMessage, videoMessage, ...tasks]), + }; + + // console.log('-------------------------------------'); + // console.log(JSON.stringify({...parameters, data: JSON.parse(parameters.data)}, ' ', 2)); + + API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {}); +} + /** * Completes the engagement modal that new NewDot users see when they first sign up/log in by doing the following: * @@ -3029,4 +3132,5 @@ export { setGroupDraft, clearGroupChat, startNewChat, + completeOnboarding, }; From c34e8a20a35940b035b987d18e9797211fd3526e Mon Sep 17 00:00:00 2001 From: bartektomczyk Date: Fri, 5 Apr 2024 16:31:47 +0200 Subject: [PATCH 047/283] feat: call method --- src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index ae43f850018b..bd8a7ab40917 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -80,7 +80,14 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on return; } - Report.completeEngagementModal(CONST.ONBOARDING_CONCIERGE[selectedPurpose], selectedPurpose); + // Report.completeEngagementModal(CONST.ONBOARDING_CONCIERGE[selectedPurpose], selectedPurpose); + Report.completeOnboarding({ + data: CONST.ONBOARDING_MESSAGES[selectedPurpose], + engagementChoice: selectedPurpose, + targetEmail: CONST.EMAIL.CONCIERGE, + firstName: 'Test', + lastName: 'Testovsky', + }); Navigation.dismissModal(); // Only navigate to concierge chat when central pane is visible From 3bd9aad21f9666b9a863a41318bd5af05be1fdc8 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 5 Apr 2024 17:39:53 +0200 Subject: [PATCH 048/283] fix: apply requested changes --- ...neyTemporaryForRefactorRequestConfirmationList.js | 6 +++--- src/languages/en.ts | 4 ++-- src/languages/es.ts | 4 ++-- src/languages/types.ts | 5 +++-- .../iou/request/step/IOURequestStepParticipants.js | 12 +++++++----- src/pages/iou/request/step/IOURequestStepRate.tsx | 7 ++++--- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 91274c119625..51436b5d8737 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -769,7 +769,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ /> ), shouldShow: isDistanceRequest && !canUseP2PDistanceRequests, - isSupplementary: true, + isSupplementary: false, }, { item: ( @@ -796,7 +796,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ item: ( ), shouldShow: isDistanceRequest && canUseP2PDistanceRequests, diff --git a/src/languages/en.ts b/src/languages/en.ts index 55313718222d..4c93e8bb6207 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -49,7 +49,7 @@ import type { PayerPaidAmountParams, PayerPaidParams, PayerSettledParams, - ReimbursementRateUnit, + ReimbursementRateParams, RemovedTheRequestParams, RenamedRoomActionParams, ReportArchiveReasonsClosedParams, @@ -737,7 +737,7 @@ export default { set: 'set', changed: 'changed', removed: 'removed', - chooseARate: ({unit}: ReimbursementRateUnit) => `Select a workspace reimbursement rate per ${unit}`, + chooseARate: ({unit}: ReimbursementRateParams) => `Select a workspace reimbursement rate per ${unit}`, }, notificationPreferencesPage: { header: 'Notification preferences', diff --git a/src/languages/es.ts b/src/languages/es.ts index 108a781bb298..406466d44489 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -48,7 +48,7 @@ import type { PayerPaidAmountParams, PayerPaidParams, PayerSettledParams, - ReimbursementRateUnit, + ReimbursementRateParams, RemovedTheRequestParams, RenamedRoomActionParams, ReportArchiveReasonsClosedParams, @@ -735,7 +735,7 @@ export default { set: 'estableció', changed: 'cambió', removed: 'eliminó', - chooseARate: ({unit}: ReimbursementRateUnit) => `Seleccione una tasa de reembolso del espacio de trabajo por ${unit}`, + chooseARate: ({unit}: ReimbursementRateParams) => `Seleccione una tasa de reembolso del espacio de trabajo por ${unit}`, }, notificationPreferencesPage: { header: 'Preferencias de avisos', diff --git a/src/languages/types.ts b/src/languages/types.ts index 86f4b4709181..59e248e4e43a 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -1,4 +1,5 @@ import type {ReportAction} from '@src/types/onyx'; +import type {Unit} from '@src/types/onyx/Policy'; import type en from './en'; type AddressLineParams = { @@ -297,7 +298,7 @@ type HeldRequestParams = {comment: string}; type DistanceRateOperationsParams = {count: number}; -type ReimbursementRateUnit = {unit: string}; +type ReimbursementRateParams = {unit: Unit}; export type { AdminCanceledRequestParams, @@ -402,5 +403,5 @@ export type { ZipCodeExampleFormatParams, LogSizeParams, HeldRequestParams, - ReimbursementRateUnit, + ReimbursementRateParams, }; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index f66d998e1d1f..c6ae0c0fdb0e 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -110,13 +110,15 @@ function IOURequestStepParticipants({ IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); + const participant = val[0]; + // change customUnitRateID when choosing - if (val[0].isPolicyExpenseChat && TransactionUtils.isCustomUnitRateIDForP2P(transaction)) { + if (participant.isPolicyExpenseChat && TransactionUtils.isCustomUnitRateIDForP2P(transaction)) { let customUnitRateID = ''; - if (val[0].policyID && lastSelectedDistanceRates[val[0].policyID]) { - customUnitRateID = lastSelectedDistanceRates[val[0].policyID]; + if (lastSelectedDistanceRates[participant.policyID]) { + customUnitRateID = lastSelectedDistanceRates[participant.policyID]; } else { - const policy = ReportUtils.getPolicy(val[0].policyID); + const policy = ReportUtils.getPolicy(participant.policyID); const defaultRate = DistanceRequestUtils.getDefaultMileageRate(policy); customUnitRateID = defaultRate ? defaultRate.customUnitRateID : ''; } @@ -135,7 +137,7 @@ function IOURequestStepParticipants({ } // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. - selectedReportID.current = lodashGet(val, '[0].reportID', reportID); + selectedReportID.current = participant.reportID; }, [iouType, participants, transactionID, transaction, reportID, updateRouteParams, lastSelectedDistanceRates], ); diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 8395dbc60ffa..43f37a97eb76 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -14,6 +14,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import type {Policy} from '@src/types/onyx'; +import type {Unit} from '@src/types/onyx/Policy'; import StepScreenWrapper from './StepScreenWrapper'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; @@ -47,7 +48,7 @@ function IOURequestStepRate({ const lastSelectedRate = lastSelectedDistanceRates[policy?.id ?? '0'] ?? rates[0]?.customUnitRateID; - const data = Object.values(rates).map((rate) => ({ + const sections = Object.values(rates).map((rate) => ({ text: rate.name ?? '', alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), keyForList: rate.name ?? '', @@ -55,7 +56,7 @@ function IOURequestStepRate({ isSelected: lastSelectedRate ? lastSelectedRate === rate.customUnitRateID : Boolean(rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE), })); - const unit = Object.values(rates)[0]?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); + const unit = (Object.values(rates)[0]?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer')) as Unit; const initiallyFocusedOption = rates[lastSelectedRate]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; @@ -75,7 +76,7 @@ function IOURequestStepRate({ {translate('iou.chooseARate', {unit})} selectDistanceRate(value ?? '')} initiallyFocusedOptionKey={initiallyFocusedOption} From bd83588de6a8763f62aedbdbddc7e038640f8ad5 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 5 Apr 2024 17:40:48 +0200 Subject: [PATCH 049/283] fix: revert unnecessary change --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 51436b5d8737..755871c3d551 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -796,7 +796,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ item: ( ), shouldShow: isDistanceRequest && canUseP2PDistanceRequests, From b7b53dd129075fb0c3165e64d74c23e32b1cfac5 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 5 Apr 2024 23:44:10 +0300 Subject: [PATCH 050/283] added admin-submit case --- src/libs/ReportUtils.ts | 47 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0c9197c1796c..2cb8b205061b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3314,9 +3314,50 @@ function buildOptimisticExpenseReport(chatReportID: string, policyID: string, pa function getIOUSubmittedMessage(report: OnyxEntry) { const policy = getPolicy(report?.policyID); - const ownerPersonalDetails = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); - let submittedToDisplayName = `${ownerPersonalDetails.displayName ?? ''}${ownerPersonalDetails.displayName !== ownerPersonalDetails.login ? ` (${ownerPersonalDetails.login})` : ''}`; - if (ownerPersonalDetails?.accountID === currentUserAccountID) { + + if (report?.ownerAccountID !== currentUserAccountID && policy.role === CONST.POLICY.ROLE.ADMIN) { + const ownerPersonalDetail = getPersonalDetailsForAccountID(report?.ownerAccountID ?? 0); + const ownerDisplayName = `${ownerPersonalDetail.displayName ?? ''}${ownerPersonalDetail.displayName !== ownerPersonalDetail.login ? ` (${ownerPersonalDetail.login})` : ''}`; + + return [ + { + style: 'normal', + text: 'You (on behalf of ', + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + }, + { + style: 'strong', + text: ownerDisplayName, + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + }, + { + style: 'normal', + text: ' via admin-submit)', + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + }, + { + style: 'normal', + text: ' submitted this report', + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + }, + { + style: 'normal', + text: ' to ', + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + }, + { + style: 'strong', + text: 'you', + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + }, + ]; + } + + const submittedToPersonalDetail = getPersonalDetailsForAccountID(policy?.submitsTo ?? 0); + let submittedToDisplayName = `${submittedToPersonalDetail.displayName ?? ''}${ + submittedToPersonalDetail.displayName !== submittedToPersonalDetail.login ? ` (${submittedToPersonalDetail.login})` : '' + }`; + if (submittedToPersonalDetail?.accountID === currentUserAccountID) { submittedToDisplayName = 'yourself'; } From 7712ceca4d980517d9330caf155ee50357923f19 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 8 Apr 2024 15:27:17 +0200 Subject: [PATCH 051/283] fix: get rates from withOnyx hoc, rename DefaultMileageRate --- src/components/MoneyRequestConfirmationList.tsx | 4 ++-- src/libs/DistanceRequestUtils.ts | 10 +++++----- src/pages/iou/request/step/IOURequestStepRate.tsx | 12 +++++++++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index e1e39279336f..86506933e6f4 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -12,7 +12,7 @@ import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CurrencyUtils from '@libs/CurrencyUtils'; -import type {DefaultMileageRate} from '@libs/DistanceRequestUtils'; +import type {MileageRate} from '@libs/DistanceRequestUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Log from '@libs/Log'; @@ -64,7 +64,7 @@ type MoneyRequestConfirmationListOnyxProps = { session: OnyxEntry; /** Unit and rate used for if the money request is a distance request */ - mileageRate: OnyxEntry; + mileageRate: OnyxEntry; }; type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & { /** Callback to inform parent modal of success */ diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 0aa5a0c4b3a5..c930e6ec9068 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -8,7 +8,7 @@ import type Policy from '@src/types/onyx/Policy'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; -type DefaultMileageRate = { +type MileageRate = { customUnitRateID?: string; rate?: number; currency?: string; @@ -38,7 +38,7 @@ Onyx.connect({ * @returns [currency] - The currency associated with the rate. * @returns [unit] - The unit of measurement for the distance. */ -function getDefaultMileageRate(policy: OnyxEntry): DefaultMileageRate | null { +function getDefaultMileageRate(policy: OnyxEntry): MileageRate | null { if (!policy?.customUnits) { return null; } @@ -180,8 +180,8 @@ function getDistanceMerchant( * * @returns An array of mileage rates or an empty array if not found. */ -function getMileageRates(policyID?: string): Record { - const mileageRates: Record = {}; +function getMileageRates(policyID?: string): Record { + const mileageRates: Record = {}; if (!policyID) { return mileageRates; @@ -239,4 +239,4 @@ export default { getRateForP2P, }; -export type {DefaultMileageRate}; +export type {MileageRate}; diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 43f37a97eb76..3dee03fa0f60 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -8,6 +8,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as IOU from '@libs/actions/IOU'; import compose from '@libs/compose'; +import type {MileageRate} from '@libs/DistanceRequestUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; @@ -33,6 +34,9 @@ type Props = { backTo: Route; }; }; + + /** Mileage rates */ + rates: Record; }; function IOURequestStepRate({ @@ -41,10 +45,10 @@ function IOURequestStepRate({ params: {backTo}, }, lastSelectedDistanceRates = {}, + rates, }: Props) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); - const rates = DistanceRequestUtils.getMileageRates(policy?.id); const lastSelectedRate = lastSelectedDistanceRates[policy?.id ?? '0'] ?? rates[0]?.customUnitRateID; @@ -99,6 +103,12 @@ export default compose( lastSelectedDistanceRates: { key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, }, + rates: { + // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, + // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS + selector: (policy) => DistanceRequestUtils.getMileageRates(policy ? policy.id : ''), + }, }), // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS )(IOURequestStepRate); From 72d31422b1fc1d6f1756cf4591f103a4975ec1c2 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 9 Apr 2024 01:13:41 +0200 Subject: [PATCH 052/283] fix: apply requested changes --- src/CONST.ts | 2 +- ...oraryForRefactorRequestConfirmationList.js | 12 +++--- .../ReportActionItem/MoneyRequestView.tsx | 12 +++++- src/libs/DistanceRequestUtils.ts | 42 ++++++++++++++----- src/libs/Permissions.ts | 1 + src/libs/actions/IOU.ts | 35 +++++++++++----- .../step/IOURequestStepParticipants.js | 41 ++---------------- .../iou/request/step/IOURequestStepRate.tsx | 6 +-- 8 files changed, 78 insertions(+), 73 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index a8584ab3859d..fb1d2ac7d288 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4286,6 +4286,6 @@ const CONST = { type Country = keyof typeof CONST.ALL_COUNTRIES; -export type {Country}; +export type {Country, RateAndUnit}; export default CONST; diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 1d23b80ff099..70f12af2dd42 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -261,19 +261,16 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeTrackExpense = iouType === CONST.IOU.TYPE.TRACK_EXPENSE; const canEditDistance = isTypeRequest || (canUseP2PDistanceRequests && isTypeSplit); - // if there is no policyID that means that the transaction was started from Global Create and the participant is not a policy - const personalPolicy = policyID === CONST.POLICY.ID_FAKE || !policyID ? PolicyUtils.getPersonalPolicy() : policy; + const customUnitRateID = lodashGet(transaction, 'comment.customUnit.customUnitRateID', ''); - const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) - ? DistanceRequestUtils.getRateForP2P(personalPolicy.outputCurrency) - : mileageRates[transaction.comment.customUnit.customUnitRateID]; + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policy.outputCurrency) : mileageRates[customUnitRateID]; const {unit, rate} = mileageRate || { unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * 100, }; - const currency = mileageRate.currency || personalPolicy.outputCurrency; + const currency = mileageRate && mileageRate.currency ? mileageRate.currency : policy.outputCurrency; const distance = lodashGet(transaction, 'routes.route0.distance', 0); const taxRates = lodashGet(policy, 'taxRates', {}); @@ -1097,10 +1094,11 @@ export default compose( }, mileageRates: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - selector: (policy) => DistanceRequestUtils.getMileageRates(policy ? policy.id : ''), + selector: DistanceRequestUtils.getMileageRates, }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: (policy, props) => (!policy || (props && props.policyID) === CONST.POLICY.ID_FAKE ? PolicyUtils.getPersonalPolicy() : policy), }, }), )(MoneyTemporaryForRefactorRequestConfirmationList); diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 0f48eb9d683f..2dff075afc14 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -21,6 +21,7 @@ import type {ViolationField} from '@hooks/useViolations'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as CardUtils from '@libs/CardUtils'; import * as CurrencyUtils from '@libs/CurrencyUtils'; +import type {MileageRate} from '@libs/DistanceRequestUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; @@ -64,6 +65,9 @@ type MoneyRequestViewOnyxPropsWithoutTransaction = { /** The actions from the parent report */ parentReportActions: OnyxEntry; + + /** The rates for the policy */ + rates: Record; }; type MoneyRequestViewPropsWithoutTransaction = MoneyRequestViewOnyxPropsWithoutTransaction & { @@ -90,6 +94,7 @@ function MoneyRequestView({ policy, transactionViolations, shouldShowAnimatedBackground, + rates, }: MoneyRequestViewProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -172,15 +177,14 @@ function MoneyRequestView({ let amountDescription = `${translate('iou.amount')}`; const hasRoute = TransactionUtils.hasRoute(transaction); - const distance = transaction?.routes?.route0?.distance ?? 0; const rateID = transaction?.comment.customUnit?.customUnitRateID ?? '0'; - const rates = DistanceRequestUtils.getMileageRates(policy?.id); const currency = policy ? policy.outputCurrency : PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(currency) : rates[rateID as string]; const {unit, rate} = mileageRate; + const distance = DistanceRequestUtils.getDistanceFromMerchant(transactionMerchant, unit); const rateToDisplay = DistanceRequestUtils.getRateForDisplay(hasRoute, unit, rate, currency, translate, toLocaleDigit); const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate); @@ -554,6 +558,10 @@ export default withOnyx `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`, canEvict: false, }, + rates: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, + selector: DistanceRequestUtils.getMileageRates, + }, })( withOnyx({ transaction: { diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index c930e6ec9068..61629c5d58bf 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -1,10 +1,12 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; +import type {RateAndUnit} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Unit} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; @@ -28,6 +30,9 @@ Onyx.connect({ }, }); +const METERS_TO_KM = 0.001; // 1 kilometer is 1000 meters +const METERS_TO_MILES = 0.000621371; // There are approximately 0.000621371 miles in a meter + /** * Retrieves the default mileage rate based on a given policy. * @@ -38,7 +43,7 @@ Onyx.connect({ * @returns [currency] - The currency associated with the rate. * @returns [unit] - The unit of measurement for the distance. */ -function getDefaultMileageRate(policy: OnyxEntry): MileageRate | null { +function getDefaultMileageRate(policy: OnyxEntry | EmptyObject): MileageRate | null { if (!policy?.customUnits) { return null; } @@ -71,9 +76,6 @@ function getDefaultMileageRate(policy: OnyxEntry): MileageRate | null { * @returns The converted distance in the specified unit. */ function convertDistanceUnit(distanceInMeters: number, unit: Unit): number { - const METERS_TO_KM = 0.001; // 1 kilometer is 1000 meters - const METERS_TO_MILES = 0.000621371; // There are approximately 0.000621371 miles in a meter - switch (unit) { case CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS: return distanceInMeters * METERS_TO_KM; @@ -176,20 +178,18 @@ function getDistanceMerchant( /** * Retrieves the mileage rates for given policy. * - * @param policyID - The policy ID from which to extract the mileage rates. + * @param policy - The policy from which to extract the mileage rates. * * @returns An array of mileage rates or an empty array if not found. */ -function getMileageRates(policyID?: string): Record { +function getMileageRates(policy: OnyxEntry): Record { const mileageRates: Record = {}; - if (!policyID) { + if (!policy) { return mileageRates; } - const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? null; - - if (!policy || !policy?.customUnits) { + if (!policy?.customUnits) { return mileageRates; } @@ -211,7 +211,7 @@ function getMileageRates(policyID?: string): Record { return mileageRates; } -function getRateForP2P(currency: string) { +function getRateForP2P(currency: string): RateAndUnit { return CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currency] ?? CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE.USD; } @@ -229,6 +229,25 @@ function getDistanceRequestAmount(distance: number, unit: Unit, rate: number): n return Math.round(roundedDistance * rate); } +/** + * Extracts the distance from a merchant string. + * + * @param merchant - The merchant string containing the distance. + * @returns The distance extracted from the merchant string. + */ +function getDistanceFromMerchant(merchant: string | undefined, unit: Unit): number { + if (!merchant) { + return 0; + } + + const distance = Number(merchant.split(' ')[0]); + if (!distance) { + return 0; + } + + return unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS ? distance / METERS_TO_KM : distance / METERS_TO_MILES; +} + export default { getDefaultMileageRate, getDistanceMerchant, @@ -237,6 +256,7 @@ export default { getMileageRates, getDistanceForDisplay, getRateForP2P, + getDistanceFromMerchant, }; export type {MileageRate}; diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 1973e665b20f..e71359724677 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -27,6 +27,7 @@ function canUseTrackExpense(betas: OnyxEntry): boolean { } function canUseP2PDistanceRequests(betas: OnyxEntry): boolean { + return true; return !!betas?.includes(CONST.BETAS.P2P_DISTANCE_REQUESTS) || canUseAllBetas(betas); } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0240b711d804..e122078c17d9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -284,13 +284,15 @@ function initMoneyRequest(reportID: string, policy: OnyxEntry, waypoint0: {}, waypoint1: {}, }; - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; - if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { - customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; + if (!isFromGlobalCreate) { + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; + const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; + let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; + if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { + customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; + } + comment.customUnit = {customUnitRateID}; } - comment.customUnit = {customUnitRateID}; } // Store the transaction in Onyx and mark it as not saved so it can be cleaned up later @@ -418,13 +420,24 @@ function setMoneyRequestReceipt(transactionID: string, source: string, filename: }); } -/** Set the last selected distance rate for policy */ -function setLastSelectedDistanceRates(policyID: string, rateID: string) { - Onyx.merge(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, {[policyID]: rateID}); +/** Set custom unit rateID for the transaction draft */ +function setCustomUnitRateID(transactionID: string, reportID: string) { + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; + const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; + const policy = getPolicy(report?.policyID) ?? null; + + let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; + + if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { + customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; + } + + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {comment: {customUnit: {customUnitRateID}}}); } /** Update transaction distance rate */ -function updateDistanceRequestRate(transactionID: string, rateID: string) { +function updateDistanceRequestRate(transactionID: string, rateID: string, policyID: string) { + Onyx.merge(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, {[policyID]: rateID}); Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {comment: {customUnit: {customUnitRateID: rateID}}}); } @@ -5296,6 +5309,7 @@ export { setMoneyRequestTaxAmount, setMoneyRequestTaxRate, setShownHoldUseExplanation, + setCustomUnitRateID, updateMoneyRequestDate, updateMoneyRequestBillable, updateMoneyRequestMerchant, @@ -5317,6 +5331,5 @@ export { trackExpense, canIOUBePaid, canApproveIOU, - setLastSelectedDistanceRates, updateDistanceRequestRate, }; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index c6ae0c0fdb0e..734adfbc486c 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -1,21 +1,16 @@ import {useNavigation} from '@react-navigation/native'; import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; -import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; -import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import StepScreenWrapper from './StepScreenWrapper'; @@ -29,9 +24,6 @@ const propTypes = { /* Onyx Props */ /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, - - // eslint-disable-next-line react/require-default-props - lastSelectedDistanceRates: PropTypes.shape({}), }; const defaultProps = { @@ -44,7 +36,6 @@ function IOURequestStepParticipants({ }, transaction, transaction: {participants = []}, - lastSelectedDistanceRates = {}, }) { const {translate} = useLocalize(); const navigation = useNavigation(); @@ -109,23 +100,7 @@ function IOURequestStepParticipants({ } IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); - - const participant = val[0]; - - // change customUnitRateID when choosing - if (participant.isPolicyExpenseChat && TransactionUtils.isCustomUnitRateIDForP2P(transaction)) { - let customUnitRateID = ''; - if (lastSelectedDistanceRates[participant.policyID]) { - customUnitRateID = lastSelectedDistanceRates[participant.policyID]; - } else { - const policy = ReportUtils.getPolicy(participant.policyID); - const defaultRate = DistanceRequestUtils.getDefaultMileageRate(policy); - customUnitRateID = defaultRate ? defaultRate.customUnitRateID : ''; - } - IOU.updateDistanceRequestRate(transactionID, customUnitRateID); - } else if (!TransactionUtils.isCustomUnitRateIDForP2P(transaction)) { - IOU.updateDistanceRequestRate(transactionID, CONST.CUSTOM_UNITS.FAKE_P2P_ID); - } + IOU.setCustomUnitRateID(transactionID, reportID); numberOfParticipants.current = val.length; @@ -137,9 +112,9 @@ function IOURequestStepParticipants({ } // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. - selectedReportID.current = participant.reportID; + selectedReportID.current = val[0].reportID; }, - [iouType, participants, transactionID, transaction, reportID, updateRouteParams, lastSelectedDistanceRates], + [iouType, participants, transactionID, reportID, updateRouteParams], ); const goToNextStep = useCallback( @@ -190,12 +165,4 @@ IOURequestStepParticipants.displayName = 'IOURequestStepParticipants'; IOURequestStepParticipants.propTypes = propTypes; IOURequestStepParticipants.defaultProps = defaultProps; -export default compose( - withWritableReportOrNotFound, - withFullTransactionOrNotFound, - withOnyx({ - lastSelectedDistanceRates: { - key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, - }, - }), -)(IOURequestStepParticipants); +export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepParticipants); diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 3dee03fa0f60..59a5ab67a8a7 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -65,8 +65,7 @@ function IOURequestStepRate({ const initiallyFocusedOption = rates[lastSelectedRate]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; function selectDistanceRate(customUnitRateID: string) { - IOU.setLastSelectedDistanceRates(policy?.id ?? '', customUnitRateID); - IOU.updateDistanceRequestRate('1', customUnitRateID); + IOU.updateDistanceRequestRate('1', customUnitRateID, policy?.id ?? ''); Navigation.goBack(backTo); } @@ -106,8 +105,7 @@ export default compose( rates: { // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, - // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS - selector: (policy) => DistanceRequestUtils.getMileageRates(policy ? policy.id : ''), + selector: DistanceRequestUtils.getMileageRates, }, }), // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS From 69cfe20b8e6794d1e2ca8dc2c072e6d66ad30446 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 9 Apr 2024 20:43:38 +0700 Subject: [PATCH 053/283] Get correct transaction data in ReportActionItem --- src/pages/home/report/ReportActionItem.tsx | 13 +++++++++---- src/pages/home/report/ReportActionsList.tsx | 6 ++++++ .../home/report/ReportActionsListItemRenderer.tsx | 5 +++++ src/pages/home/report/ReportActionsView.tsx | 11 ++++++++--- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index e3e32f398fb6..5391db6d59ea 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -120,6 +120,11 @@ type ReportActionItemProps = { /** Report action belonging to the report's parent */ parentReportAction: OnyxEntry; + /** The transaction thread report's parentReportAction */ + /** It's used by withOnyx HOC */ + // eslint-disable-next-line react/no-unused-prop-types + parentReportActionForTransactionThread: OnyxEntry; + /** All the data of the action item */ action: OnyxTypes.ReportAction; @@ -939,10 +944,10 @@ export default withOnyx({ key: ONYXKEYS.USER_WALLET, }, transaction: { - key: ({transactionThreadReport, reportActions}) => { - const parentReportActionID = isEmptyObject(transactionThreadReport) ? '0' : transactionThreadReport.parentReportActionID; - const action = reportActions?.find((reportAction) => reportAction.reportActionID === parentReportActionID); - const transactionID = (action as OnyxTypes.OriginalMessageIOU)?.originalMessage.IOUTransactionID ? (action as OnyxTypes.OriginalMessageIOU).originalMessage.IOUTransactionID : 0; + key: ({parentReportActionForTransactionThread}) => { + const transactionID = (parentReportActionForTransactionThread as OnyxTypes.OriginalMessageIOU)?.originalMessage.IOUTransactionID + ? (parentReportActionForTransactionThread as OnyxTypes.OriginalMessageIOU).originalMessage.IOUTransactionID + : 0; return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; }, }, diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index d1b9c420b0af..210867100d3c 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -50,6 +50,9 @@ type ReportActionsListProps = WithCurrentUserPersonalDetailsProps & { /** The report's parentReportAction */ parentReportAction: OnyxEntry; + /** The transaction thread report's parentReportAction */ + parentReportActionForTransactionThread: OnyxEntry; + /** Sorted actions prepared for display */ sortedReportActions: OnyxTypes.ReportAction[]; @@ -148,6 +151,7 @@ function ReportActionsList({ listID, onContentSizeChange, shouldEnableAutoScrollToTopThreshold, + parentReportActionForTransactionThread, }: ReportActionsListProps) { const personalDetailsList = usePersonalDetails() || CONST.EMPTY_OBJECT; const styles = useThemeStyles(); @@ -524,6 +528,7 @@ function ReportActionsList({ reportAction={reportAction} reportActions={reportActions} parentReportAction={parentReportAction} + parentReportActionForTransactionThread={parentReportActionForTransactionThread} index={index} report={report} transactionThreadReport={transactionThreadReport} @@ -546,6 +551,7 @@ function ReportActionsList({ parentReportAction, reportActions, transactionThreadReport, + parentReportActionForTransactionThread, ], ); diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx index 263415d90c39..32ca9861d7d0 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx @@ -17,6 +17,9 @@ type ReportActionsListItemRendererProps = { /** The report's parentReportAction */ parentReportAction: OnyxEntry; + /** The transaction thread report's parentReportAction */ + parentReportActionForTransactionThread: OnyxEntry; + /** Position index of the report action in the overall report FlatList view */ index: number; @@ -58,6 +61,7 @@ function ReportActionsListItemRenderer({ shouldDisplayNewMarker, linkedReportActionID = '', shouldDisplayReplyDivider, + parentReportActionForTransactionThread, }: ReportActionsListItemRendererProps) { const shouldDisplayParentAction = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && ReportUtils.isChatThread(report) && !ReportActionsUtils.isTransactionThread(parentReportAction); @@ -145,6 +149,7 @@ function ReportActionsListItemRenderer({ parentReportAction={parentReportAction} report={report} transactionThreadReport={transactionThreadReport} + parentReportActionForTransactionThread={parentReportActionForTransactionThread} action={action} reportActions={reportActions} linkedReportActionID={linkedReportActionID} diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 6040c1e67212..7eb355ef3e85 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -137,20 +137,24 @@ function ReportActionsView({ // Get a sorted array of reportActions for both the current report and the transaction thread report associated with this report (if there is one) // so that we display transaction-level and report-level report actions in order in the one-transaction view - const combinedReportActions = useMemo(() => { + const [combinedReportActions, parentReportActionForTransactionThread] = useMemo(() => { if (isEmptyObject(transactionThreadReportActions)) { - return allReportActions; + return [allReportActions, null]; } // Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions` const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); + const moneyRequestAction = allReportActions.find((action) => { + const actionType = (action as OnyxTypes.OriginalMessageIOU).originalMessage?.type ?? ''; + return actionType === CONST.IOU.REPORT_ACTION_TYPE.CREATE || actionType === CONST.IOU.REPORT_ACTION_TYPE.TRACK || !ReportActionsUtils.isSentMoneyReportAction(action); + }); // Filter out the money request actions because we don't want to show any preview actions for one-transaction reports const filteredReportActions = [...allReportActions, ...filteredTransactionThreadReportActions].filter((action) => { const actionType = (action as OnyxTypes.OriginalMessageIOU).originalMessage?.type ?? ''; return actionType !== CONST.IOU.REPORT_ACTION_TYPE.CREATE && actionType !== CONST.IOU.REPORT_ACTION_TYPE.TRACK && !ReportActionsUtils.isSentMoneyReportAction(action); }); - return ReportActionsUtils.getSortedReportActions(filteredReportActions, true); + return [ReportActionsUtils.getSortedReportActions(filteredReportActions, true), moneyRequestAction ?? null]; }, [allReportActions, transactionThreadReportActions]); const indexOfLinkedAction = useMemo(() => { @@ -517,6 +521,7 @@ function ReportActionsView({ transactionThreadReport={transactionThreadReport} reportActions={reportActions} parentReportAction={parentReportAction} + parentReportActionForTransactionThread={parentReportActionForTransactionThread} onLayout={recordTimeToMeasureItemLayout} sortedReportActions={reportActionsToDisplay} mostRecentIOUReportActionID={mostRecentIOUReportActionID} From bacc983501bf9ebe73492233ea619190550cccb1 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 9 Apr 2024 22:16:55 +0700 Subject: [PATCH 054/283] fix: The background of fallback avatar has the same color as selection highlight --- src/styles/utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index e9efc84e8807..97dad7df1efe 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1185,7 +1185,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ let backgroundColor; if (currentEmojiIndex === highlightedEmojiIndex) { - backgroundColor = theme.activeComponentBG; + backgroundColor = theme.buttonHoveredBG; } else if (isHovered) { backgroundColor = theme.hoverComponentBG; } From faec57128745bdd08b58aa64a5c551a8da59d882 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 9 Apr 2024 23:26:55 +0700 Subject: [PATCH 055/283] update correctly filter condition --- src/pages/home/report/ReportActionsView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 7eb355ef3e85..45b58a807877 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -146,7 +146,7 @@ function ReportActionsView({ const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); const moneyRequestAction = allReportActions.find((action) => { const actionType = (action as OnyxTypes.OriginalMessageIOU).originalMessage?.type ?? ''; - return actionType === CONST.IOU.REPORT_ACTION_TYPE.CREATE || actionType === CONST.IOU.REPORT_ACTION_TYPE.TRACK || !ReportActionsUtils.isSentMoneyReportAction(action); + return actionType === CONST.IOU.REPORT_ACTION_TYPE.CREATE || actionType === CONST.IOU.REPORT_ACTION_TYPE.TRACK || ReportActionsUtils.isSentMoneyReportAction(action); }); // Filter out the money request actions because we don't want to show any preview actions for one-transaction reports From e2de27005699da62d4bfcc07c0d897830656b612 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 9 Apr 2024 23:37:27 +0700 Subject: [PATCH 056/283] fix lint --- src/pages/home/report/ReportActionItem.tsx | 2 +- tests/perf-test/ReportActionsList.perf-test.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 5391db6d59ea..298822364f21 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -123,7 +123,7 @@ type ReportActionItemProps = { /** The transaction thread report's parentReportAction */ /** It's used by withOnyx HOC */ // eslint-disable-next-line react/no-unused-prop-types - parentReportActionForTransactionThread: OnyxEntry; + parentReportActionForTransactionThread?: OnyxEntry; /** All the data of the action item */ action: OnyxTypes.ReportAction; diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index a952d149a598..7d45e3e4b45d 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -95,6 +95,7 @@ function ReportActionsListWrapper() { Date: Tue, 9 Apr 2024 21:09:49 +0200 Subject: [PATCH 057/283] fix: remove unnecessary line --- src/libs/Permissions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index e71359724677..1973e665b20f 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -27,7 +27,6 @@ function canUseTrackExpense(betas: OnyxEntry): boolean { } function canUseP2PDistanceRequests(betas: OnyxEntry): boolean { - return true; return !!betas?.includes(CONST.BETAS.P2P_DISTANCE_REQUESTS) || canUseAllBetas(betas); } From 7bf1e976e6e7db510ef1afaf1f0cc54604d423a5 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 9 Apr 2024 21:24:42 +0200 Subject: [PATCH 058/283] fix: change hardcoded transactionID value for the one from the route params --- src/pages/iou/request/step/IOURequestStepRate.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 59a5ab67a8a7..5952f37c81b3 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -32,6 +32,9 @@ type Props = { params: { /** The route to go back to */ backTo: Route; + + /** The ID of the transaction being configured */ + transactionID: string; }; }; @@ -42,7 +45,7 @@ type Props = { function IOURequestStepRate({ policy, route: { - params: {backTo}, + params: {backTo, transactionID}, }, lastSelectedDistanceRates = {}, rates, @@ -65,7 +68,7 @@ function IOURequestStepRate({ const initiallyFocusedOption = rates[lastSelectedRate]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; function selectDistanceRate(customUnitRateID: string) { - IOU.updateDistanceRequestRate('1', customUnitRateID, policy?.id ?? ''); + IOU.updateDistanceRequestRate(transactionID, customUnitRateID, policy?.id ?? ''); Navigation.goBack(backTo); } From 880a34a6d6266fbcee789255f7f05ad852436f44 Mon Sep 17 00:00:00 2001 From: bartektomczyk Date: Fri, 5 Apr 2024 12:52:45 +0200 Subject: [PATCH 059/283] feat: added new method for handliung onboarding, build new messages structure for onboarding --- src/CONST.ts | 240 +++++++++++++++++++++++++++++++++++++ src/libs/API/types.ts | 1 + src/libs/actions/Report.ts | 104 ++++++++++++++++ 3 files changed, 345 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index b07b622cec05..f317a2dca3fa 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3587,6 +3587,246 @@ const CONST = { "Expensify is best known for expense and corporate card management, but we do a lot more than that. Let me know what you're interested in and I'll help get you started.", }, + ONBOARDING_MESSAGES: { + [onboardingChoices.TRACK]: { + message: 'Here are some essential tasks to keep your business spend in shape for tax season.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Create a workspace', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more. ', + message: + 'Here’s how to create a workspace:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Workspaces > New workspace.\n' + + '\n' + + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + }, + { + title: 'Track an expense', + subtitle: 'Track an expense in any currency, in just a few clicks.', + message: + 'Here’s how to track an expense:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Track expense.\n' + + '3. Enter an amount or scan a receipt.\n' + + '4. Click Track.\n' + + '\n' + + 'And you’re done! Yep, it’s that easy.', + }, + ], + }, + [onboardingChoices.EMPLOYER]: { + message: 'Getting paid back is as easy as sending a message. Let’s go over the basics.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Submit an expense', + subtitle: 'Submit an expense by entering an amount or scanning a receipt.', + message: + 'Here’s how to submit an expense:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Submit expense.\n' + + '3. Enter an amount or scan a receipt.\n' + + '4. Add your reimburser to the request.\n' + + '\n' + + 'Then, send your request and wait for that sweet “Cha-ching!” when it’s complete.', + }, + { + title: 'Enable your wallet', + subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', + message: + 'Here’s how to set up your wallet:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Wallet > Enable wallet.\n' + + '3. Connect your bank account.\n' + + '\n' + + 'Once that’s done, you can request money from anyone and get paid back right into your personal bank account.', + }, + ], + }, + [onboardingChoices.MANAGE_TEAM]: { + message: 'Here are some important tasks to help get your team’s expenses under control.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Create a workspace', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more. ', + message: + 'Here’s how to create a workspace:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Workspaces > New workspace.\n' + + '\n' + + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + }, + { + title: 'Meet your setup specialist', + subtitle: '', + message: + 'Meet your setup specialist, {guideName}, who can answer any questions as you get started with Expensify. Yes, a real human!\n' + + '\n' + + 'Chat with {guideName} in your [admins room]({adminsRoomID}) or [schedule a call]({guideCalendarLink}) today', + }, + { + title: 'Set up categories', + subtitle: 'Set up categories so your team can code expenses for easy reporting.', + message: + 'Here’s how to set up categories:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Go to Workspaces > [your workspace].\n' + + '3. Click Categories.\n' + + '4. Enable and disable default categories.\n' + + '5. Click Add categories to make your own.\n' + + '\n' + + 'For more controls like requiring a category for every expense, click Settings. ', + }, + { + title: 'Add expense approvals', + subtitle: 'Add expense approvals to review your team’s spend and keep it under control.', + message: + 'Here’s how to add expense approvals:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Go to Workspaces > [your workspace].\n' + + '3. Click More features.\n' + + '4. Enable Workflows.\n' + + '5. In Workflows, enable Add approvals.\n' + + '\n' + + 'You’ll be set as the expense approver. You can change this to any admin once you invite your team. ', + }, + { + title: 'Invite your team', + subtitle: 'Invite your team to Expensify so they can start tracking expenses today.', + message: + 'Here’s how to invite your team:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Go to Workspaces > [your workspace].\n' + + '3. Click Members > Invite member.\n' + + '4. Enter emails or phone numbers. \n' + + '5. Add an invite message if you want.\n' + + '\n' + + 'That’s it! Happy expensing :)', + }, + ], + }, + [onboardingChoices.PERSONAL_SPEND]: { + message: 'Here’s how to track your spend in a few clicks.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Track an expense', + subtitle: 'Track an expense in any currency, whether you have a receipt or not.', + message: + 'Here’s how to track an expense:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Track expense.\n' + + '3. Enter an amount or scan a receipt.\n' + + '4. Click Track.\n' + + '\n' + + 'And you’re done! Yep, it’s that easy.', + }, + ], + }, + [onboardingChoices.CHAT_SPLIT]: { + message: 'Splitting bills with friends is as easy as sending a message. Here’s how.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [ + { + title: 'Start a chat', + subtitle: 'Start a chat with a friend or group using their email or phone number.', + message: + 'Here’s how to start a chat:\n' + + '\n' + + '1. rClick the green + button.\n' + + '2. Choose Start chat.\n' + + '3. Enter emails or phone numbers.\n' + + '\n' + + 'If any of your friends aren’t using Expensify already, they’ll be invited automatically. \n' + + '\n' + + 'Every chat will also turn into an email or text that they can respond to directly.', + }, + { + title: 'Split an expense', + subtitle: 'Split an expense right in your chat with one or more friends.', + message: + 'Here’s how to request money:\n' + + '\n' + + '1. Click the green + button.\n' + + '2. Choose Split expense.\n' + + '3. Scan a receipt or enter an amount.\n' + + '4. Add your friend(s) to the request.\n' + + '\n' + + 'Feel free to add more details if you want, or just send it off. Let’s get you paid back!', + }, + { + title: 'Enable your wallet', + subtitle: 'You’ll need to enable your Expensify Wallet to get paid back. Don’t worry, it’s easy!', + message: + 'Here’s how to enable your wallet:\n' + + '\n' + + '1. Click your profile picture.\n' + + '2. Click Wallet > Enable wallet.\n' + + '3. Add your bank account.\n' + + '\n' + + 'Once that’s done, you can request money from anyone and get paid right into your personal bank account.', + }, + ], + }, + [onboardingChoices.LOOKING_AROUND]: { + message: + '# Welcome to Expensify!\n' + + "Hi there, I'm Concierge. Chat with me here for anything you need.\n" + + '\n' + + "Expensify is best known for expense and corporate card management, but we do a lot more than that. Let me know what you're interested in and I'll help get you started.", + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, + tasks: [], + }, + }, + REPORT_FIELD_TITLE_FIELD_ID: 'text_title', MOBILE_PAGINATION_SIZE: 15, diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index fc19ba60693c..c33e96731bdc 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -134,6 +134,7 @@ const WRITE_COMMANDS = { REOPEN_TASK: 'ReopenTask', COMPLETE_TASK: 'CompleteTask', COMPLETE_ENGAGEMENT_MODAL: 'CompleteEngagementModal', + COMPLETE_GUIDED_SETUP: 'CompleteGuidedSetup', SET_NAME_VALUE_PAIR: 'SetNameValuePair', SET_REPORT_FIELD: 'Report_SetFields', DELETE_REPORT_FIELD: 'RemoveReportField', diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index d2f85362baf8..8fe56d93d634 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2774,6 +2774,109 @@ function getReportPrivateNote(reportID: string | undefined) { API.read(READ_COMMANDS.GET_REPORT_PRIVATE_NOTE, parameters, {optimisticData, successData, failureData}); } +function completeOnboarding(properties: { + data: ValueOf; + firstName: string; + lastName: string; + targetEmail: ValueOf; + engagementChoice: ValueOf; +}) { + const {data, engagementChoice, targetEmail, firstName, lastName} = properties; + const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; + const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); + const targetChatReportID = targetChatReport?.reportID ?? ''; + const targetChatPolicyID = targetChatReport?.policyID ?? ''; + + // Text message + const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined); + const textCommentAction: OptimisticAddCommentReportAction = textComment.reportAction; + const textCommentText = textComment.commentText; + + const textMessage: AddCommentOrAttachementParams & {type: string} = { + reportID: targetChatReportID, + reportActionID: textCommentAction.reportActionID, + reportComment: textCommentText, + type: 'message', + }; + + // Video message + const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined); + const videoCommentAction: OptimisticAddCommentReportAction = videoComment.reportAction; + const videoCommentText = videoComment.commentText; + + const videoMessage: AddCommentOrAttachementParams & {type: string} & typeof data.video = { + reportID: targetChatReportID, + reportActionID: videoCommentAction.reportActionID, + reportComment: videoCommentText, + type: 'video', + ...data.video, + }; + + // tasks + const tasks = data.tasks.reduce((acc, curr) => { + const currTask = ReportUtils.buildOptimisticTaskReport(actorAccountID, undefined, targetChatReportID, curr.title, undefined, targetChatPolicyID); + const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(targetEmail); + const taskAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(currTask.reportID, curr.title, 0, `task for ${curr.title}`, targetChatReportID); + + // subtitle message + const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(curr.subtitle, undefined, actorAccountID); + const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; + const subtitleCommentText = subtitleComment.commentText; + + const subtitleMessage: AddCommentOrAttachementParams = { + reportID: currTask.reportID, + reportActionID: subtitleCommentAction.reportActionID, + reportComment: subtitleCommentText, + }; + + // instruction message + const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(curr.message, undefined, actorAccountID); + const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; + const instructionCommentText = instructionComment.commentText; + + const instructionMessage: AddCommentOrAttachementParams = { + reportID: currTask.reportID, + reportActionID: instructionCommentAction.reportActionID, + reportComment: instructionCommentText, + }; + + return [ + ...acc, + { + parentReportActionID: taskAddCommentReport.reportAction.reportActionID, + parentReportID: currTask.parentReportID, + taskReportID: currTask.reportID, + createdTaskReportActionID: taskCreatedAction.reportActionID, + title: currTask.reportName, + description: currTask.description, + assigneeChatReportID: '', + type: 'task', + task: 'trackExpense', + }, + { + type: 'message', + ...subtitleMessage, + }, + { + type: 'message', + ...instructionMessage, + }, + ]; + }, []); + + const parameters = { + engagementChoice, + firstName, + lastName, + data: JSON.stringify([textMessage, videoMessage, ...tasks]), + }; + + // console.log('-------------------------------------'); + // console.log(JSON.stringify({...parameters, data: JSON.parse(parameters.data)}, ' ', 2)); + + API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {}); +} + /** * Completes the engagement modal that new NewDot users see when they first sign up/log in by doing the following: * @@ -3093,4 +3196,5 @@ export { setGroupDraft, clearGroupChat, startNewChat, + completeOnboarding, }; From c396f2af93022c2b13f38804517c122aa423a550 Mon Sep 17 00:00:00 2001 From: bartektomczyk Date: Fri, 5 Apr 2024 16:31:47 +0200 Subject: [PATCH 060/283] feat: call method --- src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index ae43f850018b..bd8a7ab40917 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -80,7 +80,14 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on return; } - Report.completeEngagementModal(CONST.ONBOARDING_CONCIERGE[selectedPurpose], selectedPurpose); + // Report.completeEngagementModal(CONST.ONBOARDING_CONCIERGE[selectedPurpose], selectedPurpose); + Report.completeOnboarding({ + data: CONST.ONBOARDING_MESSAGES[selectedPurpose], + engagementChoice: selectedPurpose, + targetEmail: CONST.EMAIL.CONCIERGE, + firstName: 'Test', + lastName: 'Testovsky', + }); Navigation.dismissModal(); // Only navigate to concierge chat when central pane is visible From 9ad33297839823d773b05331be3ca8a522aae2eb Mon Sep 17 00:00:00 2001 From: bartektomczyk Date: Wed, 10 Apr 2024 08:41:54 +0200 Subject: [PATCH 061/283] feat: added optimistic data --- src/libs/actions/Report.ts | 133 ++++++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8fe56d93d634..8b280e11973b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -71,6 +71,7 @@ import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/NewRoomForm'; import type {PersonalDetails, PersonalDetailsList, PolicyReportField, RecentlyUsedReportFields, ReportActionReactions, ReportMetadata, ReportUserIsTyping} from '@src/types/onyx'; +import type * as OnyxTypes from '@src/types/onyx'; import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import type {NotificationPreference, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; @@ -2788,38 +2789,50 @@ function completeOnboarding(properties: { const targetChatPolicyID = targetChatReport?.policyID ?? ''; // Text message - const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined); + const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID); const textCommentAction: OptimisticAddCommentReportAction = textComment.reportAction; const textCommentText = textComment.commentText; - const textMessage: AddCommentOrAttachementParams & {type: string} = { + const textMessage: AddCommentOrAttachementParams = { reportID: targetChatReportID, reportActionID: textCommentAction.reportActionID, reportComment: textCommentText, - type: 'message', }; // Video message - const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined); + const videoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID); const videoCommentAction: OptimisticAddCommentReportAction = videoComment.reportAction; const videoCommentText = videoComment.commentText; - const videoMessage: AddCommentOrAttachementParams & {type: string} & typeof data.video = { + const videoMessage: AddCommentOrAttachementParams = { reportID: targetChatReportID, reportActionID: videoCommentAction.reportActionID, reportComment: videoCommentText, - type: 'video', - ...data.video, }; - // tasks - const tasks = data.tasks.reduce((acc, curr) => { - const currTask = ReportUtils.buildOptimisticTaskReport(actorAccountID, undefined, targetChatReportID, curr.title, undefined, targetChatPolicyID); + const tasksData = data.tasks.map((task) => { + const currTask = ReportUtils.buildOptimisticTaskReport(actorAccountID, undefined, targetChatReportID, task.title, undefined, targetChatPolicyID); const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(targetEmail); - const taskAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(currTask.reportID, curr.title, 0, `task for ${curr.title}`, targetChatReportID); + const taskAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(currTask.reportID, task.title, 0, `task for ${task.title}`, targetChatReportID); // subtitle message - const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(curr.subtitle, undefined, actorAccountID); + const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID); + + // instruction message + const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(task.message, undefined, actorAccountID); + + return { + currTask, + taskCreatedAction, + taskAddCommentReport, + subtitleComment, + instructionComment, + }; + }); + + // tasks + const tasksForParameters = tasksData.reduce((acc, {currTask, taskCreatedAction, taskAddCommentReport, subtitleComment, instructionComment}) => { + // subtitle message const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; const subtitleCommentText = subtitleComment.commentText; @@ -2830,7 +2843,6 @@ function completeOnboarding(properties: { }; // instruction message - const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(curr.message, undefined, actorAccountID); const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; const instructionCommentText = instructionComment.commentText; @@ -2851,7 +2863,7 @@ function completeOnboarding(properties: { description: currTask.description, assigneeChatReportID: '', type: 'task', - task: 'trackExpense', + task: engagementChoice, }, { type: 'message', @@ -2864,17 +2876,101 @@ function completeOnboarding(properties: { ]; }, []); + const tasksForOptimisticData = tasksData.reduce((acc, {currTask, taskCreatedAction, subtitleComment, instructionComment}) => { + // subtitle message + const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; + const subtitleCommentText = subtitleComment.commentText; + + const subtitleMessage: AddCommentOrAttachementParams = { + reportID: currTask.reportID, + reportActionID: subtitleCommentAction.reportActionID, + reportComment: subtitleCommentText, + }; + + // instruction message + const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; + const instructionCommentText = instructionComment.commentText; + + const instructionMessage: AddCommentOrAttachementParams = { + reportID: currTask.reportID, + reportActionID: instructionCommentAction.reportActionID, + reportComment: instructionCommentText, + }; + + return [ + ...acc, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${currTask.reportID}`, + value: { + ...currTask, + pendingFields: { + createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + reportName: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + description: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + managerID: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + isOptimisticReport: true, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currTask.reportID}`, + value: {[taskCreatedAction.reportActionID]: taskCreatedAction as OnyxTypes.ReportAction}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${currTask.reportID}`, + value: {[subtitleMessage.reportID ?? '']: subtitleMessage, [instructionMessage.reportID ?? '']: instructionMessage}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [subtitleCommentAction.reportActionID ?? '']: subtitleCommentAction as ReportAction, + [instructionCommentAction.reportActionID ?? '']: instructionCommentAction as ReportAction, + }, + }, + ]; + }, []); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${targetChatReportID}`, + value: {[textMessage.reportID ?? '']: textMessage, [videoMessage.reportID ?? '']: videoMessage}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: {[textCommentAction.reportActionID ?? '']: textCommentAction as ReportAction, [videoCommentAction.reportActionID ?? '']: videoCommentAction as ReportAction}, + }, + ...tasksForOptimisticData, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_INTRO_SELECTED, + value: {choice: engagementChoice}, + }, + ]; + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: {[textCommentAction.reportActionID ?? '']: {pendingAction: null}, [videoCommentAction.reportActionID ?? '']: {pendingAction: null}}, + }, + ]; + const parameters = { engagementChoice, firstName, lastName, - data: JSON.stringify([textMessage, videoMessage, ...tasks]), + data: JSON.stringify([{type: 'message', ...textMessage}, {type: 'video', ...data.video, ...videoMessage}, ...tasksForParameters]), }; // console.log('-------------------------------------'); // console.log(JSON.stringify({...parameters, data: JSON.parse(parameters.data)}, ' ', 2)); - API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {}); + API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData}); } /** @@ -2949,6 +3045,11 @@ function completeEngagementModal(text: string, choice: ValueOf Date: Wed, 10 Apr 2024 08:47:26 +0200 Subject: [PATCH 062/283] feat: added optimistic data --- src/libs/actions/Report.ts | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 8b280e11973b..3edd4515a1d2 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -726,6 +726,11 @@ function openReport( key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: settledPersonalDetails, }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: settledPersonalDetails, + }); // Add the createdReportActionID parameter to the API call parameters.createdReportActionID = optimisticCreatedAction.reportActionID; @@ -1939,16 +1944,6 @@ function deleteReport(reportID: string) { Onyx.multiSet(onyxData); - // Clear the optimistic personal detail - const participantPersonalDetails: OnyxCollection = {}; - report?.participantAccountIDs?.forEach((accountID) => { - if (!allPersonalDetails?.[accountID]?.isOptimisticPersonalDetail) { - return; - } - participantPersonalDetails[accountID] = null; - }); - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, participantPersonalDetails); - // Delete linked IOU report if (report?.iouReportID) { deleteReport(report.iouReportID); @@ -2900,7 +2895,7 @@ function completeOnboarding(properties: { return [ ...acc, { - onyxMethod: Onyx.METHOD.MERGE, + onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${currTask.reportID}`, value: { ...currTask, @@ -2967,9 +2962,6 @@ function completeOnboarding(properties: { data: JSON.stringify([{type: 'message', ...textMessage}, {type: 'video', ...data.video, ...videoMessage}, ...tasksForParameters]), }; - // console.log('-------------------------------------'); - // console.log(JSON.stringify({...parameters, data: JSON.parse(parameters.data)}, ' ', 2)); - API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData}); } From 1077782645bd53ca71eb3cc53d0cfbada1a29ec5 Mon Sep 17 00:00:00 2001 From: bartektomczyk Date: Wed, 10 Apr 2024 11:01:34 +0200 Subject: [PATCH 063/283] feat: update method --- src/libs/actions/Report.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3edd4515a1d2..3f288c29723c 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2920,7 +2920,7 @@ function completeOnboarding(properties: { }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currTask.reportID}`, value: { [subtitleCommentAction.reportActionID ?? '']: subtitleCommentAction as ReportAction, [instructionCommentAction.reportActionID ?? '']: instructionCommentAction as ReportAction, @@ -2940,7 +2940,6 @@ function completeOnboarding(properties: { key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, value: {[textCommentAction.reportActionID ?? '']: textCommentAction as ReportAction, [videoCommentAction.reportActionID ?? '']: videoCommentAction as ReportAction}, }, - ...tasksForOptimisticData, { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_INTRO_SELECTED, @@ -2962,7 +2961,7 @@ function completeOnboarding(properties: { data: JSON.stringify([{type: 'message', ...textMessage}, {type: 'video', ...data.video, ...videoMessage}, ...tasksForParameters]), }; - API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData}); + API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {}); } /** @@ -3037,11 +3036,6 @@ function completeEngagementModal(text: string, choice: ValueOf Date: Wed, 10 Apr 2024 14:44:40 +0200 Subject: [PATCH 064/283] fix: type IOURequestStepRate properly --- src/libs/Navigation/types.ts | 6 ++ .../iou/request/step/IOURequestStepRate.tsx | 64 +++++++------------ .../step/withWritableReportOrNotFound.tsx | 1 + 3 files changed, 31 insertions(+), 40 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 1f1e7ec9a459..0bc20cb2baa1 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -439,6 +439,12 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; + [SCREENS.MONEY_REQUEST.STEP_RATE]: { + iouType: ValueOf; + transactionID: string; + backTo: Routes; + reportID: string; + }; [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: { action: ValueOf; iouType: ValueOf; diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 5952f37c81b3..e56ea39e9b13 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -7,41 +7,31 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as IOU from '@libs/actions/IOU'; -import compose from '@libs/compose'; import type {MileageRate} from '@libs/DistanceRequestUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import type {Policy} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import StepScreenWrapper from './StepScreenWrapper'; +import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -type Props = { +type IOURequestStepRateOnyxProps = { /** Object of last selected rates for the policies */ - lastSelectedDistanceRates: Record; + lastSelectedDistanceRates: OnyxEntry>; /** Policy details */ policy: OnyxEntry; - /** The route object passed to this screen */ - route: { - /** The params passed to this screen */ - params: { - /** The route to go back to */ - backTo: Route; - - /** The ID of the transaction being configured */ - transactionID: string; - }; - }; - /** Mileage rates */ rates: Record; }; +type IOURequestStepRateProps = IOURequestStepRateOnyxProps & WithWritableReportOrNotFoundProps; + function IOURequestStepRate({ policy, route: { @@ -49,23 +39,23 @@ function IOURequestStepRate({ }, lastSelectedDistanceRates = {}, rates, -}: Props) { +}: IOURequestStepRateProps) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); - const lastSelectedRate = lastSelectedDistanceRates[policy?.id ?? '0'] ?? rates[0]?.customUnitRateID; + const lastSelectedRateID = lastSelectedDistanceRates?.[policy?.id ?? '0'] ?? rates[0]?.customUnitRateID ?? ''; const sections = Object.values(rates).map((rate) => ({ text: rate.name ?? '', alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), keyForList: rate.name ?? '', value: rate.customUnitRateID, - isSelected: lastSelectedRate ? lastSelectedRate === rate.customUnitRateID : Boolean(rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE), + isSelected: lastSelectedRateID ? lastSelectedRateID === rate.customUnitRateID : Boolean(rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE), })); const unit = (Object.values(rates)[0]?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer')) as Unit; - const initiallyFocusedOption = rates[lastSelectedRate]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; + const initiallyFocusedOption = rates[lastSelectedRateID]?.name ?? CONST.CUSTOM_UNITS.DEFAULT_RATE; function selectDistanceRate(customUnitRateID: string) { IOU.updateDistanceRequestRate(transactionID, customUnitRateID, policy?.id ?? ''); @@ -93,23 +83,17 @@ function IOURequestStepRate({ IOURequestStepRate.displayName = 'IOURequestStepRate'; -export default compose( - withWritableReportOrNotFound, - withOnyx({ - // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS - policy: { - // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, - }, - // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS - lastSelectedDistanceRates: { - key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, - }, - rates: { - // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, - selector: DistanceRequestUtils.getMileageRates, - }, - }), - // @ts-expect-error TODO: fix when withWritableReportOrNotFound will be migrated to TS -)(IOURequestStepRate); +const IOURequestStepRateWithOnyx = withOnyx({ + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + }, + lastSelectedDistanceRates: { + key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, + }, + rates: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? '0'}`, + selector: DistanceRequestUtils.getMileageRates, + }, +})(IOURequestStepRate); + +export default withWritableReportOrNotFound(IOURequestStepRateWithOnyx); diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index 515f6f97f280..b7cd77c6c8a6 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -21,6 +21,7 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_WAYPOINT | typeof SCREENS.MONEY_REQUEST.STEP_DESCRIPTION | typeof SCREENS.MONEY_REQUEST.STEP_CATEGORY + | typeof SCREENS.MONEY_REQUEST.STEP_RATE | typeof SCREENS.MONEY_REQUEST.STEP_CONFIRMATION | typeof SCREENS.MONEY_REQUEST.STEP_TAX_RATE | typeof SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT; From 7fb35ab243994dac4573956f4eb317712d364bad Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 10 Apr 2024 16:46:30 +0200 Subject: [PATCH 065/283] fix: minor fix --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 623de07a05f8..70b0b7e3e6b2 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -181,7 +181,7 @@ function MoneyRequestView({ const currency = policy ? policy.outputCurrency : PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; - const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(currency) : rates[rateID as string]; + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(currency) : rates[rateID as string] ?? {}; const {unit, rate} = mileageRate; const distance = DistanceRequestUtils.getDistanceFromMerchant(transactionMerchant, unit); From 994ea52e6f9cbcf3e2d44aef68c648a508c76c34 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 11:57:17 -0400 Subject: [PATCH 066/283] return promises from push notification callbacks --- .../PushNotification/index.native.ts | 18 ++++++++++-------- ...ubscribeToReportCommentPushNotifications.ts | 7 ++++++- .../Notification/PushNotification/types.ts | 4 ++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/libs/Notification/PushNotification/index.native.ts b/src/libs/Notification/PushNotification/index.native.ts index 4e028ad82392..b15e30b94b85 100644 --- a/src/libs/Notification/PushNotification/index.native.ts +++ b/src/libs/Notification/PushNotification/index.native.ts @@ -10,7 +10,7 @@ import NotificationType from './NotificationType'; import type {ClearNotifications, Deregister, Init, OnReceived, OnSelected, Register} from './types'; import type PushNotificationType from './types'; -type NotificationEventActionCallback = (data: NotificationData) => void; +type NotificationEventActionCallback = (data: NotificationData) => Promise; type NotificationEventActionMap = Partial>>; @@ -56,7 +56,13 @@ function pushNotificationEventCallback(eventType: EventType, notification: PushP }); return; } - action(data); + + /** + * The action callback should return a promise. It's very important we return that promise so that + * when these callbacks are run in Android's background process (via Headless JS), the process waits + * for the promise to resolve before quitting + */ + return action(data); } /** @@ -83,15 +89,11 @@ function refreshNotificationOptInStatus() { */ const init: Init = () => { // Setup event listeners - Airship.addListener(EventType.PushReceived, (notification) => { - pushNotificationEventCallback(EventType.PushReceived, notification.pushPayload); - }); + Airship.addListener(EventType.PushReceived, (notification) => pushNotificationEventCallback(EventType.PushReceived, notification.pushPayload)); // Note: the NotificationResponse event has a nested PushReceived event, // so event.notification refers to the same thing as notification above ^ - Airship.addListener(EventType.NotificationResponse, (event) => { - pushNotificationEventCallback(EventType.NotificationResponse, event.pushPayload); - }); + Airship.addListener(EventType.NotificationResponse, (event) => pushNotificationEventCallback(EventType.NotificationResponse, event.pushPayload)); // Keep track of which users have enabled push notifications via an NVP. Airship.addListener(EventType.NotificationOptInStatus, refreshNotificationOptInStatus); diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts index 36051fa35c56..b31509f2aeb8 100644 --- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts +++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts @@ -33,8 +33,9 @@ export default function subscribeToReportCommentPushNotifications() { PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportActionID, onyxData, lastUpdateID, previousUpdateID}) => { if (!ActiveClientManager.isClientTheLeader()) { Log.info('[PushNotification] received report comment notification, but ignoring it since this is not the active client'); - return; + return Promise.resolve(); } + Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID}); if (onyxData && lastUpdateID && previousUpdateID) { @@ -55,6 +56,8 @@ export default function subscribeToReportCommentPushNotifications() { } else { Log.hmmm("[PushNotification] Didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); } + + return Promise.resolve(); }); // Open correct report when push notification is clicked @@ -96,5 +99,7 @@ export default function subscribeToReportCommentPushNotifications() { } }); }); + + return Promise.resolve(); }); } diff --git a/src/libs/Notification/PushNotification/types.ts b/src/libs/Notification/PushNotification/types.ts index 4399c10b4a95..cf7e54abd094 100644 --- a/src/libs/Notification/PushNotification/types.ts +++ b/src/libs/Notification/PushNotification/types.ts @@ -5,8 +5,8 @@ import type NotificationType from './NotificationType'; type Init = () => void; type Register = (notificationID: string | number) => void; type Deregister = () => void; -type OnReceived = >(notificationType: T, callback: (data: NotificationDataMap[T]) => void) => void; -type OnSelected = >(notificationType: T, callback: (data: NotificationDataMap[T]) => void) => void; +type OnReceived = >(notificationType: T, callback: (data: NotificationDataMap[T]) => Promise) => void; +type OnSelected = >(notificationType: T, callback: (data: NotificationDataMap[T]) => Promise) => void; type ClearNotifications = () => void; type PushNotification = { From 37b4241f4ddb878554ed35ce5f182a7e471c0a69 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 12:20:15 -0400 Subject: [PATCH 067/283] optionally handle onyx update gaps synchronously --- src/libs/actions/OnyxUpdateManager.ts | 146 ++++++++++++++------------ src/libs/actions/OnyxUpdates.ts | 8 +- 2 files changed, 83 insertions(+), 71 deletions(-) diff --git a/src/libs/actions/OnyxUpdateManager.ts b/src/libs/actions/OnyxUpdateManager.ts index 40f522e215b5..54fcc22b1ad1 100644 --- a/src/libs/actions/OnyxUpdateManager.ts +++ b/src/libs/actions/OnyxUpdateManager.ts @@ -1,9 +1,11 @@ +import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import Log from '@libs/Log'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {OnyxUpdatesFromServer} from '@src/types/onyx'; import * as App from './App'; import * as OnyxUpdates from './OnyxUpdates'; @@ -36,81 +38,85 @@ Onyx.connect({ }, }); -export default () => { - console.debug('[OnyxUpdateManager] Listening for updates from the server'); - Onyx.connect({ - key: ONYXKEYS.ONYX_UPDATES_FROM_SERVER, - callback: (value) => { - // When the OpenApp command hasn't finished yet, we should not process any updates. - if (!value || isLoadingApp) { - if (isLoadingApp) { - console.debug(`[OnyxUpdateManager] Ignoring Onyx updates while OpenApp hans't finished yet.`); - } - return; - } - // This key is shared across clients, thus every client/tab will have a copy and try to execute this method. - // It is very important to only process the missing onyx updates from leader client otherwise requests we'll execute - // several duplicated requests that are not controlled by the SequentialQueue. - if (!ActiveClientManager.isClientTheLeader()) { - return; - } +function handleOnyxUpdateGap(onyxUpdatesFromServer: OnyxEntry) { + // When the OpenApp command hasn't finished yet, we should not process any updates. + if (!onyxUpdatesFromServer || isLoadingApp) { + if (isLoadingApp) { + console.debug(`[OnyxUpdateManager] Ignoring Onyx updates while OpenApp hans't finished yet.`); + } + return; + } + // This key is shared across clients, thus every client/tab will have a copy and try to execute this method. + // It is very important to only process the missing onyx updates from leader client otherwise requests we'll execute + // several duplicated requests that are not controlled by the SequentialQueue. + if (!ActiveClientManager.isClientTheLeader()) { + return; + } - // Since we used the same key that used to store another object, let's confirm that the current object is - // following the new format before we proceed. If it isn't, then let's clear the object in Onyx. - if ( - !(typeof value === 'object' && !!value) || - !('type' in value) || - (!(value.type === CONST.ONYX_UPDATE_TYPES.HTTPS && value.request && value.response) && - !((value.type === CONST.ONYX_UPDATE_TYPES.PUSHER || value.type === CONST.ONYX_UPDATE_TYPES.AIRSHIP) && value.updates)) - ) { - console.debug('[OnyxUpdateManager] Invalid format found for updates, cleaning and unpausing the queue'); - Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); - SequentialQueue.unpause(); - return; - } + // Since we used the same key that used to store another object, let's confirm that the current object is + // following the new format before we proceed. If it isn't, then let's clear the object in Onyx. + if ( + !(typeof onyxUpdatesFromServer === 'object' && !!onyxUpdatesFromServer) || + !('type' in onyxUpdatesFromServer) || + (!(onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.HTTPS && onyxUpdatesFromServer.request && onyxUpdatesFromServer.response) && + !((onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.PUSHER || onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.AIRSHIP) && onyxUpdatesFromServer.updates)) + ) { + console.debug('[OnyxUpdateManager] Invalid format found for updates, cleaning and unpausing the queue'); + Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); + SequentialQueue.unpause(); + return; + } - const updateParams = value; - const lastUpdateIDFromServer = value.lastUpdateID; - const previousUpdateIDFromServer = value.previousUpdateID; + const updateParams = onyxUpdatesFromServer; + const lastUpdateIDFromServer = onyxUpdatesFromServer.lastUpdateID; + const previousUpdateIDFromServer = onyxUpdatesFromServer.previousUpdateID; - // In cases where we received a previousUpdateID and it doesn't match our lastUpdateIDAppliedToClient - // we need to perform one of the 2 possible cases: - // - // 1. This is the first time we're receiving an lastUpdateID, so we need to do a final reconnectApp before - // fully migrating to the reliable updates mode. - // 2. This client already has the reliable updates mode enabled, but it's missing some updates and it - // needs to fetch those. - // - // For both of those, we need to pause the sequential queue. This is important so that the updates are - // applied in their correct and specific order. If this queue was not paused, then there would be a lot of - // onyx data being applied while we are fetching the missing updates and that would put them all out of order. - SequentialQueue.pause(); - let canUnpauseQueuePromise; + // In cases where we received a previousUpdateID and it doesn't match our lastUpdateIDAppliedToClient + // we need to perform one of the 2 possible cases: + // + // 1. This is the first time we're receiving an lastUpdateID, so we need to do a final reconnectApp before + // fully migrating to the reliable updates mode. + // 2. This client already has the reliable updates mode enabled, but it's missing some updates and it + // needs to fetch those. + // + // For both of those, we need to pause the sequential queue. This is important so that the updates are + // applied in their correct and specific order. If this queue was not paused, then there would be a lot of + // onyx data being applied while we are fetching the missing updates and that would put them all out of order. + SequentialQueue.pause(); + let canUnpauseQueuePromise; - // The flow below is setting the promise to a reconnect app to address flow (1) explained above. - if (!lastUpdateIDAppliedToClient) { - Log.info('Client has not gotten reliable updates before so reconnecting the app to start the process'); + // The flow below is setting the promise to a reconnect app to address flow (1) explained above. + if (!lastUpdateIDAppliedToClient) { + Log.info('Client has not gotten reliable updates before so reconnecting the app to start the process'); - // Since this is a full reconnectApp, we'll not apply the updates we received - those will come in the reconnect app request. - canUnpauseQueuePromise = App.finalReconnectAppAfterActivatingReliableUpdates(); - } else { - // The flow below is setting the promise to a getMissingOnyxUpdates to address flow (2) explained above. - console.debug(`[OnyxUpdateManager] Client is behind the server by ${Number(previousUpdateIDFromServer) - lastUpdateIDAppliedToClient} so fetching incremental updates`); - Log.info('Gap detected in update IDs from server so fetching incremental updates', true, { - lastUpdateIDFromServer, - previousUpdateIDFromServer, - lastUpdateIDAppliedToClient, - }); - canUnpauseQueuePromise = App.getMissingOnyxUpdates(lastUpdateIDAppliedToClient, previousUpdateIDFromServer); - } + // Since this is a full reconnectApp, we'll not apply the updates we received - those will come in the reconnect app request. + canUnpauseQueuePromise = App.finalReconnectAppAfterActivatingReliableUpdates(); + } else { + // The flow below is setting the promise to a getMissingOnyxUpdates to address flow (2) explained above. + console.debug(`[OnyxUpdateManager] Client is behind the server by ${Number(previousUpdateIDFromServer) - lastUpdateIDAppliedToClient} so fetching incremental updates`); + Log.info('Gap detected in update IDs from server so fetching incremental updates', true, { + lastUpdateIDFromServer, + previousUpdateIDFromServer, + lastUpdateIDAppliedToClient, + }); + canUnpauseQueuePromise = App.getMissingOnyxUpdates(lastUpdateIDAppliedToClient, previousUpdateIDFromServer); + } - canUnpauseQueuePromise.finally(() => { - OnyxUpdates.apply(updateParams).finally(() => { - console.debug('[OnyxUpdateManager] Done applying all updates'); - Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); - SequentialQueue.unpause(); - }); - }); - }, + canUnpauseQueuePromise.finally(() => { + OnyxUpdates.apply(updateParams).finally(() => { + console.debug('[OnyxUpdateManager] Done applying all updates'); + Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); + SequentialQueue.unpause(); + }); + }); +} + +export default () => { + console.debug('[OnyxUpdateManager] Listening for updates from the server'); + Onyx.connect({ + key: ONYXKEYS.ONYX_UPDATES_FROM_SERVER, + callback: handleOnyxUpdateGap, }); }; + +export {handleOnyxUpdateGap}; diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index e093ce9a964b..157f7f16ae0d 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -9,6 +9,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxUpdateEvent, OnyxUpdatesFromServer, Request} from '@src/types/onyx'; import type Response from '@src/types/onyx/Response'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import {handleOnyxUpdateGap} from './OnyxUpdateManager'; import * as QueuedOnyxUpdates from './QueuedOnyxUpdates'; // This key needs to be separate from ONYXKEYS.ONYX_UPDATES_FROM_SERVER so that it can be updated without triggering the callback when the server IDs are updated. If that @@ -142,13 +143,18 @@ function doesClientNeedToBeUpdated(previousUpdateID = 0): boolean { return lastUpdateIDAppliedToClient < previousUpdateID; } -function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer) { +function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer, shouldRunSync = false) { const previousUpdateID = Number(updates.previousUpdateID) || 0; if (!doesClientNeedToBeUpdated(previousUpdateID)) { apply(updates); return; } + if (shouldRunSync) { + handleOnyxUpdateGap(updates); + return; + } + // If we reached this point, we need to pause the queue while we prepare to fetch older OnyxUpdates. SequentialQueue.pause(); saveUpdateInformation(updates); From 9e0ef4b8a97aca33da8d45e8c57a475fdd05fb57 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 12:44:08 -0400 Subject: [PATCH 068/283] fix dependency cycle --- ...bscribeToReportCommentPushNotifications.ts | 4 +-- src/libs/actions/OnyxUpdates.ts | 21 +------------ src/libs/actions/User.ts | 4 +-- src/libs/actions/applyOnyxUpdatesReliably.ts | 30 +++++++++++++++++++ 4 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 src/libs/actions/applyOnyxUpdatesReliably.ts diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts index b31509f2aeb8..7845aa6705ad 100644 --- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts +++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts @@ -1,5 +1,5 @@ import Onyx from 'react-native-onyx'; -import * as OnyxUpdates from '@libs/actions/OnyxUpdates'; +import applyOnyxUpdatesReliably from '@libs/actions/applyOnyxUpdatesReliably'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -52,7 +52,7 @@ export default function subscribeToReportCommentPushNotifications() { }, ], }; - OnyxUpdates.applyOnyxUpdatesReliably(updates); + applyOnyxUpdatesReliably(updates); } else { Log.hmmm("[PushNotification] Didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); } diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 157f7f16ae0d..e23ddaac33e6 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -2,14 +2,12 @@ import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {Merge} from 'type-fest'; import Log from '@libs/Log'; -import * as SequentialQueue from '@libs/Network/SequentialQueue'; import PusherUtils from '@libs/PusherUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxUpdateEvent, OnyxUpdatesFromServer, Request} from '@src/types/onyx'; import type Response from '@src/types/onyx/Response'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import {handleOnyxUpdateGap} from './OnyxUpdateManager'; import * as QueuedOnyxUpdates from './QueuedOnyxUpdates'; // This key needs to be separate from ONYXKEYS.ONYX_UPDATES_FROM_SERVER so that it can be updated without triggering the callback when the server IDs are updated. If that @@ -143,22 +141,5 @@ function doesClientNeedToBeUpdated(previousUpdateID = 0): boolean { return lastUpdateIDAppliedToClient < previousUpdateID; } -function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer, shouldRunSync = false) { - const previousUpdateID = Number(updates.previousUpdateID) || 0; - if (!doesClientNeedToBeUpdated(previousUpdateID)) { - apply(updates); - return; - } - - if (shouldRunSync) { - handleOnyxUpdateGap(updates); - return; - } - - // If we reached this point, we need to pause the queue while we prepare to fetch older OnyxUpdates. - SequentialQueue.pause(); - saveUpdateInformation(updates); -} - // eslint-disable-next-line import/prefer-default-export -export {saveUpdateInformation, doesClientNeedToBeUpdated, apply, applyOnyxUpdatesReliably}; +export {saveUpdateInformation, doesClientNeedToBeUpdated, apply}; diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index dcd6e025e23b..4487dcf407c4 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -44,8 +44,8 @@ import type ReportAction from '@src/types/onyx/ReportAction'; import type {OriginalMessage} from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; import * as Link from './Link'; -import * as OnyxUpdates from './OnyxUpdates'; import * as Report from './Report'; import * as Session from './Session'; @@ -597,7 +597,7 @@ function subscribeToUserEvents() { updates: pushJSON.updates ?? [], previousUpdateID: Number(pushJSON.previousUpdateID || 0), }; - OnyxUpdates.applyOnyxUpdatesReliably(updates); + applyOnyxUpdatesReliably(updates); }); // Handles Onyx updates coming from Pusher through the mega multipleEvents. diff --git a/src/libs/actions/applyOnyxUpdatesReliably.ts b/src/libs/actions/applyOnyxUpdatesReliably.ts new file mode 100644 index 000000000000..cae3e3a34d26 --- /dev/null +++ b/src/libs/actions/applyOnyxUpdatesReliably.ts @@ -0,0 +1,30 @@ +import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import type {OnyxUpdatesFromServer} from '@src/types/onyx'; +import {handleOnyxUpdateGap} from './OnyxUpdateManager'; +import * as OnyxUpdates from './OnyxUpdates'; + +/** + * Checks for and handles gaps of onyx updates between the client and the given server updates before applying them + * + * This is in it's own lib to fix a dependency cycle from OnyxUpdateManager + * + * @param updates + * @param shouldRunSync + * @returns + */ +export default function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer, shouldRunSync = false) { + const previousUpdateID = Number(updates.previousUpdateID) || 0; + if (!OnyxUpdates.doesClientNeedToBeUpdated(previousUpdateID)) { + OnyxUpdates.apply(updates); + return; + } + + if (shouldRunSync) { + handleOnyxUpdateGap(updates); + return; + } + + // If we reached this point, we need to pause the queue while we prepare to fetch older OnyxUpdates. + SequentialQueue.pause(); + OnyxUpdates.saveUpdateInformation(updates); +} From dc7ceb2a909c2a30a1b795eed910d620393833a5 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 12:45:16 -0400 Subject: [PATCH 069/283] apply reliable update synchronously --- .../subscribeToReportCommentPushNotifications.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts index 7845aa6705ad..3d06ff1fb25a 100644 --- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts +++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts @@ -52,7 +52,7 @@ export default function subscribeToReportCommentPushNotifications() { }, ], }; - applyOnyxUpdatesReliably(updates); + applyOnyxUpdatesReliably(updates, true); } else { Log.hmmm("[PushNotification] Didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); } From 9f56daf34846c0868bee93831b313297146d288b Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 13:04:53 -0400 Subject: [PATCH 070/283] simplify promise --- ...bscribeToReportCommentPushNotifications.ts | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts index 3d06ff1fb25a..ad1637e07a4f 100644 --- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts +++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts @@ -30,35 +30,40 @@ Onyx.connect({ * Setup reportComment push notification callbacks. */ export default function subscribeToReportCommentPushNotifications() { - PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportActionID, onyxData, lastUpdateID, previousUpdateID}) => { - if (!ActiveClientManager.isClientTheLeader()) { - Log.info('[PushNotification] received report comment notification, but ignoring it since this is not the active client'); - return Promise.resolve(); - } - - Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID}); + PushNotification.onReceived( + PushNotification.TYPE.REPORT_COMMENT, + ({reportID, reportActionID, onyxData, lastUpdateID, previousUpdateID}) => + new Promise((resolve) => { + Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID}); - if (onyxData && lastUpdateID && previousUpdateID) { - Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + if (!ActiveClientManager.isClientTheLeader()) { + Log.info('[PushNotification] received report comment notification, but ignoring it since this is not the active client'); + resolve(); + return; + } - const updates: OnyxUpdatesFromServer = { - type: CONST.ONYX_UPDATE_TYPES.AIRSHIP, - lastUpdateID, - previousUpdateID, - updates: [ - { - eventType: 'eventType', - data: onyxData, - }, - ], - }; - applyOnyxUpdatesReliably(updates, true); - } else { - Log.hmmm("[PushNotification] Didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); - } + if (!onyxData || !lastUpdateID || !previousUpdateID) { + Log.hmmm("[PushNotification] didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + resolve(); + return; + } - return Promise.resolve(); - }); + Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + const updates: OnyxUpdatesFromServer = { + type: CONST.ONYX_UPDATE_TYPES.AIRSHIP, + lastUpdateID, + previousUpdateID, + updates: [ + { + eventType: 'eventType', + data: onyxData, + }, + ], + }; + applyOnyxUpdatesReliably(updates, true); + resolve(); + }), + ); // Open correct report when push notification is clicked PushNotification.onSelected(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportActionID}) => { From 7367ef3e3384a9f2bc2bc82e06582e8b285bdcfc Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 13:23:21 -0400 Subject: [PATCH 071/283] add optional clientLastUpdateID to override lastUpdateIDAppliedToClient --- src/libs/actions/OnyxUpdateManager.ts | 19 +++++++++++++------ src/libs/actions/OnyxUpdates.ts | 11 +++++++---- src/libs/actions/applyOnyxUpdatesReliably.ts | 6 +++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/OnyxUpdateManager.ts b/src/libs/actions/OnyxUpdateManager.ts index 54fcc22b1ad1..2deaa134d30c 100644 --- a/src/libs/actions/OnyxUpdateManager.ts +++ b/src/libs/actions/OnyxUpdateManager.ts @@ -38,7 +38,13 @@ Onyx.connect({ }, }); -function handleOnyxUpdateGap(onyxUpdatesFromServer: OnyxEntry) { +/** + * + * @param onyxUpdatesFromServer + * @param clientLastUpdateID an optional override for the lastUpdateIDAppliedToClient + * @returns + */ +function handleOnyxUpdateGap(onyxUpdatesFromServer: OnyxEntry, clientLastUpdateID = 0) { // When the OpenApp command hasn't finished yet, we should not process any updates. if (!onyxUpdatesFromServer || isLoadingApp) { if (isLoadingApp) { @@ -70,6 +76,7 @@ function handleOnyxUpdateGap(onyxUpdatesFromServer: OnyxEntry { @@ -115,7 +122,7 @@ export default () => { console.debug('[OnyxUpdateManager] Listening for updates from the server'); Onyx.connect({ key: ONYXKEYS.ONYX_UPDATES_FROM_SERVER, - callback: handleOnyxUpdateGap, + callback: (value) => handleOnyxUpdateGap(value), }); }; diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index e23ddaac33e6..a20f615e545c 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -126,19 +126,22 @@ function saveUpdateInformation(updateParams: OnyxUpdatesFromServer) { * This function will receive the previousUpdateID from any request/pusher update that has it, compare to our current app state * and return if an update is needed * @param previousUpdateID The previousUpdateID contained in the response object + * @param clientLastUpdateID an optional override for the lastUpdateIDAppliedToClient */ -function doesClientNeedToBeUpdated(previousUpdateID = 0): boolean { +function doesClientNeedToBeUpdated(previousUpdateID = 0, clientLastUpdateID = 0): boolean { // If no previousUpdateID is sent, this is not a WRITE request so we don't need to update our current state if (!previousUpdateID) { return false; } - // If we don't have any value in lastUpdateIDAppliedToClient, this is the first time we're receiving anything, so we need to do a last reconnectApp - if (!lastUpdateIDAppliedToClient) { + const clientUpdateID = clientLastUpdateID || lastUpdateIDAppliedToClient; + + // If we don't have any value in clientUpdateID, this is the first time we're receiving anything, so we need to do a last reconnectApp + if (!clientUpdateID) { return true; } - return lastUpdateIDAppliedToClient < previousUpdateID; + return clientUpdateID < previousUpdateID; } // eslint-disable-next-line import/prefer-default-export diff --git a/src/libs/actions/applyOnyxUpdatesReliably.ts b/src/libs/actions/applyOnyxUpdatesReliably.ts index cae3e3a34d26..0dfba1388089 100644 --- a/src/libs/actions/applyOnyxUpdatesReliably.ts +++ b/src/libs/actions/applyOnyxUpdatesReliably.ts @@ -12,15 +12,15 @@ import * as OnyxUpdates from './OnyxUpdates'; * @param shouldRunSync * @returns */ -export default function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer, shouldRunSync = false) { +export default function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer, shouldRunSync = false, clientLastUpdateID = 0) { const previousUpdateID = Number(updates.previousUpdateID) || 0; - if (!OnyxUpdates.doesClientNeedToBeUpdated(previousUpdateID)) { + if (!OnyxUpdates.doesClientNeedToBeUpdated(previousUpdateID, clientLastUpdateID)) { OnyxUpdates.apply(updates); return; } if (shouldRunSync) { - handleOnyxUpdateGap(updates); + handleOnyxUpdateGap(updates, clientLastUpdateID); return; } From 89b0c3f5a84f81d362355b09e9eb56a4705c5f6f Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 13:32:17 -0400 Subject: [PATCH 072/283] manually read lastUpdateIDAppliedToClient from onyx before applying updates --- ...bscribeToReportCommentPushNotifications.ts | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts index ad1637e07a4f..cd34807a5735 100644 --- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts +++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts @@ -26,44 +26,52 @@ Onyx.connect({ }, }); +function getLastUpdateIDAppliedToClient(): Promise { + return new Promise((resolve) => { + Onyx.connect({ + key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, + callback: (value) => resolve(value ?? 0), + }); + }); +} + /** * Setup reportComment push notification callbacks. */ export default function subscribeToReportCommentPushNotifications() { - PushNotification.onReceived( - PushNotification.TYPE.REPORT_COMMENT, - ({reportID, reportActionID, onyxData, lastUpdateID, previousUpdateID}) => - new Promise((resolve) => { - Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID}); + PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportActionID, onyxData, lastUpdateID, previousUpdateID}) => { + Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID}); - if (!ActiveClientManager.isClientTheLeader()) { - Log.info('[PushNotification] received report comment notification, but ignoring it since this is not the active client'); - resolve(); - return; - } + if (!ActiveClientManager.isClientTheLeader()) { + Log.info('[PushNotification] received report comment notification, but ignoring it since this is not the active client'); + return Promise.resolve(); + } - if (!onyxData || !lastUpdateID || !previousUpdateID) { - Log.hmmm("[PushNotification] didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); - resolve(); - return; - } + if (!onyxData || !lastUpdateID || !previousUpdateID) { + Log.hmmm("[PushNotification] didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + return Promise.resolve(); + } + + Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + const updates: OnyxUpdatesFromServer = { + type: CONST.ONYX_UPDATE_TYPES.AIRSHIP, + lastUpdateID, + previousUpdateID, + updates: [ + { + eventType: 'eventType', + data: onyxData, + }, + ], + }; - Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); - const updates: OnyxUpdatesFromServer = { - type: CONST.ONYX_UPDATE_TYPES.AIRSHIP, - lastUpdateID, - previousUpdateID, - updates: [ - { - eventType: 'eventType', - data: onyxData, - }, - ], - }; - applyOnyxUpdatesReliably(updates, true); - resolve(); - }), - ); + /** + * When this callback runs in the background on Android (via Headless JS), no other Onyx.connect callbacks will run. This means that + * lastUpdateIDAppliedToClient will NOT be populated in other libs. To workaround this, we manually read the value here + * and pass it as a param + */ + return getLastUpdateIDAppliedToClient().then((lastUpdateIDAppliedToClient) => applyOnyxUpdatesReliably(updates, true, lastUpdateIDAppliedToClient)); + }); // Open correct report when push notification is clicked PushNotification.onSelected(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportActionID}) => { From f62e13b30645bb2845e96ae575943d32cb5efed7 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 10 Apr 2024 13:37:58 -0400 Subject: [PATCH 073/283] use consistent naming --- src/libs/actions/OnyxUpdates.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index a20f615e545c..5dc26e5e49a9 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -134,14 +134,14 @@ function doesClientNeedToBeUpdated(previousUpdateID = 0, clientLastUpdateID = 0) return false; } - const clientUpdateID = clientLastUpdateID || lastUpdateIDAppliedToClient; + const lastUpdateIDFromClient = clientLastUpdateID || lastUpdateIDAppliedToClient; - // If we don't have any value in clientUpdateID, this is the first time we're receiving anything, so we need to do a last reconnectApp - if (!clientUpdateID) { + // If we don't have any value in lastUpdateIDFromClient, this is the first time we're receiving anything, so we need to do a last reconnectApp + if (!lastUpdateIDFromClient) { return true; } - return clientUpdateID < previousUpdateID; + return lastUpdateIDFromClient < previousUpdateID; } // eslint-disable-next-line import/prefer-default-export From ddf117d2ae48aaf939f0b69e2c12ab3d4339fd83 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 10 Apr 2024 20:24:20 +0200 Subject: [PATCH 074/283] fix: minor fixes --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 2 +- src/pages/iou/request/step/IOURequestStepParticipants.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index ea783e6322b8..cda8a1a7048b 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -253,7 +253,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); - const {canUseViolations} = usePermissions(); + const {canUseP2PDistanceRequests, canUseViolations} = usePermissions(iouType); const isTypeRequest = iouType === CONST.IOU.TYPE.REQUEST; const isTypeSplit = iouType === CONST.IOU.TYPE.SPLIT; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.js b/src/pages/iou/request/step/IOURequestStepParticipants.js index 734adfbc486c..7dc94af66cd0 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.js +++ b/src/pages/iou/request/step/IOURequestStepParticipants.js @@ -100,7 +100,7 @@ function IOURequestStepParticipants({ } IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); - IOU.setCustomUnitRateID(transactionID, reportID); + IOU.setCustomUnitRateID(transactionID, val[0].reportID); numberOfParticipants.current = val.length; From 118aa7beef2563d65f1d84cb4dc44ecc5bd8ae12 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 10 Apr 2024 22:18:20 +0200 Subject: [PATCH 075/283] fix: add comment --- src/libs/DistanceRequestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 61629c5d58bf..28de14fba57d 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -244,7 +244,7 @@ function getDistanceFromMerchant(merchant: string | undefined, unit: Unit): numb if (!distance) { return 0; } - + // we need to convert the distance back to meters (it's saved in kilometers or miles in merchant) to pass it to getDistanceForDisplay return unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS ? distance / METERS_TO_KM : distance / METERS_TO_MILES; } From 1511b2e76d8bc6dab733af078c0b70c4c1e46493 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 12:38:20 +0200 Subject: [PATCH 076/283] fix: display only rate when no rate name --- .../iou/request/step/IOURequestStepRate.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index e56ea39e9b13..8d1d7a97af63 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -45,13 +45,17 @@ function IOURequestStepRate({ const lastSelectedRateID = lastSelectedDistanceRates?.[policy?.id ?? '0'] ?? rates[0]?.customUnitRateID ?? ''; - const sections = Object.values(rates).map((rate) => ({ - text: rate.name ?? '', - alternateText: DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit), - keyForList: rate.name ?? '', - value: rate.customUnitRateID, - isSelected: lastSelectedRateID ? lastSelectedRateID === rate.customUnitRateID : Boolean(rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE), - })); + const sections = Object.values(rates).map((rate) => { + const rateForDisplay = DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit); + + return { + text: rate.name ?? rateForDisplay, + alternateText: rate.name ? rateForDisplay : '', + keyForList: rate.customUnitRateID, + value: rate.customUnitRateID, + isSelected: lastSelectedRateID ? lastSelectedRateID === rate.customUnitRateID : Boolean(rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE), + }; + }); const unit = (Object.values(rates)[0]?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer')) as Unit; From 5c03b38814489542fb45e354242efcec0c51585b Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 13:03:38 +0200 Subject: [PATCH 077/283] fix: remove use of lastSelectedDistanceRate --- src/libs/TransactionUtils.ts | 8 +++++ .../iou/request/step/IOURequestStepRate.tsx | 35 ++++++++++++------- .../step/withFullTransactionOrNotFound.tsx | 1 + 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index d42d0775d4b4..831cc46ff553 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -638,6 +638,13 @@ function isCustomUnitRateIDForP2P(transaction: OnyxEntry): boolean return transaction?.comment?.customUnit?.customUnitRateID === CONST.CUSTOM_UNITS.FAKE_P2P_ID; } +/** + * Check if the customUnitRateID has a value default for P2P distance requests + */ +function getRateID(transaction: OnyxEntry): string | undefined { + return transaction?.comment?.customUnit?.customUnitRateID?.toString(); +} + /** * Gets the default tax name */ @@ -710,6 +717,7 @@ export { getRecentTransactions, hasViolation, isCustomUnitRateIDForP2P, + getRateID, }; export type {TransactionChanges}; diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 8d1d7a97af63..915bc0c5c8b5 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -10,19 +10,18 @@ import * as IOU from '@libs/actions/IOU'; import type {MileageRate} from '@libs/DistanceRequestUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as TransactionUtils from '@libs/TransactionUtils'; +import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {Policy} from '@src/types/onyx'; +import type {Policy, Transaction} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import StepScreenWrapper from './StepScreenWrapper'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; type IOURequestStepRateOnyxProps = { - /** Object of last selected rates for the policies */ - lastSelectedDistanceRates: OnyxEntry>; - /** Policy details */ policy: OnyxEntry; @@ -30,20 +29,28 @@ type IOURequestStepRateOnyxProps = { rates: Record; }; -type IOURequestStepRateProps = IOURequestStepRateOnyxProps & WithWritableReportOrNotFoundProps; +type IOURequestStepRateProps = IOURequestStepRateOnyxProps & + WithWritableReportOrNotFoundProps & { + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + transaction: OnyxEntry; + }; function IOURequestStepRate({ policy, route: { params: {backTo, transactionID}, }, - lastSelectedDistanceRates = {}, + transaction, rates, }: IOURequestStepRateProps) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); - const lastSelectedRateID = lastSelectedDistanceRates?.[policy?.id ?? '0'] ?? rates[0]?.customUnitRateID ?? ''; + const lastSelectedRateID = TransactionUtils.getRateID(transaction) ?? ''; + + const navigateBack = () => { + Navigation.goBack(backTo); + }; const sections = Object.values(rates).map((rate) => { const rateForDisplay = DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit); @@ -63,13 +70,13 @@ function IOURequestStepRate({ function selectDistanceRate(customUnitRateID: string) { IOU.updateDistanceRequestRate(transactionID, customUnitRateID, policy?.id ?? ''); - Navigation.goBack(backTo); + navigateBack(); } return ( Navigation.goBack(backTo)} + onBackButtonPress={navigateBack} shouldShowWrapper={Boolean(backTo)} testID="rate" > @@ -91,13 +98,15 @@ const IOURequestStepRateWithOnyx = withOnyx `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, - lastSelectedDistanceRates: { - key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, - }, rates: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? '0'}`, selector: DistanceRequestUtils.getMileageRates, }, })(IOURequestStepRate); -export default withWritableReportOrNotFound(IOURequestStepRateWithOnyx); +// eslint-disable-next-line rulesdir/no-negated-variables +const IOURequestStepRateWithWritableReportOrNotFound = withWritableReportOrNotFound(IOURequestStepRateWithOnyx); +// eslint-disable-next-line rulesdir/no-negated-variables +const IOURequestStepRateWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepRateWithWritableReportOrNotFound); + +export default IOURequestStepRateWithFullTransactionOrNotFound; diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx index 3d741725032b..d1bbf3956bc2 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx @@ -24,6 +24,7 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS | typeof SCREENS.MONEY_REQUEST.STEP_MERCHANT | typeof SCREENS.MONEY_REQUEST.STEP_TAG + | typeof SCREENS.MONEY_REQUEST.STEP_RATE | typeof SCREENS.MONEY_REQUEST.STEP_CONFIRMATION | typeof SCREENS.MONEY_REQUEST.STEP_CATEGORY | typeof SCREENS.MONEY_REQUEST.STEP_TAX_RATE; From 2836636881d5eb984537b14abdf46da6643139ea Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Thu, 11 Apr 2024 17:04:12 +0530 Subject: [PATCH 078/283] fix: types for SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS. Signed-off-by: Krishna Gupta --- src/libs/Navigation/types.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index bb0800ffcdc6..40326db78502 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -357,7 +357,7 @@ type MoneyRequestNavigatorParamList = { backTo: string; }; [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: { - iouType: string; + iouType: ValueOf; transactionID: string; reportID: string; backTo: string; @@ -443,11 +443,6 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; - [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: { - iouType: ValueOf; - transactionID: string; - reportID: string; - }; [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: { action: ValueOf; iouType: ValueOf; From 96c281d4682e765ca245a1a52bc0c3e2207c5128 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 13:45:04 +0200 Subject: [PATCH 079/283] fix: minor fix --- src/pages/iou/request/step/IOURequestStepRate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepRate.tsx index 915bc0c5c8b5..d7ae3ee0a19e 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepRate.tsx @@ -11,13 +11,13 @@ import type {MileageRate} from '@libs/DistanceRequestUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as TransactionUtils from '@libs/TransactionUtils'; -import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {Policy, Transaction} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import StepScreenWrapper from './StepScreenWrapper'; +import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; From 4f8f78e6593ca3a3d6215790703c72ed9c3f8d72 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 11 Apr 2024 17:00:33 +0200 Subject: [PATCH 080/283] minor fixes of onboarding messages --- src/CONST.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index f5b8ae572cea..65192eedfc94 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3610,12 +3610,12 @@ const CONST = { tasks: [ { title: 'Create a workspace', - subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more. ', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more.', message: 'Here’s how to create a workspace:\n' + '\n' + '1. Click your profile picture.\n' + - '2. Click Workspaces > New workspace.\n' + + '2. Click Workspaces > New workspace.\n' + '\n' + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', }, @@ -3683,7 +3683,7 @@ const CONST = { tasks: [ { title: 'Create a workspace', - subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more. ', + subtitle: 'Create a workspace to track expenses, scan receipts, chat, and more.', message: 'Here’s how to create a workspace:\n' + '\n' + @@ -3712,7 +3712,7 @@ const CONST = { '4. Enable and disable default categories.\n' + '5. Click Add categories to make your own.\n' + '\n' + - 'For more controls like requiring a category for every expense, click Settings. ', + 'For more controls like requiring a category for every expense, click Settings.', }, { title: 'Add expense approvals', @@ -3726,7 +3726,7 @@ const CONST = { '4. Enable Workflows.\n' + '5. In Workflows, enable Add approvals.\n' + '\n' + - 'You’ll be set as the expense approver. You can change this to any admin once you invite your team. ', + 'You’ll be set as the expense approver. You can change this to any admin once you invite your team.', }, { title: 'Invite your team', @@ -3785,7 +3785,7 @@ const CONST = { message: 'Here’s how to start a chat:\n' + '\n' + - '1. rClick the green + button.\n' + + '1. Click the green + button.\n' + '2. Choose Start chat.\n' + '3. Enter emails or phone numbers.\n' + '\n' + From e08f94d3239463edcbbdc47bc6d892120c2020a7 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 11 Apr 2024 17:00:57 +0200 Subject: [PATCH 081/283] create CompleteGuidedSetupParams --- src/libs/API/parameters/CompleteGuidedSetupParams.ts | 11 +++++++++++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 1 + 3 files changed, 13 insertions(+) create mode 100644 src/libs/API/parameters/CompleteGuidedSetupParams.ts diff --git a/src/libs/API/parameters/CompleteGuidedSetupParams.ts b/src/libs/API/parameters/CompleteGuidedSetupParams.ts new file mode 100644 index 000000000000..5ed7b18815af --- /dev/null +++ b/src/libs/API/parameters/CompleteGuidedSetupParams.ts @@ -0,0 +1,11 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type CompleteGuidedSetupParams = { + firstName: string; + lastName: string; + guidedSetupData: string; + engagementChoice: ValueOf; +}; + +export default CompleteGuidedSetupParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 6f5505b263fb..de12dfbe10d3 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -199,3 +199,4 @@ export type {default as SetPolicyForeignCurrencyDefaultParams} from './SetPolicy export type {default as SetPolicyCurrencyDefaultParams} from './SetPolicyCurrencyDefaultParams'; export type {default as UpdatePolicyConnectionConfigParams} from './UpdatePolicyConnectionConfigParams'; export type {default as RenamePolicyTaxParams} from './RenamePolicyTaxParams'; +export type {default as CompleteGuidedSetupParams} from './CompleteGuidedSetupParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index c33e96731bdc..ad48aecb08f0 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -324,6 +324,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.REOPEN_TASK]: Parameters.ReopenTaskParams; [WRITE_COMMANDS.COMPLETE_TASK]: Parameters.CompleteTaskParams; [WRITE_COMMANDS.COMPLETE_ENGAGEMENT_MODAL]: Parameters.CompleteEngagementModalParams; + [WRITE_COMMANDS.COMPLETE_GUIDED_SETUP]: Parameters.CompleteGuidedSetupParams; [WRITE_COMMANDS.SET_NAME_VALUE_PAIR]: Parameters.SetNameValuePairParams; [WRITE_COMMANDS.SET_REPORT_FIELD]: Parameters.SetReportFieldParams; [WRITE_COMMANDS.SET_REPORT_NAME]: Parameters.SetReportNameParams; From 202b1117f5c8949784ed1d1435955f16776e00c4 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 11 Apr 2024 17:01:37 +0200 Subject: [PATCH 082/283] integrate setDisplayName --- src/libs/actions/PersonalDetails.ts | 18 ++++++++++++++++++ .../BaseOnboardingPersonalDetails.tsx | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 8248b721c416..b9cea5c9447c 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -65,6 +65,23 @@ function updatePronouns(pronouns: string) { }); } +function setDisplayName(firstName: string, lastName: string) { + if (!currentUserAccountID) { + return; + } + + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [currentUserAccountID]: { + firstName, + lastName, + displayName: PersonalDetailsUtils.createDisplayName(currentUserEmail ?? '', { + firstName, + lastName, + }), + }, + }); +} + function updateDisplayName(firstName: string, lastName: string) { if (!currentUserAccountID) { return; @@ -411,6 +428,7 @@ export { updateAutomaticTimezone, updateAvatar, updateDateOfBirth, + setDisplayName, updateDisplayName, updateLegalName, updatePronouns, diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index f49c5c0f506e..248d4af4e9d0 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -37,7 +37,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const {shouldUseNarrowLayout} = useOnboardingLayout(); const saveAndNavigate = useCallback((values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { - PersonalDetails.updateDisplayName(values.firstName.trim(), values.lastName.trim()); + PersonalDetails.setDisplayName(values.firstName.trim(), values.lastName.trim()); Navigation.navigate(ROUTES.ONBOARDING_PURPOSE); }, []); From fdb7f46dc1b5d612740e2da1ac4571bfd3ae1a54 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 11 Apr 2024 17:02:23 +0200 Subject: [PATCH 083/283] extend functions in ReportUtils --- src/libs/ReportUtils.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e49c2f25ccc4..48296cb03892 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3173,7 +3173,14 @@ function updateOptimisticParentReportAction(parentReportAction: OnyxEntry Date: Thu, 11 Apr 2024 17:03:51 +0200 Subject: [PATCH 084/283] improve completeOnboarding --- src/libs/actions/Report.ts | 128 ++++++++++++------ .../BaseOnboardingPurpose.tsx | 4 - 2 files changed, 89 insertions(+), 43 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3f288c29723c..21217ee12bd6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -15,6 +15,7 @@ import type { AddEmojiReactionParams, AddWorkspaceRoomParams, CompleteEngagementModalParams, + CompleteGuidedSetupParams, DeleteCommentParams, ExpandURLPreviewParams, FlagCommentParams, @@ -70,13 +71,20 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/NewRoomForm'; -import type {PersonalDetails, PersonalDetailsList, PolicyReportField, RecentlyUsedReportFields, ReportActionReactions, ReportMetadata, ReportUserIsTyping} from '@src/types/onyx'; -import type * as OnyxTypes from '@src/types/onyx'; +import type { + PersonalDetails, + PersonalDetailsList, + PolicyReportField, + RecentlyUsedReportFields, + ReportAction, + ReportActionReactions, + ReportMetadata, + ReportUserIsTyping, +} from '@src/types/onyx'; import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import type {NotificationPreference, RoomVisibility, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; -import type ReportAction from '@src/types/onyx/ReportAction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CachedPDFPaths from './CachedPDFPaths'; @@ -2770,19 +2778,29 @@ function getReportPrivateNote(reportID: string | undefined) { API.read(READ_COMMANDS.GET_REPORT_PRIVATE_NOTE, parameters, {optimisticData, successData, failureData}); } -function completeOnboarding(properties: { - data: ValueOf; - firstName: string; - lastName: string; - targetEmail: ValueOf; - engagementChoice: ValueOf; -}) { - const {data, engagementChoice, targetEmail, firstName, lastName} = properties; +// #region completeOnboarding +function completeOnboarding(properties: {data: ValueOf; engagementChoice: ValueOf}) { + const {data, engagementChoice} = properties; + const targetEmail = CONST.EMAIL.CONCIERGE; + const login = allPersonalDetails?.[currentUserAccountID]?.login ?? ''; + const firstName = allPersonalDetails?.[currentUserAccountID]?.firstName ?? ''; + const lastName = allPersonalDetails?.[currentUserAccountID]?.lastName ?? ''; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); const targetChatReportID = targetChatReport?.reportID ?? ''; const targetChatPolicyID = targetChatReport?.policyID ?? ''; + // Mention message + const mentionComment = ReportUtils.buildOptimisticAddCommentReportAction(`Hey @${login} 👋`, undefined, actorAccountID); + const mentionCommentAction: OptimisticAddCommentReportAction = mentionComment.reportAction; + const mentionCommentText = mentionComment.commentText; + + const mentionMessage: AddCommentOrAttachementParams = { + reportID: targetChatReportID, + reportActionID: mentionCommentAction.reportActionID, + reportComment: mentionCommentText, + }; + // Text message const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID); const textCommentAction: OptimisticAddCommentReportAction = textComment.reportAction; @@ -2806,9 +2824,17 @@ function completeOnboarding(properties: { }; const tasksData = data.tasks.map((task) => { - const currTask = ReportUtils.buildOptimisticTaskReport(actorAccountID, undefined, targetChatReportID, task.title, undefined, targetChatPolicyID); + const currentTask = ReportUtils.buildOptimisticTaskReport( + actorAccountID, + undefined, + targetChatReportID, + task.title, + undefined, + targetChatPolicyID, + CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + ); const taskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(targetEmail); - const taskAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(currTask.reportID, task.title, 0, `task for ${task.title}`, targetChatReportID); + const taskReportAction = ReportUtils.buildOptimisticTaskCommentReportAction(currentTask.reportID, task.title, 0, `task for ${task.title}`, targetChatReportID, actorAccountID); // subtitle message const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID); @@ -2817,22 +2843,22 @@ function completeOnboarding(properties: { const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(task.message, undefined, actorAccountID); return { - currTask, + currentTask, taskCreatedAction, - taskAddCommentReport, + taskReportAction, subtitleComment, instructionComment, }; }); // tasks - const tasksForParameters = tasksData.reduce((acc, {currTask, taskCreatedAction, taskAddCommentReport, subtitleComment, instructionComment}) => { + const tasksForParameters = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { // subtitle message const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; const subtitleCommentText = subtitleComment.commentText; const subtitleMessage: AddCommentOrAttachementParams = { - reportID: currTask.reportID, + reportID: currentTask.reportID, reportActionID: subtitleCommentAction.reportActionID, reportComment: subtitleCommentText, }; @@ -2842,7 +2868,7 @@ function completeOnboarding(properties: { const instructionCommentText = instructionComment.commentText; const instructionMessage: AddCommentOrAttachementParams = { - reportID: currTask.reportID, + reportID: currentTask.reportID, reportActionID: instructionCommentAction.reportActionID, reportComment: instructionCommentText, }; @@ -2850,12 +2876,12 @@ function completeOnboarding(properties: { return [ ...acc, { - parentReportActionID: taskAddCommentReport.reportAction.reportActionID, - parentReportID: currTask.parentReportID, - taskReportID: currTask.reportID, + parentReportActionID: taskReportAction.reportAction.reportActionID, + parentReportID: currentTask.parentReportID, + taskReportID: currentTask.reportID, createdTaskReportActionID: taskCreatedAction.reportActionID, - title: currTask.reportName, - description: currTask.description, + title: currentTask.reportName, + description: currentTask.description, assigneeChatReportID: '', type: 'task', task: engagementChoice, @@ -2871,13 +2897,13 @@ function completeOnboarding(properties: { ]; }, []); - const tasksForOptimisticData = tasksData.reduce((acc, {currTask, taskCreatedAction, subtitleComment, instructionComment}) => { + const tasksForOptimisticData = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { // subtitle message const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; const subtitleCommentText = subtitleComment.commentText; const subtitleMessage: AddCommentOrAttachementParams = { - reportID: currTask.reportID, + reportID: currentTask.reportID, reportActionID: subtitleCommentAction.reportActionID, reportComment: subtitleCommentText, }; @@ -2887,18 +2913,25 @@ function completeOnboarding(properties: { const instructionCommentText = instructionComment.commentText; const instructionMessage: AddCommentOrAttachementParams = { - reportID: currTask.reportID, + reportID: currentTask.reportID, reportActionID: instructionCommentAction.reportActionID, reportComment: instructionCommentText, }; return [ ...acc, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, + value: { + [taskReportAction.reportAction.reportActionID ?? '']: taskReportAction.reportAction as ReportAction, + }, + }, { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${currTask.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${currentTask.reportID}`, value: { - ...currTask, + ...currentTask, pendingFields: { createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, reportName: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, @@ -2910,17 +2943,20 @@ function completeOnboarding(properties: { }, { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currTask.reportID}`, - value: {[taskCreatedAction.reportActionID]: taskCreatedAction as OnyxTypes.ReportAction}, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentTask.reportID}`, + value: {[taskCreatedAction.reportActionID]: taskCreatedAction as ReportAction}, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${currTask.reportID}`, - value: {[subtitleMessage.reportID ?? '']: subtitleMessage, [instructionMessage.reportID ?? '']: instructionMessage}, + key: `${ONYXKEYS.COLLECTION.REPORT}${currentTask.reportID}`, + value: { + [subtitleMessage.reportID ?? '']: subtitleMessage, + [instructionMessage.reportID ?? '']: instructionMessage, + }, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currTask.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentTask.reportID}`, value: { [subtitleCommentAction.reportActionID ?? '']: subtitleCommentAction as ReportAction, [instructionCommentAction.reportActionID ?? '']: instructionCommentAction as ReportAction, @@ -2930,15 +2966,25 @@ function completeOnboarding(properties: { }, []); const optimisticData: OnyxUpdate[] = [ + ...tasksForOptimisticData, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${targetChatReportID}`, - value: {[textMessage.reportID ?? '']: textMessage, [videoMessage.reportID ?? '']: videoMessage}, + value: { + [mentionMessage.reportID ?? '']: mentionMessage, + [textMessage.reportID ?? '']: textMessage, + [videoMessage.reportID ?? '']: videoMessage, + lastMentionedTime: DateUtils.getDBTime(), + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: {[textCommentAction.reportActionID ?? '']: textCommentAction as ReportAction, [videoCommentAction.reportActionID ?? '']: videoCommentAction as ReportAction}, + value: { + [mentionCommentAction.reportActionID ?? '']: mentionCommentAction as ReportAction, + [textCommentAction.reportActionID ?? '']: textCommentAction as ReportAction, + [videoCommentAction.reportActionID ?? '']: videoCommentAction as ReportAction, + }, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2950,18 +2996,22 @@ function completeOnboarding(properties: { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetChatReportID}`, - value: {[textCommentAction.reportActionID ?? '']: {pendingAction: null}, [videoCommentAction.reportActionID ?? '']: {pendingAction: null}}, + value: { + [mentionCommentAction.reportActionID ?? '']: {pendingAction: null}, + [textCommentAction.reportActionID ?? '']: {pendingAction: null}, + [videoCommentAction.reportActionID ?? '']: {pendingAction: null}, + }, }, ]; - const parameters = { + const parameters: CompleteGuidedSetupParams = { engagementChoice, firstName, lastName, - data: JSON.stringify([{type: 'message', ...textMessage}, {type: 'video', ...data.video, ...videoMessage}, ...tasksForParameters]), + guidedSetupData: JSON.stringify([{type: 'message', ...mentionMessage, ...textMessage}, {type: 'video', ...data.video, ...videoMessage}, ...tasksForParameters]), }; - API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {}); + API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData}); } /** diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index bd8a7ab40917..62b8b072381b 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -80,13 +80,9 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on return; } - // Report.completeEngagementModal(CONST.ONBOARDING_CONCIERGE[selectedPurpose], selectedPurpose); Report.completeOnboarding({ data: CONST.ONBOARDING_MESSAGES[selectedPurpose], engagementChoice: selectedPurpose, - targetEmail: CONST.EMAIL.CONCIERGE, - firstName: 'Test', - lastName: 'Testovsky', }); Navigation.dismissModal(); From 4658af361ed628db87f0384d12cbe2d4c4423f77 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 17:31:41 +0200 Subject: [PATCH 085/283] fix: amend comment --- src/libs/TransactionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 831cc46ff553..1847e69a21e6 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -639,7 +639,7 @@ function isCustomUnitRateIDForP2P(transaction: OnyxEntry): boolean } /** - * Check if the customUnitRateID has a value default for P2P distance requests + * Get rate ID from the transaction object */ function getRateID(transaction: OnyxEntry): string | undefined { return transaction?.comment?.customUnit?.customUnitRateID?.toString(); From 6234c99ae920095d354dbdf64c897698d7383af9 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 18:25:59 +0200 Subject: [PATCH 086/283] fix: lint --- ...eyTemporaryForRefactorRequestConfirmationList.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 7d382a10f30d..b8c5e5f2417e 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -335,7 +335,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0); IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency ?? ''); - }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency]); + }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency, transactionID]); // Calculate and set tax amount in transaction draft useEffect(() => { @@ -343,11 +343,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); if (transaction?.taxAmount && previousTransactionAmount === transaction?.amount) { - return IOU.setMoneyRequestTaxAmount(transaction?.transactionID, transaction?.taxAmount, true); + return IOU.setMoneyRequestTaxAmount(transactionID, transaction?.taxAmount, true); } IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits, true); - }, [taxRates?.defaultValue, transaction, previousTransactionAmount]); + }, [taxRates?.defaultValue, transaction, previousTransactionAmount, transactionID]); /** * Returns the participants with amount @@ -475,7 +475,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ 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]); + }, [isDistanceRequestWithPendingRoute, hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transactionID]); // Auto select the category if there is only one enabled category and it is required useEffect(() => { @@ -484,7 +484,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return; } IOU.setMoneyRequestCategory(transactionID, enabledCategories[0].name); - }, [iouCategory, shouldShowCategories, policyCategories, transaction, isCategoryRequired]); + }, [iouCategory, shouldShowCategories, policyCategories, isCategoryRequired, transactionID]); // Auto select the tag if there is only one enabled tag and it is required useEffect(() => { @@ -500,7 +500,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (updatedTagsString !== TransactionUtils.getTag(transaction) && updatedTagsString) { IOU.setMoneyRequestTag(transactionID, updatedTagsString); } - }, [policyTagLists, transaction, policyTags, canUseViolations]); + }, [policyTagLists, transaction, policyTags, canUseViolations, transactionID]); /** */ From 96a70328ed190d8f2e51f724d16d6e6d65f0ac1b Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 19:21:11 +0200 Subject: [PATCH 087/283] fix: add missing arg --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index b8c5e5f2417e..e7fc364799c8 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -210,7 +210,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {canUseP2PDistanceRequests, canUseViolations} = usePermissions(); + const {canUseP2PDistanceRequests, canUseViolations} = usePermissions(iouType); const isTypeRequest = iouType === CONST.IOU.TYPE.REQUEST; const isTypeSplit = iouType === CONST.IOU.TYPE.SPLIT; From 5de20289c5d4076b5a6dbfefca92a41dd1ef08c7 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 20:39:08 +0200 Subject: [PATCH 088/283] fix: minor fix --- src/libs/actions/IOU.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 549e17aa8407..5a8514830571 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -425,7 +425,9 @@ function setMoneyRequestReceipt(transactionID: string, source: string, filename: }); } -/** Set custom unit rateID for the transaction draft */ +/** + * Set custom unit rateID for the transaction draft + */ function setCustomUnitRateID(transactionID: string, reportID: string) { const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; From 80f9b5e99c854023f93fe45c028cef5204c7130a Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 11 Apr 2024 22:58:16 +0200 Subject: [PATCH 089/283] fix: recalculate amount, fallback to default rate --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index e7fc364799c8..e87516dc5e00 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -221,15 +221,16 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policy?.outputCurrency ?? CONST.CURRENCY.USD) - : mileageRates?.[customUnitRateID]; - - const shouldCalculateDistanceAmount = isDistanceRequest && iouAmount === 0; + : mileageRates?.[customUnitRateID] ?? DistanceRequestUtils.getDefaultMileageRate(policy); const {unit, rate} = mileageRate ?? { unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * 100, }; + const prevRate = usePrevious(rate); + const shouldCalculateDistanceAmount = isDistanceRequest && (iouAmount === 0 || prevRate !== rate); + const currency = (mileageRate as MileageRate)?.currency ?? policy?.outputCurrency ?? CONST.CURRENCY.USD; const distance = transaction?.routes?.route0?.distance ?? 0; From 4f291f065706f049c42249fc9458ecd7703cd8d7 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 12:21:47 +0200 Subject: [PATCH 090/283] simplify CompleteGuidedSetupParams --- src/libs/API/parameters/CompleteGuidedSetupParams.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libs/API/parameters/CompleteGuidedSetupParams.ts b/src/libs/API/parameters/CompleteGuidedSetupParams.ts index 5ed7b18815af..6835949382ca 100644 --- a/src/libs/API/parameters/CompleteGuidedSetupParams.ts +++ b/src/libs/API/parameters/CompleteGuidedSetupParams.ts @@ -1,11 +1,8 @@ -import type {ValueOf} from 'type-fest'; -import type CONST from '@src/CONST'; - type CompleteGuidedSetupParams = { firstName: string; lastName: string; guidedSetupData: string; - engagementChoice: ValueOf; + engagementChoice: string; }; export default CompleteGuidedSetupParams; From a32e5ea885d006af6852090fff52139e4d09ad32 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 12:23:08 +0200 Subject: [PATCH 091/283] add todo --- src/libs/actions/Report.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 209ead38f4d7..b77979aadd45 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2788,6 +2788,7 @@ function getReportPrivateNote(reportID: string | undefined) { API.read(READ_COMMANDS.GET_REPORT_PRIVATE_NOTE, parameters, {optimisticData, successData, failureData}); } +// TODO: Clear region // #region completeOnboarding function completeOnboarding(properties: {data: ValueOf; engagementChoice: ValueOf}) { const {data, engagementChoice} = properties; From 30678e2d54a70d5cde2bddcd107d0c6884a1a1fa Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 12:40:35 +0200 Subject: [PATCH 092/283] improve completeOnboarding --- src/libs/actions/Report.ts | 50 +++++++++++++------ .../BaseOnboardingPurpose.tsx | 5 +- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b77979aadd45..34097945269e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -99,6 +99,27 @@ type ActionSubscriber = { callback: SubscriberCallback; }; +type TaskForParameters = + | { + type: 'task'; + task: string; + taskReportID: string; + parentReportID: string; + parentReportActionID: string; + assigneeChatReportID: string; + createdTaskReportActionID: string; + title: string; + description: string; + } + | { + type: 'message'; + reportID: string; + reportActionID: string; + reportComment: string; + }; + +type TaskMessage = Required>; + let conciergeChatReportID: string | undefined; let currentUserAccountID = -1; let currentUserEmail: string | undefined; @@ -2790,12 +2811,9 @@ function getReportPrivateNote(reportID: string | undefined) { // TODO: Clear region // #region completeOnboarding -function completeOnboarding(properties: {data: ValueOf; engagementChoice: ValueOf}) { - const {data, engagementChoice} = properties; +function completeOnboarding(engagementChoice: string, data: ValueOf) { const targetEmail = CONST.EMAIL.CONCIERGE; - const login = allPersonalDetails?.[currentUserAccountID]?.login ?? ''; - const firstName = allPersonalDetails?.[currentUserAccountID]?.firstName ?? ''; - const lastName = allPersonalDetails?.[currentUserAccountID]?.lastName ?? ''; + const {login = '', firstName = '', lastName = ''} = allPersonalDetails?.[currentUserAccountID] ?? {}; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); const targetChatReportID = targetChatReport?.reportID ?? ''; @@ -2863,12 +2881,12 @@ function completeOnboarding(properties: {data: ValueOf((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { + const tasksForParameters = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { // subtitle message const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; const subtitleCommentText = subtitleComment.commentText; - const subtitleMessage: AddCommentOrAttachementParams = { + const subtitleMessage: TaskMessage = { reportID: currentTask.reportID, reportActionID: subtitleCommentAction.reportActionID, reportComment: subtitleCommentText, @@ -2878,7 +2896,7 @@ function completeOnboarding(properties: {data: ValueOf Date: Fri, 12 Apr 2024 13:02:34 +0200 Subject: [PATCH 093/283] fix: extract logic to a new function --- src/libs/actions/IOU.ts | 83 ++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 5a8514830571..50405164360f 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -268,6 +268,22 @@ function getPolicy(policyID: string | undefined): OnyxTypes.Policy | EmptyObject return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; } +/** + * Returns custom unit rate ID for the transaction + */ +function getCustomUnitRateID(reportID: string) { + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; + const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; + const policy = getPolicy(report?.policyID) ?? null; + + let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; + + if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { + customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; + } + return customUnitRateID; +} + /** * Initialize money request info * @param reportID to attach the transaction to @@ -290,12 +306,7 @@ function initMoneyRequest(reportID: string, policy: OnyxEntry, waypoint1: {}, }; if (!isFromGlobalCreate) { - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; - if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { - customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; - } + const customUnitRateID = getCustomUnitRateID(reportID); comment.customUnit = {customUnitRateID}; } } @@ -362,7 +373,11 @@ function startMoneyRequest(iouType: ValueOf, reportID: st // eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestAmount_temporaryForRefactor(transactionID: string, amount: number, currency: string, removeOriginalCurrency = false) { if (removeOriginalCurrency) { - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency, originalCurrency: null}); + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, { + amount, + currency, + originalCurrency: null, + }); return; } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency}); @@ -377,7 +392,10 @@ function setMoneyRequestCreated(transactionID: string, created: string, isDraft: function setMoneyRequestCurrency_temporaryForRefactor(transactionID: string, currency: string, removeOriginalCurrency = false, isEditing = false) { const fieldToUpdate = isEditing ? 'modifiedCurrency' : 'currency'; if (removeOriginalCurrency) { - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {[fieldToUpdate]: currency, originalCurrency: null}); + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, { + [fieldToUpdate]: currency, + originalCurrency: null, + }); return; } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {[fieldToUpdate]: currency}); @@ -429,15 +447,7 @@ function setMoneyRequestReceipt(transactionID: string, source: string, filename: * Set custom unit rateID for the transaction draft */ function setCustomUnitRateID(transactionID: string, reportID: string) { - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - const policy = getPolicy(report?.policyID) ?? null; - - let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; - - if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { - customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; - } + const customUnitRateID = getCustomUnitRateID(reportID); Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {comment: {customUnit: {customUnitRateID}}}); } @@ -475,7 +485,14 @@ function resetMoneyRequestInfo(id = '') { function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true, errorKey?: number): Errors | ErrorFields { return isEmptyObject(receipt) || !isScanRequest ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage', false, errorKey) - : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source?.toString() ?? '', filename: filename ?? ''}, errorKey); + : ErrorUtils.getMicroSecondOnyxErrorObject( + { + error: CONST.IOU.RECEIPT_ERROR, + source: receipt.source?.toString() ?? '', + filename: filename ?? '', + }, + errorKey, + ); } /** Builds the Onyx data for a money request */ @@ -2711,7 +2728,13 @@ function createSplitsAndOnyxData( // Loop through participants creating individual chats, iouReports and reportActionIDs as needed const splitAmount = IOUUtils.calculateAmount(participants.length, amount, currency, false); - const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, currency, true)}]; + const splits: Split[] = [ + { + email: currentUserEmailForIOUSplit, + accountID: currentUserAccountID, + amount: IOUUtils.calculateAmount(participants.length, amount, currency, true), + }, + ]; const hasMultipleParticipants = participants.length > 1; participants.forEach((participant) => { @@ -5307,10 +5330,25 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On const shouldAddAsReport = !isEmptyObject(chatReport) && ReportUtils.isSelfDM(chatReport); const participants: Participant[] = ReportUtils.isPolicyExpenseChat(chatReport) || shouldAddAsReport - ? [{accountID: 0, reportID: chatReport?.reportID, isPolicyExpenseChat: ReportUtils.isPolicyExpenseChat(chatReport), selected: true}] - : (chatReport?.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); + ? [ + { + accountID: 0, + reportID: chatReport?.reportID, + isPolicyExpenseChat: ReportUtils.isPolicyExpenseChat(chatReport), + selected: true, + }, + ] + : (chatReport?.participantAccountIDs ?? []) + .filter((accountID) => currentUserAccountID !== accountID) + .map((accountID) => ({ + accountID, + selected: true, + })); - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants, participantsAutoAssigned: true}); + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, { + participants, + participantsAutoAssigned: true, + }); } function setMoneyRequestId(id: string) { @@ -5466,6 +5504,7 @@ function unholdRequest(transactionID: string, reportID: string) { {optimisticData, successData, failureData}, ); } + // eslint-disable-next-line rulesdir/no-negated-variables function navigateToStartStepIfScanFileCannotBeRead( receiptFilename: string | undefined, From 40c567696964dc381f8b1ea0c74c498fa25acfc4 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 14:08:40 +0200 Subject: [PATCH 094/283] pass timestamp into getDBTimeWithSkew --- src/libs/DateUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 44c7682b47f2..cd0ad4dace94 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -378,11 +378,11 @@ function getDBTime(timestamp: string | number = ''): string { /** * Returns the current time plus skew in milliseconds in the format expected by the database */ -function getDBTimeWithSkew(): string { +function getDBTimeWithSkew(timestamp: string | number = ''): string { if (networkTimeSkew > 0) { - return getDBTime(new Date().valueOf() + networkTimeSkew); + return getDBTime(new Date(timestamp).valueOf() + networkTimeSkew); } - return getDBTime(); + return getDBTime(timestamp); } function subtractMillisecondsFromDateTime(dateTime: string, milliseconds: number): string { From 98175c1a989d5d0ec5c6c5261bdcd67b7a016d60 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 14:08:59 +0200 Subject: [PATCH 095/283] pass createdOffset into buildOptimistic function --- src/libs/ReportUtils.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 24482de2c1d2..dd84cc7e2920 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3095,7 +3095,7 @@ function getPolicyDescriptionText(policy: OnyxEntry): string { return parser.htmlToText(policy.description); } -function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, actorAccountID?: number): OptimisticReportAction { +function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, actorAccountID?: number, createdOffset = 0): OptimisticReportAction { const parser = new ExpensiMark(); const commentText = getParsedComment(text ?? ''); const isAttachmentOnly = file && !text; @@ -3134,7 +3134,7 @@ function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, ], automatic: false, avatar: allPersonalDetails?.[accountID ?? -1]?.avatar ?? UserUtils.getDefaultAvatarURL(accountID), - created: DateUtils.getDBTimeWithSkew(), + created: DateUtils.getDBTimeWithSkew(Date.now() + createdOffset), message: [ { translationKey: isAttachmentOnly ? CONST.TRANSLATION_KEYS.ATTACHMENT : '', @@ -3202,6 +3202,7 @@ function updateOptimisticParentReportAction(parentReportAction: OnyxEntry Date: Fri, 12 Apr 2024 14:09:30 +0200 Subject: [PATCH 096/283] fix bugs and improve completeOnboarding --- src/libs/actions/Report.ts | 82 +++++++++++--------------------------- 1 file changed, 24 insertions(+), 58 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 34097945269e..0dbf8b561299 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2809,29 +2809,19 @@ function getReportPrivateNote(reportID: string | undefined) { API.read(READ_COMMANDS.GET_REPORT_PRIVATE_NOTE, parameters, {optimisticData, successData, failureData}); } -// TODO: Clear region -// #region completeOnboarding function completeOnboarding(engagementChoice: string, data: ValueOf) { const targetEmail = CONST.EMAIL.CONCIERGE; const {login = '', firstName = '', lastName = ''} = allPersonalDetails?.[currentUserAccountID] ?? {}; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); - const targetChatReportID = targetChatReport?.reportID ?? ''; - const targetChatPolicyID = targetChatReport?.policyID ?? ''; + const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; // Mention message - const mentionComment = ReportUtils.buildOptimisticAddCommentReportAction(`Hey @${login} 👋`, undefined, actorAccountID); + const mentionComment = ReportUtils.buildOptimisticAddCommentReportAction(`Hey @${login.split('@')[0]} 👋`, undefined, actorAccountID); const mentionCommentAction: OptimisticAddCommentReportAction = mentionComment.reportAction; - const mentionCommentText = mentionComment.commentText; - - const mentionMessage: AddCommentOrAttachementParams = { - reportID: targetChatReportID, - reportActionID: mentionCommentAction.reportActionID, - reportComment: mentionCommentText, - }; // Text message - const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID); + const textComment = ReportUtils.buildOptimisticAddCommentReportAction(data.message, undefined, actorAccountID, 1); const textCommentAction: OptimisticAddCommentReportAction = textComment.reportAction; const textCommentText = textComment.commentText; @@ -2842,7 +2832,7 @@ function completeOnboarding(engagementChoice: string, data: ValueOf { + const tasksData = data.tasks.map((task, index) => { const currentTask = ReportUtils.buildOptimisticTaskReport( actorAccountID, undefined, @@ -2863,7 +2853,15 @@ function completeOnboarding(engagementChoice: string, data: ValueOf((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { - // subtitle message const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; - const subtitleCommentText = subtitleComment.commentText; - - const subtitleMessage: AddCommentOrAttachementParams = { - reportID: currentTask.reportID, - reportActionID: subtitleCommentAction.reportActionID, - reportComment: subtitleCommentText, - }; - - // instruction message const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; - const instructionCommentText = instructionComment.commentText; - - const instructionMessage: AddCommentOrAttachementParams = { - reportID: currentTask.reportID, - reportActionID: instructionCommentAction.reportActionID, - reportComment: instructionCommentText, - }; return [ ...acc, @@ -2953,7 +2934,7 @@ function completeOnboarding(engagementChoice: string, data: ValueOf Date: Fri, 12 Apr 2024 15:08:58 +0200 Subject: [PATCH 097/283] avoid text escaping --- src/libs/ReportUtils.ts | 8 ++++---- src/libs/actions/Report.ts | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index dd84cc7e2920..33e667e0b250 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3053,7 +3053,7 @@ function hasReportNameError(report: OnyxEntry): boolean { * For comments shorter than or equal to 10k chars, convert the comment from MD into HTML because that's how it is stored in the database * For longer comments, skip parsing, but still escape the text, and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!! */ -function getParsedComment(text: string): string { +function getParsedComment(text: string, shouldEscapeText?: boolean): string { const parser = new ExpensiMark(); const textWithMention = text.replace(CONST.REGEX.SHORT_MENTION, (match) => { const mention = match.substring(1); @@ -3074,7 +3074,7 @@ function getParsedComment(text: string): string { return match; }); - return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(textWithMention, {shouldEscapeText: !shouldAllowRawHTMLMessages()}) : lodashEscape(text); + return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(textWithMention, {shouldEscapeText: shouldEscapeText ?? !shouldAllowRawHTMLMessages()}) : lodashEscape(text); } function getReportDescriptionText(report: Report): string { @@ -3095,9 +3095,9 @@ function getPolicyDescriptionText(policy: OnyxEntry): string { return parser.htmlToText(policy.description); } -function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, actorAccountID?: number, createdOffset = 0): OptimisticReportAction { +function buildOptimisticAddCommentReportAction(text?: string, file?: FileObject, actorAccountID?: number, createdOffset = 0, shouldEscapeText?: boolean): OptimisticReportAction { const parser = new ExpensiMark(); - const commentText = getParsedComment(text ?? ''); + const commentText = getParsedComment(text ?? '', shouldEscapeText); const isAttachmentOnly = file && !text; const isTextOnly = text && !file; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 0dbf8b561299..d62cd8498edd 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2865,9 +2865,7 @@ function completeOnboarding(engagementChoice: string, data: ValueOf Date: Fri, 12 Apr 2024 15:35:11 +0200 Subject: [PATCH 098/283] add replies config --- src/libs/ReportUtils.ts | 27 +++++++++++++++++++++++++++ src/libs/actions/Report.ts | 8 ++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 33e667e0b250..b5a180547a47 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -140,6 +140,10 @@ type OptimisticAddCommentReportAction = Pick< | 'childStatusNum' | 'childStateNum' | 'errors' + | 'childVisibleActionCount' + | 'childCommenterCount' + | 'childLastVisibleActionCreated' + | 'childOldestFourAccountIDs' > & {isOptimisticAction: boolean}; type OptimisticReportAction = { @@ -3212,6 +3216,12 @@ function buildOptimisticTaskCommentReportAction( parentReportID: string, actorAccountID?: number, createdOffset = 0, + repliesConfig?: { + childVisibleActionCount?: number; + childCommenterCount?: number; + childLastVisibleActionCreated?: string; + childOldestFourAccountIDs?: string; + }, ): OptimisticReportAction { const reportAction = buildOptimisticAddCommentReportAction(text, undefined, undefined, createdOffset); if (reportAction.reportAction.message?.[0]) { @@ -3231,10 +3241,27 @@ function buildOptimisticTaskCommentReportAction( reportAction.reportAction.childManagerAccountID = taskAssigneeAccountID; reportAction.reportAction.childStatusNum = CONST.REPORT.STATUS_NUM.OPEN; reportAction.reportAction.childStateNum = CONST.REPORT.STATE_NUM.OPEN; + if (actorAccountID) { reportAction.reportAction.actorAccountID = actorAccountID; } + if (repliesConfig?.childVisibleActionCount) { + reportAction.reportAction.childVisibleActionCount = repliesConfig.childVisibleActionCount; + } + + if (repliesConfig?.childCommenterCount) { + reportAction.reportAction.childCommenterCount = repliesConfig.childCommenterCount; + } + + if (repliesConfig?.childLastVisibleActionCreated) { + reportAction.reportAction.childLastVisibleActionCreated = repliesConfig.childLastVisibleActionCreated; + } + + if (repliesConfig?.childOldestFourAccountIDs) { + reportAction.reportAction.childOldestFourAccountIDs = repliesConfig.childOldestFourAccountIDs; + } + return reportAction; } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index d62cd8498edd..4f7965b6839b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2861,9 +2861,13 @@ function completeOnboarding(engagementChoice: string, data: ValueOf Date: Fri, 12 Apr 2024 16:02:30 +0200 Subject: [PATCH 099/283] pass mentionMessage and prettify --- src/libs/actions/Report.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4f7965b6839b..31e0662501a9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2819,27 +2819,28 @@ function completeOnboarding(engagementChoice: string, data: ValueOf { @@ -2880,22 +2881,17 @@ function completeOnboarding(engagementChoice: string, data: ValueOf((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { - // subtitle message const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; const subtitleCommentText = subtitleComment.commentText; - const subtitleMessage: TaskMessage = { reportID: currentTask.reportID, reportActionID: subtitleCommentAction.reportActionID, reportComment: subtitleCommentText, }; - // instruction message const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; const instructionCommentText = instructionComment.commentText; - const instructionMessage: TaskMessage = { reportID: currentTask.reportID, reportActionID: instructionCommentAction.reportActionID, @@ -3005,7 +3001,7 @@ function completeOnboarding(engagementChoice: string, data: ValueOf Date: Fri, 12 Apr 2024 16:46:36 +0200 Subject: [PATCH 100/283] change ordering of pages --- src/components/TestToolMenu.tsx | 2 +- src/languages/en.ts | 1 - src/languages/es.ts | 1 - .../Navigators/OnboardingModalNavigator.tsx | 8 +-- .../BottomTabBar.tsx | 10 +-- src/libs/Navigation/linkingConfig/config.ts | 10 +-- .../BaseOnboardingPersonalDetails.tsx | 67 +++++++++++++------ .../index.native.tsx | 4 +- src/pages/OnboardingPersonalDetails/index.tsx | 4 +- .../BaseOnboardingPurpose.tsx | 34 ++-------- src/pages/OnboardingPurpose/types.ts | 23 ++++++- 11 files changed, 90 insertions(+), 74 deletions(-) diff --git a/src/components/TestToolMenu.tsx b/src/components/TestToolMenu.tsx index 527b92d4d7dc..4d1f7fe51a62 100644 --- a/src/components/TestToolMenu.tsx +++ b/src/components/TestToolMenu.tsx @@ -97,7 +97,7 @@ function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) { text="Navigate" onPress={() => { Navigation.dismissModal(); - Navigation.navigate(ROUTES.ONBOARDING_PERSONAL_DETAILS); + Navigation.navigate(ROUTES.ONBOARDING_PURPOSE); }} /> diff --git a/src/languages/en.ts b/src/languages/en.ts index dbdda0d35635..076666fc595b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1316,7 +1316,6 @@ export default { notYou: ({user}: NotYouParams) => `Not ${user}?`, }, onboarding: { - welcome: 'Welcome!', welcomeVideo: { title: 'Welcome to Expensify', description: 'Getting paid is as easy as sending a message.', diff --git a/src/languages/es.ts b/src/languages/es.ts index e81efa07a58c..30cada0d6534 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1318,7 +1318,6 @@ export default { notYou: ({user}: NotYouParams) => `¿No eres ${user}?`, }, onboarding: { - welcome: '¡Bienvenido!', welcomeVideo: { title: 'Bienvenido a Expensify', description: 'Cobrar es tan fácil como enviar un mensaje.', diff --git a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx index 6f4fbb08403b..3791cbc03641 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx @@ -23,14 +23,14 @@ function OnboardingModalNavigator() { - + diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx index 391468578fab..b48b54d22f89 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx @@ -48,16 +48,8 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps return; } - // Welcome.isOnboardingFlowCompleted({onNotCompleted: () => Navigation.navigate(ROUTES.ONBOARDING_PERSONAL_DETAILS)}); Welcome.isOnboardingFlowCompleted({ - onNotCompleted: () => - Navigation.navigate( - // Uncomment once Stage 1 Onboarding Flow is ready - // - // ROUTES.ONBOARDING_PERSONAL_DETAILS - // - ROUTES.ONBOARD, - ), + onNotCompleted: () => Navigation.navigate(ROUTES.ONBOARDING_PURPOSE), }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoadingApp]); diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 95294b7711b5..9401aa40281d 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -91,16 +91,16 @@ const config: LinkingOptions['config'] = { }, [NAVIGATORS.ONBOARDING_MODAL_NAVIGATOR]: { path: ROUTES.ONBOARDING_ROOT, - initialRouteName: SCREENS.ONBOARDING.PERSONAL_DETAILS, + initialRouteName: SCREENS.ONBOARDING.PURPOSE, screens: { - [SCREENS.ONBOARDING.PERSONAL_DETAILS]: { - path: ROUTES.ONBOARDING_PERSONAL_DETAILS, - exact: true, - }, [SCREENS.ONBOARDING.PURPOSE]: { path: ROUTES.ONBOARDING_PURPOSE, exact: true, }, + [SCREENS.ONBOARDING.PERSONAL_DETAILS]: { + path: ROUTES.ONBOARDING_PERSONAL_DETAILS, + exact: true, + }, }, }, [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: { diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 248d4af4e9d0..80d48a7cc29d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -1,5 +1,6 @@ import React, {useCallback} from 'react'; import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import type {FormOnyxValues} from '@components/Form/types'; @@ -8,39 +9,56 @@ import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; import OfflineIndicator from '@components/OfflineIndicator'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; -import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnboardingLayout from '@hooks/useOnboardingLayout'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; +import type {BaseOnboardingPersonalDetailsOnyxProps, BaseOnboardingPersonalDetailsProps} from '@pages/OnboardingPurpose/types'; +import variables from '@styles/variables'; import * as PersonalDetails from '@userActions/PersonalDetails'; +import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/DisplayNameForm'; -type BaseOnboardingPersonalDetailsProps = { - /* Whether to use native styles tailored for native devices */ - shouldUseNativeStyles: boolean; -}; - -function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNativeStyles}: WithCurrentUserPersonalDetailsProps & BaseOnboardingPersonalDetailsProps) { - const theme = useTheme(); +function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNativeStyles, onboardingPurposeSelected}: BaseOnboardingPersonalDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useOnboardingLayout(); - const saveAndNavigate = useCallback((values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { - PersonalDetails.setDisplayName(values.firstName.trim(), values.lastName.trim()); + const completeEngagement = useCallback( + (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { + PersonalDetails.setDisplayName(values.firstName.trim(), values.lastName.trim()); - Navigation.navigate(ROUTES.ONBOARDING_PURPOSE); - }, []); + if (!onboardingPurposeSelected) { + return; + } + + Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected]); + + Navigation.dismissModal(); + // Only navigate to concierge chat when central pane is visible + // Otherwise stay on the chats screen. + if (isSmallScreenWidth) { + Navigation.navigate(ROUTES.HOME); + } else { + Report.navigateToConciergeChat(); + } + + // Small delay purely due to design considerations, + // no special technical reasons behind that. + setTimeout(() => { + Navigation.navigate(ROUTES.WELCOME_VIDEO_ROOT); + }, variables.welcomeVideoDelay); + }, + [isSmallScreenWidth, onboardingPurposeSelected], + ); const validate = (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { const errors = {}; @@ -74,14 +92,18 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat return errors; }; + const handleGoBack = useCallback(() => { + Navigation.goBack(); + }, []); + const PersonalDetailsFooterInstance = ; return ( - {translate('onboarding.welcome')} {translate('onboarding.whatsYourName')} @@ -141,6 +162,10 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat BaseOnboardingPersonalDetails.displayName = 'BaseOnboardingPersonalDetails'; -export default withCurrentUserPersonalDetails(BaseOnboardingPersonalDetails); - -export type {BaseOnboardingPersonalDetailsProps}; +export default withCurrentUserPersonalDetails( + withOnyx({ + onboardingPurposeSelected: { + key: ONYXKEYS.ONBOARDING_PURPOSE_SELECTED, + }, + })(BaseOnboardingPersonalDetails), +); diff --git a/src/pages/OnboardingPersonalDetails/index.native.tsx b/src/pages/OnboardingPersonalDetails/index.native.tsx index 3c49a13178e6..778d68e39458 100644 --- a/src/pages/OnboardingPersonalDetails/index.native.tsx +++ b/src/pages/OnboardingPersonalDetails/index.native.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import type {BaseOnboardingPersonalDetailsProps} from './BaseOnboardingPersonalDetails'; +import type {OnboardingPersonalDetailsProps} from '@pages/OnboardingPurpose/types'; import BaseOnboardingPersonalDetails from './BaseOnboardingPersonalDetails'; -function OnboardingPersonalDetails({...rest}: Omit) { +function OnboardingPersonalDetails({...rest}: Omit) { return ( ) { +function OnboardingPersonalDetails({...rest}: Omit) { return ( { - Navigation.goBack(); - }, []); - const selectedCheckboxIcon = useMemo( () => ( @@ -75,28 +70,13 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on [styles.pointerEventsAuto, styles.popoverMenuIcon, theme.success], ); - const completeEngagement = useCallback(() => { + const saveAndNavigate = useCallback(() => { if (selectedPurpose === undefined) { return; } - Report.completeOnboarding(selectedPurpose, CONST.ONBOARDING_MESSAGES[selectedPurpose]); - - Navigation.dismissModal(); - // Only navigate to concierge chat when central pane is visible - // Otherwise stay on the chats screen. - if (isSmallScreenWidth) { - Navigation.navigate(ROUTES.HOME); - } else { - Report.navigateToConciergeChat(); - } - - // Small delay purely due to design considerations, - // no special technical reasons behind that. - setTimeout(() => { - Navigation.navigate(ROUTES.WELCOME_VIDEO_ROOT); - }, variables.welcomeVideoDelay); - }, [isSmallScreenWidth, selectedPurpose]); + Navigation.navigate(ROUTES.ONBOARDING_PERSONAL_DETAILS); + }, [selectedPurpose]); const menuItems: MenuItemProps[] = Object.values(CONST.ONBOARDING_CHOICES).map((choice) => { const translationKey = `onboarding.purpose.${choice}` as const; @@ -126,9 +106,9 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on @@ -152,7 +132,7 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on return; } setError(false); - completeEngagement(); + saveAndNavigate(); }} message={errorMessage} isAlertVisible={error || Boolean(errorMessage)} diff --git a/src/pages/OnboardingPurpose/types.ts b/src/pages/OnboardingPurpose/types.ts index 586463a26bb0..15d6beec1f6f 100644 --- a/src/pages/OnboardingPurpose/types.ts +++ b/src/pages/OnboardingPurpose/types.ts @@ -1,4 +1,5 @@ import type {OnyxEntry} from 'react-native-onyx'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; type OnboardingPurposeProps = Record; @@ -16,4 +17,24 @@ type BaseOnboardingPurposeProps = OnboardingPurposeProps & shouldEnableMaxHeight: boolean; }; -export type {BaseOnboardingPurposeOnyxProps, BaseOnboardingPurposeProps, OnboardingPurposeProps}; +type OnboardingPersonalDetailsProps = Record; + +type BaseOnboardingPersonalDetailsOnyxProps = { + /** Saved onboarding purpose selected by the user */ + onboardingPurposeSelected: OnyxEntry; +}; + +type BaseOnboardingPersonalDetailsProps = WithCurrentUserPersonalDetailsProps & + BaseOnboardingPersonalDetailsOnyxProps & { + /* Whether to use native styles tailored for native devices */ + shouldUseNativeStyles: boolean; + }; + +export type { + BaseOnboardingPurposeOnyxProps, + BaseOnboardingPurposeProps, + OnboardingPurposeProps, + OnboardingPersonalDetailsProps, + BaseOnboardingPersonalDetailsOnyxProps, + BaseOnboardingPersonalDetailsProps, +}; From f5cd44bbba30f2294149d7f96454b67ca1b063d4 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 16:52:00 +0200 Subject: [PATCH 101/283] fix bottom padding --- .../OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 80d48a7cc29d..bf39940e1536 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -110,7 +110,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat behavior="padding" > Date: Fri, 12 Apr 2024 16:56:50 +0200 Subject: [PATCH 102/283] pass current user info --- src/libs/actions/Report.ts | 14 +++++++++++++- .../BaseOnboardingPersonalDetails.tsx | 13 ++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 31e0662501a9..75ab3c530e36 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2809,7 +2809,19 @@ function getReportPrivateNote(reportID: string | undefined) { API.read(READ_COMMANDS.GET_REPORT_PRIVATE_NOTE, parameters, {optimisticData, successData, failureData}); } -function completeOnboarding(engagementChoice: string, data: ValueOf) { +function completeOnboarding( + engagementChoice: string, + data: ValueOf, + { + login, + firstName, + lastName, + }: { + login: string; + firstName: string; + lastName: string; + }, +) { const targetEmail = CONST.EMAIL.CONCIERGE; const {login = '', firstName = '', lastName = ''} = allPersonalDetails?.[currentUserAccountID] ?? {}; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index bf39940e1536..776050dc681d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -34,13 +34,20 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const completeEngagement = useCallback( (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { - PersonalDetails.setDisplayName(values.firstName.trim(), values.lastName.trim()); + const firstName = values.firstName.trim(); + const lastName = values.lastName.trim(); + + PersonalDetails.setDisplayName(firstName, lastName); if (!onboardingPurposeSelected) { return; } - Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected]); + Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], { + login: currentUserPersonalDetails.login ?? '', + firstName, + lastName, + }); Navigation.dismissModal(); // Only navigate to concierge chat when central pane is visible @@ -57,7 +64,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat Navigation.navigate(ROUTES.WELCOME_VIDEO_ROOT); }, variables.welcomeVideoDelay); }, - [isSmallScreenWidth, onboardingPurposeSelected], + [currentUserPersonalDetails.login, isSmallScreenWidth, onboardingPurposeSelected], ); const validate = (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { From 1fb2411c29bb7e4c22e29a8aacbd0da230c2cf09 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 17:13:20 +0200 Subject: [PATCH 103/283] remove line --- src/libs/actions/Report.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 75ab3c530e36..c8ba2b5c0593 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2823,7 +2823,6 @@ function completeOnboarding( }, ) { const targetEmail = CONST.EMAIL.CONCIERGE; - const {login = '', firstName = '', lastName = ''} = allPersonalDetails?.[currentUserAccountID] ?? {}; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; From 592bf7a542290977e5565931023eda7f6c99811f Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 12 Apr 2024 22:18:27 +0200 Subject: [PATCH 104/283] fix: fix problem with workspace customRateID --- ...raryForRefactorRequestConfirmationList.tsx | 27 ++++++++++++++++- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/DistanceRequestUtils.ts | 26 +++++++++++++++- src/libs/actions/IOU.ts | 30 ++----------------- .../step/IOURequestStepParticipants.tsx | 4 ++- 6 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index e87516dc5e00..44042a976230 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -66,6 +66,12 @@ type MoneyRequestConfirmationListOnyxProps = { /** Unit and rate used for if the money request is a distance request */ mileageRates: OnyxEntry>; + + /** Mileage rate default for the policy */ + defaultMileageRate: OnyxEntry; + + /** Last selected distance rates */ + lastSelectedDistanceRates: OnyxEntry>; }; type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & { @@ -205,6 +211,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ onToggleBillable, hasSmartScanFailed, reportActionID, + defaultMileageRate, + lastSelectedDistanceRates, }: MoneyRequestConfirmationListProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -217,8 +225,19 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const isTypeSend = iouType === CONST.IOU.TYPE.SEND; const isTypeTrackExpense = iouType === CONST.IOU.TYPE.TRACK_EXPENSE; + const transactionID = transaction?.transactionID ?? ''; const customUnitRateID = TransactionUtils.getRateID(transaction) ?? ''; + useEffect(() => { + if (customUnitRateID || !canUseP2PDistanceRequests) { + return; + } + if (!customUnitRateID && defaultMileageRate) { + const rateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? defaultMileageRate?.customUnitRateID ?? ''; + IOU.setCustomUnitRateID(transactionID, rateID); + } + }, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, canUseP2PDistanceRequests, transactionID]); + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policy?.outputCurrency ?? CONST.CURRENCY.USD) : mileageRates?.[customUnitRateID] ?? DistanceRequestUtils.getDefaultMileageRate(policy); @@ -235,7 +254,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const distance = transaction?.routes?.route0?.distance ?? 0; const taxRates = policy?.taxRates ?? null; - const transactionID = transaction?.transactionID ?? ''; // A flag for showing the categories field const shouldShowCategories = isPolicyExpenseChat && (!!iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); @@ -1031,6 +1049,10 @@ export default withOnyx `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, }, + defaultMileageRate: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + selector: DistanceRequestUtils.getDefaultMileageRate, + }, mileageRates: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, selector: DistanceRequestUtils.getMileageRates, @@ -1038,4 +1060,7 @@ export default withOnyx `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, + lastSelectedDistanceRates: { + key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, + }, })(MoneyTemporaryForRefactorRequestConfirmationList); diff --git a/src/languages/en.ts b/src/languages/en.ts index 5ee4be293ff5..bf41b8954e63 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -635,6 +635,7 @@ export default { posted: 'Posted', deleteReceipt: 'Delete receipt', routePending: 'Route pending...', + defaultRate: 'Default rate', receiptScanning: 'Scan in progress…', receiptMissingDetails: 'Receipt missing details', missingAmount: 'Missing amount', diff --git a/src/languages/es.ts b/src/languages/es.ts index ae732c6f6418..8423e045d437 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -631,6 +631,7 @@ export default { posted: 'Contabilizado', deleteReceipt: 'Eliminar recibo', routePending: 'Ruta pendiente...', + defaultRate: 'Tasa predeterminada', receiptScanning: 'Escaneo en curso…', receiptMissingDetails: 'Recibo con campos vacíos', missingAmount: 'Falta importe', diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 28de14fba57d..c673245343b1 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -4,11 +4,13 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {RateAndUnit} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; +import * as ReportUtils from './ReportUtils'; type MileageRate = { customUnitRateID?: string; @@ -30,6 +32,13 @@ Onyx.connect({ }, }); +let allReports: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + const METERS_TO_KM = 0.001; // 1 kilometer is 1000 meters const METERS_TO_MILES = 0.000621371; // There are approximately 0.000621371 miles in a meter @@ -114,7 +123,7 @@ function getRateForDisplay( toLocaleDigit: LocaleContextProps['toLocaleDigit'], ): string { if (!hasRoute || !rate || !currency) { - return translate('iou.routePending'); + return translate('iou.defaultRate'); } const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); @@ -248,6 +257,20 @@ function getDistanceFromMerchant(merchant: string | undefined, unit: Unit): numb return unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS ? distance / METERS_TO_KM : distance / METERS_TO_MILES; } +/** + * Returns custom unit rate ID for the distance transaction + */ +function getCustomUnitRateID(reportID: string) { + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; + const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; + + if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { + return ''; + } + + return CONST.CUSTOM_UNITS.FAKE_P2P_ID; +} + export default { getDefaultMileageRate, getDistanceMerchant, @@ -257,6 +280,7 @@ export default { getDistanceForDisplay, getRateForP2P, getDistanceFromMerchant, + getCustomUnitRateID, }; export type {MileageRate}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8dd13ebec7c4..8e9ea0e73726 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -236,14 +236,6 @@ Onyx.connect({ }, }); -let lastSelectedDistanceRates: OnyxEntry = {}; -Onyx.connect({ - key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, - callback: (value) => { - lastSelectedDistanceRates = value; - }, -}); - let quickAction: OnyxEntry = {}; Onyx.connect({ key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, @@ -282,22 +274,6 @@ function getPolicy(policyID: string | undefined): OnyxTypes.Policy | EmptyObject return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; } -/** - * Returns custom unit rate ID for the transaction - */ -function getCustomUnitRateID(reportID: string) { - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - const policy = getPolicy(report?.policyID) ?? null; - - let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; - - if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { - customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? DistanceRequestUtils.getDefaultMileageRate(policy)?.customUnitRateID ?? ''; - } - return customUnitRateID; -} - /** * Initialize money request info * @param reportID to attach the transaction to @@ -320,7 +296,7 @@ function initMoneyRequest(reportID: string, policy: OnyxEntry, waypoint1: {}, }; if (!isFromGlobalCreate) { - const customUnitRateID = getCustomUnitRateID(reportID); + const customUnitRateID = DistanceRequestUtils.getCustomUnitRateID(reportID); comment.customUnit = {customUnitRateID}; } } @@ -460,9 +436,7 @@ function setMoneyRequestReceipt(transactionID: string, source: string, filename: /** * Set custom unit rateID for the transaction draft */ -function setCustomUnitRateID(transactionID: string, reportID: string) { - const customUnitRateID = getCustomUnitRateID(reportID); - +function setCustomUnitRateID(transactionID: string, customUnitRateID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {comment: {customUnit: {customUnitRateID}}}); } diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index df57c4a8c53a..76a193a1d5de 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -4,6 +4,7 @@ import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import useLocalize from '@hooks/useLocalize'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -107,7 +108,8 @@ function IOURequestStepParticipants({ } IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val); - IOU.setCustomUnitRateID(transactionID, val[0]?.reportID ?? ''); + const rateID = DistanceRequestUtils.getCustomUnitRateID(val[0]?.reportID ?? ''); + IOU.setCustomUnitRateID(transactionID, rateID); numberOfParticipants.current = val.length; From 543113159f2f1330fb767d2cdd085fe4e51b3814 Mon Sep 17 00:00:00 2001 From: kmichel Date: Sun, 14 Apr 2024 00:39:55 -0700 Subject: [PATCH 105/283] fix issue --- src/components/VideoPlayer/BaseVideoPlayer.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/VideoPlayer/BaseVideoPlayer.tsx b/src/components/VideoPlayer/BaseVideoPlayer.tsx index 9380ce43c46a..80f0fd596ed6 100644 --- a/src/components/VideoPlayer/BaseVideoPlayer.tsx +++ b/src/components/VideoPlayer/BaseVideoPlayer.tsx @@ -2,7 +2,7 @@ import type {AVPlaybackStatus, VideoFullscreenUpdateEvent} from 'expo-av'; import {ResizeMode, Video, VideoFullscreenUpdate} from 'expo-av'; import type {MutableRefObject} from 'react'; -import React, {useCallback, useEffect, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; import type {GestureResponderEvent} from 'react-native'; import {View} from 'react-native'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; @@ -172,6 +172,15 @@ function BaseVideoPlayer({ }); }, [currentVideoPlayerRef, handleFullscreenUpdate, handlePlaybackStatusUpdate]); + useLayoutEffect(() => + () => { + if(shouldUseSharedVideoElement || videoPlayerRef.current !== currentVideoPlayerRef.current) { + return; + } + currentVideoPlayerRef.current = null; + } + , [currentVideoPlayerRef, shouldUseSharedVideoElement]); + useEffect(() => { if (!isUploading || !videoPlayerRef.current) { return; From dfa02a37496039f00ed9c35c881fa26c17e70ea8 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 15 Apr 2024 10:10:40 +0200 Subject: [PATCH 106/283] fix: minor fix --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 44042a976230..fc23af450687 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -232,7 +232,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (customUnitRateID || !canUseP2PDistanceRequests) { return; } - if (!customUnitRateID && defaultMileageRate) { + if (!customUnitRateID) { const rateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? defaultMileageRate?.customUnitRateID ?? ''; IOU.setCustomUnitRateID(transactionID, rateID); } From ce96fdde8f0239a3181d8ca1571f4b65a6d60816 Mon Sep 17 00:00:00 2001 From: Kevin Brian Bader Date: Mon, 15 Apr 2024 18:27:49 +0300 Subject: [PATCH 107/283] [Fabric] iOS - Stretched border animations between screen transitions --- ...+014+iOSCoreAnimationBorderRendering.patch | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 patches/react-native+0.73.4+014+iOSCoreAnimationBorderRendering.patch diff --git a/patches/react-native+0.73.4+014+iOSCoreAnimationBorderRendering.patch b/patches/react-native+0.73.4+014+iOSCoreAnimationBorderRendering.patch new file mode 100644 index 000000000000..b59729e79622 --- /dev/null +++ b/patches/react-native+0.73.4+014+iOSCoreAnimationBorderRendering.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/react-native/React/Fabric/Mounting/RCTMountingManager.mm b/node_modules/react-native/React/Fabric/Mounting/RCTMountingManager.mm +index b4cfb3d..7aa00e5 100644 +--- a/node_modules/react-native/React/Fabric/Mounting/RCTMountingManager.mm ++++ b/node_modules/react-native/React/Fabric/Mounting/RCTMountingManager.mm +@@ -49,6 +49,9 @@ static void RCTPerformMountInstructions( + { + SystraceSection s("RCTPerformMountInstructions"); + ++ [CATransaction begin]; ++ [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; ++ + for (const auto &mutation : mutations) { + switch (mutation.type) { + case ShadowViewMutation::Create: { +@@ -147,6 +150,7 @@ static void RCTPerformMountInstructions( + } + } + } ++ [CATransaction commit]; + } + + @implementation RCTMountingManager { From 0d23206b7144b63858a5fe52ad97008cafc38b4d Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 15 Apr 2024 17:28:08 +0200 Subject: [PATCH 108/283] integrate OnboardingWork page --- src/ONYXKEYS.ts | 3 + src/ROUTES.ts | 1 + src/SCREENS.ts | 1 + src/languages/en.ts | 2 + src/languages/es.ts | 2 + .../Navigators/OnboardingModalNavigator.tsx | 5 + src/libs/Navigation/linkingConfig/config.ts | 4 + src/libs/Navigation/types.ts | 1 + .../OnboardingWork/BaseOnboardingWork.tsx | 148 ++++++++++++++++++ src/pages/OnboardingWork/index.native.tsx | 17 ++ src/pages/OnboardingWork/index.tsx | 17 ++ src/pages/OnboardingWork/types.ts | 17 ++ src/types/form/WorkForm.ts | 18 +++ src/types/form/index.ts | 1 + 14 files changed, 237 insertions(+) create mode 100644 src/pages/OnboardingWork/BaseOnboardingWork.tsx create mode 100644 src/pages/OnboardingWork/index.native.tsx create mode 100644 src/pages/OnboardingWork/index.tsx create mode 100644 src/pages/OnboardingWork/types.ts create mode 100644 src/types/form/WorkForm.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index cda74da86a54..fe06f9ca71d8 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -384,6 +384,8 @@ const ONYXKEYS = { DISPLAY_NAME_FORM_DRAFT: 'displayNameFormDraft', ONBOARDING_PERSONAL_DETAILS_FORM: 'onboardingPersonalDetailsForm', ONBOARDING_PERSONAL_DETAILS_FORM_DRAFT: 'onboardingPersonalDetailsFormDraft', + ONBOARDING_PERSONAL_WORK: 'onboardingWorkForm', + ONBOARDING_PERSONAL_WORK_DRAFT: 'onboardingWorkFormDraft', ROOM_NAME_FORM: 'roomNameForm', ROOM_NAME_FORM_DRAFT: 'roomNameFormDraft', REPORT_DESCRIPTION_FORM: 'reportDescriptionForm', @@ -474,6 +476,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: FormTypes.ProfileSettingsForm; [ONYXKEYS.FORMS.DISPLAY_NAME_FORM]: FormTypes.DisplayNameForm; [ONYXKEYS.FORMS.ONBOARDING_PERSONAL_DETAILS_FORM]: FormTypes.DisplayNameForm; + [ONYXKEYS.FORMS.ONBOARDING_PERSONAL_WORK]: FormTypes.WorkForm; [ONYXKEYS.FORMS.ROOM_NAME_FORM]: FormTypes.RoomNameForm; [ONYXKEYS.FORMS.REPORT_DESCRIPTION_FORM]: FormTypes.ReportDescriptionForm; [ONYXKEYS.FORMS.LEGAL_NAME_FORM]: FormTypes.LegalNameForm; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 8e4c84f24965..c924e8e86554 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -700,6 +700,7 @@ const ROUTES = { PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational', ONBOARDING_ROOT: 'onboarding', ONBOARDING_PERSONAL_DETAILS: 'onboarding/personal-details', + ONBOARDING_WORK: 'onboarding/work', ONBOARDING_PURPOSE: 'onboarding/purpose', WELCOME_VIDEO_ROOT: 'onboarding/welcome-video', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 96372d5bbabb..0c709dec955d 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -287,6 +287,7 @@ const SCREENS = { ONBOARDING: { PERSONAL_DETAILS: 'Onboarding_Personal_Details', PURPOSE: 'Onboarding_Purpose', + WORK: 'Onboarding_Work', }, ONBOARD_ENGAGEMENT: { diff --git a/src/languages/en.ts b/src/languages/en.ts index 0793590f69b1..538b2f0b7965 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -320,6 +320,7 @@ export default { subtitleText2: 'button above, or create something using the', subtitleText3: 'button below.', }, + businessName: 'Business name', }, location: { useCurrent: 'Use current location', @@ -1335,6 +1336,7 @@ export default { button: "Let's go", }, whatsYourName: "What's your name?", + whereYouWork: 'Where do you work?', purpose: { title: 'What do you want to do today?', error: 'Please make a selection before continuing', diff --git a/src/languages/es.ts b/src/languages/es.ts index d7e60b53a55a..9a7dc6ffdcb4 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -310,6 +310,7 @@ export default { subtitleText2: 'o crea algo usando el botón', subtitleText3: '.', }, + businessName: 'Nombre del Negocio', }, location: { useCurrent: 'Usar ubicación actual', @@ -1337,6 +1338,7 @@ export default { button: 'Vámonos', }, whatsYourName: '¿Cómo te llamas?', + whereYouWork: '¿Dónde trabajas?', purpose: { title: '¿Qué quieres hacer hoy?', error: 'Por favor, haga una selección antes de continuar.', diff --git a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx index 3791cbc03641..ef3f6340f3e4 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/OnboardingModalNavigator.tsx @@ -8,6 +8,7 @@ import OnboardingModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator import type {OnboardingModalNavigatorParamList} from '@libs/Navigation/types'; import OnboardingPersonalDetails from '@pages/OnboardingPersonalDetails'; import OnboardingPurpose from '@pages/OnboardingPurpose'; +import OnboardingWork from '@pages/OnboardingWork'; import SCREENS from '@src/SCREENS'; import Overlay from './Overlay'; @@ -31,6 +32,10 @@ function OnboardingModalNavigator() { name={SCREENS.ONBOARDING.PERSONAL_DETAILS} component={OnboardingPersonalDetails} /> + diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index e577f6843d66..e060d86e0967 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -105,6 +105,10 @@ const config: LinkingOptions['config'] = { path: ROUTES.ONBOARDING_PERSONAL_DETAILS, exact: true, }, + [SCREENS.ONBOARDING.WORK]: { + path: ROUTES.ONBOARDING_WORK, + exact: true, + }, }, }, [NAVIGATORS.RIGHT_MODAL_NAVIGATOR]: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 09591cfd78db..2520d81744e8 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -710,6 +710,7 @@ type OnboardingModalNavigatorParamList = { [SCREENS.ONBOARDING_MODAL.ONBOARDING]: undefined; [SCREENS.ONBOARDING.PERSONAL_DETAILS]: undefined; [SCREENS.ONBOARDING.PURPOSE]: undefined; + [SCREENS.ONBOARDING.WORK]: undefined; }; type WelcomeVideoModalNavigatorParamList = { diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx new file mode 100644 index 000000000000..d00f17ad955b --- /dev/null +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -0,0 +1,148 @@ +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import KeyboardAvoidingView from '@components/KeyboardAvoidingView'; +import OfflineIndicator from '@components/OfflineIndicator'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import useDisableModalDismissOnEscape from '@hooks/useDisableModalDismissOnEscape'; +import useLocalize from '@hooks/useLocalize'; +import useOnboardingLayout from '@hooks/useOnboardingLayout'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import variables from '@styles/variables'; +import * as Policy from '@userActions/Policy'; +import * as Report from '@userActions/Report'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import INPUT_IDS from '@src/types/form/WorkForm'; +import type {BaseOnboardingWorkOnyxProps, BaseOnboardingWorkProps} from './types'; + +function BaseOnboardingWork({currentUserPersonalDetails, shouldUseNativeStyles, onboardingPurposeSelected}: BaseOnboardingWorkProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {isSmallScreenWidth} = useWindowDimensions(); + const {shouldUseNarrowLayout} = useOnboardingLayout(); + + useDisableModalDismissOnEscape(); + + const completeEngagement = useCallback( + (values: FormOnyxValues<'onboardingWorkForm'>) => { + if (!onboardingPurposeSelected) { + return; + } + + const work = values.work.trim(); + + Policy.createDraftInitialWorkspace(currentUserPersonalDetails.login ?? '', work, undefined, true); + + Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], { + login: currentUserPersonalDetails.login ?? '', + firstName: currentUserPersonalDetails.firstName ?? '', + lastName: currentUserPersonalDetails.lastName ?? '', + }); + + Navigation.dismissModal(); + // Only navigate to concierge chat when central pane is visible + // Otherwise stay on the chats screen. + if (isSmallScreenWidth) { + Navigation.navigate(ROUTES.HOME); + } else { + Report.navigateToConciergeChat(); + } + + // Small delay purely due to design considerations, + // no special technical reasons behind that. + setTimeout(() => { + Navigation.navigate(ROUTES.WELCOME_VIDEO_ROOT); + }, variables.welcomeVideoDelay); + }, + [currentUserPersonalDetails.firstName, currentUserPersonalDetails.lastName, currentUserPersonalDetails.login, isSmallScreenWidth, onboardingPurposeSelected], + ); + + const validate = (values: FormOnyxValues<'onboardingWorkForm'>) => { + const errors: FormInputErrors = {}; + const work = values.work.trim(); + + if (!ValidationUtils.isRequiredFulfilled(work)) { + errors.work = 'workspace.editor.nameIsRequiredError'; + } else if ([...work].length > CONST.TITLE_CHARACTER_LIMIT) { + // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 + // code units. + ErrorUtils.addErrorMessage(errors, 'work', ['common.error.characterLimitExceedCounter', {length: [...work].length, limit: CONST.TITLE_CHARACTER_LIMIT}]); + } + + return errors; + }; + + const handleGoBack = useCallback(() => { + Navigation.goBack(); + }, []); + + const WorkFooterInstance = ; + + return ( + + + + + + {translate('onboarding.whereYouWork')} + + + + + + + + ); +} + +BaseOnboardingWork.displayName = 'BaseOnboardingWork'; + +export default withCurrentUserPersonalDetails( + withOnyx({ + onboardingPurposeSelected: { + key: ONYXKEYS.ONBOARDING_PURPOSE_SELECTED, + }, + })(BaseOnboardingWork), +); diff --git a/src/pages/OnboardingWork/index.native.tsx b/src/pages/OnboardingWork/index.native.tsx new file mode 100644 index 000000000000..3e69696aa45a --- /dev/null +++ b/src/pages/OnboardingWork/index.native.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import BaseOnboardingWork from './BaseOnboardingWork'; +import type {OnboardingWorkProps} from './types'; + +function OnboardingWork({...rest}: Omit) { + return ( + + ); +} + +OnboardingWork.displayName = 'OnboardingWork'; + +export default OnboardingWork; diff --git a/src/pages/OnboardingWork/index.tsx b/src/pages/OnboardingWork/index.tsx new file mode 100644 index 000000000000..ba1b8aaeb106 --- /dev/null +++ b/src/pages/OnboardingWork/index.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import BaseOnboardingWork from './BaseOnboardingWork'; +import type {OnboardingWorkProps} from './types'; + +function OnboardingWork({...rest}: Omit) { + return ( + + ); +} + +OnboardingWork.displayName = 'OnboardingPurpose'; + +export default OnboardingWork; diff --git a/src/pages/OnboardingWork/types.ts b/src/pages/OnboardingWork/types.ts new file mode 100644 index 000000000000..930a8081e9c4 --- /dev/null +++ b/src/pages/OnboardingWork/types.ts @@ -0,0 +1,17 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; + +type OnboardingWorkProps = Record; + +type BaseOnboardingWorkOnyxProps = { + /** Saved onboarding purpose selected by the user */ + onboardingPurposeSelected: OnyxEntry; +}; + +type BaseOnboardingWorkProps = WithCurrentUserPersonalDetailsProps & + BaseOnboardingWorkOnyxProps & { + /* Whether to use native styles tailored for native devices */ + shouldUseNativeStyles: boolean; + }; + +export type {OnboardingWorkProps, BaseOnboardingWorkOnyxProps, BaseOnboardingWorkProps}; diff --git a/src/types/form/WorkForm.ts b/src/types/form/WorkForm.ts new file mode 100644 index 000000000000..6e5fa8a89311 --- /dev/null +++ b/src/types/form/WorkForm.ts @@ -0,0 +1,18 @@ +import type {ValueOf} from 'type-fest'; +import type Form from './Form'; + +const INPUT_IDS = { + WORK: 'work', +} as const; + +type InputID = ValueOf; + +type WorkForm = Form< + InputID, + { + [INPUT_IDS.WORK]: string; + } +>; + +export type {WorkForm}; +export default INPUT_IDS; diff --git a/src/types/form/index.ts b/src/types/form/index.ts index ce3fcd428999..ddddb99ab89b 100644 --- a/src/types/form/index.ts +++ b/src/types/form/index.ts @@ -48,4 +48,5 @@ export type {WorkspaceTaxCustomName} from './WorkspaceTaxCustomName'; export type {PolicyCreateDistanceRateForm} from './PolicyCreateDistanceRateForm'; export type {PolicyDistanceRateEditForm} from './PolicyDistanceRateEditForm'; export type {NewChatNameForm} from './NewChatNameForm'; +export type {WorkForm} from './WorkForm'; export type {default as Form} from './Form'; From 19d7d50711aed59471d79c7dd6794ca887ff6fa5 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 15 Apr 2024 17:29:12 +0200 Subject: [PATCH 109/283] move types --- .../BaseOnboardingPersonalDetails.tsx | 2 +- .../index.native.tsx | 2 +- src/pages/OnboardingPersonalDetails/index.tsx | 2 +- src/pages/OnboardingPersonalDetails/types.ts | 17 ++++++++++++++ src/pages/OnboardingPurpose/types.ts | 23 +------------------ 5 files changed, 21 insertions(+), 25 deletions(-) create mode 100644 src/pages/OnboardingPersonalDetails/types.ts diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index dac3b4e3d4c1..b1e14b077931 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -18,7 +18,6 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as ValidationUtils from '@libs/ValidationUtils'; -import type {BaseOnboardingPersonalDetailsOnyxProps, BaseOnboardingPersonalDetailsProps} from '@pages/OnboardingPurpose/types'; import variables from '@styles/variables'; import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Report from '@userActions/Report'; @@ -26,6 +25,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import INPUT_IDS from '@src/types/form/DisplayNameForm'; +import type {BaseOnboardingPersonalDetailsOnyxProps, BaseOnboardingPersonalDetailsProps} from './types'; function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNativeStyles, onboardingPurposeSelected}: BaseOnboardingPersonalDetailsProps) { const styles = useThemeStyles(); diff --git a/src/pages/OnboardingPersonalDetails/index.native.tsx b/src/pages/OnboardingPersonalDetails/index.native.tsx index 778d68e39458..b5a4d42f0de1 100644 --- a/src/pages/OnboardingPersonalDetails/index.native.tsx +++ b/src/pages/OnboardingPersonalDetails/index.native.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type {OnboardingPersonalDetailsProps} from '@pages/OnboardingPurpose/types'; import BaseOnboardingPersonalDetails from './BaseOnboardingPersonalDetails'; +import type {OnboardingPersonalDetailsProps} from './types'; function OnboardingPersonalDetails({...rest}: Omit) { return ( diff --git a/src/pages/OnboardingPersonalDetails/index.tsx b/src/pages/OnboardingPersonalDetails/index.tsx index 8409e7e8f0a2..d6a408ee7381 100644 --- a/src/pages/OnboardingPersonalDetails/index.tsx +++ b/src/pages/OnboardingPersonalDetails/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type {OnboardingPersonalDetailsProps} from '@pages/OnboardingPurpose/types'; import BaseOnboardingPersonalDetails from './BaseOnboardingPersonalDetails'; +import type {OnboardingPersonalDetailsProps} from './types'; function OnboardingPersonalDetails({...rest}: Omit) { return ( diff --git a/src/pages/OnboardingPersonalDetails/types.ts b/src/pages/OnboardingPersonalDetails/types.ts new file mode 100644 index 000000000000..6d286373a1ac --- /dev/null +++ b/src/pages/OnboardingPersonalDetails/types.ts @@ -0,0 +1,17 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; + +type OnboardingPersonalDetailsProps = Record; + +type BaseOnboardingPersonalDetailsOnyxProps = { + /** Saved onboarding purpose selected by the user */ + onboardingPurposeSelected: OnyxEntry; +}; + +type BaseOnboardingPersonalDetailsProps = WithCurrentUserPersonalDetailsProps & + BaseOnboardingPersonalDetailsOnyxProps & { + /* Whether to use native styles tailored for native devices */ + shouldUseNativeStyles: boolean; + }; + +export type {OnboardingPersonalDetailsProps, BaseOnboardingPersonalDetailsOnyxProps, BaseOnboardingPersonalDetailsProps}; diff --git a/src/pages/OnboardingPurpose/types.ts b/src/pages/OnboardingPurpose/types.ts index 15d6beec1f6f..586463a26bb0 100644 --- a/src/pages/OnboardingPurpose/types.ts +++ b/src/pages/OnboardingPurpose/types.ts @@ -1,5 +1,4 @@ import type {OnyxEntry} from 'react-native-onyx'; -import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; type OnboardingPurposeProps = Record; @@ -17,24 +16,4 @@ type BaseOnboardingPurposeProps = OnboardingPurposeProps & shouldEnableMaxHeight: boolean; }; -type OnboardingPersonalDetailsProps = Record; - -type BaseOnboardingPersonalDetailsOnyxProps = { - /** Saved onboarding purpose selected by the user */ - onboardingPurposeSelected: OnyxEntry; -}; - -type BaseOnboardingPersonalDetailsProps = WithCurrentUserPersonalDetailsProps & - BaseOnboardingPersonalDetailsOnyxProps & { - /* Whether to use native styles tailored for native devices */ - shouldUseNativeStyles: boolean; - }; - -export type { - BaseOnboardingPurposeOnyxProps, - BaseOnboardingPurposeProps, - OnboardingPurposeProps, - OnboardingPersonalDetailsProps, - BaseOnboardingPersonalDetailsOnyxProps, - BaseOnboardingPersonalDetailsProps, -}; +export type {BaseOnboardingPurposeOnyxProps, BaseOnboardingPurposeProps, OnboardingPurposeProps}; From ed44ffdb16a55cc84abcaae96101951ed56f39c3 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 15 Apr 2024 17:43:08 +0200 Subject: [PATCH 110/283] integrate onboarding work --- .../BaseOnboardingPersonalDetails.tsx | 8 ++++++++ src/pages/OnboardingWork/BaseOnboardingWork.tsx | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index b1e14b077931..a2e3765baa9c 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -46,6 +46,14 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat return; } + const openWorkPagePurposes = [CONST.ONBOARDING_CHOICES.EMPLOYER, CONST.ONBOARDING_CHOICES.MANAGE_TEAM]; + + if (openWorkPagePurposes.includes(onboardingPurposeSelected)) { + Navigation.navigate(ROUTES.ONBOARDING_WORK); + + return; + } + Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], { login: currentUserPersonalDetails.login ?? '', firstName, diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index d00f17ad955b..01ec23c8315c 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -43,7 +43,7 @@ function BaseOnboardingWork({currentUserPersonalDetails, shouldUseNativeStyles, const work = values.work.trim(); - Policy.createDraftInitialWorkspace(currentUserPersonalDetails.login ?? '', work, undefined, true); + Policy.createWorkspace(currentUserPersonalDetails.login ?? '', true, work); Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], { login: currentUserPersonalDetails.login ?? '', From 41851acf298e8146bb503bbbfcdd911d2e75ef70 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 15 Apr 2024 18:34:02 +0200 Subject: [PATCH 111/283] clarify work cases --- .../OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index a2e3765baa9c..5657f8553113 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -46,7 +46,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat return; } - const openWorkPagePurposes = [CONST.ONBOARDING_CHOICES.EMPLOYER, CONST.ONBOARDING_CHOICES.MANAGE_TEAM]; + const openWorkPagePurposes = [CONST.ONBOARDING_CHOICES.TRACK, CONST.ONBOARDING_CHOICES.MANAGE_TEAM]; if (openWorkPagePurposes.includes(onboardingPurposeSelected)) { Navigation.navigate(ROUTES.ONBOARDING_WORK); From fb5d2823922e60f8efe34ed6bbda48de3699f5ab Mon Sep 17 00:00:00 2001 From: usman-ghani564 Date: Mon, 15 Apr 2024 21:35:19 +0500 Subject: [PATCH 112/283] Add live markdown to private notes --- src/pages/PrivateNotes/PrivateNotesEditPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx index 7d0b8fff8e41..7ab8fadc496a 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx @@ -165,6 +165,7 @@ function PrivateNotesEditPage({route, personalDetailsList, report}: PrivateNotes privateNotesInput.current = el; updateMultilineInputRange(privateNotesInput.current); }} + isMarkdownEnabled /> From 410fdb17d092650dcc795b4edad2b03b40401b0e Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 15 Apr 2024 18:59:41 +0200 Subject: [PATCH 113/283] fix: apply requested changes --- src/ROUTES.ts | 6 ++--- src/SCREENS.ts | 2 +- ...raryForRefactorRequestConfirmationList.tsx | 21 +++++++++--------- .../ReportActionItem/MoneyRequestView.tsx | 13 +++++++++-- src/libs/DistanceRequestUtils.ts | 14 +++++++----- .../ModalStackNavigators/index.tsx | 2 +- src/libs/Navigation/linkingConfig/config.ts | 2 +- src/libs/Navigation/types.ts | 2 +- ...ate.tsx => IOURequestStepDistanceRate.tsx} | 22 +++++++++---------- .../step/withFullTransactionOrNotFound.tsx | 2 +- .../step/withWritableReportOrNotFound.tsx | 2 +- 11 files changed, 51 insertions(+), 37 deletions(-) rename src/pages/iou/request/step/{IOURequestStepRate.tsx => IOURequestStepDistanceRate.tsx} (82%) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a0909b2c582f..e92b29464005 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -370,10 +370,10 @@ const ROUTES = { getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`${action}/${iouType}/distance/${transactionID}/${reportID}`, backTo), }, - MONEY_REQUEST_STEP_RATE: { - route: ':action/:iouType/rate/:transactionID/:reportID', + MONEY_REQUEST_STEP_DISTANCE_RATE: { + route: ':action/:iouType/distanceRate/:transactionID/:reportID', getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action}/${iouType}/rate/${transactionID}/${reportID}`, backTo), + getUrlWithBackToParam(`${action}/${iouType}/distanceRate/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_MERCHANT: { route: ':action/:iouType/merchant/:transactionID/:reportID', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 303e6e322799..ac81bf1c5f92 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -148,7 +148,7 @@ const SCREENS = { STEP_DATE: 'Money_Request_Step_Date', STEP_DESCRIPTION: 'Money_Request_Step_Description', STEP_DISTANCE: 'Money_Request_Step_Distance', - STEP_RATE: 'Money_Request_Step_Rate', + STEP_DISTANCE_RATE: 'Money_Request_Step_Rate', STEP_MERCHANT: 'Money_Request_Step_Merchant', STEP_PARTICIPANTS: 'Money_Request_Step_Participants', STEP_SCAN: 'Money_Request_Step_Scan', diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index fc23af450687..66ba8b78a735 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -9,6 +9,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; import usePrevious from '@hooks/usePrevious'; import useTheme from '@hooks/useTheme'; @@ -219,6 +220,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const {translate, toLocaleDigit} = useLocalize(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const {canUseP2PDistanceRequests, canUseViolations} = usePermissions(iouType); + const {isOffline} = useNetwork(); const isTypeRequest = iouType === CONST.IOU.TYPE.REQUEST; const isTypeSplit = iouType === CONST.IOU.TYPE.SPLIT; @@ -242,10 +244,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ ? DistanceRequestUtils.getRateForP2P(policy?.outputCurrency ?? CONST.CURRENCY.USD) : mileageRates?.[customUnitRateID] ?? DistanceRequestUtils.getDefaultMileageRate(policy); - const {unit, rate} = mileageRate ?? { - unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, - rate: CONST.CUSTOM_UNITS.MILEAGE_IRS_RATE * 100, - }; + const {unit, rate} = mileageRate ?? {}; const prevRate = usePrevious(rate); const shouldCalculateDistanceAmount = isDistanceRequest && (iouAmount === 0 || prevRate !== rate); @@ -283,7 +282,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const formattedAmount = isDistanceRequestWithPendingRoute ? '' : CurrencyUtils.convertToDisplayString( - shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0) : iouAmount, + shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate ?? 0) : iouAmount, isDistanceRequest ? currency : iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); @@ -352,7 +351,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ return; } - const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate ?? 0); IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency ?? ''); }, [shouldCalculateDistanceAmount, distance, rate, unit, transaction, currency, transactionID]); @@ -763,16 +762,18 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ item: ( { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_RATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams())); + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_DISTANCE_RATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), + ); }} disabled={didConfirm} - interactive={!isReadOnly && isPolicyExpenseChat} + interactive={Boolean(rate) && !isReadOnly && isPolicyExpenseChat} /> ), shouldShow: isDistanceRequest && canUseP2PDistanceRequests, diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 79f59d4e4868..74d04c8807e1 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -12,6 +12,7 @@ import Switch from '@components/Switch'; import Text from '@components/Text'; import ViolationMessages from '@components/ViolationMessages'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -99,6 +100,7 @@ function MoneyRequestView({ const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); + const {isOffline} = useNetwork(); const {isSmallScreenWidth} = useWindowDimensions(); const {translate, toLocaleDigit} = useLocalize(); const {canUseViolations, canUseP2PDistanceRequests} = usePermissions(); @@ -185,7 +187,7 @@ function MoneyRequestView({ const {unit, rate} = mileageRate; const distance = DistanceRequestUtils.getDistanceFromMerchant(transactionMerchant, unit); - const rateToDisplay = DistanceRequestUtils.getRateForDisplay(hasRoute, unit, rate, currency, translate, toLocaleDigit); + const rateToDisplay = DistanceRequestUtils.getRateForDisplay(hasRoute, unit, rate, currency, translate, toLocaleDigit, isOffline); const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate); const saveBillable = useCallback( @@ -471,7 +473,14 @@ function MoneyRequestView({ ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, orderWeight, transaction?.transactionID ?? '', report.reportID), ) } - brickRoadIndicator={getErrorForField('tag', {tagListIndex: index, tagListName: name}) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + brickRoadIndicator={ + getErrorForField('tag', { + tagListIndex: index, + tagListName: name, + }) + ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR + : undefined + } error={getErrorForField('tag', {tagListIndex: index, tagListName: name})} /> diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index c673245343b1..66ab9238be7c 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -116,15 +116,19 @@ function getRoundedDistanceInUnits(distanceInMeters: number, unit: Unit): string */ function getRateForDisplay( hasRoute: boolean, - unit: Unit, + unit: Unit | undefined, rate: number | undefined, currency: string | undefined, translate: LocaleContextProps['translate'], toLocaleDigit: LocaleContextProps['toLocaleDigit'], + isOffline?: boolean, ): string { - if (!hasRoute || !rate || !currency) { + if (isOffline) { return translate('iou.defaultRate'); } + if (!hasRoute || !rate || !currency || !unit) { + return translate('iou.routePending'); + } const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); const ratePerUnit = PolicyUtils.getUnitRateValue(toLocaleDigit, {rate}); @@ -142,8 +146,8 @@ function getRateForDisplay( * @param translate Translate function * @returns A string that describes the distance traveled */ -function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit: Unit, rate: number | undefined, translate: LocaleContextProps['translate']): string { - if (!hasRoute || !rate) { +function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit: Unit | undefined, rate: number | undefined, translate: LocaleContextProps['translate']): string { + if (!hasRoute || !rate || !unit) { return translate('iou.routePending'); } @@ -168,7 +172,7 @@ function getDistanceForDisplay(hasRoute: boolean, distanceInMeters: number, unit function getDistanceMerchant( hasRoute: boolean, distanceInMeters: number, - unit: Unit, + unit: Unit | undefined, rate: number | undefined, currency: string, translate: LocaleContextProps['translate'], diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 062a146e2624..6568cab36a0d 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -81,7 +81,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/iou/request/step/IOURequestStepDate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: () => require('../../../../pages/iou/request/step/IOURequestStepDescription').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: () => require('../../../../pages/iou/request/step/IOURequestStepDistance').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.STEP_RATE]: () => require('../../../../pages/iou/request/step/IOURequestStepRate').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.STEP_DISTANCE_RATE]: () => require('@pages/iou/request/step/IOURequestStepDistanceRate').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_MERCHANT]: () => require('../../../../pages/iou/request/step/IOURequestStepMerchant').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: () => require('../../../../pages/iou/request/step/IOURequestStepParticipants').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_SCAN]: () => require('../../../../pages/iou/request/step/IOURequestStepScan').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 67eab41ada5a..554dd963bcdc 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -568,7 +568,7 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.STEP_DATE]: ROUTES.MONEY_REQUEST_STEP_DATE.route, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.route, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: ROUTES.MONEY_REQUEST_STEP_DISTANCE.route, - [SCREENS.MONEY_REQUEST.STEP_RATE]: ROUTES.MONEY_REQUEST_STEP_RATE.route, + [SCREENS.MONEY_REQUEST.STEP_DISTANCE_RATE]: ROUTES.MONEY_REQUEST_STEP_DISTANCE_RATE.route, [SCREENS.MONEY_REQUEST.HOLD]: ROUTES.MONEY_REQUEST_HOLD_REASON.route, [SCREENS.MONEY_REQUEST.STEP_MERCHANT]: ROUTES.MONEY_REQUEST_STEP_MERCHANT.route, [SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a48b2ba10e35..e89bb61fe566 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -478,7 +478,7 @@ type MoneyRequestNavigatorParamList = { transactionID: string; reportID: string; }; - [SCREENS.MONEY_REQUEST.STEP_RATE]: { + [SCREENS.MONEY_REQUEST.STEP_DISTANCE_RATE]: { iouType: ValueOf; transactionID: string; backTo: Routes; diff --git a/src/pages/iou/request/step/IOURequestStepRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx similarity index 82% rename from src/pages/iou/request/step/IOURequestStepRate.tsx rename to src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index d7ae3ee0a19e..dc1f6fff2968 100644 --- a/src/pages/iou/request/step/IOURequestStepRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -21,7 +21,7 @@ import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -type IOURequestStepRateOnyxProps = { +type IOURequestStepDistanceRateOnyxProps = { /** Policy details */ policy: OnyxEntry; @@ -29,20 +29,20 @@ type IOURequestStepRateOnyxProps = { rates: Record; }; -type IOURequestStepRateProps = IOURequestStepRateOnyxProps & - WithWritableReportOrNotFoundProps & { +type IOURequestStepDistanceRateProps = IOURequestStepDistanceRateOnyxProps & + WithWritableReportOrNotFoundProps & { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ transaction: OnyxEntry; }; -function IOURequestStepRate({ +function IOURequestStepDistanceRate({ policy, route: { params: {backTo, transactionID}, }, transaction, rates, -}: IOURequestStepRateProps) { +}: IOURequestStepDistanceRateProps) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); @@ -92,9 +92,9 @@ function IOURequestStepRate({ ); } -IOURequestStepRate.displayName = 'IOURequestStepRate'; +IOURequestStepDistanceRate.displayName = 'IOURequestStepDistanceRate'; -const IOURequestStepRateWithOnyx = withOnyx({ +const IOURequestStepDistanceRateWithOnyx = withOnyx({ policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, @@ -102,11 +102,11 @@ const IOURequestStepRateWithOnyx = withOnyx `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? '0'}`, selector: DistanceRequestUtils.getMileageRates, }, -})(IOURequestStepRate); +})(IOURequestStepDistanceRate); // eslint-disable-next-line rulesdir/no-negated-variables -const IOURequestStepRateWithWritableReportOrNotFound = withWritableReportOrNotFound(IOURequestStepRateWithOnyx); +const IOURequestStepDistanceRateWithWritableReportOrNotFound = withWritableReportOrNotFound(IOURequestStepDistanceRateWithOnyx); // eslint-disable-next-line rulesdir/no-negated-variables -const IOURequestStepRateWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepRateWithWritableReportOrNotFound); +const IOURequestStepDistanceRateWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepDistanceRateWithWritableReportOrNotFound); -export default IOURequestStepRateWithFullTransactionOrNotFound; +export default IOURequestStepDistanceRateWithFullTransactionOrNotFound; diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx index 76050afaa734..70d5642f3873 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx @@ -27,7 +27,7 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS | typeof SCREENS.MONEY_REQUEST.STEP_MERCHANT | typeof SCREENS.MONEY_REQUEST.STEP_TAG - | typeof SCREENS.MONEY_REQUEST.STEP_RATE + | typeof SCREENS.MONEY_REQUEST.STEP_DISTANCE_RATE | typeof SCREENS.MONEY_REQUEST.STEP_CONFIRMATION | typeof SCREENS.MONEY_REQUEST.STEP_CATEGORY | typeof SCREENS.MONEY_REQUEST.STEP_TAX_RATE diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index 723200fa0c12..ece519d87a1b 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -21,7 +21,7 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_WAYPOINT | typeof SCREENS.MONEY_REQUEST.STEP_DESCRIPTION | typeof SCREENS.MONEY_REQUEST.STEP_CATEGORY - | typeof SCREENS.MONEY_REQUEST.STEP_RATE + | typeof SCREENS.MONEY_REQUEST.STEP_DISTANCE_RATE | typeof SCREENS.MONEY_REQUEST.STEP_CONFIRMATION | typeof SCREENS.MONEY_REQUEST.STEP_TAX_RATE | typeof SCREENS.MONEY_REQUEST.STEP_AMOUNT From 23268d16205517037012dd334129bc9426fd7893 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 15 Apr 2024 19:10:58 +0200 Subject: [PATCH 114/283] fix onboarding screen options --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index fde0202d3d2f..0edceee43647 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -369,7 +369,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie /> Date: Mon, 15 Apr 2024 19:27:54 +0200 Subject: [PATCH 115/283] fix: restore setting customUnitRateID when data available --- src/libs/DistanceRequestUtils.ts | 17 ++++++++++++++--- src/libs/PolicyUtils.ts | 11 +++++++++++ src/libs/actions/IOU.ts | 23 +++-------------------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 66ab9238be7c..f827606ca927 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -4,7 +4,7 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {RateAndUnit} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report} from '@src/types/onyx'; +import type {LastSelectedDistanceRates, Report} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -32,6 +32,14 @@ Onyx.connect({ }, }); +let lastSelectedDistanceRates: OnyxEntry = {}; +Onyx.connect({ + key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, + callback: (value) => { + lastSelectedDistanceRates = value; + }, +}); + let allReports: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -267,12 +275,15 @@ function getDistanceFromMerchant(merchant: string | undefined, unit: Unit): numb function getCustomUnitRateID(reportID: string) { const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; + const policy = PolicyUtils.getPolicy(report?.policyID ?? parentReport?.policyID ?? ''); + + let customUnitRateID: string = CONST.CUSTOM_UNITS.FAKE_P2P_ID; if (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isPolicyExpenseChat(parentReport)) { - return ''; + customUnitRateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? getDefaultMileageRate(policy)?.customUnitRateID ?? ''; } - return CONST.CUSTOM_UNITS.FAKE_P2P_ID; + return customUnitRateID; } export default { diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 8002ad5145df..7df88296651a 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -331,6 +331,16 @@ function getPolicyIDFromNavigationState() { return getPolicyIDFromState(navigationRef.getRootState() as State); } +/** + * Returns the policy of the report + */ +function getPolicy(policyID: string | undefined): Policy | EmptyObject { + if (!allPolicies || !policyID) { + return {}; + } + return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; +} + export { getActivePolicies, hasAccountingConnections, @@ -371,6 +381,7 @@ export { getTaxByID, hasPolicyCategoriesError, getPolicyIDFromNavigationState, + getPolicy, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8e9ea0e73726..fde110776096 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -244,13 +244,6 @@ Onyx.connect({ }, }); -let allPolicies: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - waitForCollectionCallback: true, - callback: (value) => (allPolicies = value), -}); - const reportActionsByReport: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, @@ -264,16 +257,6 @@ Onyx.connect({ }, }); -/** - * Returns the policy of the report - */ -function getPolicy(policyID: string | undefined): OnyxTypes.Policy | EmptyObject { - if (!allPolicies || !policyID) { - return {}; - } - return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}; -} - /** * Initialize money request info * @param reportID to attach the transaction to @@ -4916,7 +4899,7 @@ function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObj return Object.values(chatReportActions).some((action) => { const iouReport = ReportUtils.getReport(action.childReportID ?? ''); - const policy = getPolicy(iouReport?.policyID); + const policy = PolicyUtils.getPolicy(iouReport?.policyID); const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, chatReport, policy); return action.childReportID?.toString() !== excludedIOUReportID && action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && shouldShowSettlementButton; }); @@ -5058,7 +5041,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full function submitReport(expenseReport: OnyxTypes.Report) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; const parentReport = ReportUtils.getReport(expenseReport.parentReportID); - const policy = getPolicy(expenseReport.policyID); + const policy = PolicyUtils.getPolicy(expenseReport.policyID); const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); const adminAccountID = policy.role === CONST.POLICY.ROLE.ADMIN ? currentUserPersonalDetails.accountID : undefined; @@ -5182,7 +5165,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { function cancelPayment(expenseReport: OnyxTypes.Report, chatReport: OnyxTypes.Report) { const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(expenseReport.reportID, -(expenseReport.total ?? 0), expenseReport.currency ?? ''); - const policy = getPolicy(chatReport.policyID); + const policy = PolicyUtils.getPolicy(chatReport.policyID); const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE; const approvalMode = policy.approvalMode ?? CONST.POLICY.APPROVAL_MODE.BASIC; let stateNum: ValueOf = CONST.REPORT.STATE_NUM.SUBMITTED; From b5c4fc5e51b6147705ec03e2dd7a639a724a3834 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 16 Apr 2024 13:33:36 +0800 Subject: [PATCH 116/283] add pending fields to transaction thread --- src/libs/actions/IOU.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 85f4b74f3436..420d5e6a8e29 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -554,7 +554,10 @@ function buildOnyxDataForMoneyRequest( { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, - value: transactionThreadReport, + value: { + ...transactionThreadReport, + pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + }, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -737,6 +740,7 @@ function buildOnyxDataForMoneyRequest( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, value: { + pendingFields: null, errorFields: { createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, @@ -895,7 +899,10 @@ function buildOnyxDataForTrackExpense( { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, - value: transactionThreadReport, + value: { + ...transactionThreadReport, + pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + }, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1061,6 +1068,7 @@ function buildOnyxDataForTrackExpense( onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport.reportID}`, value: { + pendingFields: null, errorFields: { createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, From 6afbf9a3072ef1012c0130dc79ce22b309f1b283 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 16 Apr 2024 15:40:28 +0700 Subject: [PATCH 117/283] fix Unsynchronized transition of left and right page when deleting workspace --- src/pages/workspace/WorkspaceInitialPage.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index a6a131f5372c..1f00e2fe58cc 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -247,6 +247,12 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r // We check isPendingDelete for both policy and prevPolicy to prevent the NotFound view from showing right after we delete the workspace (PolicyUtils.isPendingDeletePolicy(policy) && PolicyUtils.isPendingDeletePolicy(prevPolicy)); + useEffect(() => { + if (!isEmptyObject(prevPolicy) && !PolicyUtils.isPendingDeletePolicy(prevPolicy) && PolicyUtils.isPendingDeletePolicy(policy)) { + Navigation.navigateWithSwitchPolicyID({policyID: undefined}); + } + }, [policy, prevPolicy]); + // We are checking if the user can access the route. // If user can't access the route, we are dismissing any modals that are open when the NotFound view is shown const canAccessRoute = activeRoute && menuItems.some((item) => item.routeName === activeRoute); @@ -298,6 +304,8 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r onClose={() => dismissError(policyID)} errors={policy?.errors} errorRowStyles={[styles.ph5, styles.pv2]} + shouldDisableStrikeThrough={false} + shouldHideOnDelete={false} > {/* From fce86cd7e70d310d642eaf4d673259ec150bdac3 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 16 Apr 2024 16:22:39 +0700 Subject: [PATCH 118/283] fix fallback icon bg --- src/components/Avatar.tsx | 2 +- src/styles/utils/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 358f5333bfba..bf48894beaab 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -90,7 +90,7 @@ function Avatar({ if (isWorkspace) { iconColors = StyleUtils.getDefaultWorkspaceAvatarColor(name); } else if (useFallBackAvatar) { - iconColors = StyleUtils.getBackgroundColorAndFill(theme.border, theme.icon); + iconColors = StyleUtils.getBackgroundColorAndFill(theme.buttonHoveredBG, theme.icon); } else { iconColors = null; } diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 97dad7df1efe..e9efc84e8807 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1185,7 +1185,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ let backgroundColor; if (currentEmojiIndex === highlightedEmojiIndex) { - backgroundColor = theme.buttonHoveredBG; + backgroundColor = theme.activeComponentBG; } else if (isHovered) { backgroundColor = theme.hoverComponentBG; } From d1ce7356e042390ee716fe6bc0ccf76b7a97019e Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 16 Apr 2024 16:28:25 +0700 Subject: [PATCH 119/283] fix navigate issue --- src/pages/workspace/WorkspaceInitialPage.tsx | 2 +- src/pages/workspace/WorkspaceProfilePage.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 1f00e2fe58cc..9acdc3f673da 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -249,7 +249,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r useEffect(() => { if (!isEmptyObject(prevPolicy) && !PolicyUtils.isPendingDeletePolicy(prevPolicy) && PolicyUtils.isPendingDeletePolicy(policy)) { - Navigation.navigateWithSwitchPolicyID({policyID: undefined}); + PolicyUtils.goBackFromInvalidPolicy(); } }, [policy, prevPolicy]); diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index 662335d0b358..b73c6c0d7062 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -97,7 +97,6 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfi } Policy.deleteWorkspace(policy?.id, policyName); - PolicyUtils.goBackFromInvalidPolicy(); setIsDeleteModalOpen(false); // If the workspace being deleted is the active workspace, switch to the "All Workspaces" view From 804801138dd61f58f9d34c10f94710d80559cf8c Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 16 Apr 2024 16:38:54 +0700 Subject: [PATCH 120/283] fix lint --- src/pages/workspace/WorkspaceInitialPage.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 9acdc3f673da..7a627ae5015d 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -248,9 +248,10 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r (PolicyUtils.isPendingDeletePolicy(policy) && PolicyUtils.isPendingDeletePolicy(prevPolicy)); useEffect(() => { - if (!isEmptyObject(prevPolicy) && !PolicyUtils.isPendingDeletePolicy(prevPolicy) && PolicyUtils.isPendingDeletePolicy(policy)) { - PolicyUtils.goBackFromInvalidPolicy(); + if (isEmptyObject(prevPolicy) || PolicyUtils.isPendingDeletePolicy(prevPolicy) || !PolicyUtils.isPendingDeletePolicy(policy)) { + return; } + PolicyUtils.goBackFromInvalidPolicy(); }, [policy, prevPolicy]); // We are checking if the user can access the route. From 949bf4ebac3bd6d4ffde596fb43c3a107e896944 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 16 Apr 2024 12:26:43 +0200 Subject: [PATCH 121/283] fix: fallback to first rate when all custom named --- src/libs/DistanceRequestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index f827606ca927..d35d7b0d711c 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -70,7 +70,7 @@ function getDefaultMileageRate(policy: OnyxEntry | EmptyObject): Mileage return null; } - const distanceRate = Object.values(distanceUnit.rates).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + const distanceRate = Object.values(distanceUnit.rates).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE) ?? Object.values(distanceUnit.rates)[0]; if (!distanceRate) { return null; } From d1f641af2b880510017b18ab49f0fe31ce5f6638 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 16 Apr 2024 17:31:29 +0700 Subject: [PATCH 122/283] compare reportID --- src/pages/home/report/ReportActionsView.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 1ed74b488837..40307b75f1a1 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -145,8 +145,7 @@ function ReportActionsView({ // Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions` const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); const moneyRequestAction = allReportActions.find((action) => { - const actionType = (action as OnyxTypes.OriginalMessageIOU).originalMessage?.type ?? ''; - return actionType === CONST.IOU.REPORT_ACTION_TYPE.CREATE || actionType === CONST.IOU.REPORT_ACTION_TYPE.TRACK || ReportActionsUtils.isSentMoneyReportAction(action); + return action.reportActionID === transactionThreadReport?.parentReportActionID; }); // Filter out the money request actions because we don't want to show any preview actions for one-transaction reports @@ -155,7 +154,7 @@ function ReportActionsView({ return actionType !== CONST.IOU.REPORT_ACTION_TYPE.CREATE && actionType !== CONST.IOU.REPORT_ACTION_TYPE.TRACK && !ReportActionsUtils.isSentMoneyReportAction(action); }); return [ReportActionsUtils.getSortedReportActions(filteredReportActions, true), moneyRequestAction ?? null]; - }, [allReportActions, transactionThreadReportActions]); + }, [allReportActions, transactionThreadReportActions, transactionThreadReport?.parentReportActionID]); const indexOfLinkedAction = useMemo(() => { if (!reportActionID) { From 52fde4a3350d75b66084e80e69ce560d960a306d Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 13:07:50 +0200 Subject: [PATCH 123/283] add video to tasks --- src/CONST.ts | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/CONST.ts b/src/CONST.ts index a9d8ba6baece..039598dd2b8a 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3620,6 +3620,13 @@ const CONST = { '2. Click Workspaces > New workspace.\n' + '\n' + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Track an expense', @@ -3633,6 +3640,13 @@ const CONST = { '4. Click Track.\n' + '\n' + 'And you’re done! Yep, it’s that easy.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, ], }, @@ -3658,6 +3672,13 @@ const CONST = { '4. Add your reimburser to the request.\n' + '\n' + 'Then, send your request and wait for that sweet “Cha-ching!” when it’s complete.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Enable your wallet', @@ -3670,6 +3691,13 @@ const CONST = { '3. Connect your bank account.\n' + '\n' + 'Once that’s done, you can request money from anyone and get paid back right into your personal bank account.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, ], }, @@ -3693,6 +3721,13 @@ const CONST = { '2. Click Workspaces > New workspace.\n' + '\n' + 'Your new workspace is ready! It’ll keep all of your spend (and chats) in one place.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Meet your setup specialist', @@ -3701,6 +3736,13 @@ const CONST = { 'Meet your setup specialist, {guideName}, who can answer any questions as you get started with Expensify. Yes, a real human!\n' + '\n' + 'Chat with {guideName} in your [admins room]({adminsRoomID}) or [schedule a call]({guideCalendarLink}) today', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Set up categories', @@ -3715,6 +3757,13 @@ const CONST = { '5. Click Add categories to make your own.\n' + '\n' + 'For more controls like requiring a category for every expense, click Settings.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Add expense approvals', @@ -3729,6 +3778,13 @@ const CONST = { '5. In Workflows, enable Add approvals.\n' + '\n' + 'You’ll be set as the expense approver. You can change this to any admin once you invite your team.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Invite your team', @@ -3743,6 +3799,13 @@ const CONST = { '5. Add an invite message if you want.\n' + '\n' + 'That’s it! Happy expensing :)', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, ], }, @@ -3768,6 +3831,13 @@ const CONST = { '4. Click Track.\n' + '\n' + 'And you’re done! Yep, it’s that easy.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, ], }, @@ -3794,6 +3864,13 @@ const CONST = { 'If any of your friends aren’t using Expensify already, they’ll be invited automatically. \n' + '\n' + 'Every chat will also turn into an email or text that they can respond to directly.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Split an expense', @@ -3807,6 +3884,13 @@ const CONST = { '4. Add your friend(s) to the request.\n' + '\n' + 'Feel free to add more details if you want, or just send it off. Let’s get you paid back!', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, { title: 'Enable your wallet', @@ -3819,6 +3903,13 @@ const CONST = { '3. Add your bank account.\n' + '\n' + 'Once that’s done, you can request money from anyone and get paid right into your personal bank account.', + video: { + url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, + thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, + duration: 55, + width: 1280, + height: 960, + }, }, ], }, From e743e38f9291c30cde76c99c4390ca9c89718e58 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 16 Apr 2024 18:09:51 +0700 Subject: [PATCH 124/283] fix lint --- src/pages/home/report/ReportActionsView.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 40307b75f1a1..db6d470ec724 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -144,9 +144,7 @@ function ReportActionsView({ // Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions` const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); - const moneyRequestAction = allReportActions.find((action) => { - return action.reportActionID === transactionThreadReport?.parentReportActionID; - }); + const moneyRequestAction = allReportActions.find((action) => action.reportActionID === transactionThreadReport?.parentReportActionID); // Filter out the money request actions because we don't want to show any preview actions for one-transaction reports const filteredReportActions = [...allReportActions, ...filteredTransactionThreadReportActions].filter((action) => { From 5df95e03a0bf73c9c5238fcde740066204f04bac Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 13:24:08 +0200 Subject: [PATCH 125/283] fix workspace creating --- src/pages/OnboardingWork/BaseOnboardingWork.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index 01ec23c8315c..22ebb57489c2 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -43,7 +43,7 @@ function BaseOnboardingWork({currentUserPersonalDetails, shouldUseNativeStyles, const work = values.work.trim(); - Policy.createWorkspace(currentUserPersonalDetails.login ?? '', true, work); + Policy.createWorkspace(undefined, true, work); Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], { login: currentUserPersonalDetails.login ?? '', From ca821a012f3de8599139f740dfa846b58e77f124 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 13:34:11 +0200 Subject: [PATCH 126/283] integrate video to tasks --- src/libs/actions/Report.ts | 106 +++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 39 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index cdf38af7f3f2..4e2aa729c706 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -118,6 +118,13 @@ type TaskForParameters = createdTaskReportActionID: string; title: string; description: string; + video: { + url: string; + thumbnailUrl: string; + duration: number; + width: number; + height: number; + }; } | { type: 'message'; @@ -3019,60 +3026,80 @@ function completeOnboarding( }, ); const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID); + const taskVideoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID); const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(task.message, undefined, actorAccountID, undefined, false); return { + task, currentTask, taskCreatedAction, taskReportAction, subtitleComment, + taskVideoComment, instructionComment, }; }); - const tasksForParameters = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { - const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; - const subtitleCommentText = subtitleComment.commentText; - const subtitleMessage: TaskMessage = { - reportID: currentTask.reportID, - reportActionID: subtitleCommentAction.reportActionID, - reportComment: subtitleCommentText, - }; + const tasksForParameters = tasksData.reduce( + (acc, {task, currentTask, taskCreatedAction, taskReportAction, subtitleComment, taskVideoComment, instructionComment}) => { + const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; + const subtitleCommentText = subtitleComment.commentText; + const subtitleMessage: TaskMessage = { + reportID: currentTask.reportID, + reportActionID: subtitleCommentAction.reportActionID, + reportComment: subtitleCommentText, + }; - const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; - const instructionCommentText = instructionComment.commentText; - const instructionMessage: TaskMessage = { - reportID: currentTask.reportID, - reportActionID: instructionCommentAction.reportActionID, - reportComment: instructionCommentText, - }; + const taskVideoCommentAction: OptimisticAddCommentReportAction = taskVideoComment.reportAction; + const taskVideoCommentText = instructionComment.commentText; + const taskVideoMessage: TaskMessage = { + reportID: currentTask.reportID, + reportActionID: taskVideoCommentAction.reportActionID, + reportComment: taskVideoCommentText, + }; - return [ - ...acc, - { - type: 'task', - task: engagementChoice, - taskReportID: currentTask.reportID, - parentReportID: currentTask.parentReportID ?? '', - parentReportActionID: taskReportAction.reportAction.reportActionID, - assigneeChatReportID: '', - createdTaskReportActionID: taskCreatedAction.reportActionID, - title: currentTask.reportName ?? '', - description: currentTask.description ?? '', - }, - { - type: 'message', - ...subtitleMessage, - }, - { - type: 'message', - ...instructionMessage, - }, - ]; - }, []); + const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; + const instructionCommentText = instructionComment.commentText; + const instructionMessage: TaskMessage = { + reportID: currentTask.reportID, + reportActionID: instructionCommentAction.reportActionID, + reportComment: instructionCommentText, + }; + + return [ + ...acc, + { + type: 'task', + task: engagementChoice, + taskReportID: currentTask.reportID, + parentReportID: currentTask.parentReportID ?? '', + parentReportActionID: taskReportAction.reportAction.reportActionID, + assigneeChatReportID: '', + createdTaskReportActionID: taskCreatedAction.reportActionID, + title: currentTask.reportName ?? '', + description: currentTask.description ?? '', + video: task.video, + }, + { + type: 'message', + ...taskVideoMessage, + }, + { + type: 'message', + ...subtitleMessage, + }, + { + type: 'message', + ...instructionMessage, + }, + ]; + }, + [], + ); - const tasksForOptimisticData = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, instructionComment}) => { + const tasksForOptimisticData = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, taskVideoComment, instructionComment}) => { const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; + const taskVideoCommentAction: OptimisticAddCommentReportAction = taskVideoComment.reportAction; const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; return [ @@ -3104,6 +3131,7 @@ function completeOnboarding( value: { [taskCreatedAction.reportActionID]: taskCreatedAction as ReportAction, [subtitleCommentAction.reportActionID]: subtitleCommentAction as ReportAction, + [taskVideoCommentAction.reportActionID]: taskVideoCommentAction as ReportAction, [instructionCommentAction.reportActionID]: instructionCommentAction as ReportAction, }, }, From 9591026558a273e022ff1c97c17676af19f59546 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 13:43:55 +0200 Subject: [PATCH 127/283] integrate OnboardingPurposeType type --- src/CONST.ts | 4 +++- src/libs/API/parameters/CompleteGuidedSetupParams.ts | 4 +++- src/libs/actions/Report.ts | 3 ++- src/pages/OnboardingPersonalDetails/types.ts | 3 ++- src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx | 8 +++----- src/pages/OnboardingPurpose/types.ts | 3 ++- src/pages/OnboardingWork/types.ts | 3 ++- src/types/onyx/IntroSelected.ts | 5 ++--- 8 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 039598dd2b8a..b91c1a06a0fb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -66,6 +66,8 @@ const onboardingChoices = { LOOKING_AROUND: 'newDotLookingAround', }; +type OnboardingPurposeType = ValueOf; + const CONST = { MERGED_ACCOUNT_PREFIX: 'MERGED_', DEFAULT_POLICY_ROOM_CHAT_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL], @@ -4673,6 +4675,6 @@ const CONST = { type Country = keyof typeof CONST.ALL_COUNTRIES; type IOUType = ValueOf; -export type {Country, IOUType}; +export type {Country, IOUType, OnboardingPurposeType}; export default CONST; diff --git a/src/libs/API/parameters/CompleteGuidedSetupParams.ts b/src/libs/API/parameters/CompleteGuidedSetupParams.ts index 6835949382ca..e3a0309d5113 100644 --- a/src/libs/API/parameters/CompleteGuidedSetupParams.ts +++ b/src/libs/API/parameters/CompleteGuidedSetupParams.ts @@ -1,8 +1,10 @@ +import type {OnboardingPurposeType} from '@src/CONST'; + type CompleteGuidedSetupParams = { firstName: string; lastName: string; guidedSetupData: string; - engagementChoice: string; + engagementChoice: OnboardingPurposeType; }; export default CompleteGuidedSetupParams; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4e2aa729c706..e05dceb9fead 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -72,6 +72,7 @@ import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; import * as UserUtils from '@libs/UserUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; +import type {OnboardingPurposeType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -3190,7 +3191,7 @@ function completeOnboarding( * - Sets the introSelected NVP to the choice the user made * - Creates an optimistic report comment from concierge */ -function completeEngagementModal(text: string, choice: ValueOf) { +function completeEngagementModal(text: string, choice: OnboardingPurposeType) { const conciergeAccountID = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE])[0]; const reportComment = ReportUtils.buildOptimisticAddCommentReportAction(text, undefined, conciergeAccountID); const reportCommentAction: OptimisticAddCommentReportAction = reportComment.reportAction; diff --git a/src/pages/OnboardingPersonalDetails/types.ts b/src/pages/OnboardingPersonalDetails/types.ts index 6d286373a1ac..4828cc6e73bd 100644 --- a/src/pages/OnboardingPersonalDetails/types.ts +++ b/src/pages/OnboardingPersonalDetails/types.ts @@ -1,11 +1,12 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import type {OnboardingPurposeType} from '@src/CONST'; type OnboardingPersonalDetailsProps = Record; type BaseOnboardingPersonalDetailsOnyxProps = { /** Saved onboarding purpose selected by the user */ - onboardingPurposeSelected: OnyxEntry; + onboardingPurposeSelected: OnyxEntry; }; type BaseOnboardingPersonalDetailsProps = WithCurrentUserPersonalDetailsProps & diff --git a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx index 8ccbda809d97..a6aec605a8f0 100644 --- a/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx +++ b/src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx @@ -21,14 +21,12 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import * as Welcome from '@userActions/Welcome'; +import type {OnboardingPurposeType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {BaseOnboardingPurposeOnyxProps, BaseOnboardingPurposeProps} from './types'; -type ValuesType = T[keyof T]; -type SelectedPurposeType = ValuesType | undefined; - const menuIcons = { [CONST.ONBOARDING_CHOICES.TRACK]: Illustrations.CompanyCard, [CONST.ONBOARDING_CHOICES.EMPLOYER]: Illustrations.ReceiptUpload, @@ -42,7 +40,7 @@ function BaseOnboardingPurpose({shouldUseNativeStyles, shouldEnableMaxHeight, on const styles = useThemeStyles(); const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useOnboardingLayout(); - const [selectedPurpose, setSelectedPurpose] = useState(undefined); + const [selectedPurpose, setSelectedPurpose] = useState(undefined); const {isSmallScreenWidth, windowHeight} = useWindowDimensions(); const [error, setError] = useState(false); const theme = useTheme(); @@ -155,4 +153,4 @@ export default withOnyx; type BaseOnboardingPurposeOnyxProps = { /** Saved onboarding purpose selected by the user */ - onboardingPurposeSelected: OnyxEntry; + onboardingPurposeSelected: OnyxEntry; }; type BaseOnboardingPurposeProps = OnboardingPurposeProps & diff --git a/src/pages/OnboardingWork/types.ts b/src/pages/OnboardingWork/types.ts index 930a8081e9c4..5bef8048628d 100644 --- a/src/pages/OnboardingWork/types.ts +++ b/src/pages/OnboardingWork/types.ts @@ -1,11 +1,12 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import type {OnboardingPurposeType} from '@src/CONST'; type OnboardingWorkProps = Record; type BaseOnboardingWorkOnyxProps = { /** Saved onboarding purpose selected by the user */ - onboardingPurposeSelected: OnyxEntry; + onboardingPurposeSelected: OnyxEntry; }; type BaseOnboardingWorkProps = WithCurrentUserPersonalDetailsProps & diff --git a/src/types/onyx/IntroSelected.ts b/src/types/onyx/IntroSelected.ts index 9917f4b44550..14a0d2f70dfe 100644 --- a/src/types/onyx/IntroSelected.ts +++ b/src/types/onyx/IntroSelected.ts @@ -1,9 +1,8 @@ -import type {ValueOf} from 'type-fest'; -import type CONST from '@src/CONST'; +import type {OnboardingPurposeType} from '@src/CONST'; type IntroSelected = { /** The choice that the user selected in the engagement modal */ - choice: ValueOf; + choice: OnboardingPurposeType; }; export default IntroSelected; From d87069dc843ebf65a0e2f7dca51fedf45d590366 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 13:48:19 +0200 Subject: [PATCH 128/283] integrate GuidedSetupData type --- src/libs/actions/Report.ts | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e05dceb9fead..cea442efecf7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -108,6 +108,14 @@ type ActionSubscriber = { callback: SubscriberCallback; }; +type Video = { + url: string; + thumbnailUrl: string; + duration: number; + width: number; + height: number; +}; + type TaskForParameters = | { type: 'task'; @@ -119,13 +127,7 @@ type TaskForParameters = createdTaskReportActionID: string; title: string; description: string; - video: { - url: string; - thumbnailUrl: string; - duration: number; - width: number; - height: number; - }; + video: Video; } | { type: 'message'; @@ -136,6 +138,15 @@ type TaskForParameters = type TaskMessage = Required>; +type GuidedSetupData = Array< + | AddCommentOrAttachementParams + | TaskForParameters + | ({ + type: 'video'; + } & Video & + AddCommentOrAttachementParams) +>; + let conciergeChatReportID: string | undefined; let currentUserAccountID = -1; let currentUserEmail: string | undefined; @@ -3175,11 +3186,18 @@ function completeOnboarding( }, ]; + const guidedSetupData: GuidedSetupData = [ + {type: 'message', ...mentionMessage}, + {type: 'message', ...textMessage}, + {type: 'video', ...data.video, ...videoMessage}, + ...tasksForParameters, + ]; + const parameters: CompleteGuidedSetupParams = { engagementChoice, firstName, lastName, - guidedSetupData: JSON.stringify([{type: 'message', ...mentionMessage}, {type: 'message', ...textMessage}, {type: 'video', ...data.video, ...videoMessage}, ...tasksForParameters]), + guidedSetupData: JSON.stringify(guidedSetupData), }; API.write(WRITE_COMMANDS.COMPLETE_GUIDED_SETUP, parameters, {optimisticData, successData}); From e4a4fa646d28ac9d3a60e83aba05220f1011a702 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 14:04:30 +0200 Subject: [PATCH 129/283] use OnboardingPurposeType --- src/libs/actions/Welcome.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index 87762bc856ca..84066c05e80e 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -1,6 +1,6 @@ import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import type {SelectedPurposeType} from '@pages/OnboardingPurpose/BaseOnboardingPurpose'; +import type {OnboardingPurposeType} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type OnyxPolicy from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -112,7 +112,7 @@ function getPersonalDetails(accountID: number | undefined) { }); } -function setOnboardingPurposeSelected(value: SelectedPurposeType) { +function setOnboardingPurposeSelected(value: OnboardingPurposeType) { Onyx.set(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED, value ?? null); } From ef7f8c6d779cb557db08b9e65d488672786b60f5 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 14:31:20 +0200 Subject: [PATCH 130/283] change progress bar values --- .../OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx | 2 +- src/pages/OnboardingPurpose/BaseOnboardingPurpose.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 5657f8553113..ee9693a950fd 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -120,7 +120,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat From 3c6e51247af49f731c7196ebd747fbc0a60b145a Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 14:54:56 +0200 Subject: [PATCH 131/283] fix task actions ordering --- src/libs/actions/Report.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index cea442efecf7..9921816a3e5c 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3038,8 +3038,8 @@ function completeOnboarding( }, ); const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID); - const taskVideoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID); - const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(task.message, undefined, actorAccountID, undefined, false); + const taskVideoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 1); + const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(task.message, undefined, actorAccountID, 2, false); return { task, From 6eb2c290b7f7e42a43816add805784d35d7e2b40 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 15:54:46 +0200 Subject: [PATCH 132/283] integrate addition options --- src/CONST.ts | 14 +-- src/libs/actions/Report.ts | 114 ++++++++++++------ .../OnboardingWork/BaseOnboardingWork.tsx | 19 +-- 3 files changed, 96 insertions(+), 51 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index b91c1a06a0fb..e6675916f15d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3734,17 +3734,11 @@ const CONST = { { title: 'Meet your setup specialist', subtitle: '', - message: - 'Meet your setup specialist, {guideName}, who can answer any questions as you get started with Expensify. Yes, a real human!\n' + + message: ({adminsRoomLink, guideCalendarLink}: {adminsRoomLink: string; guideCalendarLink: string}) => + `Meet your setup specialist, who can answer any questions as you get started with Expensify. Yes, a real human!\n` + '\n' + - 'Chat with {guideName} in your [admins room]({adminsRoomID}) or [schedule a call]({guideCalendarLink}) today', - video: { - url: `${CLOUDFRONT_URL}/videos/intro-1280.mp4`, - thumbnailUrl: `${CLOUDFRONT_URL}/images/expensify__favicon.png`, - duration: 55, - width: 1280, - height: 960, - }, + `Chat with the specialist in your [#admins room](${adminsRoomLink}) or [schedule a call](${guideCalendarLink}) today.`, + video: null, }, { title: 'Set up categories', diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9921816a3e5c..03636d869ed6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -127,7 +127,7 @@ type TaskForParameters = createdTaskReportActionID: string; title: string; description: string; - video: Video; + video?: Video; } | { type: 'message'; @@ -163,6 +163,14 @@ Onyx.connect({ }, }); +let guideCalendarLink: string | undefined; +Onyx.connect({ + key: ONYXKEYS.ACCOUNT, + callback: (value) => { + guideCalendarLink = value?.guideCalendarLink ?? ''; + }, +}); + let preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE; Onyx.connect({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, @@ -2978,6 +2986,7 @@ function completeOnboarding( firstName: string; lastName: string; }, + adminsChatReportID?: string, ) { const targetEmail = CONST.EMAIL.CONCIERGE; const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail])[0]; @@ -3037,9 +3046,16 @@ function completeOnboarding( childOldestFourAccountIDs: `${actorAccountID}`, }, ); - const subtitleComment = ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID); - const taskVideoComment = ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 1); - const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(task.message, undefined, actorAccountID, 2, false); + const subtitleComment = task.subtitle ? ReportUtils.buildOptimisticAddCommentReportAction(task.subtitle, undefined, actorAccountID) : null; + const taskVideoComment = task.video ? ReportUtils.buildOptimisticAddCommentReportAction(CONST.ATTACHMENT_MESSAGE_TEXT, undefined, actorAccountID, 1) : null; + const taskMessage = + typeof task.message === 'function' + ? task.message({ + adminsRoomLink: `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.REPORT_WITH_ID.getRoute(adminsChatReportID ?? '')}`, + guideCalendarLink: guideCalendarLink ?? CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL, + }) + : task.message; + const instructionComment = ReportUtils.buildOptimisticAddCommentReportAction(taskMessage, undefined, actorAccountID, 2, false); return { task, @@ -3054,22 +3070,6 @@ function completeOnboarding( const tasksForParameters = tasksData.reduce( (acc, {task, currentTask, taskCreatedAction, taskReportAction, subtitleComment, taskVideoComment, instructionComment}) => { - const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; - const subtitleCommentText = subtitleComment.commentText; - const subtitleMessage: TaskMessage = { - reportID: currentTask.reportID, - reportActionID: subtitleCommentAction.reportActionID, - reportComment: subtitleCommentText, - }; - - const taskVideoCommentAction: OptimisticAddCommentReportAction = taskVideoComment.reportAction; - const taskVideoCommentText = instructionComment.commentText; - const taskVideoMessage: TaskMessage = { - reportID: currentTask.reportID, - reportActionID: taskVideoCommentAction.reportActionID, - reportComment: taskVideoCommentText, - }; - const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; const instructionCommentText = instructionComment.commentText; const instructionMessage: TaskMessage = { @@ -3078,7 +3078,7 @@ function completeOnboarding( reportComment: instructionCommentText, }; - return [ + const tasksForParametersAcc: TaskForParameters[] = [ ...acc, { type: 'task', @@ -3090,31 +3090,53 @@ function completeOnboarding( createdTaskReportActionID: taskCreatedAction.reportActionID, title: currentTask.reportName ?? '', description: currentTask.description ?? '', - video: task.video, + video: task.video ?? undefined, }, { type: 'message', - ...taskVideoMessage, + ...instructionMessage, }, - { + ]; + + if (subtitleComment) { + const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; + const subtitleCommentText = subtitleComment.commentText; + const subtitleMessage: TaskMessage = { + reportID: currentTask.reportID, + reportActionID: subtitleCommentAction.reportActionID, + reportComment: subtitleCommentText, + }; + + tasksForParametersAcc.push({ type: 'message', ...subtitleMessage, - }, - { + }); + } + + if (taskVideoComment) { + const taskVideoCommentAction: OptimisticAddCommentReportAction = taskVideoComment.reportAction; + const taskVideoCommentText = instructionComment.commentText; + const taskVideoMessage: TaskMessage = { + reportID: currentTask.reportID, + reportActionID: taskVideoCommentAction.reportActionID, + reportComment: taskVideoCommentText, + }; + + tasksForParametersAcc.push({ type: 'message', - ...instructionMessage, - }, - ]; + ...taskVideoMessage, + }); + } + + return tasksForParametersAcc; }, [], ); const tasksForOptimisticData = tasksData.reduce((acc, {currentTask, taskCreatedAction, taskReportAction, subtitleComment, taskVideoComment, instructionComment}) => { - const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; - const taskVideoCommentAction: OptimisticAddCommentReportAction = taskVideoComment.reportAction; const instructionCommentAction: OptimisticAddCommentReportAction = instructionComment.reportAction; - return [ + const tasksForOptimisticDataAcc: OnyxUpdate[] = [ ...acc, { onyxMethod: Onyx.METHOD.MERGE, @@ -3142,12 +3164,36 @@ function completeOnboarding( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentTask.reportID}`, value: { [taskCreatedAction.reportActionID]: taskCreatedAction as ReportAction, - [subtitleCommentAction.reportActionID]: subtitleCommentAction as ReportAction, - [taskVideoCommentAction.reportActionID]: taskVideoCommentAction as ReportAction, [instructionCommentAction.reportActionID]: instructionCommentAction as ReportAction, }, }, ]; + + if (subtitleComment) { + const subtitleCommentAction: OptimisticAddCommentReportAction = subtitleComment.reportAction; + + tasksForOptimisticDataAcc.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentTask.reportID}`, + value: { + [subtitleCommentAction.reportActionID]: subtitleCommentAction as ReportAction, + }, + }); + } + + if (taskVideoComment) { + const taskVideoCommentAction: OptimisticAddCommentReportAction = taskVideoComment.reportAction; + + tasksForOptimisticDataAcc.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentTask.reportID}`, + value: { + [taskVideoCommentAction.reportActionID]: taskVideoCommentAction as ReportAction, + }, + }); + } + + return tasksForOptimisticDataAcc; }, []); const optimisticData: OnyxUpdate[] = [ diff --git a/src/pages/OnboardingWork/BaseOnboardingWork.tsx b/src/pages/OnboardingWork/BaseOnboardingWork.tsx index 22ebb57489c2..81be2bcc6ac5 100644 --- a/src/pages/OnboardingWork/BaseOnboardingWork.tsx +++ b/src/pages/OnboardingWork/BaseOnboardingWork.tsx @@ -43,13 +43,18 @@ function BaseOnboardingWork({currentUserPersonalDetails, shouldUseNativeStyles, const work = values.work.trim(); - Policy.createWorkspace(undefined, true, work); - - Report.completeOnboarding(onboardingPurposeSelected, CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], { - login: currentUserPersonalDetails.login ?? '', - firstName: currentUserPersonalDetails.firstName ?? '', - lastName: currentUserPersonalDetails.lastName ?? '', - }); + const adminsChatReportID = Policy.createWorkspace(undefined, true, work); + + Report.completeOnboarding( + onboardingPurposeSelected, + CONST.ONBOARDING_MESSAGES[onboardingPurposeSelected], + { + login: currentUserPersonalDetails.login ?? '', + firstName: currentUserPersonalDetails.firstName ?? '', + lastName: currentUserPersonalDetails.lastName ?? '', + }, + adminsChatReportID, + ); Navigation.dismissModal(); // Only navigate to concierge chat when central pane is visible From 820360ffa66ef70375250f73f1e8bf0d36d2735f Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 16 Apr 2024 15:56:46 +0200 Subject: [PATCH 133/283] fix default value of guideCalendarLink --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 03636d869ed6..c08e4928eea1 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -167,7 +167,7 @@ let guideCalendarLink: string | undefined; Onyx.connect({ key: ONYXKEYS.ACCOUNT, callback: (value) => { - guideCalendarLink = value?.guideCalendarLink ?? ''; + guideCalendarLink = value?.guideCalendarLink ?? undefined; }, }); From 28948abcf110c4edc6a616f1df87c323d7a3dc15 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 16 Apr 2024 18:30:15 +0200 Subject: [PATCH 134/283] fix: apply requested changes --- src/libs/DistanceRequestUtils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index d35d7b0d711c..6f82ec155c2d 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -71,9 +71,6 @@ function getDefaultMileageRate(policy: OnyxEntry | EmptyObject): Mileage } const distanceRate = Object.values(distanceUnit.rates).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE) ?? Object.values(distanceUnit.rates)[0]; - if (!distanceRate) { - return null; - } return { customUnitRateID: distanceRate.customUnitRateID, @@ -131,7 +128,7 @@ function getRateForDisplay( toLocaleDigit: LocaleContextProps['toLocaleDigit'], isOffline?: boolean, ): string { - if (isOffline) { + if (isOffline && !rate) { return translate('iou.defaultRate'); } if (!hasRoute || !rate || !currency || !unit) { From 6bcd2570348c1f220c7e00888193ccd5bc09c6b2 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 16 Apr 2024 18:55:14 +0200 Subject: [PATCH 135/283] fix: check for quantity instead of route --- src/components/MoneyRequestConfirmationList.tsx | 2 +- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- src/libs/TransactionUtils.ts | 4 ++-- src/pages/iou/request/step/IOURequestStepDistance.tsx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index aaf86a052960..035bd440e256 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -264,7 +264,7 @@ function MoneyRequestConfirmationList({ // A flag for showing the billable field const shouldShowBillable = !(policy?.disabledFields?.defaultBillable ?? true); - const hasRoute = TransactionUtils.hasRoute(transaction); + const hasRoute = TransactionUtils.hasRoute(transaction, isDistanceRequest); const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate); const formattedAmount = isDistanceRequestWithPendingRoute ? '' diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 66ba8b78a735..e43a2db22ec0 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -277,7 +277,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // A flag for showing the billable field const shouldShowBillable = policy?.disabledFields?.defaultBillable === false; - const hasRoute = TransactionUtils.hasRoute(transaction); + const hasRoute = TransactionUtils.hasRoute(transaction, isDistanceRequest); const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate); const formattedAmount = isDistanceRequestWithPendingRoute ? '' diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 74d04c8807e1..2e148292c0d7 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -178,7 +178,7 @@ function MoneyRequestView({ let amountDescription = `${translate('iou.amount')}`; - const hasRoute = TransactionUtils.hasRoute(transaction); + const hasRoute = TransactionUtils.hasRoute(transaction, isDistanceRequest); const rateID = transaction?.comment.customUnit?.customUnitRateID ?? '0'; const currency = policy ? policy.outputCurrency : PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 39edac4d5c17..a54ec416cad0 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -517,8 +517,8 @@ function hasMissingSmartscanFields(transaction: OnyxEntry): boolean /** * Check if the transaction has a defined route */ -function hasRoute(transaction: OnyxEntry): boolean { - return !!transaction?.routes?.route0?.geometry?.coordinates; +function hasRoute(transaction: OnyxEntry, isDistanceRequestType: boolean): boolean { + return isDistanceRequestType && !!transaction?.comment?.customUnit?.quantity; } function getAllReportTransactions(reportID?: string): Transaction[] { diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index fae07ad2249c..45da24c55c5c 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -67,7 +67,7 @@ function IOURequestStepDistance({ const isLoadingRoute = transaction?.comment?.isLoading ?? false; const isLoading = transaction?.isLoading ?? false; const hasRouteError = !!transaction?.errorFields?.route; - const hasRoute = TransactionUtils.hasRoute(transaction); + const hasRoute = TransactionUtils.hasRoute(transaction, true); const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); const previousValidatedWaypoints = usePrevious(validatedWaypoints); const haveValidatedWaypointsChanged = !isEqual(previousValidatedWaypoints, validatedWaypoints); From 520cf1f022739c66476f775d2416e7082c719658 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 16 Apr 2024 19:39:07 +0200 Subject: [PATCH 136/283] fix: minor fix --- src/libs/TransactionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index a54ec416cad0..806c879a3709 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -518,7 +518,7 @@ function hasMissingSmartscanFields(transaction: OnyxEntry): boolean * Check if the transaction has a defined route */ function hasRoute(transaction: OnyxEntry, isDistanceRequestType: boolean): boolean { - return isDistanceRequestType && !!transaction?.comment?.customUnit?.quantity; + return !!transaction?.routes?.route0?.geometry?.coordinates || (isDistanceRequestType && !!transaction?.comment?.customUnit?.quantity); } function getAllReportTransactions(reportID?: string): Transaction[] { From 706ba6fc51b70a6087dbf583a98c4409c8cef7c1 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 16 Apr 2024 20:47:03 +0200 Subject: [PATCH 137/283] fix: resolve conflicts --- ...raryForRefactorRequestConfirmationList.tsx | 72 +++++++++++-------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 5abb602df995..a8c1c22093ab 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -709,9 +709,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ title={iouComment} description={translate('common.description')} onPress={() => { - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), - ); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams())); }} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} @@ -732,17 +730,49 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.distance')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), - ) - } + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing disabled={didConfirm} // todo: handle edit for transaction while moving from track expense interactive={!isReadOnly && !isMovingTransactionFromTrackExpense} /> ), + shouldShow: isDistanceRequest && !canUseP2PDistanceRequests, + isSupplementary: false, + }, + { + item: ( + + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.CREATE, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams())) + } + disabled={didConfirm} + interactive={!isReadOnly} + /> + ), + shouldShow: isDistanceRequest && canUseP2PDistanceRequests, + isSupplementary: false, + }, + { + item: ( + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + disabled={didConfirm} + interactive={Boolean(rate) && !isReadOnly && isPolicyExpenseChat} + /> + ), shouldShow: isDistanceRequest, isSupplementary: true, }, @@ -798,9 +828,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ title={iouCategory} description={translate('common.category')} numberOfLinesTitle={2} - onPress={() => - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams())) - } + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} disabled={didConfirm} @@ -821,11 +849,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ title={TransactionUtils.getTagForDisplay(transaction, index)} description={name} numberOfLinesTitle={2} - onPress={() => - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(action, iouType, index, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), - ) - } + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAG.getRoute(action, iouType, index, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} style={[styles.moneyRequestMenuItem]} disabled={didConfirm} interactive={!isReadOnly} @@ -845,9 +869,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={taxRates?.name} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams())) - } + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} disabled={didConfirm} interactive={!isReadOnly} /> @@ -864,11 +886,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('iou.taxAmount')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), - ) - } + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} disabled={didConfirm} interactive={!isReadOnly} /> @@ -970,11 +988,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ !isDistanceRequest && iouType === CONST.IOU.TYPE.REQUEST && ( - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()), - ) - } + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} /> ))} {primaryFields} From fb59ef104cc108b2ad99cf66234c7b2958a95f69 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 16 Apr 2024 20:52:10 +0200 Subject: [PATCH 138/283] fix: navigation fix --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index a8c1c22093ab..14a061b59c21 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -768,7 +768,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ description={translate('common.rate')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE_RATE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()))} disabled={didConfirm} interactive={Boolean(rate) && !isReadOnly && isPolicyExpenseChat} /> From 2955ee9aae1d449fbf3260e7ad66dd76c07cc4f1 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 16 Apr 2024 22:20:40 +0200 Subject: [PATCH 139/283] fix: translation --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 9f0022c41ff0..2450ce6a3c7a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -635,7 +635,7 @@ export default { canceled: 'Canceled', posted: 'Posted', deleteReceipt: 'Delete receipt', - routePending: 'Route pending...', + routePending: 'Pending...', defaultRate: 'Default rate', receiptScanning: 'Scan in progress…', receiptMissingDetails: 'Receipt missing details', diff --git a/src/languages/es.ts b/src/languages/es.ts index a0cedeb5fd82..ee4b20479ee7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -631,7 +631,7 @@ export default { canceled: 'Canceló', posted: 'Contabilizado', deleteReceipt: 'Eliminar recibo', - routePending: 'Ruta pendiente...', + routePending: 'Pendiente...', defaultRate: 'Tasa predeterminada', receiptScanning: 'Escaneo en curso…', receiptMissingDetails: 'Recibo con campos vacíos', From 068f3abceefcb559685c3175e29b6fec5814f8d3 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 17 Apr 2024 12:12:27 +0700 Subject: [PATCH 140/283] fix bump enpensify-common --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 478ae3c12b3c..46d9e3c8aef2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", @@ -20212,8 +20212,8 @@ }, "node_modules/expensify-common": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e", - "integrity": "sha512-/NAZoAXqeqFWHvC61dueqq9VjRugF69urUtDdDhsfvu1sQE2PCnBoM7a+ACoAEWRYrnP82cyHHhdSA8e7fPuAg==", + "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", + "integrity": "sha512-zz0/y0apISP1orxXEQOgn+Uod45O4wVypwwtaqcDPV4dH1tC3i4L98NoLSZvLn7Y17EcceSkfN6QCEsscgFTDQ==", "license": "MIT", "dependencies": { "classnames": "2.5.0", diff --git a/package.json b/package.json index e5092e132eae..83b140cd23f5 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "date-fns-tz": "^2.0.0", "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", - "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e", + "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219", "expo": "^50.0.3", "expo-av": "~13.10.4", "expo-image": "1.11.0", From 80efdea3e2f6766123ef518ac1372c45c2dc6342 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 17 Apr 2024 12:38:20 +0700 Subject: [PATCH 141/283] Display violation when tax rate is invalid --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 ++ src/libs/Permissions.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 73fc7e9bae6e..6d4a0bd7c308 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -451,6 +451,8 @@ function MoneyRequestView({ ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(CONST.IOU.ACTION.EDIT, CONST.IOU.TYPE.REQUEST, transaction?.transactionID ?? '', report.reportID), ) } + brickRoadIndicator={getErrorForField('tax') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + error={getErrorForField('tax')} /> )} diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 105736faeba0..80916a1c7bb2 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -4,6 +4,7 @@ import type {IOUType} from '@src/CONST'; import type Beta from '@src/types/onyx/Beta'; function canUseAllBetas(betas: OnyxEntry): boolean { + return true; return !!betas?.includes(CONST.BETAS.ALL); } From f6ef05fdd6160654ee814967ae9aecc5471efbc6 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 17 Apr 2024 11:26:26 +0200 Subject: [PATCH 142/283] fix: minor fix --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index bbcb1dea5365..4d536a13a619 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -253,7 +253,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const prevRate = usePrevious(rate); const shouldCalculateDistanceAmount = isDistanceRequest && (iouAmount === 0 || prevRate !== rate); - const currency = (mileageRate as MileageRate)?.currency ?? policy?.outputCurrency ?? CONST.CURRENCY.USD; + const currency = (mileageRate as MileageRate)?.currency ?? policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; const distance = transaction?.routes?.route0?.distance ?? 0; const taxRates = policy?.taxRates ?? null; From a73490b51c6bccf48b43df791bfb8c4188d3d9c6 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 17 Apr 2024 11:41:17 +0200 Subject: [PATCH 143/283] fix: currency and rate for p2p --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 4d536a13a619..6a5a1ad8104f 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -244,8 +244,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ } }, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, canUseP2PDistanceRequests, transactionID]); + const policyCurrency = policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; + const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) - ? DistanceRequestUtils.getRateForP2P(policy?.outputCurrency ?? CONST.CURRENCY.USD) + ? DistanceRequestUtils.getRateForP2P(policyCurrency) : mileageRates?.[customUnitRateID] ?? DistanceRequestUtils.getDefaultMileageRate(policy); const {unit, rate} = mileageRate ?? {}; @@ -253,7 +255,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const prevRate = usePrevious(rate); const shouldCalculateDistanceAmount = isDistanceRequest && (iouAmount === 0 || prevRate !== rate); - const currency = (mileageRate as MileageRate)?.currency ?? policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; + const currency = (mileageRate as MileageRate).currency ?? policyCurrency; const distance = transaction?.routes?.route0?.distance ?? 0; const taxRates = policy?.taxRates ?? null; From 2e7d5c072cd802d2b92bb9d9519b4730d1b3be29 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 17 Apr 2024 15:30:05 +0200 Subject: [PATCH 144/283] fix task video assigning --- src/libs/actions/Report.ts | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e95132418068..9530fe40d1e7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -116,6 +116,8 @@ type Video = { height: number; }; +type TaskMessage = Required>; + type TaskForParameters = | { type: 'task'; @@ -127,25 +129,16 @@ type TaskForParameters = createdTaskReportActionID: string; title: string; description: string; - video?: Video; } - | { + | ({ type: 'message'; - reportID: string; - reportActionID: string; - reportComment: string; - }; - -type TaskMessage = Required>; - -type GuidedSetupData = Array< - | AddCommentOrAttachementParams - | TaskForParameters + } & TaskMessage) | ({ type: 'video'; - } & Video & - AddCommentOrAttachementParams) ->; + } & TaskMessage & + Video); + +type GuidedSetupData = Array; let conciergeChatReportID: string | undefined; let currentUserAccountID = -1; @@ -3095,7 +3088,6 @@ function completeOnboarding( createdTaskReportActionID: taskCreatedAction.reportActionID, title: currentTask.reportName ?? '', description: currentTask.description ?? '', - video: task.video ?? undefined, }, { type: 'message', @@ -3118,9 +3110,9 @@ function completeOnboarding( }); } - if (taskVideoComment) { + if (taskVideoComment && task.video) { const taskVideoCommentAction: OptimisticAddCommentReportAction = taskVideoComment.reportAction; - const taskVideoCommentText = instructionComment.commentText; + const taskVideoCommentText = taskVideoComment.commentText; const taskVideoMessage: TaskMessage = { reportID: currentTask.reportID, reportActionID: taskVideoCommentAction.reportActionID, @@ -3128,7 +3120,8 @@ function completeOnboarding( }; tasksForParametersAcc.push({ - type: 'message', + type: 'video', + ...task.video, ...taskVideoMessage, }); } From cb102de3d9c50658c3f52af431eb1e23f7dd4f14 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 17 Apr 2024 15:40:12 +0200 Subject: [PATCH 145/283] minor improvement --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e89b3048d8cb..82e4eab5c2df 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2973,7 +2973,7 @@ function getReportPrivateNote(reportID: string | undefined) { } function completeOnboarding( - engagementChoice: string, + engagementChoice: OnboardingPurposeType, data: ValueOf, { login, From d6d6f96144378e98b3ea9b6bc19c946c0a44ee32 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 17 Apr 2024 18:10:31 +0200 Subject: [PATCH 146/283] fix: remove hasRoute condition from getRateForDisplay --- .../MoneyTemporaryForRefactorRequestConfirmationList.tsx | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- src/libs/DistanceRequestUtils.ts | 6 ++---- src/pages/iou/request/step/IOURequestStepDistanceRate.tsx | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx index 6a5a1ad8104f..ce50eb949574 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.tsx @@ -768,7 +768,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ { - const rateForDisplay = DistanceRequestUtils.getRateForDisplay(true, rate.unit, rate.rate, rate.currency, translate, toLocaleDigit); + const rateForDisplay = DistanceRequestUtils.getRateForDisplay(rate.unit, rate.rate, rate.currency, translate, toLocaleDigit); return { text: rate.name ?? rateForDisplay, From 56ec29db4de7b9853e5da10e8a99ac17e73e9b03 Mon Sep 17 00:00:00 2001 From: Getabalew Date: Wed, 17 Apr 2024 21:38:38 +0300 Subject: [PATCH 147/283] refactor: remove redundant route code --- ...yForRefactorRequestParticipantsSelector.js | 48 +++---------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 3c65f0fa9a96..a867d092178c 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -7,11 +7,9 @@ import Button from '@components/Button'; import FormHelpMessage from '@components/FormHelpMessage'; import {usePersonalDetails} from '@components/OnyxProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; -import {PressableWithFeedback} from '@components/Pressable'; import ReferralProgramCTA from '@components/ReferralProgramCTA'; -import SelectCircle from '@components/SelectCircle'; import SelectionList from '@components/SelectionList'; -import UserListItem from '@components/SelectionList/UserListItem'; +import InviteMemberListItem from '@components/SelectionList/InviteMemberListItem'; import useDebouncedState from '@hooks/useDebouncedState'; import useDismissedReferralBanners from '@hooks/useDismissedReferralBanners'; import useLocalize from '@hooks/useLocalize'; @@ -76,6 +74,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({betas, participan const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; + const isIOUSplit = iouType === CONST.IOU.TYPE.SPLIT; + useEffect(() => { Report.searchInServer(debouncedSearchTerm.trim()); }, [debouncedSearchTerm]); @@ -296,7 +296,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({betas, participan {!!participants.length && (