From 0275158c76cd04454ed2365262db4f4ea5219212 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 4 Apr 2022 15:23:32 -0700 Subject: [PATCH 01/59] Once logged in, wait for betas before navigating --- src/pages/LogInWithShortLivedTokenPage.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 5e091ead6f8..c406e47e01e 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -10,6 +10,9 @@ import Navigation from '../libs/Navigation/Navigation'; import Log from '../libs/Log'; const propTypes = { + /** List of betas available to current user */ + betas: PropTypes.arrayOf(PropTypes.string), + /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ /** The name of the route */ @@ -42,6 +45,7 @@ const propTypes = { }; const defaultProps = { + betas: null, route: { params: {}, }, @@ -74,6 +78,21 @@ class LogInWithShortLivedTokenPage extends Component { Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); return; } + this.navigateToExitRoute(); + } + + componentDidUpdate() { + this.navigateToExitRoute(); + } + + navigateToExitRoute() { + if (!this.props.betas) { + // Wait to navigate until the betas are loaded. Some pages like ReimbursementAccountPage require betas, so keep loading until they are available. + return; + } + + // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") + const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` @@ -111,6 +130,9 @@ LogInWithShortLivedTokenPage.propTypes = propTypes; LogInWithShortLivedTokenPage.defaultProps = defaultProps; export default withOnyx({ + betas: { + key: ONYXKEYS.BETAS, + }, session: { key: ONYXKEYS.SESSION, }, From ae4f49946b030c1607969fb58a855ee93223baa8 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 5 Apr 2022 09:54:50 -0700 Subject: [PATCH 02/59] Remove extra update to session and accountID Prevent multiple updates to the session and store the accountID as int --- src/libs/actions/Session/index.js | 7 +------ src/pages/LogInWithShortLivedTokenPage.js | 3 +-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 719a0a7be7d..9e57592d909 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -255,19 +255,14 @@ function signIn(password, twoFactorAuthCode) { /** * Uses a short lived authToken to continue a user's session from OldDot * - * @param {String} accountID * @param {String} email * @param {String} shortLivedToken * @param {String} exitTo */ -function signInWithShortLivedToken(accountID, email, shortLivedToken) { +function signInWithShortLivedToken(email, shortLivedToken) { Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); createTemporaryLogin(shortLivedToken, email).then((response) => { - Onyx.merge(ONYXKEYS.SESSION, { - accountID, - email, - }); if (response.jsonCode === 200) { User.getUserDetails(); Onyx.merge(ONYXKEYS.ACCOUNT, {success: true}); diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index c406e47e01e..dbb5763ec2a 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -54,14 +54,13 @@ const defaultProps = { class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - const accountID = lodashGet(this.props.route.params, 'accountID', ''); const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); const isUserSignedIn = this.props.session && this.props.session.authToken; if (!isUserSignedIn) { Log.info('[LoginWithShortLivedTokenPage] User not signed in - signing in with short lived token'); - Session.signInWithShortLivedToken(accountID, email, shortLivedToken); + Session.signInWithShortLivedToken(email, shortLivedToken); return; } From 37dfb3bf0ea26988478529ec1484341c5b296a73 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Thu, 28 Apr 2022 11:42:35 -0700 Subject: [PATCH 03/59] Remove beta check on reimbursement account page Everyone is allowed to view this page so don't navigate away if the betas haven't loaded yet --- .../ReimbursementAccountPage.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index b5ef196ff10..60107f7926f 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -5,12 +5,10 @@ import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import Log from '../../libs/Log'; import ScreenWrapper from '../../components/ScreenWrapper'; import * as BankAccounts from '../../libs/actions/BankAccounts'; import ONYXKEYS from '../../ONYXKEYS'; import ReimbursementAccountLoadingIndicator from '../../components/ReimbursementAccountLoadingIndicator'; -import Permissions from '../../libs/Permissions'; import Navigation from '../../libs/Navigation/Navigation'; import CONST from '../../CONST'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; @@ -33,9 +31,6 @@ import reimbursementAccountPropTypes from './reimbursementAccountPropTypes'; import WorkspaceResetBankAccountModal from '../workspace/WorkspaceResetBankAccountModal'; const propTypes = { - /** List of betas */ - betas: PropTypes.arrayOf(PropTypes.string).isRequired, - /** ACH data for the withdrawal account actively being set up */ reimbursementAccount: reimbursementAccountPropTypes, @@ -145,12 +140,6 @@ class ReimbursementAccountPage extends React.Component { } render() { - if (!Permissions.canUseFreePlan(this.props.betas)) { - Log.info('Not showing new bank account page because user is not on free plan beta'); - Navigation.dismissModal(); - return null; - } - // The SetupWithdrawalAccount flow allows us to continue the flow from various points depending on where the // user left off. This view will refer to the achData as the single source of truth to determine which route to // display. We can also specify a specific route to navigate to via route params when the component first @@ -250,9 +239,6 @@ export default compose( session: { key: ONYXKEYS.SESSION, }, - betas: { - key: ONYXKEYS.BETAS, - }, plaidLinkToken: { key: ONYXKEYS.PLAID_LINK_TOKEN, }, From f5ac630d21f34e8231faba8acd9480e009ba3081 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Thu, 28 Apr 2022 11:45:01 -0700 Subject: [PATCH 04/59] Don't wait for betas before navigating --- src/pages/LogInWithShortLivedTokenPage.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index dbb5763ec2a..b44d11f21b7 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -10,9 +10,6 @@ import Navigation from '../libs/Navigation/Navigation'; import Log from '../libs/Log'; const propTypes = { - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), - /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ /** The name of the route */ @@ -45,7 +42,6 @@ const propTypes = { }; const defaultProps = { - betas: null, route: { params: {}, }, @@ -85,11 +81,6 @@ class LogInWithShortLivedTokenPage extends Component { } navigateToExitRoute() { - if (!this.props.betas) { - // Wait to navigate until the betas are loaded. Some pages like ReimbursementAccountPage require betas, so keep loading until they are available. - return; - } - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); @@ -129,9 +120,6 @@ LogInWithShortLivedTokenPage.propTypes = propTypes; LogInWithShortLivedTokenPage.defaultProps = defaultProps; export default withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, session: { key: ONYXKEYS.SESSION, }, From 1ae8690f8cb06071e09a29acc34fe64b88539b03 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 15:38:16 -0700 Subject: [PATCH 05/59] Separate transition page to log out the old user --- src/pages/LogOutOldUserPage.js | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/pages/LogOutOldUserPage.js diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js new file mode 100644 index 00000000000..0189df47f93 --- /dev/null +++ b/src/pages/LogOutOldUserPage.js @@ -0,0 +1,70 @@ +import React, {Component} from 'react'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../ONYXKEYS'; +import * as Session from '../libs/actions/Session'; +import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; +import Log from '../libs/Log'; + +const propTypes = { + /** The parameters needed to authenticate with a short lived token are in the URL */ + route: PropTypes.shape({ + /** The name of the route */ + name: PropTypes.string, + + /** Unique key associated with the route */ + key: PropTypes.string, + + /** Each parameter passed via the URL */ + params: PropTypes.shape({ + /** AccountID associated with the validation link */ + accountID: PropTypes.string, + + /** Short lived token */ + shortLivedToken: PropTypes.string, + + /** URL to exit to */ + exitTo: PropTypes.string, + }), + }), + + /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ + session: PropTypes.shape({ + /** The authToken for the current session */ + authToken: PropTypes.string, + + /** The authToken for the current session */ + email: PropTypes.string, + }), +}; + +const defaultProps = { + route: { + params: {}, + }, + session: {}, +}; + +class LogOutOldUserPage extends Component { + componentDidMount() { + const email = lodashGet(this.props.route.params, 'email', ''); + if (this.props.session && this.props.session.email !== email) { + Log.info('[LogOutOldUserPage] Different user signed in - signing out'); + Session.signOutAndRedirectToSignIn(); + } + } + + render() { + return ; + } +} + +LogOutOldUserPage.propTypes = propTypes; +LogOutOldUserPage.defaultProps = defaultProps; + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(LogOutOldUserPage); From 732517db64b702706d944b04a83773e9b3436b00 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 15:43:01 -0700 Subject: [PATCH 06/59] Only log in the transitioning user --- src/pages/LogInWithShortLivedTokenPage.js | 80 ++--------------------- 1 file changed, 4 insertions(+), 76 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index b44d11f21b7..60648e93481 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -1,12 +1,8 @@ import React, {Component} from 'react'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import ROUTES from '../ROUTES'; -import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Navigation from '../libs/Navigation/Navigation'; import Log from '../libs/Log'; const propTypes = { @@ -30,85 +26,21 @@ const propTypes = { exitTo: PropTypes.string, }), }), - - /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ - session: PropTypes.shape({ - /** The authToken for the current session */ - authToken: PropTypes.string, - - /** The authToken for the current session */ - email: PropTypes.string, - }), }; const defaultProps = { route: { params: {}, }, - session: {}, }; class LogInWithShortLivedTokenPage extends Component { componentDidMount() { + const accountID = lodashGet(this.props.route.params, 'accountID', ''); const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); - - const isUserSignedIn = this.props.session && this.props.session.authToken; - if (!isUserSignedIn) { - Log.info('[LoginWithShortLivedTokenPage] User not signed in - signing in with short lived token'); - Session.signInWithShortLivedToken(email, shortLivedToken); - return; - } - - if (this.signOutIfNeeded(email)) { - return; - } - - Log.info('[LoginWithShortLivedTokenPage] User is signed in'); - - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") - const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); - if (exitTo === ROUTES.WORKSPACE_NEW) { - // New workspace creation is handled in AuthScreens, not in its own screen - Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); - return; - } - this.navigateToExitRoute(); - } - - componentDidUpdate() { - this.navigateToExitRoute(); - } - - navigateToExitRoute() { - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") - const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); - - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. - Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); - Navigation.dismissModal(); - Navigation.navigate(exitTo); - } - - /** - * If the user is trying to transition with a different account than the one - * they are currently signed in as we will sign them out, clear Onyx, - * and cancel all network requests. This component will mount again from - * PublicScreens and since they are no longer signed in, a request will be - * made to sign them in with their new account. - * @param {String} email The user's email passed as a route param. - * @returns {Boolean} - */ - signOutIfNeeded(email) { - if (this.props.session && this.props.session.email === email) { - return false; - } - - Log.info('[LoginWithShortLivedTokenPage] Different user signed in - signing out'); - Session.signOutAndRedirectToSignIn(); - return true; + Log.info('[LoginWithShortLivedTokenPage] signing in the transitioning user'); + Session.signInWithShortLivedToken(accountID, email, shortLivedToken); } render() { @@ -119,8 +51,4 @@ class LogInWithShortLivedTokenPage extends Component { LogInWithShortLivedTokenPage.propTypes = propTypes; LogInWithShortLivedTokenPage.defaultProps = defaultProps; -export default withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, -})(LogInWithShortLivedTokenPage); +export default LogInWithShortLivedTokenPage; From c4a6a67fefe74291e438d64cc7c48928fab9c8d6 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 16:19:03 -0700 Subject: [PATCH 07/59] Separate transition pages for the transition route --- src/ROUTES.js | 2 +- src/SCREENS.js | 2 +- src/libs/Navigation/AppNavigator/AuthScreens.js | 8 ++++---- src/libs/Navigation/AppNavigator/PublicScreens.js | 2 +- src/libs/Navigation/linkingConfig.js | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index 54b27b27334..84be0d68aa7 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -81,7 +81,7 @@ export default { getReportDetailsRoute: reportID => `r/${reportID}/details`, REPORT_SETTINGS: 'r/:reportID/settings', getReportSettingsRoute: reportID => `r/${reportID}/settings`, - LOGIN_WITH_SHORT_LIVED_TOKEN: 'transition', + TRANSITION: 'transition', VALIDATE_LOGIN: 'v/:accountID/:validateCode', GET_ASSISTANCE: 'get-assistance/:taskID', getGetAssistanceRoute: taskID => `get-assistance/${taskID}`, diff --git a/src/SCREENS.js b/src/SCREENS.js index 7b92986721a..3f523838220 100644 --- a/src/SCREENS.js +++ b/src/SCREENS.js @@ -6,5 +6,5 @@ export default { HOME: 'Home', LOADING: 'Loading', REPORT: 'Report', - LOG_IN_WITH_SHORT_LIVED_TOKEN: 'LogInWithShortLivedToken', + TRANSITION: 'Transition', }; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index c64a5f790da..cc50a7d271b 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -38,11 +38,11 @@ import MainDrawerNavigator from './MainDrawerNavigator'; import * as ModalStackNavigators from './ModalStackNavigators'; import SCREENS from '../../../SCREENS'; import Timers from '../../Timers'; -import LogInWithShortLivedTokenPage from '../../../pages/LogInWithShortLivedTokenPage'; import ValidateLoginPage from '../../../pages/ValidateLoginPage'; import defaultScreenOptions from './defaultScreenOptions'; import * as App from '../../actions/App'; import * as Session from '../../actions/Session'; +import LogOutOldUserPage from '../../../pages/LogOutOldUserPage'; Onyx.connect({ key: ONYXKEYS.MY_PERSONAL_DETAILS, @@ -198,7 +198,7 @@ class AuthScreens extends React.Component { const email = params.get('email'); const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); return !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.LOGIN_WITH_SHORT_LIVED_TOKEN)) + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) && exitTo === ROUTES.WORKSPACE_NEW; } @@ -257,9 +257,9 @@ class AuthScreens extends React.Component { component={ValidateLoginPage} /> {/* These are the various modal routes */} diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.js b/src/libs/Navigation/AppNavigator/PublicScreens.js index 2db9f91c47d..f7094377268 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.js +++ b/src/libs/Navigation/AppNavigator/PublicScreens.js @@ -17,7 +17,7 @@ const PublicScreens = () => ( component={SignInPage} /> diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 5b781d5b647..bab85efec76 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -27,7 +27,7 @@ export default { // Main Routes SetPassword: ROUTES.SET_PASSWORD_WITH_VALIDATE_CODE, ValidateLogin: ROUTES.VALIDATE_LOGIN, - [SCREENS.LOG_IN_WITH_SHORT_LIVED_TOKEN]: ROUTES.LOGIN_WITH_SHORT_LIVED_TOKEN, + [SCREENS.TRANSITION]: ROUTES.TRANSITION, // Modal Screens Settings: { From 908a938a2f3a0eb4791e5044f8e7c4c17a83eca6 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 17:12:28 -0700 Subject: [PATCH 08/59] Navigate to transition exit from the auth screens --- .../Navigation/AppNavigator/AuthScreens.js | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index cc50a7d271b..678fd9f0326 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -30,6 +30,7 @@ import * as Policy from '../../actions/Policy'; import modalCardStyleInterpolator from './modalCardStyleInterpolator'; import createCustomModalStackNavigator from './createCustomModalStackNavigator'; import * as BankAccounts from '../../actions/BankAccounts'; +import Log from '../../Log'; // Main drawer navigator import MainDrawerNavigator from './MainDrawerNavigator'; @@ -131,12 +132,25 @@ class AuthScreens extends React.Component { // Load policies, maybe creating a new policy first. Linking.getInitialURL() .then((url) => { - if (this.shouldCreateFreePolicy(url)) { + if (!url) { + return; + } + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; + const shouldCreateFreePolicy = !isLoggingInAsNewUser + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; + if (shouldCreateFreePolicy) { Policy.createAndGetPolicyList(); return; } - Policy.getPolicyList(); + if (!isLoggingInAsNewUser && exitTo) { + this.navigateToExitRoute(exitTo); + } }); // Refresh the personal details, timezone and betas every 30 minutes @@ -184,22 +198,17 @@ class AuthScreens extends React.Component { } /** - * @param {String} [url] - * @returns {Boolean} + * Navigate to the transition exit route + * + * @param {String} exitTo */ - shouldCreateFreePolicy(url = '') { - if (!url) { - return false; - } - - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); - return !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; + navigateToExitRoute(exitTo) { + // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, + // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` + // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); + Navigation.dismissModal(); + Navigation.navigate(exitTo); } render() { From 4c65e1edd803d1d40e0a16c3c52dc51ead31d51f Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 09:39:14 -0700 Subject: [PATCH 09/59] Remove extra param from signInWithShortLivedToken --- src/pages/LogInWithShortLivedTokenPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 60648e93481..4b4e778d8b3 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -36,11 +36,10 @@ const defaultProps = { class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - const accountID = lodashGet(this.props.route.params, 'accountID', ''); const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); Log.info('[LoginWithShortLivedTokenPage] signing in the transitioning user'); - Session.signInWithShortLivedToken(accountID, email, shortLivedToken); + Session.signInWithShortLivedToken(email, shortLivedToken); } render() { From d1800a56659a62b3cb6c93c2fb68be4aa4e9fbb1 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 13:05:32 -0700 Subject: [PATCH 10/59] Navigate in transition page so navigation is ready --- .../Navigation/AppNavigator/AuthScreens.js | 42 ++++++++----------- src/pages/LogOutOldUserPage.js | 18 ++++++++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 678fd9f0326..b63e0b8dd29 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -132,25 +132,12 @@ class AuthScreens extends React.Component { // Load policies, maybe creating a new policy first. Linking.getInitialURL() .then((url) => { - if (!url) { - return; - } - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; - const shouldCreateFreePolicy = !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; - if (shouldCreateFreePolicy) { + if (this.shouldCreateFreePolicy(url)) { Policy.createAndGetPolicyList(); return; } + Policy.getPolicyList(); - if (!isLoggingInAsNewUser && exitTo) { - this.navigateToExitRoute(exitTo); - } }); // Refresh the personal details, timezone and betas every 30 minutes @@ -198,17 +185,22 @@ class AuthScreens extends React.Component { } /** - * Navigate to the transition exit route - * - * @param {String} exitTo + * @param {String} [url] + * @returns {Boolean} */ - navigateToExitRoute(exitTo) { - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. - Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); - Navigation.dismissModal(); - Navigation.navigate(exitTo); + shouldCreateFreePolicy(url = '') { + if (!url) { + return false; + } + + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); + return !isLoggingInAsNewUser + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; } render() { diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 0189df47f93..a031c73edce 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -6,6 +6,8 @@ import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import Log from '../libs/Log'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -52,7 +54,23 @@ class LogOutOldUserPage extends Component { if (this.props.session && this.props.session.email !== email) { Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); + return; } + + // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") + const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); + if (exitTo === ROUTES.WORKSPACE_NEW) { + // New workspace creation is handled in AuthScreens, not in its own screen + Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); + return; + } + + // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, + // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` + // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); + Navigation.dismissModal(); + Navigation.navigate(exitTo); } render() { From 9ac316a7d8a6e6b96cd9e24414071ffd97481873 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 15:04:25 -0700 Subject: [PATCH 11/59] Simplify the comment about dismissing the modal --- src/pages/LogOutOldUserPage.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index a031c73edce..2626948d86c 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -65,9 +65,7 @@ class LogOutOldUserPage extends Component { return; } - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + // We must call dismissModal() to remove the /transition route from history Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); Navigation.dismissModal(); Navigation.navigate(exitTo); From e6478682f5ff0295a67a33739f45e9b00be665cb Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 15:38:25 -0700 Subject: [PATCH 12/59] Revert "Navigate in transition page so navigation is ready" This reverts commit d1800a56659a62b3cb6c93c2fb68be4aa4e9fbb1. --- .../Navigation/AppNavigator/AuthScreens.js | 42 +++++++++++-------- src/pages/LogOutOldUserPage.js | 16 ------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index b63e0b8dd29..678fd9f0326 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -132,12 +132,25 @@ class AuthScreens extends React.Component { // Load policies, maybe creating a new policy first. Linking.getInitialURL() .then((url) => { - if (this.shouldCreateFreePolicy(url)) { + if (!url) { + return; + } + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; + const shouldCreateFreePolicy = !isLoggingInAsNewUser + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; + if (shouldCreateFreePolicy) { Policy.createAndGetPolicyList(); return; } - Policy.getPolicyList(); + if (!isLoggingInAsNewUser && exitTo) { + this.navigateToExitRoute(exitTo); + } }); // Refresh the personal details, timezone and betas every 30 minutes @@ -185,22 +198,17 @@ class AuthScreens extends React.Component { } /** - * @param {String} [url] - * @returns {Boolean} + * Navigate to the transition exit route + * + * @param {String} exitTo */ - shouldCreateFreePolicy(url = '') { - if (!url) { - return false; - } - - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); - return !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; + navigateToExitRoute(exitTo) { + // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, + // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` + // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); + Navigation.dismissModal(); + Navigation.navigate(exitTo); } render() { diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 2626948d86c..0189df47f93 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -6,8 +6,6 @@ import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import Log from '../libs/Log'; -import Navigation from '../libs/Navigation/Navigation'; -import ROUTES from '../ROUTES'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -54,21 +52,7 @@ class LogOutOldUserPage extends Component { if (this.props.session && this.props.session.email !== email) { Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); - return; } - - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") - const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); - if (exitTo === ROUTES.WORKSPACE_NEW) { - // New workspace creation is handled in AuthScreens, not in its own screen - Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); - return; - } - - // We must call dismissModal() to remove the /transition route from history - Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); - Navigation.dismissModal(); - Navigation.navigate(exitTo); } render() { From 019dba97a548382c6b7a7317a4f8cb785dd46df7 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 16:21:38 -0700 Subject: [PATCH 13/59] Navigate in AuthScreens when navigation is ready --- .../Navigation/AppNavigator/AuthScreens.js | 30 ++++++++++--------- src/libs/Navigation/Navigation.js | 5 ++++ src/pages/LogOutOldUserPage.js | 4 +++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 678fd9f0326..1e2c3d678af 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -135,22 +135,24 @@ class AuthScreens extends React.Component { if (!url) { return; } - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; - const shouldCreateFreePolicy = !isLoggingInAsNewUser + Navigation.isNavigationReady().then(() => { + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; + const shouldCreateFreePolicy = !isLoggingInAsNewUser && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) && exitTo === ROUTES.WORKSPACE_NEW; - if (shouldCreateFreePolicy) { - Policy.createAndGetPolicyList(); - return; - } - Policy.getPolicyList(); - if (!isLoggingInAsNewUser && exitTo) { - this.navigateToExitRoute(exitTo); - } + if (shouldCreateFreePolicy) { + Policy.createAndGetPolicyList(); + return; + } + Policy.getPolicyList(); + if (!isLoggingInAsNewUser && exitTo) { + this.navigateToExitRoute(exitTo); + } + }); }); // Refresh the personal details, timezone and betas every 30 minutes diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 360afa67afc..143e59c4eb1 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -11,6 +11,9 @@ import CustomActions from './CustomActions'; import ONYXKEYS from '../../ONYXKEYS'; import linkingConfig from './linkingConfig'; import navigationRef from './navigationRef'; +import createOnReadyTask from '../createOnReadyTask'; + +const [isNavigationReady, setIsNavigationReady] = createOnReadyTask(); let isLoggedIn = false; Onyx.connect({ @@ -227,6 +230,8 @@ export default { closeDrawer, getDefaultDrawerState, setDidTapNotification, + isNavigationReady, + setIsNavigationReady, }; export { diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 0189df47f93..2013f89bc0a 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -6,6 +6,7 @@ import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import Log from '../libs/Log'; +import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -53,6 +54,9 @@ class LogOutOldUserPage extends Component { Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); } + + // Set isNavigationReady so that we can navigate in the AuthScreens + Navigation.setIsNavigationReady(); } render() { From 56a79d29120ffa19d27a64fee9972d73973f1baf Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 21 Mar 2022 19:55:32 -0700 Subject: [PATCH 14/59] Fix create login requests before Network is ready --- src/libs/actions/Session/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index a38d5d9a25e..c5cc82e6e1d 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -170,9 +170,10 @@ function fetchAccountDetails(login) { * * @param {String} authToken * @param {String} email + * @param {Boolean} shouldProcessImmediately * @return {Promise} */ -function createTemporaryLogin(authToken, email) { +function createTemporaryLogin(authToken, email, shouldProcessImmediately = true) { const autoGeneratedLogin = Str.guid('expensify.cash-'); const autoGeneratedPassword = Str.guid(); @@ -184,6 +185,7 @@ function createTemporaryLogin(authToken, email) { partnerUserSecret: autoGeneratedPassword, shouldRetry: false, forceNetworkRequest: true, + shouldProcessImmediately, email, includeEncryptedAuthToken: true, }) @@ -262,7 +264,7 @@ function signIn(password, twoFactorAuthCode) { function signInWithShortLivedToken(email, shortLivedToken) { Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); - createTemporaryLogin(shortLivedToken, email).then((response) => { + createTemporaryLogin(shortLivedToken, email, false).then((response) => { if (response.jsonCode === 200) { User.getUserDetails(); Onyx.merge(ONYXKEYS.ACCOUNT, {success: true}); From 328f385f431ba2018daf2a9d94e34d38b5cd4075 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 6 May 2022 16:28:36 -0700 Subject: [PATCH 15/59] Use the new createOnReadyTask for navigation --- src/libs/Navigation/Navigation.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 143e59c4eb1..fe5b1e54dbe 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -13,7 +13,7 @@ import linkingConfig from './linkingConfig'; import navigationRef from './navigationRef'; import createOnReadyTask from '../createOnReadyTask'; -const [isNavigationReady, setIsNavigationReady] = createOnReadyTask(); +const navigationReadyTask = createOnReadyTask(); let isLoggedIn = false; Onyx.connect({ @@ -45,6 +45,7 @@ function canNavigate(methodName, params = {}) { } Log.hmmm(`[Navigation] ${methodName} failed because navigation ref was not yet ready`, params); + navigationReadyTask.reset(); return false; } @@ -191,6 +192,17 @@ function isActiveRoute(routePath) { return getActiveRoute().substring(1) === routePath; } +/** + * @returns {Boolean} isNavigationReady + */ +function isNavigationReady() { + return navigationReadyTask.isNavigationReady; +} + +function setIsNavigationReady() { + navigationReadyTask.setIsNavigationReady(); +} + /** * Alternative to the `Navigation.dismissModal()` function that we can use inside * the render function of other components to avoid breaking React rules about side-effects. From f4b40d4fcaaab9291cca539111803eebeed9e8a1 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 6 May 2022 16:33:55 -0700 Subject: [PATCH 16/59] Revert "Fix create login requests before Network is ready" This reverts commit 56a79d29120ffa19d27a64fee9972d73973f1baf. --- src/libs/actions/Session/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 5fa77be776c..009c3291544 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -177,10 +177,9 @@ function fetchAccountDetails(login) { * * @param {String} authToken * @param {String} email - * @param {Boolean} shouldProcessImmediately * @return {Promise} */ -function createTemporaryLogin(authToken, email, shouldProcessImmediately = true) { +function createTemporaryLogin(authToken, email) { const autoGeneratedLogin = Str.guid('expensify.cash-'); const autoGeneratedPassword = Str.guid(); @@ -192,7 +191,6 @@ function createTemporaryLogin(authToken, email, shouldProcessImmediately = true) partnerUserSecret: autoGeneratedPassword, shouldRetry: false, forceNetworkRequest: true, - shouldProcessImmediately, email, includeEncryptedAuthToken: true, }) From 8ccf60f59f20b85af5f6bce232ea0670a49fb3ea Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 6 May 2022 16:53:35 -0700 Subject: [PATCH 17/59] Call the proper navigationReadyTask methods --- src/libs/Navigation/Navigation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index fe5b1e54dbe..f9d9e815fcb 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -196,11 +196,11 @@ function isActiveRoute(routePath) { * @returns {Boolean} isNavigationReady */ function isNavigationReady() { - return navigationReadyTask.isNavigationReady; + return navigationReadyTask.isReady(); } function setIsNavigationReady() { - navigationReadyTask.setIsNavigationReady(); + navigationReadyTask.setIsReady(); } /** From 60635af5db0458968f817de7eca25d13f29fdb7b Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 13:51:50 -0700 Subject: [PATCH 18/59] Fix don't show create menu on workspace/new --- src/libs/actions/Welcome.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Welcome.js b/src/libs/actions/Welcome.js index 58935bcf54b..cd2390a216d 100644 --- a/src/libs/actions/Welcome.js +++ b/src/libs/actions/Welcome.js @@ -9,6 +9,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import NameValuePair from './NameValuePair'; import CONST from '../../CONST'; import createOnReadyTask from '../createOnReadyTask'; +import SCREENS from '../../SCREENS'; const readyTask = createOnReadyTask(); @@ -109,8 +110,8 @@ function show({routes, showCreateMenu}) { // If we are rendering the SidebarScreen at the same time as a workspace route that means we've already created a workspace via workspace/new and should not open the global // create menu right now. const topRouteName = lodashGet(_.last(routes), 'name', ''); - const loginWithShortLivedTokenRoute = _.find(routes, route => route.name === 'LogInWithShortLivedToken'); - const exitingToWorkspaceRoute = lodashGet(loginWithShortLivedTokenRoute, 'params.exitTo', '') === 'workspace/new'; + const transitionRoute = _.find(routes, route => route.name === SCREENS.TRANSITION); + const exitingToWorkspaceRoute = lodashGet(transitionRoute, 'params.exitTo', '') === 'workspace/new'; const isDisplayingWorkspaceRoute = topRouteName.toLowerCase().includes('workspace') || exitingToWorkspaceRoute; // If user is not already an admin of a free policy and we are not navigating them to their workspace or creating a new workspace via workspace/new then From 96e7fd4b7aba091fec8e801ddb0d9e9f2f6177e1 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 13:57:18 -0700 Subject: [PATCH 19/59] Simplify comment and remove log lines --- src/libs/Navigation/AppNavigator/AuthScreens.js | 5 +---- src/pages/LogInWithShortLivedTokenPage.js | 2 -- src/pages/LogOutOldUserPage.js | 2 -- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 20df19f9337..b82027e14b3 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -185,10 +185,7 @@ class AuthScreens extends React.Component { * @param {String} exitTo */ navigateToExitRoute(exitTo) { - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. - Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); + // We must call dismissModal() to remove the /transition route from history Navigation.dismissModal(); Navigation.navigate(exitTo); } diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 4b4e778d8b3..4090e3bf071 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -3,7 +3,6 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Log from '../libs/Log'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -38,7 +37,6 @@ class LogInWithShortLivedTokenPage extends Component { componentDidMount() { const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); - Log.info('[LoginWithShortLivedTokenPage] signing in the transitioning user'); Session.signInWithShortLivedToken(email, shortLivedToken); } diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 2013f89bc0a..1047b0d51cc 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Log from '../libs/Log'; import Navigation from '../libs/Navigation/Navigation'; const propTypes = { @@ -51,7 +50,6 @@ class LogOutOldUserPage extends Component { componentDidMount() { const email = lodashGet(this.props.route.params, 'email', ''); if (this.props.session && this.props.session.email !== email) { - Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); } From d47dce8443f8f1678e09c88c11f8d9c9646b0595 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:01:46 -0700 Subject: [PATCH 20/59] Rename LogOutOldUserPage to LogOutPreviousUserPage --- src/libs/Navigation/AppNavigator/AuthScreens.js | 4 ++-- .../{LogOutOldUserPage.js => LogOutPreviousUserPage.js} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/pages/{LogOutOldUserPage.js => LogOutPreviousUserPage.js} (91%) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index b82027e14b3..c3c070d6bf1 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -39,7 +39,7 @@ import ValidateLoginPage from '../../../pages/ValidateLoginPage'; import defaultScreenOptions from './defaultScreenOptions'; import * as App from '../../actions/App'; import * as Session from '../../actions/Session'; -import LogOutOldUserPage from '../../../pages/LogOutOldUserPage'; +import LogOutPreviousUserPage from '../../../pages/LogOutPreviousUserPage'; import networkPropTypes from '../../../components/networkPropTypes'; import {withNetwork} from '../../../components/OnyxProvider'; @@ -247,7 +247,7 @@ class AuthScreens extends React.Component { {/* These are the various modal routes */} diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutPreviousUserPage.js similarity index 91% rename from src/pages/LogOutOldUserPage.js rename to src/pages/LogOutPreviousUserPage.js index 1047b0d51cc..c1521b6da4d 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -46,7 +46,7 @@ const defaultProps = { session: {}, }; -class LogOutOldUserPage extends Component { +class LogOutPreviousUserPage extends Component { componentDidMount() { const email = lodashGet(this.props.route.params, 'email', ''); if (this.props.session && this.props.session.email !== email) { @@ -62,11 +62,11 @@ class LogOutOldUserPage extends Component { } } -LogOutOldUserPage.propTypes = propTypes; -LogOutOldUserPage.defaultProps = defaultProps; +LogOutPreviousUserPage.propTypes = propTypes; +LogOutPreviousUserPage.defaultProps = defaultProps; export default withOnyx({ session: { key: ONYXKEYS.SESSION, }, -})(LogOutOldUserPage); +})(LogOutPreviousUserPage); From 6dd45735ed97ca64a63eea6d1641e9eb5fe975e3 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:21:47 -0700 Subject: [PATCH 21/59] Only set navigation ready when we are logged in. --- src/pages/LogOutPreviousUserPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index c1521b6da4d..39580964138 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -51,6 +51,7 @@ class LogOutPreviousUserPage extends Component { const email = lodashGet(this.props.route.params, 'email', ''); if (this.props.session && this.props.session.email !== email) { Session.signOutAndRedirectToSignIn(); + return; } // Set isNavigationReady so that we can navigate in the AuthScreens From b490551844d7842b29654e288d074bacd3ec227b Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:29:03 -0700 Subject: [PATCH 22/59] Clean up default props and their use --- src/pages/LogOutPreviousUserPage.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 39580964138..e534bba2d2c 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -40,16 +40,15 @@ const propTypes = { }; const defaultProps = { - route: { - params: {}, - }, - session: {}, + route: null, + session: null, }; class LogOutPreviousUserPage extends Component { componentDidMount() { - const email = lodashGet(this.props.route.params, 'email', ''); - if (this.props.session && this.props.session.email !== email) { + const paramsEmail = lodashGet(this.props, 'route.params.email', null); + const sessionEmail = lodashGet(this.props.session, 'email', ''); + if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); return; } From 174e012b46cd09b90dc647717854f50387d8edf0 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:32:29 -0700 Subject: [PATCH 23/59] Fix shouldCreateFreePolicy indentation --- src/libs/Navigation/AppNavigator/AuthScreens.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index c3c070d6bf1..15996f49d30 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -122,8 +122,8 @@ class AuthScreens extends React.Component { const email = params.get('email'); const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; const shouldCreateFreePolicy = !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; if (shouldCreateFreePolicy) { Policy.createAndGetPolicyList(); return; From 607e48dfe372184a93af47fc8f628a366ab2eb3e Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 15:06:54 -0700 Subject: [PATCH 24/59] Remove unused Log import --- src/libs/Navigation/AppNavigator/AuthScreens.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 15996f49d30..2d2c14258e4 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -26,7 +26,6 @@ import * as Modal from '../../actions/Modal'; import * as Policy from '../../actions/Policy'; import modalCardStyleInterpolator from './modalCardStyleInterpolator'; import createCustomModalStackNavigator from './createCustomModalStackNavigator'; -import Log from '../../Log'; // Main drawer navigator import MainDrawerNavigator from './MainDrawerNavigator'; From dd54853e7295bd50aa1ca79daf64dac6a225f39c Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 16:08:24 -0700 Subject: [PATCH 25/59] Clean up propTypes and props more --- src/pages/LogInWithShortLivedTokenPage.js | 23 ++++++----------------- src/pages/LogOutPreviousUserPage.js | 21 +++------------------ 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 4090e3bf071..808e7698467 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -7,36 +7,25 @@ import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ - /** The name of the route */ - name: PropTypes.string, - - /** Unique key associated with the route */ - key: PropTypes.string, - /** Each parameter passed via the URL */ params: PropTypes.shape({ - /** AccountID associated with the validation link */ - accountID: PropTypes.string, - - /** Short lived token */ + /** Short lived token to sign in a user */ shortLivedToken: PropTypes.string, - /** URL to exit to */ - exitTo: PropTypes.string, + /** The email of the transitioning user */ + email: PropTypes.string, }), }), }; const defaultProps = { - route: { - params: {}, - }, + route: null, }; class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - const email = lodashGet(this.props.route.params, 'email', ''); - const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); + const email = lodashGet(this.props, 'route.params.email', ''); + const shortLivedToken = lodashGet(this.props, 'route.params.shortLivedToken', ''); Session.signInWithShortLivedToken(email, shortLivedToken); } diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index e534bba2d2c..0a4f521b318 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -10,31 +10,16 @@ import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ - /** The name of the route */ - name: PropTypes.string, - - /** Unique key associated with the route */ - key: PropTypes.string, - /** Each parameter passed via the URL */ params: PropTypes.shape({ - /** AccountID associated with the validation link */ - accountID: PropTypes.string, - - /** Short lived token */ - shortLivedToken: PropTypes.string, - - /** URL to exit to */ - exitTo: PropTypes.string, + /** The email of the transitioning user */ + email: PropTypes.string, }), }), /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ session: PropTypes.shape({ - /** The authToken for the current session */ - authToken: PropTypes.string, - - /** The authToken for the current session */ + /** The user's email for the current session */ email: PropTypes.string, }), }; From 468c5a053140103bad2723529891f6f398e2bdc5 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 17 May 2022 09:16:46 -0700 Subject: [PATCH 26/59] Use a NavigationReadyDetector in the navigator --- .../AppNavigator/NavigationReadyDetector.js | 14 ++++++++++++++ src/libs/Navigation/AppNavigator/index.js | 6 +++++- src/pages/LogOutPreviousUserPage.js | 5 ----- 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 src/libs/Navigation/AppNavigator/NavigationReadyDetector.js diff --git a/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js b/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js new file mode 100644 index 00000000000..8b23947bc95 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js @@ -0,0 +1,14 @@ +import React from 'react'; +import Navigation from '../Navigation'; + +class NavigationReadyDetector extends React.Component { + componentDidUpdate() { + Navigation.setIsNavigationReady(); + } + + render() { + return null; + } +} + +export default NavigationReadyDetector; diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index 6a7b910ebae..ac8180d3261 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import PublicScreens from './PublicScreens'; import AuthScreens from './AuthScreens'; +import NavigationReadyDetector from './NavigationReadyDetector'; const propTypes = { /** If we have an authToken this is true */ @@ -13,7 +14,10 @@ const AppNavigator = props => ( ? ( // These are the protected screens and only accessible when an authToken is present - + <> + + + ) : ( diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 0a4f521b318..8b2891ec615 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -35,11 +34,7 @@ class LogOutPreviousUserPage extends Component { const sessionEmail = lodashGet(this.props.session, 'email', ''); if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); - return; } - - // Set isNavigationReady so that we can navigate in the AuthScreens - Navigation.setIsNavigationReady(); } render() { From f90190425b952d1ce4be50a12b7ba3627f96f993 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Wed, 18 May 2022 15:07:51 -0700 Subject: [PATCH 27/59] Initial wait for onyx to clear --- .../AppNavigator/NavigationReadyDetector.js | 6 ++++- src/libs/Navigation/Navigation.js | 5 ++++ src/libs/actions/SignInRedirect.js | 3 +++ src/libs/waitForOnyxToClear.js | 23 +++++++++++++++++++ src/pages/LogInWithShortLivedTokenPage.js | 10 +++++--- 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 src/libs/waitForOnyxToClear.js diff --git a/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js b/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js index 8b23947bc95..d7cb05dcfcf 100644 --- a/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js +++ b/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js @@ -2,10 +2,14 @@ import React from 'react'; import Navigation from '../Navigation'; class NavigationReadyDetector extends React.Component { - componentDidUpdate() { + componentDidMount() { Navigation.setIsNavigationReady(); } + componentWillUnmount() { + Navigation.resetIsNavigationReady(); + } + render() { return null; } diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index f9d9e815fcb..892700112c5 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -203,6 +203,10 @@ function setIsNavigationReady() { navigationReadyTask.setIsReady(); } +function resetIsNavigationReady() { + navigationReadyTask.reset(); +} + /** * Alternative to the `Navigation.dismissModal()` function that we can use inside * the render function of other components to avoid breaking React rules about side-effects. @@ -244,6 +248,7 @@ export default { setDidTapNotification, isNavigationReady, setIsNavigationReady, + resetIsNavigationReady, }; export { diff --git a/src/libs/actions/SignInRedirect.js b/src/libs/actions/SignInRedirect.js index ffde6f2f955..d0cebf677f2 100644 --- a/src/libs/actions/SignInRedirect.js +++ b/src/libs/actions/SignInRedirect.js @@ -1,6 +1,7 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; import * as MainQueue from '../Network/MainQueue'; +import * as waitForOnyxToClear from '../waitForOnyxToClear'; let currentActiveClients; Onyx.connect({ @@ -24,8 +25,10 @@ function clearStorageAndRedirect(errorMessage) { const preferredLocale = currentPreferredLocale; // Clearing storage discards the authToken. This causes a redirect to the SignIn screen + waitForOnyxToClear.reset(); Onyx.clear() .then(() => { + waitForOnyxToClear.setIsReady(); if (preferredLocale) { Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, preferredLocale); } diff --git a/src/libs/waitForOnyxToClear.js b/src/libs/waitForOnyxToClear.js new file mode 100644 index 00000000000..53b58533c6a --- /dev/null +++ b/src/libs/waitForOnyxToClear.js @@ -0,0 +1,23 @@ +import createOnReadyTask from './createOnReadyTask'; + +const onyxClearReadyTask = createOnReadyTask(); +onyxClearReadyTask.setIsReady(); + +function waitForOnyxToClear() { + return onyxClearReadyTask.isReady(); +} + +function reset() { + onyxClearReadyTask.reset(); +} + +function setIsReady() { + onyxClearReadyTask.setIsReady(); +} + +export default waitForOnyxToClear; + +export { + reset, + setIsReady, +}; diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 808e7698467..7e05eb459c5 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -3,6 +3,7 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; +import waitForOnyxToClear from '../libs/waitForOnyxToClear'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -24,9 +25,12 @@ const defaultProps = { class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - const email = lodashGet(this.props, 'route.params.email', ''); - const shortLivedToken = lodashGet(this.props, 'route.params.shortLivedToken', ''); - Session.signInWithShortLivedToken(email, shortLivedToken); + waitForOnyxToClear() + .then(() => { + const email = lodashGet(this.props, 'route.params.email', ''); + const shortLivedToken = lodashGet(this.props, 'route.params.shortLivedToken', ''); + Session.signInWithShortLivedToken(email, shortLivedToken); + }); } render() { From d34921aa91bae8582d5c357dd229ac149183b4f8 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Thu, 19 May 2022 11:56:14 -0700 Subject: [PATCH 28/59] Reset required data ready task on log out --- src/libs/Network/NetworkStore.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libs/Network/NetworkStore.js b/src/libs/Network/NetworkStore.js index 81fd59e4c8a..5dffdef6432 100644 --- a/src/libs/Network/NetworkStore.js +++ b/src/libs/Network/NetworkStore.js @@ -14,22 +14,23 @@ let authenticating = false; const [triggerConnectivityResumed, onConnectivityResumed] = createCallback(); const requiredDataReadyTask = createOnReadyTask(); +function resetHasReadRequiredDataFromStorage() { + requiredDataReadyTask.reset(); +} + /** * This is a hack to workaround the fact that Onyx may not yet have read these values from storage by the time Network starts processing requests. * If the values are undefined we haven't read them yet. If they are null or have a value then we have and the network is "ready". */ function checkRequiredData() { - if (_.isUndefined(authToken) || _.isUndefined(credentials)) { + if (!authToken || !credentials) { + resetHasReadRequiredDataFromStorage(); return; } requiredDataReadyTask.setIsReady(); } -function resetHasReadRequiredDataFromStorage() { - requiredDataReadyTask.reset(); -} - Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { From f976228d6425967a24066f14767d38e6e75e1b80 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Thu, 19 May 2022 13:54:21 -0700 Subject: [PATCH 29/59] Revert "Reset required data ready task on log out" This reverts commit d34921aa91bae8582d5c357dd229ac149183b4f8. --- src/libs/Network/NetworkStore.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libs/Network/NetworkStore.js b/src/libs/Network/NetworkStore.js index 5dffdef6432..81fd59e4c8a 100644 --- a/src/libs/Network/NetworkStore.js +++ b/src/libs/Network/NetworkStore.js @@ -14,23 +14,22 @@ let authenticating = false; const [triggerConnectivityResumed, onConnectivityResumed] = createCallback(); const requiredDataReadyTask = createOnReadyTask(); -function resetHasReadRequiredDataFromStorage() { - requiredDataReadyTask.reset(); -} - /** * This is a hack to workaround the fact that Onyx may not yet have read these values from storage by the time Network starts processing requests. * If the values are undefined we haven't read them yet. If they are null or have a value then we have and the network is "ready". */ function checkRequiredData() { - if (!authToken || !credentials) { - resetHasReadRequiredDataFromStorage(); + if (_.isUndefined(authToken) || _.isUndefined(credentials)) { return; } requiredDataReadyTask.setIsReady(); } +function resetHasReadRequiredDataFromStorage() { + requiredDataReadyTask.reset(); +} + Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { From 511cc68dfc2ab3fd2ca29ea5ca6cbf43770fc5e2 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 20 May 2022 11:52:17 -0700 Subject: [PATCH 30/59] Don't navigate to the workspace chat on transition --- src/libs/actions/Welcome.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Welcome.js b/src/libs/actions/Welcome.js index b5dba4120ee..f1de0598b64 100644 --- a/src/libs/actions/Welcome.js +++ b/src/libs/actions/Welcome.js @@ -100,20 +100,20 @@ function show({routes, showCreateMenu}) { // Set the NVP back to false so we don't automatically run welcome actions again NameValuePair.set(CONST.NVP.IS_FIRST_TIME_NEW_EXPENSIFY_USER, false, ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER); - // We want to display the Workspace chat first since that means a user is already in a Workspace and doesn't need to create another one - const workspaceChatReport = _.find(allReports, report => ReportUtils.isPolicyExpenseChat(report)); - if (workspaceChatReport) { - Navigation.navigate(ROUTES.getReportRoute(workspaceChatReport.reportID)); - return; - } - // If we are rendering the SidebarScreen at the same time as a workspace route that means we've already created a workspace via workspace/new and should not open the global - // create menu right now. + // create menu right now. We should also stay on the workspace page if that is our destination. const topRouteName = lodashGet(_.last(routes), 'name', ''); const transitionRoute = _.find(routes, route => route.name === SCREENS.TRANSITION); const exitingToWorkspaceRoute = lodashGet(transitionRoute, 'params.exitTo', '') === 'workspace/new'; const isDisplayingWorkspaceRoute = topRouteName.toLowerCase().includes('workspace') || exitingToWorkspaceRoute; + // We want to display the Workspace chat first since that means a user is already in a Workspace and doesn't need to create another one + const workspaceChatReport = _.find(allReports, report => ReportUtils.isPolicyExpenseChat(report)); + if (workspaceChatReport && !isDisplayingWorkspaceRoute) { + Navigation.navigate(ROUTES.getReportRoute(workspaceChatReport.reportID)); + return; + } + // If user is not already an admin of a free policy and we are not navigating them to their workspace or creating a new workspace via workspace/new then // we will show the create menu. if (!Policy.isAdminOfFreePolicy(allPolicies) && !isDisplayingWorkspaceRoute) { From 3a158f399c724ffa39ad2614307da5a068d1141a Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 20 May 2022 15:38:55 -0700 Subject: [PATCH 31/59] Navigation is ready once the user has transitioned --- .../AppNavigator/NavigationReadyDetector.js | 18 ------------------ src/libs/Navigation/AppNavigator/index.js | 2 -- src/pages/LogOutPreviousUserPage.js | 5 +++++ 3 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 src/libs/Navigation/AppNavigator/NavigationReadyDetector.js diff --git a/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js b/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js deleted file mode 100644 index d7cb05dcfcf..00000000000 --- a/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import Navigation from '../Navigation'; - -class NavigationReadyDetector extends React.Component { - componentDidMount() { - Navigation.setIsNavigationReady(); - } - - componentWillUnmount() { - Navigation.resetIsNavigationReady(); - } - - render() { - return null; - } -} - -export default NavigationReadyDetector; diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index ac8180d3261..cf6797251b1 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import PublicScreens from './PublicScreens'; import AuthScreens from './AuthScreens'; -import NavigationReadyDetector from './NavigationReadyDetector'; const propTypes = { /** If we have an authToken this is true */ @@ -16,7 +15,6 @@ const AppNavigator = props => ( // These are the protected screens and only accessible when an authToken is present <> - ) : ( diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 8b2891ec615..0a4f521b318 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; +import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -34,7 +35,11 @@ class LogOutPreviousUserPage extends Component { const sessionEmail = lodashGet(this.props.session, 'email', ''); if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); + return; } + + // Set isNavigationReady so that we can navigate in the AuthScreens + Navigation.setIsNavigationReady(); } render() { From 35a18a0a2eacfa6cb5980a553c0be7d16c41df46 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 09:19:59 -0700 Subject: [PATCH 32/59] Get the policy list even if there's no initial URL --- src/libs/Navigation/AppNavigator/AuthScreens.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index cac192e785b..a721a83b128 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -114,6 +114,7 @@ class AuthScreens extends React.Component { Linking.getInitialURL() .then((url) => { if (!url) { + Policy.getPolicyList(); return; } Navigation.isNavigationReady().then(() => { From 6607d59cc7fd031069342c3cf7304d4d3fda1bcc Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 09:21:26 -0700 Subject: [PATCH 33/59] Remove leftover fragment tags --- src/libs/Navigation/AppNavigator/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index cf6797251b1..6a7b910ebae 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -13,9 +13,7 @@ const AppNavigator = props => ( ? ( // These are the protected screens and only accessible when an authToken is present - <> - - + ) : ( From ecdbe41e82afca73112d1a550403ecb23544ae4f Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 09:22:53 -0700 Subject: [PATCH 34/59] Add JSDoc to waitForOnyxToClear() --- src/libs/waitForOnyxToClear.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/waitForOnyxToClear.js b/src/libs/waitForOnyxToClear.js index 53b58533c6a..5e1cdf7ce8f 100644 --- a/src/libs/waitForOnyxToClear.js +++ b/src/libs/waitForOnyxToClear.js @@ -3,6 +3,9 @@ import createOnReadyTask from './createOnReadyTask'; const onyxClearReadyTask = createOnReadyTask(); onyxClearReadyTask.setIsReady(); +/** + * @returns {Promise} + */ function waitForOnyxToClear() { return onyxClearReadyTask.isReady(); } From bb8bf06226219135493d6580260a6f6ee48396dc Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 09:27:39 -0700 Subject: [PATCH 35/59] Make transition page props required --- src/pages/LogInWithShortLivedTokenPage.js | 7 +------ src/pages/LogOutPreviousUserPage.js | 10 ++-------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 7e05eb459c5..f769690f06b 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -16,11 +16,7 @@ const propTypes = { /** The email of the transitioning user */ email: PropTypes.string, }), - }), -}; - -const defaultProps = { - route: null, + }).isRequired, }; class LogInWithShortLivedTokenPage extends Component { @@ -39,6 +35,5 @@ class LogInWithShortLivedTokenPage extends Component { } LogInWithShortLivedTokenPage.propTypes = propTypes; -LogInWithShortLivedTokenPage.defaultProps = defaultProps; export default LogInWithShortLivedTokenPage; diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 0a4f521b318..c2acde07f51 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -15,18 +15,13 @@ const propTypes = { /** The email of the transitioning user */ email: PropTypes.string, }), - }), + }).isRequired, /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ session: PropTypes.shape({ /** The user's email for the current session */ email: PropTypes.string, - }), -}; - -const defaultProps = { - route: null, - session: null, + }).isRequired, }; class LogOutPreviousUserPage extends Component { @@ -48,7 +43,6 @@ class LogOutPreviousUserPage extends Component { } LogOutPreviousUserPage.propTypes = propTypes; -LogOutPreviousUserPage.defaultProps = defaultProps; export default withOnyx({ session: { From 7e3030cb9fcd91613b66c1134435633d6ac2275f Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 11:08:51 -0700 Subject: [PATCH 36/59] Get params.email directly from the required route --- src/pages/LogOutPreviousUserPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index c2acde07f51..9cdd68777fa 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -26,7 +26,7 @@ const propTypes = { class LogOutPreviousUserPage extends Component { componentDidMount() { - const paramsEmail = lodashGet(this.props, 'route.params.email', null); + const paramsEmail = lodashGet(this.props.route, 'params.email', null); const sessionEmail = lodashGet(this.props.session, 'email', ''); if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); From f291838d6aef4d8723308bf0b78884d0d1bd7877 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 11:11:45 -0700 Subject: [PATCH 37/59] Navigate to exit route without a separate function --- src/libs/Navigation/AppNavigator/AuthScreens.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index a721a83b128..f4b42905564 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -132,7 +132,9 @@ class AuthScreens extends React.Component { } Policy.getPolicyList(); if (!isLoggingInAsNewUser && exitTo) { - this.navigateToExitRoute(exitTo); + // We must call dismissModal() to remove the /transition route from history + Navigation.dismissModal(); + Navigation.navigate(exitTo); } }); }); @@ -181,17 +183,6 @@ class AuthScreens extends React.Component { this.interval = null; } - /** - * Navigate to the transition exit route - * - * @param {String} exitTo - */ - navigateToExitRoute(exitTo) { - // We must call dismissModal() to remove the /transition route from history - Navigation.dismissModal(); - Navigation.navigate(exitTo); - } - render() { const commonModalScreenOptions = { headerShown: false, From 326b3b117549279ed6e9efa5824332ea72a5893d Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 11:43:20 -0700 Subject: [PATCH 38/59] Use a promise directly to wait for Onyx to clear --- src/libs/actions/SignInRedirect.js | 4 ++-- src/libs/waitForOnyxToClear.js | 20 +++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/SignInRedirect.js b/src/libs/actions/SignInRedirect.js index d0cebf677f2..2476cb06bfa 100644 --- a/src/libs/actions/SignInRedirect.js +++ b/src/libs/actions/SignInRedirect.js @@ -25,10 +25,10 @@ function clearStorageAndRedirect(errorMessage) { const preferredLocale = currentPreferredLocale; // Clearing storage discards the authToken. This causes a redirect to the SignIn screen - waitForOnyxToClear.reset(); + waitForOnyxToClear.setOnyxClearing(); Onyx.clear() .then(() => { - waitForOnyxToClear.setIsReady(); + waitForOnyxToClear.setOnyxDoneClearing(); if (preferredLocale) { Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, preferredLocale); } diff --git a/src/libs/waitForOnyxToClear.js b/src/libs/waitForOnyxToClear.js index 5e1cdf7ce8f..7ca753acc13 100644 --- a/src/libs/waitForOnyxToClear.js +++ b/src/libs/waitForOnyxToClear.js @@ -1,26 +1,24 @@ -import createOnReadyTask from './createOnReadyTask'; - -const onyxClearReadyTask = createOnReadyTask(); -onyxClearReadyTask.setIsReady(); +// By default Onyx is done clearing +let onyxClearPromise = Promise.resolve(); /** * @returns {Promise} */ function waitForOnyxToClear() { - return onyxClearReadyTask.isReady(); + return onyxClearPromise; } -function reset() { - onyxClearReadyTask.reset(); +function setOnyxClearing() { + onyxClearPromise = new Promise(); } -function setIsReady() { - onyxClearReadyTask.setIsReady(); +function setOnyxDoneClearing() { + onyxClearPromise.resolve(); } export default waitForOnyxToClear; export { - reset, - setIsReady, + setOnyxClearing, + setOnyxDoneClearing, }; From ed16b924c485b339b3d517a777e983a69e1d3cc2 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 13:45:57 -0700 Subject: [PATCH 39/59] Update the navigation readiness based on its state --- src/libs/Navigation/NavigationRoot.js | 7 +++++-- src/pages/LogOutPreviousUserPage.js | 4 ---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index 80c7dedec07..502ccfd076c 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -1,7 +1,8 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {getPathFromState, NavigationContainer, DefaultTheme} from '@react-navigation/native'; -import * as Navigation from './Navigation'; +import navigationRef from './navigationRef'; +import Navigation from './Navigation'; import linkingConfig from './linkingConfig'; import AppNavigator from './AppNavigator'; import * as App from '../actions/App'; @@ -40,8 +41,10 @@ class NavigationRoot extends Component { */ parseAndStoreRoute(state) { if (!state) { + Navigation.resetIsNavigationReady(); return; } + Navigation.setIsNavigationReady(); const path = getPathFromState(state, linkingConfig.config); @@ -66,7 +69,7 @@ class NavigationRoot extends Component { onStateChange={this.parseAndStoreRoute} onReady={this.props.onReady} theme={navigationTheme} - ref={Navigation.navigationRef} + ref={navigationRef} linking={linkingConfig} documentTitle={{ enabled: false, diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 9cdd68777fa..d5c41804e6b 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -30,11 +30,7 @@ class LogOutPreviousUserPage extends Component { const sessionEmail = lodashGet(this.props.session, 'email', ''); if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); - return; } - - // Set isNavigationReady so that we can navigate in the AuthScreens - Navigation.setIsNavigationReady(); } render() { From a7cb78eceb30753af4f52391e0f2a32779f8b43a Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 14:06:42 -0700 Subject: [PATCH 40/59] Remove unused import --- src/pages/LogOutPreviousUserPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index d5c41804e6b..23f837d8e7c 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ From f0bc0f8b031f70b08b012dacb4a43a39fb0e94d7 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 14:09:45 -0700 Subject: [PATCH 41/59] Track navigation readiness with a promise directly --- src/libs/Navigation/Navigation.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 7e90113109d..c19f10c635d 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -9,9 +9,11 @@ import CustomActions from './CustomActions'; import ONYXKEYS from '../../ONYXKEYS'; import linkingConfig from './linkingConfig'; import navigationRef from './navigationRef'; -import createOnReadyTask from '../createOnReadyTask'; -const navigationReadyTask = createOnReadyTask(); +let resolveNavigationIsReadyPromise; +let navigationIsReadyPromise = new Promise((resolve) => { + resolveNavigationIsReadyPromise = resolve; +}); let isLoggedIn = false; Onyx.connect({ @@ -43,7 +45,6 @@ function canNavigate(methodName, params = {}) { } Log.hmmm(`[Navigation] ${methodName} failed because navigation ref was not yet ready`, params); - navigationReadyTask.reset(); return false; } @@ -191,18 +192,20 @@ function isActiveRoute(routePath) { } /** - * @returns {Boolean} isNavigationReady + * @returns {Promise} */ function isNavigationReady() { - return navigationReadyTask.isReady(); + return navigationIsReadyPromise; } function setIsNavigationReady() { - navigationReadyTask.setIsReady(); + resolveNavigationIsReadyPromise(); } function resetIsNavigationReady() { - navigationReadyTask.reset(); + navigationIsReadyPromise = new Promise((resolve) => { + resolveNavigationIsReadyPromise = resolve; + }); } export default { From 3cd291dc72e20b0a3c27975c7e37f7a2755dcab2 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 14:14:40 -0700 Subject: [PATCH 42/59] Fix waitForOnyxToClear promises --- src/libs/waitForOnyxToClear.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libs/waitForOnyxToClear.js b/src/libs/waitForOnyxToClear.js index 7ca753acc13..1cc0d0ce7b2 100644 --- a/src/libs/waitForOnyxToClear.js +++ b/src/libs/waitForOnyxToClear.js @@ -1,5 +1,10 @@ +let resolveOnyxClearPromise; +let onyxClearPromise = new Promise((resolve) => { + resolveOnyxClearPromise = resolve; +}); + // By default Onyx is done clearing -let onyxClearPromise = Promise.resolve(); +resolveOnyxClearPromise(); /** * @returns {Promise} @@ -9,11 +14,13 @@ function waitForOnyxToClear() { } function setOnyxClearing() { - onyxClearPromise = new Promise(); + onyxClearPromise = new Promise((resolve) => { + resolveOnyxClearPromise = resolve; + }); } function setOnyxDoneClearing() { - onyxClearPromise.resolve(); + resolveOnyxClearPromise(); } export default waitForOnyxToClear; From f3b265d4a1785fe8fa3114869eacc886186ea260 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 14:19:57 -0700 Subject: [PATCH 43/59] Rename transition to transitionFromOldDot --- src/ROUTES.js | 2 +- src/SCREENS.js | 2 +- src/libs/Navigation/AppNavigator/AuthScreens.js | 4 ++-- src/libs/Navigation/AppNavigator/PublicScreens.js | 2 +- src/libs/Navigation/linkingConfig.js | 2 +- src/libs/actions/Welcome.js | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index 84be0d68aa7..5b4140399ad 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -81,7 +81,7 @@ export default { getReportDetailsRoute: reportID => `r/${reportID}/details`, REPORT_SETTINGS: 'r/:reportID/settings', getReportSettingsRoute: reportID => `r/${reportID}/settings`, - TRANSITION: 'transition', + TRANSITION_FROM_OLD_DOT: 'transitionFromOldDot', VALIDATE_LOGIN: 'v/:accountID/:validateCode', GET_ASSISTANCE: 'get-assistance/:taskID', getGetAssistanceRoute: taskID => `get-assistance/${taskID}`, diff --git a/src/SCREENS.js b/src/SCREENS.js index 3f523838220..c3a9414d5c0 100644 --- a/src/SCREENS.js +++ b/src/SCREENS.js @@ -6,5 +6,5 @@ export default { HOME: 'Home', LOADING: 'Loading', REPORT: 'Report', - TRANSITION: 'Transition', + TRANSITION_FROM_OLD_DOT: 'TransitionFromOldDot', }; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index f4b42905564..5e6676f0aba 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -124,7 +124,7 @@ class AuthScreens extends React.Component { const email = params.get('email'); const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; const shouldCreateFreePolicy = !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION_FROM_OLD_DOT)) && exitTo === ROUTES.WORKSPACE_NEW; if (shouldCreateFreePolicy) { Policy.createAndGetPolicyList(); @@ -238,7 +238,7 @@ class AuthScreens extends React.Component { component={ValidateLoginPage} /> diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.js b/src/libs/Navigation/AppNavigator/PublicScreens.js index f7094377268..e072e72b4c5 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.js +++ b/src/libs/Navigation/AppNavigator/PublicScreens.js @@ -17,7 +17,7 @@ const PublicScreens = () => ( component={SignInPage} /> diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index bab85efec76..63d286580c8 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -27,7 +27,7 @@ export default { // Main Routes SetPassword: ROUTES.SET_PASSWORD_WITH_VALIDATE_CODE, ValidateLogin: ROUTES.VALIDATE_LOGIN, - [SCREENS.TRANSITION]: ROUTES.TRANSITION, + [SCREENS.TRANSITION_FROM_OLD_DOT]: ROUTES.TRANSITION_FROM_OLD_DOT, // Modal Screens Settings: { diff --git a/src/libs/actions/Welcome.js b/src/libs/actions/Welcome.js index f1de0598b64..3c71a5ac000 100644 --- a/src/libs/actions/Welcome.js +++ b/src/libs/actions/Welcome.js @@ -103,7 +103,7 @@ function show({routes, showCreateMenu}) { // If we are rendering the SidebarScreen at the same time as a workspace route that means we've already created a workspace via workspace/new and should not open the global // create menu right now. We should also stay on the workspace page if that is our destination. const topRouteName = lodashGet(_.last(routes), 'name', ''); - const transitionRoute = _.find(routes, route => route.name === SCREENS.TRANSITION); + const transitionRoute = _.find(routes, route => route.name === SCREENS.TRANSITION_FROM_OLD_DOT); const exitingToWorkspaceRoute = lodashGet(transitionRoute, 'params.exitTo', '') === 'workspace/new'; const isDisplayingWorkspaceRoute = topRouteName.toLowerCase().includes('workspace') || exitingToWorkspaceRoute; From a34c511846bdee92943df738e0d492b5da3fb80c Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 15:22:33 -0700 Subject: [PATCH 44/59] Fix ROUTES.TRANSITION_FROM_OLD_DOT to match link --- src/ROUTES.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index 5b4140399ad..0900bf9a2f9 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -81,7 +81,7 @@ export default { getReportDetailsRoute: reportID => `r/${reportID}/details`, REPORT_SETTINGS: 'r/:reportID/settings', getReportSettingsRoute: reportID => `r/${reportID}/settings`, - TRANSITION_FROM_OLD_DOT: 'transitionFromOldDot', + TRANSITION_FROM_OLD_DOT: 'transition', VALIDATE_LOGIN: 'v/:accountID/:validateCode', GET_ASSISTANCE: 'get-assistance/:taskID', getGetAssistanceRoute: taskID => `get-assistance/${taskID}`, From 4729aca2968c8a1696472c91ffaeb08446ef528e Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 15:37:53 -0700 Subject: [PATCH 45/59] Create a setupPoliciesAndNavigate action --- .../Navigation/AppNavigator/AuthScreens.js | 39 +--------------- src/libs/actions/App.js | 45 ++++++++++++++++++- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 5e6676f0aba..acd7551ca5a 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -1,7 +1,5 @@ import React from 'react'; -import {Linking} from 'react-native'; -import Onyx, {withOnyx} from 'react-native-onyx'; -import Str from 'expensify-common/lib/str'; +import Onyx from 'react-native-onyx'; import moment from 'moment'; import _ from 'underscore'; import lodashGet from 'lodash/get'; @@ -109,35 +107,7 @@ class AuthScreens extends React.Component { App.getAppData(false); App.fixAccountAndReloadData(); - - // Load policies, maybe creating a new policy first. - Linking.getInitialURL() - .then((url) => { - if (!url) { - Policy.getPolicyList(); - return; - } - Navigation.isNavigationReady().then(() => { - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; - const shouldCreateFreePolicy = !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION_FROM_OLD_DOT)) - && exitTo === ROUTES.WORKSPACE_NEW; - if (shouldCreateFreePolicy) { - Policy.createAndGetPolicyList(); - return; - } - Policy.getPolicyList(); - if (!isLoggingInAsNewUser && exitTo) { - // We must call dismissModal() to remove the /transition route from history - Navigation.dismissModal(); - Navigation.navigate(exitTo); - } - }); - }); + App.setUpPoliciesAndNavigate(); // Refresh the personal details, timezone and betas every 30 minutes // There is no pusher event that sends updated personal details data yet @@ -351,9 +321,4 @@ AuthScreens.propTypes = propTypes; export default compose( withWindowDimensions, withNetwork(), - withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, - }), )(AuthScreens); diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index a5d8918d640..39fc3f3c245 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -1,6 +1,7 @@ -import {AppState} from 'react-native'; +import {AppState, Linking} from 'react-native'; import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; +import Str from 'expensify-common/lib/str'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; import CONST from '../../CONST'; @@ -15,6 +16,8 @@ import * as GeoLocation from './GeoLocation'; import * as BankAccounts from './BankAccounts'; import * as Policy from './Policy'; import NetworkConnection from '../NetworkConnection'; +import Navigation from '../Navigation/Navigation'; +import ROUTES from '../../ROUTES'; let currentUserAccountID; Onyx.connect({ @@ -37,6 +40,12 @@ Onyx.connect({ callback: val => currentPreferredLocale = val || CONST.DEFAULT_LOCALE, }); +let session; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: val => session = val, +}); + /** * @param {String} url */ @@ -135,6 +144,39 @@ function fixAccountAndReloadData() { }); } +/** + * Wait for the navigation to be ready, then create a new free policy if needed, + * load policies, and navigate to the transition exit route if needed. + */ +function setUpPoliciesAndNavigate() { + Navigation.isNavigationReady() + .then(Linking.getInitialURL) + .then((url) => { + if (!url) { + Policy.getPolicyList(); + return; + } + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = session && session.email !== email; + const shouldCreateFreePolicy = !isLoggingInAsNewUser + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION_FROM_OLD_DOT)) + && exitTo === ROUTES.WORKSPACE_NEW; + if (shouldCreateFreePolicy) { + Policy.createAndGetPolicyList(); + return; + } + Policy.getPolicyList(); + if (!isLoggingInAsNewUser && exitTo) { + // We must call dismissModal() to remove the /transition route from history + Navigation.dismissModal(); + Navigation.navigate(exitTo); + } + }); +} + // When the app reconnects from being offline, fetch all initialization data NetworkConnection.onReconnect(() => getAppData(true, false)); @@ -145,4 +187,5 @@ export { getLocale, getAppData, fixAccountAndReloadData, + setUpPoliciesAndNavigate, }; From 35b57776994b1f8efbdc099b55afe85bc6c388ab Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 23 May 2022 16:17:42 -0700 Subject: [PATCH 46/59] Ensure the user is logged in before policy setup --- src/libs/actions/App.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 39fc3f3c245..a2d08f23656 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -145,13 +145,17 @@ function fixAccountAndReloadData() { } /** - * Wait for the navigation to be ready, then create a new free policy if needed, - * load policies, and navigate to the transition exit route if needed. + * Wait for the navigation to be ready, get the initial URL and make sure the + * user is logged in. Next, create a new free policy if needed, load policies, + * and navigate to the transition exit route if needed. */ function setUpPoliciesAndNavigate() { Navigation.isNavigationReady() .then(Linking.getInitialURL) .then((url) => { + if (!lodashGet(session, 'authToken', null)) { + return; + } if (!url) { Policy.getPolicyList(); return; From 320ec95da0e9c99374833acb76d007b518aff061 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 24 May 2022 09:38:18 -0700 Subject: [PATCH 47/59] Remove unnecessary check for session in action --- src/libs/actions/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index a2d08f23656..ed1d0e20eb3 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -164,7 +164,7 @@ function setUpPoliciesAndNavigate() { const params = new URLSearchParams(url); const exitTo = params.get('exitTo'); const email = params.get('email'); - const isLoggingInAsNewUser = session && session.email !== email; + const isLoggingInAsNewUser = session.email !== email; const shouldCreateFreePolicy = !isLoggingInAsNewUser && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION_FROM_OLD_DOT)) && exitTo === ROUTES.WORKSPACE_NEW; From fe274ed76c99670e5c07514b81a3ee78b4598afb Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 24 May 2022 11:03:44 -0700 Subject: [PATCH 48/59] Pass session to setUpPoliciesAndNavigate --- src/libs/Navigation/AppNavigator/AuthScreens.js | 9 +++++++-- src/libs/actions/App.js | 12 ++---------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index acd7551ca5a..8a0e5e53dcf 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -1,5 +1,5 @@ import React from 'react'; -import Onyx from 'react-native-onyx'; +import Onyx, {withOnyx} from 'react-native-onyx'; import moment from 'moment'; import _ from 'underscore'; import lodashGet from 'lodash/get'; @@ -107,7 +107,7 @@ class AuthScreens extends React.Component { App.getAppData(false); App.fixAccountAndReloadData(); - App.setUpPoliciesAndNavigate(); + App.setUpPoliciesAndNavigate(this.props.session); // Refresh the personal details, timezone and betas every 30 minutes // There is no pusher event that sends updated personal details data yet @@ -321,4 +321,9 @@ AuthScreens.propTypes = propTypes; export default compose( withWindowDimensions, withNetwork(), + withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, + }), )(AuthScreens); diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index ed1d0e20eb3..cc9b0e2d8d9 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -40,12 +40,6 @@ Onyx.connect({ callback: val => currentPreferredLocale = val || CONST.DEFAULT_LOCALE, }); -let session; -Onyx.connect({ - key: ONYXKEYS.SESSION, - callback: val => session = val, -}); - /** * @param {String} url */ @@ -148,14 +142,12 @@ function fixAccountAndReloadData() { * Wait for the navigation to be ready, get the initial URL and make sure the * user is logged in. Next, create a new free policy if needed, load policies, * and navigate to the transition exit route if needed. + * @param {Object} session */ -function setUpPoliciesAndNavigate() { +function setUpPoliciesAndNavigate(session) { Navigation.isNavigationReady() .then(Linking.getInitialURL) .then((url) => { - if (!lodashGet(session, 'authToken', null)) { - return; - } if (!url) { Policy.getPolicyList(); return; From 0921b6c5cfddb7547af5bce736724c8842390b00 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 24 May 2022 11:06:34 -0700 Subject: [PATCH 49/59] Change navigationRef import in NavigationRoot --- src/libs/Navigation/NavigationRoot.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index 502ccfd076c..e54c46d7a1c 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -1,8 +1,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {getPathFromState, NavigationContainer, DefaultTheme} from '@react-navigation/native'; -import navigationRef from './navigationRef'; -import Navigation from './Navigation'; +import Navigation, {navigationRef} from './Navigation'; import linkingConfig from './linkingConfig'; import AppNavigator from './AppNavigator'; import * as App from '../actions/App'; From 09e3bf19e78b69e807aed54675ef0f2e26522f21 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 24 May 2022 11:26:08 -0700 Subject: [PATCH 50/59] Move waitForOnyxToClear into the action --- src/libs/actions/Session/index.js | 28 ++++++++++++++--------- src/pages/LogInWithShortLivedTokenPage.js | 10 +++----- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 4a43029ce9c..0ea785f1c27 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -21,6 +21,7 @@ import * as ValidationUtils from '../../ValidationUtils'; import * as Authentication from '../../Authentication'; import * as ErrorUtils from '../../ErrorUtils'; import * as Welcome from '../Welcome'; +import waitForOnyxToClear from '../../waitForOnyxToClear'; let credentials = {}; Onyx.connect({ @@ -275,18 +276,23 @@ function signIn(password, twoFactorAuthCode) { * @param {String} exitTo */ function signInWithShortLivedToken(email, shortLivedToken) { - Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); - - createTemporaryLogin(shortLivedToken, email).then((response) => { - if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { - return; - } + // Wait for Onyx to clear so that our updates won't be overridden when the + // default key states are initialized + waitForOnyxToClear() + .then(() => { + Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); + return createTemporaryLogin(shortLivedToken, email); + }) + .then((response) => { + if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { + return; + } - User.getUserDetails(); - Onyx.merge(ONYXKEYS.ACCOUNT, {success: true}); - }).finally(() => { - Onyx.merge(ONYXKEYS.ACCOUNT, {loading: false}); - }); + User.getUserDetails(); + Onyx.merge(ONYXKEYS.ACCOUNT, {success: true}); + }).finally(() => { + Onyx.merge(ONYXKEYS.ACCOUNT, {loading: false}); + }); } /** diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index f769690f06b..fef736e582b 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -3,7 +3,6 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import waitForOnyxToClear from '../libs/waitForOnyxToClear'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -21,12 +20,9 @@ const propTypes = { class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - waitForOnyxToClear() - .then(() => { - const email = lodashGet(this.props, 'route.params.email', ''); - const shortLivedToken = lodashGet(this.props, 'route.params.shortLivedToken', ''); - Session.signInWithShortLivedToken(email, shortLivedToken); - }); + const email = lodashGet(this.props, 'route.params.email', ''); + const shortLivedToken = lodashGet(this.props, 'route.params.shortLivedToken', ''); + Session.signInWithShortLivedToken(email, shortLivedToken); } render() { From b5da43ac3ed117e691d424bb96e36e45ce33b21e Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 24 May 2022 12:20:03 -0700 Subject: [PATCH 51/59] Add a detailed comment to setUpPoliciesAndNavigate --- src/libs/actions/App.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index cc9b0e2d8d9..d3f4c9baf28 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -139,9 +139,26 @@ function fixAccountAndReloadData() { } /** - * Wait for the navigation to be ready, get the initial URL and make sure the - * user is logged in. Next, create a new free policy if needed, load policies, - * and navigate to the transition exit route if needed. + * This action runs every time the AuthScreens are mounted. The navigator may + * not be ready yet, and therefore we need to wait before navigating within this + * action and any actions this method calls. + * + * getInitialURL allows us to access params from the transition link more easily + * than trying to extract them from the navigation state. + + * The transition link contains an exitTo param that contains the route to + * navigate to after the user is signed in. A user can transition from OldDot + * with a different account than the one they are currently signed in with, so + * we only navigate if they are not signing in as a new user. Once they are + * signed in as that new user, this action will run again and the navigation + * will occur. + + * When the exitTo route is 'workspace/new', we create a new + * workspace and navigate to it via Policy.createAndGetPolicyList. + * + * We subscribe to the session using withOnyx in the AuthScreens and + * pass it in as a parameter. withOnyx guarantees that the value has been read + * from Onyx because it will not render the AuthScreens until that point. * @param {Object} session */ function setUpPoliciesAndNavigate(session) { From 1c211f1b6d4841d1997d8bff3161fbbfe914e2a8 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 24 May 2022 12:26:28 -0700 Subject: [PATCH 52/59] Rename waitForOnyxToClear methods --- src/libs/actions/SignInRedirect.js | 4 ++-- src/libs/waitForOnyxToClear.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/SignInRedirect.js b/src/libs/actions/SignInRedirect.js index 2476cb06bfa..be504c06252 100644 --- a/src/libs/actions/SignInRedirect.js +++ b/src/libs/actions/SignInRedirect.js @@ -25,10 +25,10 @@ function clearStorageAndRedirect(errorMessage) { const preferredLocale = currentPreferredLocale; // Clearing storage discards the authToken. This causes a redirect to the SignIn screen - waitForOnyxToClear.setOnyxClearing(); + waitForOnyxToClear.setIsOnyxClearing(); Onyx.clear() .then(() => { - waitForOnyxToClear.setOnyxDoneClearing(); + waitForOnyxToClear.setOnyxIsDoneClearing(); if (preferredLocale) { Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, preferredLocale); } diff --git a/src/libs/waitForOnyxToClear.js b/src/libs/waitForOnyxToClear.js index 1cc0d0ce7b2..2a703a9bc78 100644 --- a/src/libs/waitForOnyxToClear.js +++ b/src/libs/waitForOnyxToClear.js @@ -13,19 +13,19 @@ function waitForOnyxToClear() { return onyxClearPromise; } -function setOnyxClearing() { +function setIsOnyxClearing() { onyxClearPromise = new Promise((resolve) => { resolveOnyxClearPromise = resolve; }); } -function setOnyxDoneClearing() { +function setOnyxIsDoneClearing() { resolveOnyxClearPromise(); } export default waitForOnyxToClear; export { - setOnyxClearing, - setOnyxDoneClearing, + setIsOnyxClearing, + setOnyxIsDoneClearing, }; From 2097b8ec314689fa52598514c2edaa018618a64f Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 31 May 2022 10:27:08 -0700 Subject: [PATCH 53/59] Fix Onyx.clear by updating to the latest version --- package-lock.json | 66 ++++++----------------------------------------- package.json | 2 +- 2 files changed, 9 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index 907bb28dc10..437c9ecd9b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16868,7 +16868,7 @@ "ascii-table": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/ascii-table/-/ascii-table-0.0.9.tgz", - "integrity": "sha1-BqZgTWpV1L9BqaR9mHLXp42jHnM=" + "integrity": "sha512-xpkr6sCDIYTPqzvjG8M3ncw1YOTaloWZOyrUmicoEifBEKzQzt+ooUpRpQ/AbOoJfO/p2ZKiyp79qHThzJDulQ==" }, "asn1": { "version": "0.2.4", @@ -36520,65 +36520,12 @@ } }, "react-native-onyx": { - "version": "git+https://github.com/Expensify/react-native-onyx.git#7ab6aed5ce9158f7017ee1c9fd8b5d725d57db73", - "from": "git+https://github.com/Expensify/react-native-onyx.git#7ab6aed5ce9158f7017ee1c9fd8b5d725d57db73", + "version": "git+https://github.com/Expensify/react-native-onyx.git#f8269086fdf5b4a544c428025677d9e213e8c153", + "from": "git+https://github.com/Expensify/react-native-onyx.git#f8269086fdf5b4a544c428025677d9e213e8c153", "requires": { "ascii-table": "0.0.9", - "expensify-common": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", - "lodash": "4.17.21", + "lodash": "^4.17.21", "underscore": "^1.13.1" - }, - "dependencies": { - "expensify-common": { - "version": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", - "from": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", - "requires": { - "classnames": "2.2.5", - "clipboard": "2.0.4", - "html-entities": "^1.3.1", - "jquery": "3.3.1", - "lodash": "4.17.21", - "prop-types": "15.7.2", - "react": "16.12.0", - "react-dom": "16.12.0", - "semver": "^7.3.4", - "simply-deferred": "git+https://github.com/Expensify/simply-deferred.git#77a08a95754660c7bd6e0b6979fdf84e8e831bf5", - "underscore": "1.9.1" - }, - "dependencies": { - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - } - } - }, - "jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" - }, - "react": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", - "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - }, - "react-dom": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", - "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" - } - } } }, "react-native-pdf": { @@ -38422,6 +38369,7 @@ "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, "requires": { "lru-cache": "^6.0.0" }, @@ -38430,6 +38378,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -38437,7 +38386,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, diff --git a/package.json b/package.json index da79fda0a99..9e6c1dd6ad8 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "react-native-image-size": "^1.1.3", "react-native-keyboard-spacer": "^0.4.1", "react-native-modal": "^13.0.0", - "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#7ab6aed5ce9158f7017ee1c9fd8b5d725d57db73", + "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#f8269086fdf5b4a544c428025677d9e213e8c153", "react-native-pdf": "^6.2.2", "react-native-performance": "^2.0.0", "react-native-permissions": "^3.0.1", From e85a80c020d6abe9a4f66091119eb18b934b316c Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 31 May 2022 10:27:33 -0700 Subject: [PATCH 54/59] Remove waitForOnyxToClear --- src/libs/actions/Session/index.js | 11 +++-------- src/libs/actions/SignInRedirect.js | 3 --- src/libs/waitForOnyxToClear.js | 31 ------------------------------ 3 files changed, 3 insertions(+), 42 deletions(-) delete mode 100644 src/libs/waitForOnyxToClear.js diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index c457f818612..0f4ac0a9548 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -21,7 +21,6 @@ import * as ValidationUtils from '../../ValidationUtils'; import * as Authentication from '../../Authentication'; import * as ErrorUtils from '../../ErrorUtils'; import * as Welcome from '../Welcome'; -import waitForOnyxToClear from '../../waitForOnyxToClear'; let credentials = {}; Onyx.connect({ @@ -276,13 +275,9 @@ function signIn(password, twoFactorAuthCode) { * @param {String} exitTo */ function signInWithShortLivedToken(email, shortLivedToken) { - // Wait for Onyx to clear so that our updates won't be overridden when the - // default key states are initialized - waitForOnyxToClear() - .then(() => { - Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); - return createTemporaryLogin(shortLivedToken, email); - }) + Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); + + createTemporaryLogin(shortLivedToken, email) .then((response) => { if (response.jsonCode !== CONST.JSON_CODE.SUCCESS) { return; diff --git a/src/libs/actions/SignInRedirect.js b/src/libs/actions/SignInRedirect.js index be504c06252..ffde6f2f955 100644 --- a/src/libs/actions/SignInRedirect.js +++ b/src/libs/actions/SignInRedirect.js @@ -1,7 +1,6 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; import * as MainQueue from '../Network/MainQueue'; -import * as waitForOnyxToClear from '../waitForOnyxToClear'; let currentActiveClients; Onyx.connect({ @@ -25,10 +24,8 @@ function clearStorageAndRedirect(errorMessage) { const preferredLocale = currentPreferredLocale; // Clearing storage discards the authToken. This causes a redirect to the SignIn screen - waitForOnyxToClear.setIsOnyxClearing(); Onyx.clear() .then(() => { - waitForOnyxToClear.setOnyxIsDoneClearing(); if (preferredLocale) { Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, preferredLocale); } diff --git a/src/libs/waitForOnyxToClear.js b/src/libs/waitForOnyxToClear.js deleted file mode 100644 index 2a703a9bc78..00000000000 --- a/src/libs/waitForOnyxToClear.js +++ /dev/null @@ -1,31 +0,0 @@ -let resolveOnyxClearPromise; -let onyxClearPromise = new Promise((resolve) => { - resolveOnyxClearPromise = resolve; -}); - -// By default Onyx is done clearing -resolveOnyxClearPromise(); - -/** - * @returns {Promise} - */ -function waitForOnyxToClear() { - return onyxClearPromise; -} - -function setIsOnyxClearing() { - onyxClearPromise = new Promise((resolve) => { - resolveOnyxClearPromise = resolve; - }); -} - -function setOnyxIsDoneClearing() { - resolveOnyxClearPromise(); -} - -export default waitForOnyxToClear; - -export { - setIsOnyxClearing, - setOnyxIsDoneClearing, -}; From dc6fa72f533f7031adf534c5eaf4bfc09148ffee Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 31 May 2022 11:52:13 -0700 Subject: [PATCH 55/59] Install Onyx using the npm package --- package-lock.json | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 437c9ecd9b5..dc5245be9cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36520,8 +36520,9 @@ } }, "react-native-onyx": { - "version": "git+https://github.com/Expensify/react-native-onyx.git#f8269086fdf5b4a544c428025677d9e213e8c153", - "from": "git+https://github.com/Expensify/react-native-onyx.git#f8269086fdf5b4a544c428025677d9e213e8c153", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.5.tgz", + "integrity": "sha512-by4fY+/3FWzlXnIeuuEKxuexKMtOSF5NaUmSlKMeuheu3Mex52GH647XpI8Vt8gefQW0dfKF2OyGGfm5ECCrmQ==", "requires": { "ascii-table": "0.0.9", "lodash": "^4.17.21", diff --git a/package.json b/package.json index 9e6c1dd6ad8..9502ec7ed30 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "react-native-image-size": "^1.1.3", "react-native-keyboard-spacer": "^0.4.1", "react-native-modal": "^13.0.0", - "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#f8269086fdf5b4a544c428025677d9e213e8c153", + "react-native-onyx": "1.0.5", "react-native-pdf": "^6.2.2", "react-native-performance": "^2.0.0", "react-native-permissions": "^3.0.1", From dcc3e7973faf1c9f5c93db5dc660c24f4489c65b Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 31 May 2022 14:15:18 -0700 Subject: [PATCH 56/59] Wait for navigation just before navigating Policy.getPolicyList() was being called after the previous login had been deleted since it wasn't called until the navigation was ready. --- src/libs/actions/App.js | 12 +++++++----- src/libs/actions/Policy.js | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 980b6f4c443..1fe571f36ed 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -162,8 +162,7 @@ function fixAccountAndReloadData() { * @param {Object} session */ function setUpPoliciesAndNavigate(session) { - Navigation.isNavigationReady() - .then(Linking.getInitialURL) + Linking.getInitialURL() .then((url) => { if (!url) { Policy.getPolicyList(); @@ -183,9 +182,12 @@ function setUpPoliciesAndNavigate(session) { } Policy.getPolicyList(); if (!isLoggingInAsNewUser && exitTo) { - // We must call dismissModal() to remove the /transition route from history - Navigation.dismissModal(); - Navigation.navigate(exitTo); + Navigation.isNavigationReady() + .then(() => { + // We must call dismissModal() to remove the /transition route from history + Navigation.dismissModal(); + Navigation.navigate(exitTo); + }); } }); } diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index cb0bea164d0..7847c0c1619 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -238,6 +238,7 @@ function createAndGetPolicyList() { newPolicyID = policyID; return getPolicyList(); }) + .then(Navigation.isNavigationReady) .then(() => { Navigation.dismissModal(); navigateToPolicy(newPolicyID); From 4baed9eb723e12694ee6a204365fd03c4ca246cb Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 31 May 2022 14:15:48 -0700 Subject: [PATCH 57/59] Clear the session error on a successful sign in --- src/libs/actions/Session/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 0f4ac0a9548..1cde31b6567 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -40,6 +40,7 @@ function setSuccessfulSignInData(data) { PushNotification.register(data.accountID); Onyx.merge(ONYXKEYS.SESSION, { shouldShowComposeInput: true, + error: null, ..._.pick(data, 'authToken', 'accountID', 'email', 'encryptedAuthToken'), }); } From 552e13c1e5984c792f73238216a28ff3612ca7d7 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 31 May 2022 15:21:45 -0700 Subject: [PATCH 58/59] Again, setIsNavigationReady() in a transition page --- src/libs/Navigation/NavigationRoot.js | 6 ++---- src/pages/LogOutPreviousUserPage.js | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index e54c46d7a1c..80c7dedec07 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {getPathFromState, NavigationContainer, DefaultTheme} from '@react-navigation/native'; -import Navigation, {navigationRef} from './Navigation'; +import * as Navigation from './Navigation'; import linkingConfig from './linkingConfig'; import AppNavigator from './AppNavigator'; import * as App from '../actions/App'; @@ -40,10 +40,8 @@ class NavigationRoot extends Component { */ parseAndStoreRoute(state) { if (!state) { - Navigation.resetIsNavigationReady(); return; } - Navigation.setIsNavigationReady(); const path = getPathFromState(state, linkingConfig.config); @@ -68,7 +66,7 @@ class NavigationRoot extends Component { onStateChange={this.parseAndStoreRoute} onReady={this.props.onReady} theme={navigationTheme} - ref={navigationRef} + ref={Navigation.navigationRef} linking={linkingConfig} documentTitle={{ enabled: false, diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 23f837d8e7c..9cdd68777fa 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; +import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -29,7 +30,11 @@ class LogOutPreviousUserPage extends Component { const sessionEmail = lodashGet(this.props.session, 'email', ''); if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); + return; } + + // Set isNavigationReady so that we can navigate in the AuthScreens + Navigation.setIsNavigationReady(); } render() { From 0e20ad0cc05684156d7a2ad83ab1687dd948a5ca Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Wed, 1 Jun 2022 10:51:08 -0700 Subject: [PATCH 59/59] Add a detailed comment about setIsNavigationReady --- src/pages/LogOutPreviousUserPage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 9cdd68777fa..27515226e49 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -33,7 +33,12 @@ class LogOutPreviousUserPage extends Component { return; } - // Set isNavigationReady so that we can navigate in the AuthScreens + // Since we conditionally render navigators in the AppNavigator, when we + // sign out and sign back in there will be a moment where no navigator + // is rendered and the navigation state is null. We can't navigate at + // that time, so we use a promise to delay transition navigation until + // it is ready. We set the navigation ready here since we know that the + // navigator is now rendered. Navigation.setIsNavigationReady(); }