Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HOLD for payment 2023-03-13] [$4000] The details view is broken for the room that is inaccessible to the user #14513

Closed
1 task
kavimuru opened this issue Jan 24, 2023 · 97 comments
Assignees
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor Reviewing Has a PR in review

Comments

@kavimuru
Copy link

kavimuru commented Jan 24, 2023

If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!


Action Performed:

  1. Sign in on web
  2. Open any room
  3. Copy its URL from the address bar
  4. Open incognito mode and paste the copied URL
  5. Sign in with another user that does not have access to that room
  6. When "Hmm... it's not here" message shows up, append /details at the end of the URL to get details of that room

Expected Result:

If the user doesn't have access to the details view they visit, we should show a similar Hmm...it's not here page that we show when you try accessing a chat you don't have access to.

image

Actual Result:

The details view is broken

Workaround:

unknown

Platforms:

Which of our officially supported platforms is this issue occurring on?

  • MacOS / Chrome / Safari

Version Number: v1.2.58-3
Reproducible in staging?: y
Reproducible in production?: y
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos:

Recording.1353.mp4
broken.details.page.mov

Expensify/Expensify Issue URL:
Issue reported by: @adeel0202
Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1674498377054109

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~01fa0bb1360012489a
  • Upwork Job ID: 1620192912854851584
  • Last Price Increase: 2023-02-24
@kavimuru kavimuru added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels Jan 24, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jan 24, 2023

