From e90e0085e5e300fd841c6cf4cfafd7d56b71f288 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 29 Jan 2024 16:04:33 +0700 Subject: [PATCH 001/114] remove NewDistanceRequestPage and EditRequestDistancePage --- src/ROUTES.ts | 10 +- .../MoneyRequestConfirmationList.js | 12 -- ...oraryForRefactorRequestConfirmationList.js | 10 +- .../ReportActionItem/MoneyRequestView.js | 12 +- .../AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/linkingConfig.ts | 1 - src/libs/Navigation/types.ts | 5 +- src/pages/EditRequestDistancePage.js | 122 ------------------ src/pages/EditRequestPage.js | 11 -- src/pages/iou/MoneyRequestSelectorPage.js | 12 -- src/pages/iou/NewDistanceRequestPage.js | 85 ------------ 11 files changed, 27 insertions(+), 254 deletions(-) delete mode 100644 src/pages/EditRequestDistancePage.js delete mode 100644 src/pages/iou/NewDistanceRequestPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index deabdc0ac853..b985f993367a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -295,10 +295,6 @@ const ROUTES = { route: ':iouType/new/receipt/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/receipt/${reportID}` as const, }, - MONEY_REQUEST_DISTANCE: { - route: ':iouType/new/address/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/address/${reportID}` as const, - }, MONEY_REQUEST_DISTANCE_TAB: { route: ':iouType/new/:reportID?/distance', getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance` as const, @@ -350,9 +346,9 @@ const ROUTES = { getUrlWithBackToParam(`create/${iouType}/description/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_DISTANCE: { - route: 'create/:iouType/distance/:transactionID/:reportID', - getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}`, backTo), + route: ':action/:iouType/distance/:transactionID/:reportID', + getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action}/${iouType}/distance/${transactionID}/${reportID}`, backTo), }, MONEY_REQUEST_STEP_MERCHANT: { route: 'create/:iouType/merchant/:transactionID/:reportID', diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index d967d04ab94b..101e135d36e1 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -703,18 +703,6 @@ function MoneyRequestConfirmationList(props) { error={shouldDisplayFieldError && TransactionUtils.isCreatedMissing(transaction) ? translate('common.error.enterDate') : ''} /> )} - {props.isDistanceRequest && ( - Navigation.navigate(ROUTES.MONEY_REQUEST_DISTANCE.getRoute(props.iouType, props.reportID))} - disabled={didConfirm || !isTypeRequest} - interactive={!props.isReadOnly} - /> - )} {shouldShowMerchant && ( - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute( + CONST.IOU.ACTION.CREATE, + CONST.IOU.TYPE.REQUEST, + transaction.transactionID, + reportID, + Navigation.getActiveRouteWithoutParams(), + ), + ) } disabled={didConfirm || !isTypeRequest} interactive={!isReadOnly} diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 3121328138ee..6cc2119a9044 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -301,7 +301,17 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate 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, + Navigation.getActiveRouteWithoutParams(), + ), + ) + } /> ) : ( diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 3a843e400409..9c2361916934 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -103,7 +103,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/settings/Wallet/AddDebitCardPage').default as React.ComponentType, [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: () => require('../../../pages/EnablePayments/EnablePaymentsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.WAYPOINT]: () => require('../../../pages/iou/MoneyRequestWaypointPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.DISTANCE]: () => require('../../../pages/iou/NewDistanceRequestPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.RECEIPT]: () => require('../../../pages/EditRequestReceiptPage').default as React.ComponentType, }); diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index d4e04d5402e2..715b14c4cb90 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -435,7 +435,6 @@ const linkingConfig: LinkingOptions = { [SCREENS.MONEY_REQUEST.TAG]: ROUTES.MONEY_REQUEST_TAG.route, [SCREENS.MONEY_REQUEST.MERCHANT]: ROUTES.MONEY_REQUEST_MERCHANT.route, [SCREENS.MONEY_REQUEST.RECEIPT]: ROUTES.MONEY_REQUEST_RECEIPT.route, - [SCREENS.MONEY_REQUEST.DISTANCE]: ROUTES.MONEY_REQUEST_DISTANCE.route, [SCREENS.IOU_SEND.ENABLE_PAYMENTS]: ROUTES.IOU_SEND_ENABLE_PAYMENTS, [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, [SCREENS.IOU_SEND.ADD_DEBIT_CARD]: ROUTES.IOU_SEND_ADD_DEBIT_CARD, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index b4a77f96cc74..ea2b48df4ed2 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -238,9 +238,12 @@ type MoneyRequestNavigatorParamList = { waypointIndex: string; threadReportID: number; }; - [SCREENS.MONEY_REQUEST.DISTANCE]: { + [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: { + action: string; iouType: ValueOf; + transactionID: string; reportID: string; + backTo: string; }; [SCREENS.MONEY_REQUEST.RECEIPT]: { iouType: string; diff --git a/src/pages/EditRequestDistancePage.js b/src/pages/EditRequestDistancePage.js deleted file mode 100644 index f3ea76a3390a..000000000000 --- a/src/pages/EditRequestDistancePage.js +++ /dev/null @@ -1,122 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useEffect, useRef} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import DistanceRequest from '@components/DistanceRequest'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import transactionPropTypes from '@components/transactionPropTypes'; -import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; -import usePrevious from '@hooks/usePrevious'; -import Navigation from '@libs/Navigation/Navigation'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import reportPropTypes from './reportPropTypes'; - -const propTypes = { - /** The transactionID we're currently editing */ - transactionID: PropTypes.string.isRequired, - - /** The report to with which the distance request is associated */ - report: reportPropTypes.isRequired, - - /** Passed from the navigator */ - route: PropTypes.shape({ - /** Parameters the route gets */ - params: PropTypes.shape({ - /** Type of IOU */ - iouType: PropTypes.oneOf(_.values(CONST.IOU.TYPE)), - - /** Id of the report on which the distance request is being created */ - reportID: PropTypes.string, - }), - }).isRequired, - - /* Onyx props */ - /** The original transaction that is being edited */ - transaction: transactionPropTypes, - - /** backup version of the original transaction */ - transactionBackup: transactionPropTypes, -}; - -const defaultProps = { - transaction: {}, - transactionBackup: {}, -}; - -function EditRequestDistancePage({report, route, transaction, transactionBackup}) { - const {isOffline} = useNetwork(); - const {translate} = useLocalize(); - const hasWaypointError = useRef(false); - const prevIsLoading = usePrevious(transaction.isLoading); - - useEffect(() => { - hasWaypointError.current = Boolean(lodashGet(transaction, 'errorFields.route') || lodashGet(transaction, 'errorFields.waypoints')); - - // When the loading goes from true to false, then we know the transaction has just been - // saved to the server. Check for errors. If there are no errors, then the modal can be closed. - if (prevIsLoading && !transaction.isLoading && !hasWaypointError.current) { - Navigation.dismissModal(report.reportID); - } - }, [transaction, prevIsLoading, report]); - - /** - * Save the changes to the original transaction object - * @param {Object} waypoints - */ - const saveTransaction = (waypoints) => { - // If nothing was changed, simply go to transaction thread - // We compare only addresses because numbers are rounded while backup - const oldWaypoints = lodashGet(transactionBackup, 'comment.waypoints', {}); - const oldAddresses = _.mapObject(oldWaypoints, (waypoint) => _.pick(waypoint, 'address')); - const addresses = _.mapObject(waypoints, (waypoint) => _.pick(waypoint, 'address')); - if (_.isEqual(oldAddresses, addresses)) { - Navigation.dismissModal(report.reportID); - return; - } - - IOU.updateMoneyRequestDistance(transaction.transactionID, report.reportID, waypoints); - - // If the client is offline, then the modal can be closed as well (because there are no errors or other feedback to show them - // until they come online again and sync with the server). - if (isOffline) { - Navigation.dismissModal(report.reportID); - } - }; - - return ( - - Navigation.goBack()} - /> - - - ); -} - -EditRequestDistancePage.propTypes = propTypes; -EditRequestDistancePage.defaultProps = defaultProps; -EditRequestDistancePage.displayName = 'EditRequestDistancePage'; -export default withOnyx({ - transaction: { - key: (props) => `${ONYXKEYS.COLLECTION.TRANSACTION}${props.transactionID}`, - }, - transactionBackup: { - key: (props) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${props.transactionID}`, - }, -})(EditRequestDistancePage); diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 3eb9d88f1120..9f1ba51806e5 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -23,7 +23,6 @@ import EditRequestAmountPage from './EditRequestAmountPage'; import EditRequestCategoryPage from './EditRequestCategoryPage'; import EditRequestCreatedPage from './EditRequestCreatedPage'; import EditRequestDescriptionPage from './EditRequestDescriptionPage'; -import EditRequestDistancePage from './EditRequestDistancePage'; import EditRequestMerchantPage from './EditRequestMerchantPage'; import EditRequestReceiptPage from './EditRequestReceiptPage'; import EditRequestTagPage from './EditRequestTagPage'; @@ -264,16 +263,6 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep ); } - if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE) { - return ( - - ); - } - return ( { const moneyRequestID = `${iouType}${reportID}`; @@ -133,13 +128,6 @@ function MoneyRequestSelectorPage(props) { initialParams={{reportID, iouType}} /> {() => } - {shouldDisplayDistanceRequest && ( - - )} ) : ( diff --git a/src/pages/iou/NewDistanceRequestPage.js b/src/pages/iou/NewDistanceRequestPage.js deleted file mode 100644 index 750ac5d0141e..000000000000 --- a/src/pages/iou/NewDistanceRequestPage.js +++ /dev/null @@ -1,85 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import DistanceRequest from '@components/DistanceRequest'; -import Navigation from '@libs/Navigation/Navigation'; -import reportPropTypes from '@pages/reportPropTypes'; -import * as IOU from '@userActions/IOU'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import {iouPropTypes} from './propTypes'; - -const propTypes = { - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - - /** The report on which the request is initiated on */ - report: reportPropTypes, - - /** Passed from the navigator */ - route: PropTypes.shape({ - /** Parameters the route gets */ - params: PropTypes.shape({ - /** Type of IOU */ - iouType: PropTypes.oneOf(_.values(CONST.IOU.TYPE)), - /** Id of the report on which the distance request is being created */ - reportID: PropTypes.string, - }), - }), -}; - -const defaultProps = { - iou: {}, - report: {}, - route: { - params: { - iouType: '', - reportID: '', - }, - }, -}; - -// This component is responsible for getting the transactionID from the IOU key, or creating the transaction if it doesn't exist yet, and then passing the transactionID. -// You can't use Onyx props in the withOnyx mapping, so we need to set up and access the transactionID here, and then pass it down so that DistanceRequest can subscribe to the transaction. -function NewDistanceRequestPage({iou, report, route}) { - const iouType = lodashGet(route, 'params.iouType', 'request'); - const isEditingNewRequest = Navigation.getActiveRoute().includes('address'); - - useEffect(() => { - if (iou.transactionID) { - return; - } - IOU.setUpDistanceTransaction(); - }, [iou.transactionID]); - - const onSubmit = useCallback(() => { - if (isEditingNewRequest) { - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); - return; - } - IOU.navigateToNextPage(iou, iouType, report); - }, [iou, iouType, isEditingNewRequest, report]); - - return ( - - ); -} - -NewDistanceRequestPage.displayName = 'NewDistanceRequestPage'; -NewDistanceRequestPage.propTypes = propTypes; -NewDistanceRequestPage.defaultProps = defaultProps; -export default withOnyx({ - iou: {key: ONYXKEYS.IOU}, - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID')}`, - }, -})(NewDistanceRequestPage); From d0606815bcc866b3b20fb1d359b9ce1aca0e150f Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Thu, 15 Feb 2024 19:25:29 +0000 Subject: [PATCH 002/114] refactor(typescript): migrate withwritablereportornotfound --- .../step/withWritableReportOrNotFound.js | 75 ------------------- .../step/withWritableReportOrNotFound.tsx | 48 ++++++++++++ 2 files changed, 48 insertions(+), 75 deletions(-) delete mode 100644 src/pages/iou/request/step/withWritableReportOrNotFound.js create mode 100644 src/pages/iou/request/step/withWritableReportOrNotFound.tsx diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.js b/src/pages/iou/request/step/withWritableReportOrNotFound.js deleted file mode 100644 index 978b84f321d1..000000000000 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.js +++ /dev/null @@ -1,75 +0,0 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; -import React from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import getComponentDisplayName from '@libs/getComponentDisplayName'; -import * as ReportUtils from '@libs/ReportUtils'; -import reportPropTypes from '@pages/reportPropTypes'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; - -const propTypes = { - /** The HOC takes an optional ref as a prop and passes it as a ref to the wrapped component. - * That way, if a ref is passed to a component wrapped in the HOC, the ref is a reference to the wrapped component, not the HOC. */ - forwardedRef: PropTypes.func, - - /** The report corresponding to the reportID in the route params */ - report: reportPropTypes, - - route: IOURequestStepRoutePropTypes.isRequired, -}; - -const defaultProps = { - forwardedRef: () => {}, - report: {}, -}; - -export default function (WrappedComponent) { - // eslint-disable-next-line rulesdir/no-negated-variables - function WithWritableReportOrNotFound({forwardedRef, ...props}) { - const { - route: { - params: {iouType}, - }, - report, - } = props; - - const iouTypeParamIsInvalid = !_.contains(_.values(CONST.IOU.TYPE), iouType); - const canUserPerformWriteAction = ReportUtils.canUserPerformWriteAction(report); - if (iouTypeParamIsInvalid || !canUserPerformWriteAction) { - return ; - } - - return ( - - ); - } - - WithWritableReportOrNotFound.propTypes = propTypes; - WithWritableReportOrNotFound.defaultProps = defaultProps; - WithWritableReportOrNotFound.displayName = `withWritableReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - - // eslint-disable-next-line rulesdir/no-negated-variables - const WithWritableReportOrNotFoundWithRef = React.forwardRef((props, ref) => ( - - )); - - WithWritableReportOrNotFoundWithRef.displayName = 'WithWritableReportOrNotFoundWithRef'; - - return withOnyx({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '0')}`, - }, - })(WithWritableReportOrNotFoundWithRef); -} diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx new file mode 100644 index 000000000000..affd8b259d94 --- /dev/null +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -0,0 +1,48 @@ +import type {RouteProp} from '@react-navigation/native'; +import type {ComponentType, ForwardedRef} from 'react'; +import React, {forwardRef} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import * as ReportUtils from '@libs/ReportUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report} from '@src/types/onyx'; + +type WithWritableReportOrNotFoundOnyxProps = { + /** The report corresponding to the reportID in the route params */ + report: OnyxEntry; +}; + +type WithWritableReportOrNotFoundProps = WithWritableReportOrNotFoundOnyxProps & { + route: RouteProp>; +}; + +export default function (WrappedComponent: ComponentType) { + // eslint-disable-next-line rulesdir/no-negated-variables + function WithWritableReportOrNotFound(props: TProps, ref: ForwardedRef) { + const {report, route} = props; + const iouTypeParamIsInvalid = !Object.values(CONST.IOU.TYPE).includes(route.params?.iouType ?? ''); + const canUserPerformWriteAction = ReportUtils.canUserPerformWriteAction(report); + if (iouTypeParamIsInvalid || !canUserPerformWriteAction) { + return ; + } + + return ( + + ); + } + + WithWritableReportOrNotFound.displayName = `withWritableReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; + + return withOnyx({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params?.reportID ?? '0'}`, + }, + })(forwardRef(WithWritableReportOrNotFound)); +} From 150ac223e8fa53ce4a5e6d6aca63d7d370edf48e Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 21 Feb 2024 01:42:52 +0530 Subject: [PATCH 003/114] Reorder Tooltip component in IconButton --- src/components/VideoPlayer/IconButton.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/VideoPlayer/IconButton.js b/src/components/VideoPlayer/IconButton.js index 71c1a2150692..7a9a93f5a626 100644 --- a/src/components/VideoPlayer/IconButton.js +++ b/src/components/VideoPlayer/IconButton.js @@ -40,12 +40,12 @@ const defaultProps = { function IconButton({src, fill, onPress, style, hoverStyle, tooltipText, small, shouldForceRenderingTooltipBelow}) { const styles = useThemeStyles(); return ( - - - {(isHovered) => ( + + {(isHovered) => ( + - )} - - + + )} + ); } From f278edc12c5d04148ae1539290ed7303ef68b8ad Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Wed, 21 Feb 2024 02:16:10 +0530 Subject: [PATCH 004/114] Simplify component by using hoverStyle prop --- src/components/VideoPlayer/IconButton.js | 38 +++++++++++------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/components/VideoPlayer/IconButton.js b/src/components/VideoPlayer/IconButton.js index 7a9a93f5a626..eaf7ec532fb2 100644 --- a/src/components/VideoPlayer/IconButton.js +++ b/src/components/VideoPlayer/IconButton.js @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; import React from 'react'; -import Hoverable from '@components/Hoverable'; import Icon from '@components/Icon'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import Tooltip from '@components/Tooltip'; @@ -40,26 +39,23 @@ const defaultProps = { function IconButton({src, fill, onPress, style, hoverStyle, tooltipText, small, shouldForceRenderingTooltipBelow}) { const styles = useThemeStyles(); return ( - - {(isHovered) => ( - - - - - - )} - + + + + + ); } From 10e34a68998ccc74ecba3f7b981afa8231bdf947 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:29:53 -0500 Subject: [PATCH 005/114] disable submit button when user is self approving but self approval is disabled --- src/components/MoneyReportHeader.tsx | 3 +++ src/components/ReportActionItem/ReportPreview.tsx | 2 ++ src/libs/ReportUtils.ts | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 3f551da788f5..2e4e9c080224 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -91,6 +91,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money }, [isPaidGroupPolicy, isManager, isDraft, isApproved, isSettled, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; + const shouldDisableSubmitButton = !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; @@ -157,6 +158,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money text={translate('common.submit')} style={[styles.mnw120, styles.pv2, styles.pr0]} onPress={() => IOU.submitReport(moneyRequestReport)} + isDisabled={shouldDisableSubmitButton} /> )} @@ -188,6 +190,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money text={translate('common.submit')} style={[styles.w100, styles.pr0]} onPress={() => IOU.submitReport(moneyRequestReport)} + isDisabled={shouldDisableSubmitButton} /> )} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 591767234b8b..b984972ace7b 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -156,6 +156,7 @@ function ReportPreview({ }); const shouldShowSubmitButton = isDraftExpenseReport && reimbursableSpend !== 0; + const shouldDisableSubmitButton = !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport); // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( @@ -331,6 +332,7 @@ function ReportPreview({ text={translate('common.submit')} style={styles.mt3} onPress={() => iouReport && IOU.submitReport(iouReport)} + isDisabled={shouldDisableSubmitButton} /> )} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8813501e2b3f..69c903bc74d1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5011,6 +5011,15 @@ function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry): boolean { + const policy = getPolicy(report?.policyID); + const {submitsTo, isPreventSelfApprovalEnabled, preventSelfApprovalEnabled} = policy; + + const isSelfApproval = currentUserAccountID === submitsTo; + + return !((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -5211,6 +5220,7 @@ export { canEditRoomVisibility, canEditPolicyDescription, getPolicyDescriptionText, + isAllowedToSubmitDraftExpenseReport, }; export type { From 1bd040a987d028eab44cf597ae92856210ab25d9 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:43:08 -0500 Subject: [PATCH 006/114] do not show pay buttons if report is not yet approved --- src/components/MoneyReportHeader.tsx | 16 ++++++++++++---- .../ReportActionItem/ReportPreview.tsx | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 2e4e9c080224..9cedc09bbd26 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -76,10 +76,6 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy); const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); - const shouldShowPayButton = useMemo( - () => isPayer && !isDraft && !isSettled && !moneyRequestReport.isWaitingOnBankAccount && reimbursableSpend !== 0 && !ReportUtils.isArchivedRoom(chatReport) && !isAutoReimbursable, - [isPayer, isDraft, isSettled, moneyRequestReport, reimbursableSpend, chatReport, isAutoReimbursable], - ); const shouldShowApproveButton = useMemo(() => { if (!isPaidGroupPolicy) { return false; @@ -89,6 +85,18 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money } return isManager && !isDraft && !isApproved && !isSettled; }, [isPaidGroupPolicy, isManager, isDraft, isApproved, isSettled, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy]); + const shouldShowPayButton = useMemo( + () => + isPayer && + !isDraft && + !isSettled && + !shouldShowApproveButton && + !moneyRequestReport.isWaitingOnBankAccount && + reimbursableSpend !== 0 && + !ReportUtils.isArchivedRoom(chatReport) && + !isAutoReimbursable, + [isPayer, isDraft, isSettled, moneyRequestReport, reimbursableSpend, chatReport, isAutoReimbursable, shouldShowApproveButton], + ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const shouldDisableSubmitButton = !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index b984972ace7b..5a4bde217b1d 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -217,10 +217,6 @@ function ReportPreview({ : isPolicyAdmin || (isMoneyRequestReport && isCurrentUserManager); const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy); const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); - const shouldShowPayButton = useMemo( - () => isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled && !isAutoReimbursable, - [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport], - ); const shouldShowApproveButton = useMemo(() => { if (!isPaidGroupPolicy) { return false; @@ -230,6 +226,18 @@ function ReportPreview({ } return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled; }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy, iouSettled]); + const shouldShowPayButton = useMemo( + () => + isPayer && + !isDraftExpenseReport && + !iouSettled && + !shouldShowApproveButton && + !iouReport?.isWaitingOnBankAccount && + reimbursableSpend !== 0 && + !iouCanceled && + !isAutoReimbursable, + [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport, shouldShowApproveButton], + ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; /* From b1290026a2be9945632cb8fc56e9c438bc9776ea Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:01:18 -0500 Subject: [PATCH 007/114] disable approve button if user is self approving but self approval is disabled --- src/components/MoneyReportHeader.tsx | 7 ++++--- src/components/ReportActionItem/ReportPreview.tsx | 5 +++-- src/libs/ReportUtils.ts | 11 +++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 9cedc09bbd26..3c84da5cba57 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -99,7 +99,8 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; - const shouldDisableSubmitButton = !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); + const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); + const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; @@ -154,7 +155,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money shouldShowApproveButton={shouldShowApproveButton} style={[styles.pv2]} formattedAmount={formattedAmount} - isDisabled={!canAllowSettlement} + isDisabled={!canAllowSettlement || shouldDisableSettlementButton} /> )} @@ -186,7 +187,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} formattedAmount={formattedAmount} - isDisabled={!canAllowSettlement} + isDisabled={!canAllowSettlement || shouldDisableSettlementButton} /> )} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 5a4bde217b1d..4bf7c1af03b9 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -156,7 +156,7 @@ function ReportPreview({ }); const shouldShowSubmitButton = isDraftExpenseReport && reimbursableSpend !== 0; - const shouldDisableSubmitButton = !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport); + const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport); // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( @@ -239,6 +239,7 @@ function ReportPreview({ [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport, shouldShowApproveButton], ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; + const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport); /* Show subtitle if at least one of the money requests is not being smart scanned, and either: @@ -330,7 +331,7 @@ function ReportPreview({ horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }} - isDisabled={!canAllowSettlement} + isDisabled={!canAllowSettlement || shouldDisableSettlementButton} /> )} {shouldShowSubmitButton && ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 69c903bc74d1..b082ec8789e5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5020,6 +5020,16 @@ function isAllowedToSubmitDraftExpenseReport(report: OnyxEntry): boolean return !((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval); } +function isAllowedToApproveExpenseReport(report: OnyxEntry): boolean { + const policy = getPolicy(report?.policyID); + const {approver, submitsTo, isPreventSelfApprovalEnabled, preventSelfApprovalEnabled} = policy; + + // TODO: check if we should be using "approver" here instead of the same "submitsTo". "approver" is "undefined" currently. + const isSelfApproval = currentUserAccountID === (approver ?? submitsTo); + + return !((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -5221,6 +5231,7 @@ export { canEditPolicyDescription, getPolicyDescriptionText, isAllowedToSubmitDraftExpenseReport, + isAllowedToApproveExpenseReport, }; export type { From fc7670078ef0da6c4865c6dc37b16552c262a727 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Tue, 27 Feb 2024 06:49:57 -0500 Subject: [PATCH 008/114] build optimistic next step message saying you are not allowed to approve your own reports --- src/libs/NextStepUtils.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index f03c34b1696e..9a931af92518 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -244,6 +244,32 @@ function buildNextStep( ]; } + // Prevented self approval + if ((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval) { + optimisticNextStep.message = [ + { + text: "Oops! Looks like you're ", + }, + { + text: isManager ? 'reviewing' : 'approving', + }, + { + text: ' your own report. ', + type: 'strong', + }, + { + text: 'Approving your own reports is ', + }, + { + text: 'forbidden', + type: 'strong', + }, + { + text: ' by your policy.', + }, + ]; + } + break; } From 0c353fd3501046b18a2eba7be862438260aaa159 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:30:30 -0500 Subject: [PATCH 009/114] remove isAllowedToApproveExpenseReport method --- src/components/MoneyReportHeader.tsx | 2 +- src/components/ReportActionItem/ReportPreview.tsx | 2 +- src/libs/ReportUtils.ts | 11 ----------- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 3c84da5cba57..d78255afa0a3 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -100,7 +100,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); - const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport); + const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 4bf7c1af03b9..7c68291c5e78 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -239,7 +239,7 @@ function ReportPreview({ [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport, shouldShowApproveButton], ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport); + const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport); /* Show subtitle if at least one of the money requests is not being smart scanned, and either: diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b082ec8789e5..69c903bc74d1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5020,16 +5020,6 @@ function isAllowedToSubmitDraftExpenseReport(report: OnyxEntry): boolean return !((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval); } -function isAllowedToApproveExpenseReport(report: OnyxEntry): boolean { - const policy = getPolicy(report?.policyID); - const {approver, submitsTo, isPreventSelfApprovalEnabled, preventSelfApprovalEnabled} = policy; - - // TODO: check if we should be using "approver" here instead of the same "submitsTo". "approver" is "undefined" currently. - const isSelfApproval = currentUserAccountID === (approver ?? submitsTo); - - return !((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval); -} - export { getReportParticipantsTitle, isReportMessageAttachment, @@ -5231,7 +5221,6 @@ export { canEditPolicyDescription, getPolicyDescriptionText, isAllowedToSubmitDraftExpenseReport, - isAllowedToApproveExpenseReport, }; export type { From cb6108686f33371ded80a94de04b848740398103 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:33:38 -0500 Subject: [PATCH 010/114] updated shouldDisableSettlementButton logic --- src/components/MoneyReportHeader.tsx | 7 ++++--- src/components/ReportActionItem/ReportPreview.tsx | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index d78255afa0a3..697f8aa20265 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -100,7 +100,8 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); - const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); + const shouldDisableSettlementButton = + !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport)); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; @@ -155,7 +156,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money shouldShowApproveButton={shouldShowApproveButton} style={[styles.pv2]} formattedAmount={formattedAmount} - isDisabled={!canAllowSettlement || shouldDisableSettlementButton} + isDisabled={shouldDisableSettlementButton} /> )} @@ -187,7 +188,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} formattedAmount={formattedAmount} - isDisabled={!canAllowSettlement || shouldDisableSettlementButton} + isDisabled={shouldDisableSettlementButton} /> )} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 7c68291c5e78..c77839b03ad9 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -239,7 +239,7 @@ function ReportPreview({ [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport, shouldShowApproveButton], ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldDisableSettlementButton = shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport); + const shouldDisableSettlementButton = !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport)); /* Show subtitle if at least one of the money requests is not being smart scanned, and either: @@ -331,7 +331,7 @@ function ReportPreview({ horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }} - isDisabled={!canAllowSettlement || shouldDisableSettlementButton} + isDisabled={shouldDisableSettlementButton} /> )} {shouldShowSubmitButton && ( From 03c5f52f25456c04c7da5638e8d5a29dba947237 Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:20:57 -0500 Subject: [PATCH 011/114] added isAllowedToApproveExpenseReport; added test case for nextStep --- src/components/MoneyReportHeader.tsx | 3 +- .../ReportActionItem/ReportPreview.tsx | 2 +- src/libs/NextStepUtils.ts | 47 +++++++++---------- src/libs/ReportUtils.ts | 11 +++++ tests/unit/NextStepUtilsTest.ts | 32 +++++++++++++ 5 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 697f8aa20265..dc89ce1bdc3a 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -100,8 +100,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); - const shouldDisableSettlementButton = - !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport)); + const shouldDisableSettlementButton = !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport)); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index c77839b03ad9..b03c0efce81a 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -239,7 +239,7 @@ function ReportPreview({ [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport, shouldShowApproveButton], ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldDisableSettlementButton = !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(iouReport)); + const shouldDisableSettlementButton = !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport)); /* Show subtitle if at least one of the money requests is not being smart scanned, and either: diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index 9a931af92518..f8db5fdd2c6b 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -242,32 +242,29 @@ function buildNextStep( text: ' %expenses.', }, ]; - } - // Prevented self approval - if ((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval) { - optimisticNextStep.message = [ - { - text: "Oops! Looks like you're ", - }, - { - text: isManager ? 'reviewing' : 'approving', - }, - { - text: ' your own report. ', - type: 'strong', - }, - { - text: 'Approving your own reports is ', - }, - { - text: 'forbidden', - type: 'strong', - }, - { - text: ' by your policy.', - }, - ]; + // Prevented self approval + if ((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval) { + optimisticNextStep.message = [ + { + text: "Oops! Looks like you're reviewing", + }, + { + text: ' your own report. ', + type: 'strong', + }, + { + text: 'Approving your own reports is ', + }, + { + text: 'forbidden', + type: 'strong', + }, + { + text: ' by your policy.', + }, + ]; + } } break; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 69c903bc74d1..d0c3d3e6603d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5020,6 +5020,16 @@ function isAllowedToSubmitDraftExpenseReport(report: OnyxEntry): boolean return !((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isSelfApproval); } +function isAllowedToApproveExpenseReport(report: OnyxEntry): boolean { + const policy = getPolicy(report?.policyID); + const {ownerAccountID, submitsTo, isPreventSelfApprovalEnabled, preventSelfApprovalEnabled} = policy; + + const isOwner = currentUserAccountID === ownerAccountID; + const isSelfApproval = currentUserAccountID === submitsTo; + + return !((isPreventSelfApprovalEnabled ?? preventSelfApprovalEnabled) && isOwner && isSelfApproval); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -5221,6 +5231,7 @@ export { canEditPolicyDescription, getPolicyDescriptionText, isAllowedToSubmitDraftExpenseReport, + isAllowedToApproveExpenseReport, }; export type { diff --git a/tests/unit/NextStepUtilsTest.ts b/tests/unit/NextStepUtilsTest.ts index 622881bc7979..9c202ca4ebd9 100644 --- a/tests/unit/NextStepUtilsTest.ts +++ b/tests/unit/NextStepUtilsTest.ts @@ -440,6 +440,38 @@ describe('libs/NextStepUtils', () => { expect(result).toMatchObject(optimisticNextStep); }); + + test('prevented self approval', () => { + optimisticNextStep.title = 'Next Steps:'; + optimisticNextStep.message = [ + { + text: "Oops! Looks like you're reviewing", + }, + { + text: ' your own report. ', + type: 'strong', + }, + { + text: 'Approving your own reports is ', + }, + { + text: 'forbidden', + type: 'strong', + }, + { + text: ' by your policy.', + }, + ]; + + return Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + submitsTo: currentUserAccountID, + preventSelfApprovalEnabled: true, + }).then(() => { + const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.SUBMITTED); + + expect(result).toMatchObject(optimisticNextStep); + }); + }); }); describe('it generates an optimistic nextStep once a report has been approved', () => { From eea5225703ec2ff14de8b395acbbdba1e387667f Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:29:42 -0500 Subject: [PATCH 012/114] updated shouldDisableSettlementButton logic --- src/components/MoneyReportHeader.tsx | 3 ++- src/components/ReportActionItem/ReportPreview.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index dc89ce1bdc3a..ec3340b6f0f0 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -100,7 +100,8 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); - const shouldDisableSettlementButton = !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport)); + const shouldDisableSettlementButton = + shouldShowSettlementButton && ((shouldShowPayButton && !canAllowSettlement) || (shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport))); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowNextStep = isFromPaidPolicy && !!nextStep?.message?.length; const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index b03c0efce81a..9b7eb5a360a5 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -239,7 +239,8 @@ function ReportPreview({ [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport, shouldShowApproveButton], ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldDisableSettlementButton = !canAllowSettlement || (shouldShowSettlementButton && shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport)); + const shouldDisableSettlementButton = + shouldShowSettlementButton && ((shouldShowPayButton && !canAllowSettlement) || (shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport))); /* Show subtitle if at least one of the money requests is not being smart scanned, and either: From 9b1a8e6688a889ad8fd01a2ed5fba589db8cfd42 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Tue, 27 Feb 2024 23:08:31 +0000 Subject: [PATCH 013/114] fix: not found page shown when report is undefined --- src/pages/iou/request/step/withWritableReportOrNotFound.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index affd8b259d94..c73bf7ced8aa 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -22,7 +22,7 @@ type WithWritableReportOrNotFoundProps = WithWritableReportOrNotFoundOnyxProps & export default function (WrappedComponent: ComponentType) { // eslint-disable-next-line rulesdir/no-negated-variables function WithWritableReportOrNotFound(props: TProps, ref: ForwardedRef) { - const {report, route} = props; + const {report = {reportID: ''}, route} = props; const iouTypeParamIsInvalid = !Object.values(CONST.IOU.TYPE).includes(route.params?.iouType ?? ''); const canUserPerformWriteAction = ReportUtils.canUserPerformWriteAction(report); if (iouTypeParamIsInvalid || !canUserPerformWriteAction) { From 4b84689d4236d15ec2588f6367a5f55dea335880 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 28 Feb 2024 15:20:21 +0000 Subject: [PATCH 014/114] fix(typescript): incompatible type error --- src/pages/iou/request/step/IOURequestStepWaypoint.tsx | 5 +++-- src/pages/iou/request/step/withWritableReportOrNotFound.tsx | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx index eee6da9e87ef..ac5cb307da86 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx @@ -33,7 +33,7 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Waypoint} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; -import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import withWritableReportOrNotFound, {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type IOURequestStepWaypointOnyxProps = { /** List of recent waypoints */ @@ -54,7 +54,8 @@ type IOURequestStepWaypointProps = { }; }; transaction: OnyxEntry; -} & IOURequestStepWaypointOnyxProps; +} & IOURequestStepWaypointOnyxProps & + WithWritableReportOrNotFoundProps; function IOURequestStepWaypoint({ route: { diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index c73bf7ced8aa..5cc3d6e1ba01 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -46,3 +46,5 @@ export default function }, })(forwardRef(WithWritableReportOrNotFound)); } + +export type {WithWritableReportOrNotFoundProps}; From 1b2b2269814ade467c444c5c829f485c11e1d16c Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 28 Feb 2024 16:38:25 +0000 Subject: [PATCH 015/114] fix: linter and typescript issues --- src/pages/iou/request/step/IOURequestStepWaypoint.tsx | 3 ++- src/pages/iou/request/step/withWritableReportOrNotFound.tsx | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx index ac5cb307da86..91bd5e37a72e 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx @@ -33,7 +33,8 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Waypoint} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; -import withWritableReportOrNotFound, {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; +import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; +import withWritableReportOrNotFound from './withWritableReportOrNotFound'; type IOURequestStepWaypointOnyxProps = { /** List of recent waypoints */ diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index 5cc3d6e1ba01..ed4958e77f08 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -1,4 +1,3 @@ -import type {RouteProp} from '@react-navigation/native'; import type {ComponentType, ForwardedRef} from 'react'; import React, {forwardRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; @@ -16,7 +15,7 @@ type WithWritableReportOrNotFoundOnyxProps = { }; type WithWritableReportOrNotFoundProps = WithWritableReportOrNotFoundOnyxProps & { - route: RouteProp>; + route: {params: {iouType: string; reportID: string} | undefined}; }; export default function (WrappedComponent: ComponentType) { From 016d7a978c197d42c5844d66cd0482593c039321 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 1 Mar 2024 13:31:32 +0800 Subject: [PATCH 016/114] don't categorize when searching --- src/pages/EditReportFieldDropdownPage.tsx | 28 +++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/pages/EditReportFieldDropdownPage.tsx b/src/pages/EditReportFieldDropdownPage.tsx index 1ad3c766221b..ddd5330e389e 100644 --- a/src/pages/EditReportFieldDropdownPage.tsx +++ b/src/pages/EditReportFieldDropdownPage.tsx @@ -46,15 +46,29 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, const [headerMessage, setHeaderMessage] = useState(''); const sections = useMemo(() => { - const filteredRecentOptions = recentlyUsedOptions.filter((option) => option.toLowerCase().includes(searchValue.toLowerCase())); - const filteredRestOfOptions = fieldOptions.filter((option) => !filteredRecentOptions.includes(option) && option.toLowerCase().includes(searchValue.toLowerCase())); - setHeaderMessage(!filteredRecentOptions.length && !filteredRestOfOptions.length ? translate('common.noResultsFound') : ''); + if (searchValue) { + const filteredOptions = fieldOptions.filter((option) => option.toLowerCase().includes(searchValue.toLowerCase())); + setHeaderMessage(!filteredOptions.length ? translate('common.noResultsFound') : ''); + return [ + { + shouldShow: false, + data: filteredOptions.map((option) => ({ + text: option, + keyForList: option, + searchText: option, + tooltipText: option, + })), + }, + ]; + } + const restOfOptions = fieldOptions.filter((option) => !recentlyUsedOptions.includes(option)); + setHeaderMessage(!restOfOptions.length && !recentlyUsedOptions.length ? translate('common.noResultsFound') : ''); return [ { title: translate('common.recents'), - shouldShow: filteredRecentOptions.length > 0, - data: filteredRecentOptions.map((option) => ({ + shouldShow: recentlyUsedOptions.length > 0, + data: recentlyUsedOptions.map((option) => ({ text: option, keyForList: option, searchText: option, @@ -63,8 +77,8 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, }, { title: translate('common.all'), - shouldShow: filteredRestOfOptions.length > 0, - data: filteredRestOfOptions.map((option) => ({ + shouldShow: restOfOptions.length > 0, + data: restOfOptions.map((option) => ({ text: option, keyForList: option, searchText: option, From 691d12642545d1204a41e89f8e9880784845e15e Mon Sep 17 00:00:00 2001 From: Someshwar Tripathi Date: Mon, 4 Mar 2024 07:29:39 +0530 Subject: [PATCH 017/114] Update IconButton to use PressableWithFeedback --- src/components/VideoPlayer/IconButton.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/VideoPlayer/IconButton.js b/src/components/VideoPlayer/IconButton.js index eaf7ec532fb2..5af9bc87e66f 100644 --- a/src/components/VideoPlayer/IconButton.js +++ b/src/components/VideoPlayer/IconButton.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import Icon from '@components/Icon'; -import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import Tooltip from '@components/Tooltip'; import useThemeStyles from '@hooks/useThemeStyles'; import stylePropTypes from '@styles/stylePropTypes'; @@ -43,7 +43,7 @@ function IconButton({src, fill, onPress, style, hoverStyle, tooltipText, small, text={tooltipText} shouldForceRenderingBelow={shouldForceRenderingTooltipBelow} > - - + ); } From fc05a9f5fa50bb94c3ca18b7fa79bac208070ebe Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 4 Mar 2024 17:06:01 +0800 Subject: [PATCH 018/114] don't include selected value in recent and all --- src/pages/EditReportFieldDropdownPage.tsx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/pages/EditReportFieldDropdownPage.tsx b/src/pages/EditReportFieldDropdownPage.tsx index ddd5330e389e..b21a9d685062 100644 --- a/src/pages/EditReportFieldDropdownPage.tsx +++ b/src/pages/EditReportFieldDropdownPage.tsx @@ -62,13 +62,23 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, ]; } - const restOfOptions = fieldOptions.filter((option) => !recentlyUsedOptions.includes(option)); - setHeaderMessage(!restOfOptions.length && !recentlyUsedOptions.length ? translate('common.noResultsFound') : ''); + const selectedValue = fieldValue; + + setHeaderMessage(!fieldOptions.length && !recentlyUsedOptions.length ? translate('common.noResultsFound') : ''); return [ + { + shouldShow: false, + data: [{ + text: selectedValue, + keyForList: selectedValue, + searchText: selectedValue, + tooltipText: selectedValue, + }], + }, { title: translate('common.recents'), shouldShow: recentlyUsedOptions.length > 0, - data: recentlyUsedOptions.map((option) => ({ + data: recentlyUsedOptions.filter((option) => option !== selectedValue).map((option) => ({ text: option, keyForList: option, searchText: option, @@ -77,8 +87,8 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, }, { title: translate('common.all'), - shouldShow: restOfOptions.length > 0, - data: restOfOptions.map((option) => ({ + shouldShow: fieldOptions.length > 0, + data: fieldOptions.filter((option) => option !== selectedValue).map((option) => ({ text: option, keyForList: option, searchText: option, From 24309d60de21e8c71775262782c45ce751a5cb7b Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 4 Mar 2024 17:10:56 +0800 Subject: [PATCH 019/114] fix lint --- src/pages/EditReportFieldDropdownPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditReportFieldDropdownPage.tsx b/src/pages/EditReportFieldDropdownPage.tsx index b21a9d685062..1b6631b60883 100644 --- a/src/pages/EditReportFieldDropdownPage.tsx +++ b/src/pages/EditReportFieldDropdownPage.tsx @@ -96,7 +96,7 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, })), }, ]; - }, [fieldOptions, recentlyUsedOptions, searchValue, translate]); + }, [fieldValue, fieldOptions, recentlyUsedOptions, searchValue, translate]); return ( Date: Mon, 4 Mar 2024 17:16:38 +0800 Subject: [PATCH 020/114] prettier --- src/pages/EditReportFieldDropdownPage.tsx | 42 +++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/pages/EditReportFieldDropdownPage.tsx b/src/pages/EditReportFieldDropdownPage.tsx index 1b6631b60883..0fe7511b5731 100644 --- a/src/pages/EditReportFieldDropdownPage.tsx +++ b/src/pages/EditReportFieldDropdownPage.tsx @@ -68,32 +68,38 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, return [ { shouldShow: false, - data: [{ - text: selectedValue, - keyForList: selectedValue, - searchText: selectedValue, - tooltipText: selectedValue, - }], + data: [ + { + text: selectedValue, + keyForList: selectedValue, + searchText: selectedValue, + tooltipText: selectedValue, + }, + ], }, { title: translate('common.recents'), shouldShow: recentlyUsedOptions.length > 0, - data: recentlyUsedOptions.filter((option) => option !== selectedValue).map((option) => ({ - text: option, - keyForList: option, - searchText: option, - tooltipText: option, - })), + data: recentlyUsedOptions + .filter((option) => option !== selectedValue) + .map((option) => ({ + text: option, + keyForList: option, + searchText: option, + tooltipText: option, + })), }, { title: translate('common.all'), shouldShow: fieldOptions.length > 0, - data: fieldOptions.filter((option) => option !== selectedValue).map((option) => ({ - text: option, - keyForList: option, - searchText: option, - tooltipText: option, - })), + data: fieldOptions + .filter((option) => option !== selectedValue) + .map((option) => ({ + text: option, + keyForList: option, + searchText: option, + tooltipText: option, + })), }, ]; }, [fieldValue, fieldOptions, recentlyUsedOptions, searchValue, translate]); From 6f58e2f765e9860d27d47e3968fa29d5c41200b8 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Mon, 4 Mar 2024 20:20:44 +0800 Subject: [PATCH 021/114] conditionally add the section when it's not empty --- src/pages/EditReportFieldDropdownPage.tsx | 62 +++++++++++++---------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/pages/EditReportFieldDropdownPage.tsx b/src/pages/EditReportFieldDropdownPage.tsx index 0fe7511b5731..8831cca12197 100644 --- a/src/pages/EditReportFieldDropdownPage.tsx +++ b/src/pages/EditReportFieldDropdownPage.tsx @@ -65,8 +65,14 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, const selectedValue = fieldValue; setHeaderMessage(!fieldOptions.length && !recentlyUsedOptions.length ? translate('common.noResultsFound') : ''); - return [ - { + + const newSections = []; + + const filteredRecentlyUsedOptions = recentlyUsedOptions.filter((option) => option !== selectedValue); + const filteredFieldOptions = fieldOptions.filter((option) => option !== selectedValue); + + if (selectedValue) { + newSections.push({ shouldShow: false, data: [ { @@ -76,32 +82,36 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, tooltipText: selectedValue, }, ], - }, - { + }); + } + + if (filteredRecentlyUsedOptions.length > 0) { + newSections.push({ title: translate('common.recents'), - shouldShow: recentlyUsedOptions.length > 0, - data: recentlyUsedOptions - .filter((option) => option !== selectedValue) - .map((option) => ({ - text: option, - keyForList: option, - searchText: option, - tooltipText: option, - })), - }, - { + shouldShow: true, + data: filteredRecentlyUsedOptions.map((option) => ({ + text: option, + keyForList: option, + searchText: option, + tooltipText: option, + })), + }); + } + + if (filteredFieldOptions.length > 0) { + newSections.push({ title: translate('common.all'), - shouldShow: fieldOptions.length > 0, - data: fieldOptions - .filter((option) => option !== selectedValue) - .map((option) => ({ - text: option, - keyForList: option, - searchText: option, - tooltipText: option, - })), - }, - ]; + shouldShow: true, + data: filteredFieldOptions.map((option) => ({ + text: option, + keyForList: option, + searchText: option, + tooltipText: option, + })), + }); + } + + return newSections; }, [fieldValue, fieldOptions, recentlyUsedOptions, searchValue, translate]); return ( From 563ed6a0ae9b1247432810b8872293be6f86ed97 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 5 Mar 2024 11:49:09 +0800 Subject: [PATCH 022/114] moving code around --- src/pages/EditReportFieldDropdownPage.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pages/EditReportFieldDropdownPage.tsx b/src/pages/EditReportFieldDropdownPage.tsx index 8831cca12197..97688c41750b 100644 --- a/src/pages/EditReportFieldDropdownPage.tsx +++ b/src/pages/EditReportFieldDropdownPage.tsx @@ -62,15 +62,11 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, ]; } - const selectedValue = fieldValue; - setHeaderMessage(!fieldOptions.length && !recentlyUsedOptions.length ? translate('common.noResultsFound') : ''); const newSections = []; - const filteredRecentlyUsedOptions = recentlyUsedOptions.filter((option) => option !== selectedValue); - const filteredFieldOptions = fieldOptions.filter((option) => option !== selectedValue); - + const selectedValue = fieldValue; if (selectedValue) { newSections.push({ shouldShow: false, @@ -85,6 +81,7 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, }); } + const filteredRecentlyUsedOptions = recentlyUsedOptions.filter((option) => option !== selectedValue); if (filteredRecentlyUsedOptions.length > 0) { newSections.push({ title: translate('common.recents'), @@ -98,6 +95,7 @@ function EditReportFieldDropdownPage({fieldName, onSubmit, fieldID, fieldValue, }); } + const filteredFieldOptions = fieldOptions.filter((option) => option !== selectedValue); if (filteredFieldOptions.length > 0) { newSections.push({ title: translate('common.all'), From 784aded48116e6d7cc70c5eff0d9796168df7bdf Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Fri, 8 Mar 2024 14:00:32 +0530 Subject: [PATCH 023/114] fix: Hold Request - Keyboard is up but the field is not focused with error message showing. Signed-off-by: Krishna Gupta --- src/pages/iou/HoldReasonPage.tsx | 4 +++- src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index 0e3df33358c0..4ff7f16e78ff 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -8,6 +8,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; @@ -36,6 +37,7 @@ type HoldReasonPageProps = { function HoldReasonPage({route}: HoldReasonPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const {inputCallbackRef} = useAutoFocusInput(); const {transactionID, reportID, backTo} = route.params; @@ -86,7 +88,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { defaultValue={undefined} label={translate('iou.reason')} accessibilityLabel={translate('iou.reason')} - autoFocus + ref={inputCallbackRef} /> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index c43ef8dd9320..611db1a77baa 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -8,6 +8,7 @@ import type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; @@ -43,6 +44,7 @@ function ExitSurveyResponsePage({draftResponse, route, navigation}: ExitSurveyRe const {keyboardHeight} = useKeyboardState(); const {windowHeight} = useWindowDimensions(); const {top: safeAreaInsetsTop} = useSafeAreaInsets(); + const {inputCallbackRef} = useAutoFocusInput(); const {reason, backTo} = route.params; const {isOffline} = useNetwork({ @@ -132,6 +134,7 @@ function ExitSurveyResponsePage({draftResponse, route, navigation}: ExitSurveyRe return; } updateMultilineInputRange(el); + inputCallbackRef(el); }} containerStyles={[baseResponseInputContainerStyle, StyleUtils.getMaximumHeight(responseInputMaxHeight)]} shouldSaveDraft From 7bafaae42eab51930b32172001734180c1fc046e Mon Sep 17 00:00:00 2001 From: Carlos Barros <765936+barros001@users.noreply.github.com> Date: Fri, 8 Mar 2024 09:28:38 -0500 Subject: [PATCH 024/114] reverted unnecessary logic to show/hide pay button; reverted optimistic data for self-approving --- .../ReportActionItem/ReportPreview.tsx | 71 ++----------------- src/libs/NextStepUtils.ts | 23 ------ tests/unit/NextStepUtilsTest.ts | 32 --------- 3 files changed, 4 insertions(+), 122 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index e3ebfaa3ea6a..064d87ea5bd3 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -213,6 +213,10 @@ function ReportPreview({ const isPayer = ReportUtils.isPayer(session, iouReport); const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy); const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); + const shouldShowPayButton = useMemo( + () => isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled && !isAutoReimbursable, + [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport], + ); const shouldShowApproveButton = useMemo(() => { if (!isPaidGroupPolicy) { return false; @@ -222,21 +226,7 @@ function ReportPreview({ } return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled; }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy, iouSettled]); - const shouldShowPayButton = useMemo( - () => - isPayer && - !isDraftExpenseReport && - !iouSettled && - !shouldShowApproveButton && - !iouReport?.isWaitingOnBankAccount && - reimbursableSpend !== 0 && - !iouCanceled && - !isAutoReimbursable, - [isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport, shouldShowApproveButton], - ); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; - const shouldDisableSettlementButton = - shouldShowSettlementButton && ((shouldShowPayButton && !canAllowSettlement) || (shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport))); /* Show subtitle if at least one of the money requests is not being smart scanned, and either: @@ -344,59 +334,6 @@ function ReportPreview({ /> )} - - - {getDisplayAmount()} - {ReportUtils.isSettled(iouReportID) && ( - - - - )} - - - {shouldShowSubtitle && ( - - - {previewSubtitle || moneyRequestComment} - - - )} - {shouldShowSettlementButton && ( - chatReport && iouReport && paymentType && IOU.payMoneyRequest(paymentType, chatReport, iouReport)} - enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} - addBankAccountRoute={bankAccountRoute} - shouldHidePaymentOptions={!shouldShowPayButton} - shouldShowApproveButton={shouldShowApproveButton} - style={[styles.mt3]} - kycWallAnchorAlignment={{ - horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, - vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, - }} - paymentMethodDropdownAnchorAlignment={{ - horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, - vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, - }} - isDisabled={shouldDisableSettlementButton} - /> - )} - {shouldShowSubmitButton && ( -