diff --git a/src/pages/home/report/withReportAndReportActionOrNotFound.js b/src/pages/home/report/withReportAndReportActionOrNotFound.js new file mode 100644 index 000000000000..9bf3e73e761c --- /dev/null +++ b/src/pages/home/report/withReportAndReportActionOrNotFound.js @@ -0,0 +1,153 @@ +import PropTypes from 'prop-types'; +import React, {useEffect, useCallback} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; +import getComponentDisplayName from '../../../libs/getComponentDisplayName'; +import NotFoundPage from '../../ErrorPage/NotFoundPage'; +import ONYXKEYS from '../../../ONYXKEYS'; +import reportPropTypes from '../../reportPropTypes'; +import reportActionPropTypes from './reportActionPropTypes'; +import FullscreenLoadingIndicator from '../../../components/FullscreenLoadingIndicator'; +import * as ReportUtils from '../../../libs/ReportUtils'; +import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; +import * as Report from '../../../libs/actions/Report'; +import compose from '../../../libs/compose'; +import withWindowDimensions from '../../../components/withWindowDimensions'; + +export default function (WrappedComponent) { + 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 currently being looked at */ + report: reportPropTypes, + + /** Array of report actions for this report */ + reportActions: PropTypes.shape(reportActionPropTypes), + + /** The policies which the user has access to */ + policies: PropTypes.objectOf( + PropTypes.shape({ + /** The policy name */ + name: PropTypes.string, + + /** The type of the policy */ + type: PropTypes.string, + }), + ), + + /** Route params */ + route: PropTypes.shape({ + params: PropTypes.shape({ + /** Report ID passed via route */ + reportID: PropTypes.string, + + /** ReportActionID passed via route */ + reportActionID: PropTypes.string, + }), + }).isRequired, + + /** Beta features list */ + betas: PropTypes.arrayOf(PropTypes.string), + + /** Indicated whether the report data is loading */ + isLoadingReportData: PropTypes.bool, + + /** Is the window width narrow, like on a mobile device? */ + isSmallScreenWidth: PropTypes.bool.isRequired, + }; + + const defaultProps = { + forwardedRef: () => {}, + reportActions: {}, + report: {}, + policies: {}, + betas: [], + isLoadingReportData: true, + }; + + // eslint-disable-next-line rulesdir/no-negated-variables + function WithReportAndReportActionOrNotFound(props) { + const getReportAction = useCallback(() => { + let reportAction = props.reportActions[`${props.route.params.reportActionID}`]; + + // Handle threads if needed + if (reportAction === undefined || reportAction.reportActionID === undefined) { + reportAction = ReportActionsUtils.getParentReportAction(props.report); + } + + return reportAction; + }, [props.report, props.reportActions, props.route.params.reportActionID]); + + const reportAction = getReportAction(); + + // For small screen, we don't call openReport API when we go to a sub report page by deeplink + // So we need to call openReport here for small screen + useEffect(() => { + if (!props.isSmallScreenWidth || (!_.isEmpty(props.report) && !_.isEmpty(reportAction))) { + return; + } + Report.openReport(props.route.params.reportID); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.isSmallScreenWidth, props.route.params.reportID]); + + // Perform all the loading checks + const isLoadingReport = props.isLoadingReportData && (_.isEmpty(props.report) || !props.report.reportID); + const isLoadingReportAction = _.isEmpty(props.reportActions) || (props.report.isLoadingReportActions && _.isEmpty(getReportAction())); + const shouldHideReport = !isLoadingReport && (_.isEmpty(props.report) || !props.report.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas)); + + if ((isLoadingReport || isLoadingReportAction) && !shouldHideReport) { + return ; + } + + // Perform the access/not found checks + if (shouldHideReport || _.isEmpty(reportAction)) { + return ; + } + + const rest = _.omit(props, ['forwardedRef']); + return ( + + ); + } + + WithReportAndReportActionOrNotFound.propTypes = propTypes; + WithReportAndReportActionOrNotFound.defaultProps = defaultProps; + WithReportAndReportActionOrNotFound.displayName = `withReportAndReportActionOrNotFound(${getComponentDisplayName(WrappedComponent)})`; + + // eslint-disable-next-line rulesdir/no-negated-variables + const withReportAndReportActionOrNotFound = React.forwardRef((props, ref) => ( + + )); + + return compose( + withWindowDimensions, + withOnyx({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + reportActions: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${route.params.reportID}`, + canEvict: false, + }, + }), + )(withReportAndReportActionOrNotFound); +} diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js index 92edb90256b9..b638da091874 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -14,7 +14,7 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize import compose from '../../libs/compose'; import reportActionPropTypes from '../home/report/reportActionPropTypes'; import reportPropTypes from '../reportPropTypes'; -import withReportOrNotFound from '../home/report/withReportOrNotFound'; +import withReportAndReportActionOrNotFound from '../home/report/withReportAndReportActionOrNotFound'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import CONST from '../../CONST'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -50,18 +50,6 @@ const defaultProps = { reportActions: {}, }; -/** - * Get the reportID for the associated chatReport - * - * @param {Object} route - * @param {Object} route.params - * @param {String} route.params.reportID - * @returns {String} - */ -function getReportID(route) { - return route.params.reportID.toString(); -} - function SplitBillDetailsPage(props) { const reportAction = props.reportActions[`${props.route.params.reportActionID.toString()}`]; const participantAccountIDs = reportAction.originalMessage.participantAccountIDs; @@ -108,14 +96,10 @@ SplitBillDetailsPage.displayName = 'SplitBillDetailsPage'; export default compose( withLocalize, - withReportOrNotFound, + withReportAndReportActionOrNotFound, withOnyx({ personalDetails: { key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, - reportActions: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, - canEvict: false, - }, }), )(SplitBillDetailsPage);