-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Reintroduce workspace new without a modal, just the url #5396
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2b70ccf
d942fe6
3e45432
8bf40dd
4c3d690
9c8e5b9
18afdf0
85f20dc
8ea5a2e
579a6cb
31436b1
a3cf706
60ebb4c
269697b
2c5b8c1
327785f
702b89e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||
| import Onyx from 'react-native-onyx'; | ||||||
| import Str from 'expensify-common/lib/str'; | ||||||
| import _ from 'underscore'; | ||||||
| import lodashGet from 'lodash/get'; | ||||||
| import ONYXKEYS from '../../ONYXKEYS'; | ||||||
| import redirectToSignIn from './SignInRedirect'; | ||||||
| import * as API from '../API'; | ||||||
|
|
@@ -13,6 +14,7 @@ import Navigation from '../Navigation/Navigation'; | |||||
| import ROUTES from '../../ROUTES'; | ||||||
| import {translateLocal} from '../translate'; | ||||||
| import * as Network from '../Network'; | ||||||
| import {getUserDetails} from './User'; | ||||||
|
|
||||||
|
|
||||||
| let credentials = {}; | ||||||
|
|
@@ -157,12 +159,13 @@ function fetchAccountDetails(login) { | |||||
| * @param {String} authToken | ||||||
| * @param {String} encryptedAuthToken – Not required for the CreateLogin API call, but passed to setSuccessfulSignInData | ||||||
| * @param {String} email | ||||||
| * @return {Promise} | ||||||
| */ | ||||||
| function createTemporaryLogin(authToken, encryptedAuthToken, email) { | ||||||
| const autoGeneratedLogin = Str.guid('expensify.cash-'); | ||||||
| const autoGeneratedPassword = Str.guid(); | ||||||
|
|
||||||
| API.CreateLogin({ | ||||||
| return API.CreateLogin({ | ||||||
| authToken, | ||||||
| partnerName: CONFIG.EXPENSIFY.PARTNER_NAME, | ||||||
| partnerPassword: CONFIG.EXPENSIFY.PARTNER_PASSWORD, | ||||||
|
|
@@ -181,7 +184,7 @@ function createTemporaryLogin(authToken, encryptedAuthToken, email) { | |||||
|
|
||||||
| // If we have an old generated login for some reason | ||||||
| // we should delete it before storing the new details | ||||||
| if (credentials.autoGeneratedLogin) { | ||||||
| if (credentials && credentials.autoGeneratedLogin) { | ||||||
| API.DeleteLogin({ | ||||||
| partnerUserID: credentials.autoGeneratedLogin, | ||||||
| partnerName: CONFIG.EXPENSIFY.PARTNER_NAME, | ||||||
|
|
@@ -196,6 +199,7 @@ function createTemporaryLogin(authToken, encryptedAuthToken, email) { | |||||
| autoGeneratedPassword, | ||||||
| }); | ||||||
| Network.unpauseRequestQueue(); | ||||||
| return createLoginResponse; | ||||||
| }) | ||||||
| .catch((error) => { | ||||||
| Onyx.merge(ONYXKEYS.ACCOUNT, {error: error.message}); | ||||||
|
|
@@ -234,6 +238,35 @@ 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} encryptedAuthToken | ||||||
| */ | ||||||
| function signInWithShortLivedToken(accountID, email, shortLivedToken, encryptedAuthToken) { | ||||||
| Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); | ||||||
|
|
||||||
| createTemporaryLogin(shortLivedToken, encryptedAuthToken, email).then((response) => { | ||||||
| Onyx.merge(ONYXKEYS.SESSION, { | ||||||
| authToken: shortLivedToken, | ||||||
| accountID, | ||||||
| email, | ||||||
| }); | ||||||
| if (response.jsonCode === 200) { | ||||||
| getUserDetails(); | ||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mirroring comment from @marcaaron, but do we need this? And if we do, shouldn't we also include these other two: App/src/libs/Navigation/AppNavigator/AuthScreens.js Lines 186 to 187 in a3cf706
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're transitioning to an AuthScreen before doing the exitTo though, I think it's fine if we don't include
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no idea
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it was added to support a case where we are already logged in as someone else. I don't think we need to worry about that and can probably test that flow in a follow up / remove this code for now. |
||||||
| Onyx.merge(ONYXKEYS.ACCOUNT, {success: true}); | ||||||
| } else { | ||||||
| const error = lodashGet(response, 'message', 'Unable to login.'); | ||||||
| Onyx.merge(ONYXKEYS.ACCOUNT, {error}); | ||||||
| } | ||||||
| }).finally(() => { | ||||||
| Onyx.merge(ONYXKEYS.ACCOUNT, {loading: false}); | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * User forgot the password so let's send them the link to reset their password | ||||||
| */ | ||||||
|
|
@@ -306,6 +339,7 @@ export { | |||||
| fetchAccountDetails, | ||||||
| setPassword, | ||||||
| signIn, | ||||||
| signInWithShortLivedToken, | ||||||
| signOut, | ||||||
| reopenAccount, | ||||||
| resendValidationLink, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import React, {Component} from 'react'; | ||
| import lodashGet from 'lodash/get'; | ||
| import PropTypes from 'prop-types'; | ||
| import {withOnyx} from 'react-native-onyx'; | ||
| import compose from '../libs/compose'; | ||
| import ONYXKEYS from '../ONYXKEYS'; | ||
| import {signInWithShortLivedToken} 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 */ | ||
| 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 LogInWithShortLivedTokenPage extends Component { | ||
| componentDidMount() { | ||
| const accountID = parseInt(lodashGet(this.props.route.params, 'accountID', ''), 10); | ||
| const email = lodashGet(this.props.route.params, 'email', ''); | ||
| const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); | ||
| const encryptedAuthToken = lodashGet(this.props.route.params, 'encryptedAuthToken', ''); | ||
|
|
||
| // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace/<ID>/card") | ||
| const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); | ||
|
|
||
| // If the user is revisiting the component authenticated or they were already logged into the right account, we simply redirect them to the exitTo | ||
| if (this.props.session.authToken && email === this.props.session.email) { | ||
| // In order to navigate to a modal, we first have to dismiss the current modal. But there is no current | ||
| // modal you say? I know, it confuses me too. Without dismissing the current modal, if the user cancels out | ||
| // of the workspace modal, then they will be routed back to | ||
| // /transition/<accountID>/<email>/<authToken>/workspace/<policyID>/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. | ||
| Navigation.dismissModal(); | ||
| Navigation.navigate(exitTo); | ||
| } | ||
|
|
||
| signInWithShortLivedToken(accountID, email, shortLivedToken, encryptedAuthToken); | ||
| } | ||
|
|
||
| render() { | ||
| return <FullScreenLoadingIndicator />; | ||
| } | ||
| } | ||
|
|
||
| LogInWithShortLivedTokenPage.propTypes = propTypes; | ||
| LogInWithShortLivedTokenPage.defaultProps = defaultProps; | ||
|
|
||
| export default compose( | ||
| withOnyx({ | ||
| session: { | ||
| key: ONYXKEYS.SESSION, | ||
| }, | ||
| }), | ||
| )(LogInWithShortLivedTokenPage); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import React, {Component} from 'react'; | ||
| import * as Policy from '../../libs/actions/Policy'; | ||
| import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; | ||
|
|
||
| class WorkspaceNew extends Component { | ||
| componentDidMount() { | ||
| // After the workspace is created, the user will automatically be directed to its settings page | ||
| Policy.create(); | ||
| } | ||
|
|
||
| render() { | ||
| return <FullScreenLoadingIndicator />; | ||
| } | ||
| } | ||
|
|
||
| export default WorkspaceNew; |
Uh oh!
There was an error while loading. Please reload this page.