diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 79c1c500b837..066d573bf105 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3203,8 +3203,8 @@ function canSeeDefaultRoom(report, policies, betas) { /** * @param {Object} report - * @param {Array} policies - * @param {Array} betas + * @param {Object | null} policies + * @param {Array | null} betas * @param {Object} allReportActions * @returns {Boolean} */ diff --git a/src/pages/home/report/withReportOrNotFound.js b/src/pages/home/report/withReportOrNotFound.js deleted file mode 100644 index 43f3caa645e7..000000000000 --- a/src/pages/home/report/withReportOrNotFound.js +++ /dev/null @@ -1,127 +0,0 @@ -import PropTypes from 'prop-types'; -import React 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 FullscreenLoadingIndicator from '../../../components/FullscreenLoadingIndicator'; -import * as ReportUtils from '../../../libs/ReportUtils'; - -export default function (shouldRequireReportID = true) { - 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, - - /** 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, - }), - }).isRequired, - - /** Beta features list */ - betas: PropTypes.arrayOf(PropTypes.string), - - /** Indicated whether the report data is loading */ - isLoadingReportData: PropTypes.bool, - }; - - const defaultProps = { - forwardedRef: () => {}, - report: {}, - policies: {}, - betas: [], - isLoadingReportData: true, - }; - - return (WrappedComponent) => { - // eslint-disable-next-line rulesdir/no-negated-variables - function WithReportOrNotFound(props) { - const contentShown = React.useRef(false); - - const isReportIdInRoute = !_.isUndefined(props.route.params.reportID); - - // If we should require reportID or we have a reportID in the route, we will check the reportID is valid or not - if (shouldRequireReportID || isReportIdInRoute) { - const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData && (_.isEmpty(props.report) || !props.report.reportID); - // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = _.isEmpty(props.report) || !props.report.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas); - - // If the content was shown but it's not anymore that means the report was deleted and we are probably navigating out of this screen. - // Return null for this case to avoid rendering FullScreenLoadingIndicator or NotFoundPage when animating transition. - if (shouldShowNotFoundPage && contentShown.current) { - return null; - } - - if (shouldShowFullScreenLoadingIndicator) { - return ; - } - - if (shouldShowNotFoundPage) { - return ; - } - } - - if (!contentShown.current) { - contentShown.current = true; - } - - const rest = _.omit(props, ['forwardedRef']); - return ( - - ); - } - - WithReportOrNotFound.propTypes = propTypes; - WithReportOrNotFound.defaultProps = defaultProps; - WithReportOrNotFound.displayName = `withReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - - // eslint-disable-next-line rulesdir/no-negated-variables - const WithReportOrNotFoundWithRef = React.forwardRef((props, ref) => ( - - )); - - WithReportOrNotFoundWithRef.displayName = 'WithReportOrNotFoundWithRef'; - - return 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, - }, - })(WithReportOrNotFoundWithRef); - }; -} diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx new file mode 100644 index 000000000000..28d6707b085f --- /dev/null +++ b/src/pages/home/report/withReportOrNotFound.tsx @@ -0,0 +1,89 @@ +/* eslint-disable rulesdir/no-negated-variables */ +import React, {ComponentType, ForwardedRef, RefAttributes} from 'react'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; +import {RouteProp} from '@react-navigation/native'; +import getComponentDisplayName from '../../../libs/getComponentDisplayName'; +import NotFoundPage from '../../ErrorPage/NotFoundPage'; +import ONYXKEYS from '../../../ONYXKEYS'; +import FullscreenLoadingIndicator from '../../../components/FullscreenLoadingIndicator'; +import * as ReportUtils from '../../../libs/ReportUtils'; +import * as OnyxTypes from '../../../types/onyx'; + +type OnyxProps = { + /** The report currently being looked at */ + report: OnyxEntry; + /** The policies which the user has access to */ + policies: OnyxEntry; + /** Beta features list */ + betas: OnyxEntry; + /** Indicated whether the report data is loading */ + isLoadingReportData: OnyxEntry; +}; + +type ComponentProps = OnyxProps & { + route: RouteProp<{params: {reportID: string}}>; +}; + +export default function ( + shouldRequireReportID = true, +): ( + WrappedComponent: React.ComponentType>, +) => React.ComponentType, keyof OnyxProps>> { + return function (WrappedComponent: ComponentType>) { + function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { + const contentShown = React.useRef(false); + + const isReportIdInRoute = props.route.params.reportID?.length; + + if (shouldRequireReportID || isReportIdInRoute) { + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData && (!Object.entries(props.report ?? {}).length || !props.report?.reportID); + + const shouldShowNotFoundPage = + !Object.entries(props.report ?? {}).length || !props.report?.reportID || !ReportUtils.canAccessReport(props.report, props.policies, props.betas, {}); + + // If the content was shown but it's not anymore that means the report was deleted and we are probably navigating out of this screen. + // Return null for this case to avoid rendering FullScreenLoadingIndicator or NotFoundPage when animating transition. + if (shouldShowNotFoundPage && contentShown.current) { + return null; + } + + if (shouldShowFullScreenLoadingIndicator) { + return ; + } + + if (shouldShowNotFoundPage) { + return ; + } + } + + if (!contentShown.current) { + contentShown.current = true; + } + + return ( + + ); + } + + WithReportOrNotFound.displayName = `withReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; + + return withOnyx, OnyxProps>({ + 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, + }, + })(React.forwardRef(WithReportOrNotFound)); + }; +}