Triggered auto assignment to @sakluger (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details.

@melvin-bot melvin-bot bot locked and limited conversation to collaborators Jan 24, 2023
@sakluger
Copy link
Contributor

This seems like a very specific edge-case issue. I'm asking in Slack if other people think we should fix it.

@marcaaron
Copy link
Contributor

Yeah, can't really think of any reason why someone would get themselves into this situation. But if you are on a Details view of a report you can't access then it would be fine to see the same "Hmm... it's not here" view on that screen.

@melvin-bot melvin-bot bot added the Overdue label Jan 27, 2023
@sakluger
Copy link
Contributor

Alright, for the sake of consistency, let's get this one fixed. Adding the External label!

@melvin-bot melvin-bot bot removed the Overdue label Jan 30, 2023
@sakluger sakluger added the External Added to denote the issue can be worked on by a contributor label Jan 30, 2023
@melvin-bot melvin-bot bot unlocked this conversation Jan 30, 2023
@melvin-bot melvin-bot bot changed the title The details view is broken for the room that is inaccessible to the user [$1000] The details view is broken for the room that is inaccessible to the user Jan 30, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

Job added to Upwork: https://www.upwork.com/jobs/~01fa0bb1360012489a

@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

Current assignee @sakluger is eligible for the External assigner, not assigning anyone new.

@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

Triggered auto assignment to Contributor-plus team member for initial proposal review - @0xmiroslav (External)

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Jan 30, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jan 30, 2023

Triggered auto assignment to @AndrewGable (External), see https://stackoverflow.com/c/expensify/questions/7972 for more details.

@priyeshshah11
Copy link
Contributor

priyeshshah11 commented Jan 31, 2023

Proposal

We should display the NotFound page when the report is not found or accessible.

diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js
index 1b3af0732..9a8a67973 100644
--- a/src/pages/ReportDetailsPage.js
+++ b/src/pages/ReportDetailsPage.js
@@ -23,6 +23,7 @@ import Text from '../components/Text';
 import CONST from '../CONST';
 import reportPropTypes from './reportPropTypes';
 import withReportOrNavigateHome from './home/report/withReportOrNavigateHome';
+import NotFoundPage from './ErrorPage/NotFoundPage';
 
 const propTypes = {
     ...withLocalizePropTypes,
@@ -106,6 +107,11 @@ class ReportDetailsPage extends Component {
             OptionsListUtils.getPersonalDetailsForLogins(participants, this.props.personalDetails),
             isMultipleParticipant,
         );
+
+        if (this.props.report.errorFields) {
+            return <NotFoundPage />;
+        }
+
         return (
             <ScreenWrapper>
                 <HeaderWithCloseButton

@bernhardoj

This comment was marked as outdated.

@melvin-bot
Copy link

melvin-bot bot commented Jan 31, 2023

Looks like something related to react-navigation may have been mentioned in this issue discussion.

As a reminder, please make sure that all proposals are not workarounds and that any and all attempt to fix the issue holistically have been made before proceeding with a solution. Proposals to change our DeprecatedCustomActions.js files should not be accepted.

Feel free to drop a note in #expensify-open-source with any questions.

@Pujan92
Copy link
Contributor

Pujan92 commented Feb 2, 2023

Proposal

Instead showing "Hmm... it's not here", we can use WithReportOrNavigateHome which will dismiss the modal and not allow the user to operate that route. We can add report errorFields conditions like this

diff --git a/src/pages/home/report/withReportOrNavigateHome.js b/src/pages/home/report/withReportOrNavigateHome.js
index 5e74f65a0c..f7da82a3b5 100644
--- a/src/pages/home/report/withReportOrNavigateHome.js
+++ b/src/pages/home/report/withReportOrNavigateHome.js
@@ -24,7 +24,8 @@ export default function (WrappedComponent) {
 
     class WithReportOrNavigateHome extends Component {
         componentDidMount() {
-            if (!_.isEmpty(this.props.report)) {
+            if (!_.isEmpty(this.props.report)
+                && (_.isEmpty(this.props.report.errorFields) || _.isEmpty(this.props.report.errorFields.createChat))) {
                 return;
             }
             Navigation.dismissModal();
Screen.Recording.2023-02-02.at.4.50.33.PM.mp4

@melvin-bot melvin-bot bot added the Overdue label Feb 2, 2023
@sakluger
Copy link
Contributor

sakluger commented Feb 2, 2023

@0xmiroslav @AndrewGable any thoughts on the proposals we have gotten so far?

@melvin-bot melvin-bot bot removed the Overdue label Feb 2, 2023
@s77rt
Copy link
Contributor

s77rt commented Feb 5, 2023

Proposal

Please re-state the problem that we are trying to solve in this issue.

Direct link access (deep link) to report sub views (such as /details) does not render correctly.

What is the root cause of that problem?

If you navigate to /r/invalidreportid/details the details modal will be dismissed thanks to WithReportOrNavigateHome since the invalidreportid report does not exist. However if you first navigate to /r/invalidreportid although the report does not exist the server will return onyx merge instructions making the report no longer empty.

Onyx merge instructions
{
  "onyxMethod": "merge",
  "key": "report_6618435619127287",
  "value": {
    "errorFields": {
      "createChat": {
        "1675614579263380": "Report no longer exists"
      }
    }
  }
}

What changes do you think we should make in order to solve the problem?

  1. Check if the reportID field is present to ensure that the report actually exists. This check can be added here.
  2. Instead of dismissing the modal, return the <NotFoundPage /> view. Moving the validation logic to the render method.

WithReportOrNavigateHome will also be renamed to WithReportOrNotFound

What alternative solutions did you explore? (Optional)

None

@melvin-bot melvin-bot bot added the Overdue label Feb 5, 2023
@ntdiary
Copy link
Contributor

ntdiary commented Feb 6, 2023

Proposal

Please re-state the problem that we are trying to solve in this issue.

Access a report url to which you do not have access first, and then access its details url, The detail view looks like broken.

What is the root cause of that problem?

When we access a report page first, Onyx will create the report object (even if we don't have access to).
image
So this condition couldn't work well. "Broken" detail view will still be shown.

if (!_.isEmpty(this.props.report)) {
return;
}
Navigation.dismissModal();

What changes do you think we should make in order to solve the problem?

Now, we have aligned the expected result: show the "Hmm...it's not here" view.
And I think this issue has some relevance to issue #12676, #12428.
So we currently need to fix at least three pages. (details/settings/participants).
I think we need to make these changes:

  1. Revert the PR about the withReportOrNavigateHome component.
  2. Create a NoAccessView component. With this component, we can also check the loading status of the report.
  3. Add access check with higher priority in all three pages render function. If a user doesn't have access to current report, we should first return the noAccessView. Now, we have used this.props.report.reportID as a check condition in the reportScreen component, and this condition can be kept uniform if it is reasonable.
  4. Customize getStateFromPath in the linkingConfig.js. Make sure that reportScreen fetches the same report data when we visit all three pages directly. (instead of the LastAccessedReport)
  5. Add failureData in the openReport function. Make sure that report.isLoadingReportActions is false even if the request is failed.

Notes: Consider that our navigation is being rewritten, maybe we could also hold this issue for the navigation project.

What alternative solutions did you explore? (Optional)

I'm not so sure if a HOC is encouraged to fix this issue. So I also tried to rename withReportOrNavigateHome to withReportOrNoAccess, and then placed the logic of NoAccessView into withReportOrNoAccess.

Update HOC notes (from my original proposal):

  1. Rename withReportOrNavigateHome to withReportOrNoAccess, and delete its componentDidMount.
  2. Change its render logic, check report.reportID, if it exists, render the WrappedComponent. And then check report.isLoadingReportActions, if it is true, render the FullScreenLoadingIndicator component, otherwise render the FullPageNotFoundView component.
  3. Customize getStateFromPath in the linkingConfig.js. Make sure that reportScreen fetches the same report data when we visit all three pages directly. (instead of the LastAccessedReport)
  4. Add failureData in the openReport function. Make sure that report.isLoadingReportActions is false even if the request is failed.
click here to see my original proposal

Proposal

This issue has some relevance to issue #12676, #12428.
We need to confirm the next step if the user does't have access to these pages(details/settings/participants)
I personally prefer to show the user a clear hint rather than redirecting to another chat.

Option 1: remove the withReportOrNavigateHome component
diff --git a/src/components/BlockingViews/NoAccessView.js b/src/components/BlockingViews/NoAccessView.js
new file mode 100644
index 0000000000..250649bc7c
--- /dev/null
+++ b/src/components/BlockingViews/NoAccessView.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import FullPageNotFoundView from './FullPageNotFoundView';
+import ScreenWrapper from '../ScreenWrapper';
+import reportPropTypes from '../../pages/reportPropTypes';
+import FullScreenLoadingIndicator from '../FullscreenLoadingIndicator';
+
+const propTypes = {
+    /** The report currently being looked at */
+    report: reportPropTypes.isRequired,
+};
+
+const NoAccessView = (props) => {
+    if (props.report.isLoadingReportActions) {
+        return <FullScreenLoadingIndicator />;
+    }
+    return (
+        <ScreenWrapper>
+            <FullPageNotFoundView
+                shouldShow
+                subtitleKey="notFound.noAccess"
+                shouldShowCloseButton
+                shouldShowBackButton={false}
+            />
+        </ScreenWrapper>
+    );
+};
+
+NoAccessView.displayName = 'NoAccessView';
+NoAccessView.propTypes = propTypes;
+
+export default NoAccessView;
diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js
index 952920708e..623217eaa5 100644
--- a/src/libs/actions/Report.js
+++ b/src/libs/actions/Report.js
@@ -353,10 +353,18 @@ function openReport(reportID, participantList = [], newReportObject = {}) {
             isOptimisticReport: false,
         },
     };
+    const reportFailureData = {
+        onyxMethod: CONST.ONYX.METHOD.MERGE,
+        key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
+        value: {
+            isLoadingReportActions: false,
+        },
+    };

     const onyxData = {
         optimisticData: [optimisticReportData],
         successData: [reportSuccessData],
+        failureData: [reportFailureData],
     };

     const params = {
diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js
index b97b31e7e5..5a9ae5b5e1 100644
--- a/src/pages/ReportDetailsPage.js
+++ b/src/pages/ReportDetailsPage.js
@@ -22,7 +22,7 @@ import MenuItem from '../components/MenuItem';
 import Text from '../components/Text';
 import CONST from '../CONST';
 import reportPropTypes from './reportPropTypes';
-import withReportOrNavigateHome from './home/report/withReportOrNavigateHome';
+import NoAccessView from '../components/BlockingViews/NoAccessView';

 const propTypes = {
     ...withLocalizePropTypes,
@@ -33,13 +33,13 @@ const propTypes = {
     }).isRequired,

     /** The report currently being looked at */
-    report: reportPropTypes.isRequired,
+    report: reportPropTypes,

     /** The policies which the user has access to and which the report could be tied to */
     policies: PropTypes.shape({
         /** Name of the policy */
         name: PropTypes.string,
-    }).isRequired,
+    }),

     /** Route params */
     route: PropTypes.shape({
@@ -50,7 +50,15 @@ const propTypes = {
     }).isRequired,

     /** Personal details of all the users */
-    personalDetails: PropTypes.objectOf(participantPropTypes).isRequired,
+    personalDetails: PropTypes.objectOf(participantPropTypes),
+};
+
+const defaultProps = {
+    report: {
+        isLoadingReportActions: true,
+    },
+    policies: {},
+    personalDetails: {},
 };

 class ReportDetailsPage extends Component {
@@ -97,6 +105,9 @@ class ReportDetailsPage extends Component {
     }

     render() {
+        if (!this.props.report.reportID) {
+            return <NoAccessView report={this.props.report} />;
+        }
         const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(this.props.report);
         const isChatRoom = ReportUtils.isChatRoom(this.props.report);
         const chatRoomSubtitle = ReportUtils.getChatRoomSubtitle(this.props.report, this.props.policies);
@@ -175,11 +186,14 @@ class ReportDetailsPage extends Component {
 }

 ReportDetailsPage.propTypes = propTypes;
+ReportDetailsPage.defaultProps = defaultProps;

 export default compose(
     withLocalize,
-    withReportOrNavigateHome,
     withOnyx({
+        report: {
+            key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
+        },
         personalDetails: {
             key: ONYXKEYS.PERSONAL_DETAILS,
         },
diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js
index e91667d4e9..dd09564be2 100755
--- a/src/pages/ReportParticipantsPage.js
+++ b/src/pages/ReportParticipantsPage.js
@@ -19,16 +19,16 @@ import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
 import compose from '../libs/compose';
 import * as ReportUtils from '../libs/ReportUtils';
 import reportPropTypes from './reportPropTypes';
-import withReportOrNavigateHome from './home/report/withReportOrNavigateHome';
+import NoAccessView from '../components/BlockingViews/NoAccessView';

 const propTypes = {
     /* Onyx Props */

     /** The personal details of the person who is logged in */
-    personalDetails: personalDetailsPropType.isRequired,
+    personalDetails: personalDetailsPropType,

     /** The active report */
-    report: reportPropTypes.isRequired,
+    report: reportPropTypes,

     /** Route params */
     route: PropTypes.shape({
@@ -41,6 +41,13 @@ const propTypes = {
     ...withLocalizePropTypes,
 };

+const defaultProps = {
+    report: {
+        isLoadingReportActions: true,
+    },
+    personalDetails: {},
+};
+
 /**
  * Returns all the participants in the active report
  *
@@ -69,8 +76,10 @@ const getAllParticipants = (report, personalDetails) => {
 };

 const ReportParticipantsPage = (props) => {
+    if (!this.props.report.reportID) {
+        return <NoAccessView report={props.report} />;
+    }
     const participants = getAllParticipants(props.report, props.personalDetails);
-
     return (
         <ScreenWrapper>
             <HeaderWithCloseButton
@@ -110,13 +119,16 @@ const ReportParticipantsPage = (props) => {

 ReportParticipantsPage.propTypes = propTypes;
 ReportParticipantsPage.displayName = 'ReportParticipantsPage';
+ReportParticipantsPage.defaultProps = defaultProps;

 export default compose(
     withLocalize,
-    withReportOrNavigateHome,
     withOnyx({
         personalDetails: {
             key: ONYXKEYS.PERSONAL_DETAILS,
         },
+        report: {
+            key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
+        },
     }),
 )(ReportParticipantsPage);
diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js
index 1ff79c3b98..4aa1dbae78 100644
--- a/src/pages/ReportSettingsPage.js
+++ b/src/pages/ReportSettingsPage.js
@@ -20,8 +20,8 @@ import Picker from '../components/Picker';
 import * as ValidationUtils from '../libs/ValidationUtils';
 import OfflineWithFeedback from '../components/OfflineWithFeedback';
 import reportPropTypes from './reportPropTypes';
-import withReportOrNavigateHome from './home/report/withReportOrNavigateHome';
 import Form from '../components/Form';
+import NoAccessView from '../components/BlockingViews/NoAccessView';

 const propTypes = {
     /** Route params */
@@ -37,7 +37,7 @@ const propTypes = {
     /* Onyx Props */

     /** The active report */
-    report: reportPropTypes.isRequired,
+    report: reportPropTypes,

     /** The policies which the user has access to and which the report could be tied to */
     policies: PropTypes.shape({
@@ -46,7 +46,14 @@ const propTypes = {

         /** ID of the policy */
         id: PropTypes.string,
-    }).isRequired,
+    }),
+};
+
+const defaultProps = {
+    report: {
+        isLoadingReportActions: true,
+    },
+    policies: {},
 };

 class ReportSettingsPage extends Component {
@@ -109,6 +116,9 @@ class ReportSettingsPage extends Component {
     }

     render() {
+        if (!this.props.report.reportID) {
+            return <NoAccessView report={this.props.report} />;
+        }
         const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report);
         const shouldDisableRename = ReportUtils.isDefaultRoom(this.props.report)
             || ReportUtils.isArchivedRoom(this.props.report);
@@ -216,10 +226,10 @@ class ReportSettingsPage extends Component {
 }

 ReportSettingsPage.propTypes = propTypes;
+ReportSettingsPage.defaultProps = defaultProps;

 export default compose(
     withLocalize,
-    withReportOrNavigateHome,
     withOnyx({
         policies: {
             key: ONYXKEYS.COLLECTION.POLICY,
@@ -227,5 +237,8 @@ export default compose(
         reports: {
             key: ONYXKEYS.COLLECTION.REPORT,
         },
+        report: {
+            key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
+        },
     }),
 )(ReportSettingsPage);
Option 2: rename the withReportOrNavigateHome to withReportOrNoAccess
diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js
index 952920708e..623217eaa5 100644
--- a/src/libs/actions/Report.js
+++ b/src/libs/actions/Report.js
@@ -353,10 +353,18 @@ function openReport(reportID, participantList = [], newReportObject = {}) {
             isOptimisticReport: false,
         },
     };
+    const reportFailureData = {
+        onyxMethod: CONST.ONYX.METHOD.MERGE,
+        key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
+        value: {
+            isLoadingReportActions: false,
+        },
+    };

     const onyxData = {
         optimisticData: [optimisticReportData],
         successData: [reportSuccessData],
+        failureData: [reportFailureData],
     };

     const params = {
diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js
index b97b31e7e5..27c8574c58 100644
--- a/src/pages/ReportDetailsPage.js
+++ b/src/pages/ReportDetailsPage.js
@@ -22,7 +22,7 @@ import MenuItem from '../components/MenuItem';
 import Text from '../components/Text';
 import CONST from '../CONST';
 import reportPropTypes from './reportPropTypes';
-import withReportOrNavigateHome from './home/report/withReportOrNavigateHome';
+import withReportOrNoAccess from './home/report/withReportOrNoAccess';

 const propTypes = {
     ...withLocalizePropTypes,
@@ -39,7 +39,7 @@ const propTypes = {
     policies: PropTypes.shape({
         /** Name of the policy */
         name: PropTypes.string,
-    }).isRequired,
+    }),

     /** Route params */
     route: PropTypes.shape({
@@ -50,7 +50,12 @@ const propTypes = {
     }).isRequired,

     /** Personal details of all the users */
-    personalDetails: PropTypes.objectOf(participantPropTypes).isRequired,
+    personalDetails: PropTypes.objectOf(participantPropTypes),
+};
+
+const defaultProps = {
+    policies: {},
+    personalDetails: {},
 };

 class ReportDetailsPage extends Component {
@@ -175,10 +180,11 @@ class ReportDetailsPage extends Component {
 }

 ReportDetailsPage.propTypes = propTypes;
+ReportDetailsPage.defaultProps = defaultProps;

 export default compose(
     withLocalize,
-    withReportOrNavigateHome,
+    withReportOrNoAccess,
     withOnyx({
         personalDetails: {
             key: ONYXKEYS.PERSONAL_DETAILS,
diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js
index e91667d4e9..bec3ab1159 100755
--- a/src/pages/ReportParticipantsPage.js
+++ b/src/pages/ReportParticipantsPage.js
@@ -19,13 +19,13 @@ import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
 import compose from '../libs/compose';
 import * as ReportUtils from '../libs/ReportUtils';
 import reportPropTypes from './reportPropTypes';
-import withReportOrNavigateHome from './home/report/withReportOrNavigateHome';
+import withReportOrNoAccess from './home/report/withReportOrNoAccess';

 const propTypes = {
     /* Onyx Props */

     /** The personal details of the person who is logged in */
-    personalDetails: personalDetailsPropType.isRequired,
+    personalDetails: personalDetailsPropType,

     /** The active report */
     report: reportPropTypes.isRequired,
@@ -41,6 +41,10 @@ const propTypes = {
     ...withLocalizePropTypes,
 };

+const defaultProps = {
+    personalDetails: {},
+};
+
 /**
  * Returns all the participants in the active report
  *
@@ -110,10 +114,11 @@ const ReportParticipantsPage = (props) => {

 ReportParticipantsPage.propTypes = propTypes;
 ReportParticipantsPage.displayName = 'ReportParticipantsPage';
+ReportParticipantsPage.defaultProps = defaultProps;

 export default compose(
     withLocalize,
-    withReportOrNavigateHome,
+    withReportOrNoAccess,
     withOnyx({
         personalDetails: {
             key: ONYXKEYS.PERSONAL_DETAILS,
diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js
index 1ff79c3b98..463fe2f1de 100644
--- a/src/pages/ReportSettingsPage.js
+++ b/src/pages/ReportSettingsPage.js
@@ -20,7 +20,7 @@ import Picker from '../components/Picker';
 import * as ValidationUtils from '../libs/ValidationUtils';
 import OfflineWithFeedback from '../components/OfflineWithFeedback';
 import reportPropTypes from './reportPropTypes';
-import withReportOrNavigateHome from './home/report/withReportOrNavigateHome';
+import withReportOrNoAccess from './home/report/withReportOrNoAccess';
 import Form from '../components/Form';

 const propTypes = {
@@ -46,7 +46,11 @@ const propTypes = {

         /** ID of the policy */
         id: PropTypes.string,
-    }).isRequired,
+    }),
+};
+
+const defaultProps = {
+    policies: {},
 };

 class ReportSettingsPage extends Component {
@@ -216,10 +220,11 @@ class ReportSettingsPage extends Component {
 }

 ReportSettingsPage.propTypes = propTypes;
+ReportSettingsPage.defaultProps = defaultProps;

 export default compose(
     withLocalize,
-    withReportOrNavigateHome,
+    withReportOrNoAccess,
     withOnyx({
         policies: {
             key: ONYXKEYS.COLLECTION.POLICY,
diff --git a/src/pages/home/report/withReportOrNavigateHome.js b/src/pages/home/report/withReportOrNavigateHome.js
deleted file mode 100644
index 5e74f65a0c..0000000000
--- a/src/pages/home/report/withReportOrNavigateHome.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {Component} from 'react';
-import {withOnyx} from 'react-native-onyx';
-import _ from 'underscore';
-import getComponentDisplayName from '../../../libs/getComponentDisplayName';
-import Navigation from '../../../libs/Navigation/Navigation';
-import ONYXKEYS from '../../../ONYXKEYS';
-import reportPropTypes from '../../reportPropTypes';
-
-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,
-    };
-
-    const defaultProps = {
-        forwardedRef: () => {},
-        report: {},
-    };
-
-    class WithReportOrNavigateHome extends Component {
-        componentDidMount() {
-            if (!_.isEmpty(this.props.report)) {
-                return;
-            }
-            Navigation.dismissModal();
-        }
-
-        render() {
-            const rest = _.omit(this.props, ['forwardedRef']);
-
-            return (
-                <WrappedComponent
-                    // eslint-disable-next-line react/jsx-props-no-spreading
-                    {...rest}
-                    ref={this.props.forwardedRef}
-                />
-            );
-        }
-    }
-
-    WithReportOrNavigateHome.propTypes = propTypes;
-    WithReportOrNavigateHome.defaultProps = defaultProps;
-    WithReportOrNavigateHome.displayName = `withReportOrNavigateHome(${getComponentDisplayName(WrappedComponent)})`;
-    const withReportOrNavigateHome = React.forwardRef((props, ref) => (
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        <WithReportOrNavigateHome {...props} forwardedRef={ref} />
-    ));
-
-    return withOnyx({
-        report: {
-            key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
-        },
-    })(withReportOrNavigateHome);
-}
diff --git a/src/pages/home/report/withReportOrNoAccess.js b/src/pages/home/report/withReportOrNoAccess.js
new file mode 100644
index 0000000000..0f754f8e82
--- /dev/null
+++ b/src/pages/home/report/withReportOrNoAccess.js
@@ -0,0 +1,72 @@
+import PropTypes from 'prop-types';
+import React, {Component} from 'react';
+import {withOnyx} from 'react-native-onyx';
+import _ from 'underscore';
+import getComponentDisplayName from '../../../libs/getComponentDisplayName';
+import ONYXKEYS from '../../../ONYXKEYS';
+import reportPropTypes from '../../reportPropTypes';
+import FullScreenLoadingIndicator from '../../../components/FullscreenLoadingIndicator';
+import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView';
+import ScreenWrapper from '../../../components/ScreenWrapper';
+
+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,
+    };
+
+    const defaultProps = {
+        forwardedRef: () => {
+        },
+        report: {
+            isLoadingReportActions: true,
+        },
+    };
+
+    class WithReportOrNoAccess extends Component {
+        render() {
+            if (!this.props.report.reportID) {
+                if (this.props.report.isLoadingReportActions) {
+                    return <FullScreenLoadingIndicator />;
+                }
+                return (
+                    <ScreenWrapper>
+                        <FullPageNotFoundView
+                            shouldShow
+                            subtitleKey="notFound.noAccess"
+                            shouldShowCloseButton
+                            shouldShowBackButton={false}
+                        />
+                    </ScreenWrapper>
+                );
+            }
+            const rest = _.omit(this.props, ['forwardedRef']);
+
+            return (
+                <WrappedComponent
+                    // eslint-disable-next-line react/jsx-props-no-spreading
+                    {...rest}
+                    ref={this.props.forwardedRef}
+                />
+            );
+        }
+    }
+
+    WithReportOrNoAccess.propTypes = propTypes;
+    WithReportOrNoAccess.defaultProps = defaultProps;
+    WithReportOrNoAccess.displayName = `withReportOrNoAccess(${getComponentDisplayName(WrappedComponent)})`;
+    const withReportOrNoAccess = React.forwardRef((props, ref) => (
+        // eslint-disable-next-line react/jsx-props-no-spreading
+        <WithReportOrNoAccess {...props} forwardedRef={ref} />
+    ));
+
+    return withOnyx({
+        report: {
+            key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
+        },
+    })(withReportOrNoAccess);
+}
I'm not so sure if a HOC is encouraged to fix this issue. So two options are provided.

Additional change:

diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js
index e9bbeb987d..2b31f89292 100644
--- a/src/libs/Navigation/linkingConfig.js
+++ b/src/libs/Navigation/linkingConfig.js
@@ -1,3 +1,4 @@
+import {getStateFromPath} from '@react-navigation/native';
 import ROUTES from '../../ROUTES';
 import SCREENS from '../../SCREENS';
 import CONST from '../../CONST';
@@ -242,4 +243,13 @@ export default {
             [SCREENS.NOT_FOUND]: '*',
         },
     },
+    getStateFromPath: (path, options) => {
+        const state = getStateFromPath(path, options);
+        if (path.indexOf('/r/') === 0 && state.routes.length > 1) {
+            const reportPath = path.split('/').slice(0, 3).join('/');
+            const reportState = getStateFromPath(reportPath, options);
+            state.routes.splice(0, 1, reportState.routes[0]);
+        }
+        return state;
+    },
 };

This change is intended to allow the app to fetch the same report data according to the reportID from the route, if we visit the details url directly.
Consider that our navigation is being rewritten, maybe we could also hold this issue for the navigation project.

image

@MelvinBot
Copy link

Upwork job price has been updated to $4000

@MelvinBot
Copy link

Current assignee @sakluger is eligible for the External assigner, not assigning anyone new.

@MelvinBot
Copy link

Current assignees @s77rt and @parasharrajat are eligible for the External assigner, not assigning anyone new.

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Feb 24, 2023
@MelvinBot
Copy link

Triggered auto assignment to @pecanoro (External), see https://stackoverflow.com/c/expensify/questions/7972 for more details.

@mountiny
Copy link
Contributor

Sorry Rocio for the bump, Andrew is ooo so this assigned another engineer.

@luacmartins
Copy link
Contributor

@mountiny did you mean to assign and unassign Rocio?

@s77rt
Copy link
Contributor

s77rt commented Feb 24, 2023

Applied in Upwork.
PR is ready.

@s77rt
Copy link
Contributor

s77rt commented Feb 27, 2023

@mountiny Can you please remove the Help Wanted label?

@mountiny mountiny removed the Help Wanted Apply this label when an issue is open to proposals by contributors label Feb 27, 2023
@mountiny
Copy link
Contributor

@s77rt Updated

@luacmartins no, as commented above, that was the External label automation which assigned her because Andrew was ooo at the time.

@MelvinBot
Copy link

@AndrewGable, @sakluger, @s77rt, @parasharrajat, @0xmiroslav Whoops! This issue is 2 days overdue. Let's get this updated quick!

@s77rt
Copy link
Contributor

s77rt commented Mar 6, 2023

@MelvinBot The PR has been deployed to staging / production 4 days / 5 hours ago.

@mountiny mountiny added the Awaiting Payment Auto-added when associated PR is deployed to production label Mar 8, 2023
@mountiny mountiny changed the title [$4000] The details view is broken for the room that is inaccessible to the user [HOLD for payment 2023-03-13] [$4000] The details view is broken for the room that is inaccessible to the user Mar 8, 2023
@parasharrajat parasharrajat removed their assignment Mar 9, 2023
@parasharrajat
Copy link
Member

parasharrajat commented Mar 9, 2023

My assignment was wrong. Bot marked it internal and assign me while proposals were being reviewed by another C+.

@sakluger
Copy link
Contributor

Looks like the automation didn't work perfectly here, so I'll add some items manually. Payments due:

Merged 4 days after Contributor assignment, so pretty quick, but does not qualify for efficiency bonus.

@s77rt I paid you out in Upwork. @adeel0202 @0xmiroslav I send you offers in Upwork, once you accept I can pay out.

Regarding regression testing, I don't think we need to add anything since we'll be updating the details view.

@0xmiros
Copy link
Contributor

0xmiros commented Mar 14, 2023

Merged 4 days after Contributor assignment, so pretty quick, but does not qualify for efficiency bonus.

There was a weekend 🙂

@melvin-bot melvin-bot bot added Daily KSv2 and removed Daily KSv2 labels Mar 14, 2023
@s77rt
Copy link
Contributor

s77rt commented Mar 20, 2023

@sakluger I think timeline bonus applies here as @0xmiroslav clarified those 4 days included the weekend so it's about 2 business days.

@mountiny
Copy link
Contributor

Agree with @0xmiroslav and @s77rt it was merged on Feb 28 evening and @s77rt was assigned in the evening of Feb 24 so it was completed in two days

@sakluger
Copy link
Contributor

sakluger commented Mar 24, 2023

Thanks for clarifying, I added bonuses. Everyone has been paid out now, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor Reviewing Has a PR in review
Projects
None yet
Development

No branches or pull requests