From 5db2bf1491da7bef2b933032d3924d49378b67f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Tue, 14 Nov 2023 16:34:32 +0100 Subject: [PATCH 1/5] wait for protected routes in nav tree after sign in --- src/SCREENS.ts | 13 ++++++-- src/libs/Navigation/Navigation.js | 55 ++++++++++++++++++++++++++++++- src/libs/actions/Report.js | 22 ++++++------- 3 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f7de8cfab4b6..afc368858f55 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -2,15 +2,20 @@ * This is a file containing constants for all of the screen names. In most cases, we should use the routes for * navigation. But there are situations where we may need to access screen names directly. */ -export default { + +const PROTECTED_SCREENS = { HOME: 'Home', + CONCIERGE: 'Concierge', + REPORT_ATTACHMENTS: 'ReportAttachments', +} as const; + +export default { + ...PROTECTED_SCREENS, LOADING: 'Loading', REPORT: 'Report', - REPORT_ATTACHMENTS: 'ReportAttachments', NOT_FOUND: 'not-found', TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps', VALIDATE_LOGIN: 'ValidateLogin', - CONCIERGE: 'Concierge', SETTINGS: { ROOT: 'Settings_Root', PREFERENCES: 'Settings_Preferences', @@ -28,3 +33,5 @@ export default { DESKTOP_SIGN_IN_REDIRECT: 'DesktopSignInRedirect', SAML_SIGN_IN: 'SAMLSignIn', } as const; + +export {PROTECTED_SCREENS}; diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index e12cb5545240..7a2c61ea7b53 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -6,7 +6,7 @@ import Log from '@libs/Log'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; +import SCREENS, {PROTECTED_SCREENS} from '@src/SCREENS'; import getStateFromPath from './getStateFromPath'; import originalGetTopmostReportActionId from './getTopmostReportActionID'; import originalGetTopmostReportId from './getTopmostReportId'; @@ -305,6 +305,57 @@ function setIsNavigationReady() { resolveNavigationIsReadyPromise(); } +/** + * Checks if the navigation state contains routes that are protected (over the auth wall). + * + * @function + * @param {Object} state - react-navigation state object + * + * @returns {Boolean} + */ +function navContainsProtectedRoutes(state) { + if (!state || !state.routeNames || !_.isArray(state.routeNames)) { + return false; + } + + const protectedScreensName = _.values(PROTECTED_SCREENS); + const difference = _.difference(protectedScreensName, state.routeNames); + + return !difference.length; +} + +/** + * Waits for the navitgation state to contain protected routes specified in PROTECTED_SCREENS constant. + * If the navigation is in a state, where protected routes are avilable, the promise resolve immediately. + * + * @function + * @returns {Promise} A promise that resolves when the one of the PROTECTED_SCREENS screen is available in the nav tree. + * + * @example + * waitForProtectedRoutes() + * .then(()=> console.log('Protected routes are present!')) + */ +function waitForProtectedRoutes() { + return new Promise((resolve) => { + isNavigationReady().then(() => { + const currentState = navigationRef.current.getState(); + if (navContainsProtectedRoutes(currentState)) { + resolve(); + return; + } + let unsubscribe; + const handleStateChange = ({data}) => { + const state = lodashGet(data, 'state'); + if (navContainsProtectedRoutes(state)) { + unsubscribe(); + resolve(); + } + }; + unsubscribe = navigationRef.current.addListener('state', handleStateChange); + }); + }); +} + export default { setShouldPopAllStateOnUP, canNavigate, @@ -320,6 +371,8 @@ export default { getTopmostReportId, getRouteNameFromStateEvent, getTopmostReportActionId, + waitForProtectedRoutes, + navContainsProtectedRoutes, }; export {navigationRef}; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 22a1bc5441e6..8ba532b40866 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1984,7 +1984,6 @@ function toggleEmojiReaction(reportID, reportAction, reactionObject, existingRea * @param {Boolean} isAuthenticated */ function openReportFromDeepLink(url, isAuthenticated) { - const route = ReportUtils.getRouteFromLink(url); const reportID = ReportUtils.getReportIDFromLink(url); if (reportID && !isAuthenticated) { @@ -2003,17 +2002,18 @@ function openReportFromDeepLink(url, isAuthenticated) { // Navigate to the report after sign-in/sign-up. InteractionManager.runAfterInteractions(() => { Session.waitForUserSignIn().then(() => { - if (route === ROUTES.CONCIERGE) { - navigateToConciergeChat(true); - return; - } - if (Session.isAnonymousUser() && !Session.canAccessRouteByAnonymousUser(route)) { - Navigation.isNavigationReady().then(() => { + Navigation.waitForProtectedRoutes().then(() => { + const route = ReportUtils.getRouteFromLink(url); + if (route === ROUTES.CONCIERGE) { + navigateToConciergeChat(true); + return; + } + if (Session.isAnonymousUser() && !Session.canAccessRouteByAnonymousUser(route)) { Session.signOutAndRedirectToSignIn(); - }); - return; - } - Navigation.navigate(route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + return; + } + Navigation.navigate(route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + }); }); }); } From 632823899bc7a5ce09e8c149f70302d37ab38787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Tue, 14 Nov 2023 16:57:28 +0100 Subject: [PATCH 2/5] fix/crash when opening enable-payments from link when logged out --- src/libs/actions/PersonalDetails.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index db024e8db4cc..110c4e628fbc 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -1,4 +1,5 @@ import Str from 'expensify-common/lib/str'; +import _ from 'lodash'; import Onyx, {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import * as API from '@libs/API'; import {CustomRNImageManipulatorResult, FileWithUri} from '@libs/cropOrRotateImage/types'; @@ -99,8 +100,8 @@ function extractFirstAndLastNameFromAvailableDetails({login, displayName, firstN return {firstName: '', lastName: ''}; } - const firstSpaceIndex = displayName.indexOf(' '); - const lastSpaceIndex = displayName.lastIndexOf(' '); + const firstSpaceIndex = _.indexOf(displayName, ' '); + const lastSpaceIndex = _.lastIndexOf(displayName, ' '); if (firstSpaceIndex === -1) { return {firstName: displayName, lastName: ''}; } From cc365032f36226ec5e41673061fd702606d9cba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 15 Nov 2023 11:15:15 +0100 Subject: [PATCH 3/5] refactor/optional chaining and nullish coallescing instead of lodash --- src/libs/actions/PersonalDetails.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 110c4e628fbc..c9bdd149ea07 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -1,5 +1,4 @@ import Str from 'expensify-common/lib/str'; -import _ from 'lodash'; import Onyx, {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import * as API from '@libs/API'; import {CustomRNImageManipulatorResult, FileWithUri} from '@libs/cropOrRotateImage/types'; @@ -100,8 +99,8 @@ function extractFirstAndLastNameFromAvailableDetails({login, displayName, firstN return {firstName: '', lastName: ''}; } - const firstSpaceIndex = _.indexOf(displayName, ' '); - const lastSpaceIndex = _.lastIndexOf(displayName, ' '); + const firstSpaceIndex = displayName?.indexOf(' ') ?? -1; + const lastSpaceIndex = displayName?.lastIndexOf(' ') ?? -1; if (firstSpaceIndex === -1) { return {firstName: displayName, lastName: ''}; } From d482fd55948fd56a95d960780e5f2b1fd8b9fdc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 17 Nov 2023 16:44:15 +0100 Subject: [PATCH 4/5] prettier --- src/libs/actions/PersonalDetails.ts | 2 +- src/libs/actions/Report.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index cd5c8f827f09..2d51fbb9e8d2 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -98,7 +98,7 @@ function extractFirstAndLastNameFromAvailableDetails({login, displayName, firstN if (login && Str.removeSMSDomain(login) === displayName) { return {firstName: '', lastName: ''}; } - + if (displayName) { const firstSpaceIndex = displayName.indexOf(' '); const lastSpaceIndex = displayName.lastIndexOf(' '); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 940804fec1ec..d30b69c5f6a2 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2003,7 +2003,6 @@ function openReportFromDeepLink(url, isAuthenticated) { // Navigate to the report after sign-in/sign-up. InteractionManager.runAfterInteractions(() => { - Session.waitForUserSignIn().then(() => { Navigation.waitForProtectedRoutes().then(() => { const route = ReportUtils.getRouteFromLink(url); From c58367181e798c703987bd81975dce1b53fd564d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Fri, 17 Nov 2023 16:53:15 +0100 Subject: [PATCH 5/5] linting --- src/libs/actions/Report.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index d30b69c5f6a2..c225bdf5b65d 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -20,7 +20,6 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import SidebarUtils from '@libs/SidebarUtils'; import * as UserUtils from '@libs/UserUtils'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG';