From 9de436b16f55dccb233e19349bb63a463811ab5f Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 27 Feb 2023 14:11:26 +0200 Subject: [PATCH 01/36] Split ValidateCodeModal different usages in multiple components. --- .../ValidateCode/AbracadabraModal.js | 0 .../ValidateCode/ExpiredValidateCodeModal.js | 0 .../{ => ValidateCode}/ValidateCodeModal.js | 18 +++++++++--------- src/pages/ValidateLoginPage/index.website.js | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 src/components/ValidateCode/AbracadabraModal.js create mode 100644 src/components/ValidateCode/ExpiredValidateCodeModal.js rename src/components/{ => ValidateCode}/ValidateCodeModal.js (89%) diff --git a/src/components/ValidateCode/AbracadabraModal.js b/src/components/ValidateCode/AbracadabraModal.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/components/ValidateCodeModal.js b/src/components/ValidateCode/ValidateCodeModal.js similarity index 89% rename from src/components/ValidateCodeModal.js rename to src/components/ValidateCode/ValidateCodeModal.js index 9bf52d2c4795..032a008cb7fe 100644 --- a/src/components/ValidateCodeModal.js +++ b/src/components/ValidateCode/ValidateCodeModal.js @@ -1,15 +1,15 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; -import colors from '../styles/colors'; -import styles from '../styles/styles'; -import Icon from './Icon'; -import withLocalize, {withLocalizePropTypes} from './withLocalize'; -import Text from './Text'; -import * as Expensicons from './Icon/Expensicons'; -import * as Illustrations from './Icon/Illustrations'; -import variables from '../styles/variables'; -import TextLink from './TextLink'; +import colors from '../../styles/colors'; +import styles from '../../styles/styles'; +import Icon from '../Icon'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import Text from '../Text'; +import * as Expensicons from '../Icon/Expensicons'; +import * as Illustrations from '../Icon/Illustrations'; +import variables from '../../styles/variables'; +import TextLink from '../TextLink'; const propTypes = { diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index a15e13f232a9..cd3f534ec221 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -9,7 +9,7 @@ import { import * as User from '../../libs/actions/User'; import compose from '../../libs/compose'; import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; -import ValidateCodeModal from '../../components/ValidateCodeModal'; +import ValidateCodeModal from '../../components/ValidateCode/ValidateCodeModal'; import ONYXKEYS from '../../ONYXKEYS'; import * as Session from '../../libs/actions/Session'; import Permissions from '../../libs/Permissions'; From 4571bc22bf69c88233cbe7e68c576902447165f9 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 27 Feb 2023 14:44:05 +0200 Subject: [PATCH 02/36] Separate Abracadabra component. --- .../ValidateCode/AbracadabraModal.js | 52 +++++++++++++++++++ src/pages/ValidateLoginPage/index.website.js | 5 +- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/components/ValidateCode/AbracadabraModal.js b/src/components/ValidateCode/AbracadabraModal.js index e69de29bb2d1..28e3653f2f6b 100644 --- a/src/components/ValidateCode/AbracadabraModal.js +++ b/src/components/ValidateCode/AbracadabraModal.js @@ -0,0 +1,52 @@ +import React, {PureComponent} from 'react'; +import {View} from 'react-native'; +import colors from '../../styles/colors'; +import styles from '../../styles/styles'; +import Icon from '../Icon'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import Text from '../Text'; +import * as Expensicons from '../Icon/Expensicons'; +import * as Illustrations from '../Icon/Illustrations'; +import variables from '../../styles/variables'; + +const propTypes = { + + ...withLocalizePropTypes, +}; + +class AbracadabraModal extends PureComponent { + render() { + return ( + + + + + + + {this.props.translate('validateCodeModal.successfulSignInTitle')} + + + + {this.props.translate('validateCodeModal.successfulSignInDescription')} + + + + + + + + ); + } +} + +AbracadabraModal.propTypes = propTypes; +export default withLocalize(AbracadabraModal); diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index cd3f534ec221..5704f6900e6b 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -14,6 +14,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import * as Session from '../../libs/actions/Session'; import Permissions from '../../libs/Permissions'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import AbracadabraModal from '../../components/ValidateCode/AbracadabraModal'; const propTypes = { /** The accountID and validateCode are passed via the URL */ @@ -91,8 +92,8 @@ class ValidateLoginPage extends Component { return ( this.isOnPasswordlessBeta() && !this.isSignInInitiated() && !lodashGet(this.props, 'account.isLoading', true) ? ( - + : Session.signInWithValidateCodeAndNavigate(this.accountID(), this.validateCode())} From 4dfdcf08e3b15e57747ff937d5ef2bfcebece389 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 27 Feb 2023 14:46:27 +0200 Subject: [PATCH 03/36] Cleanup abracadabra related code from ValidateCodeModal component --- .../ValidateCode/ValidateCodeModal.js | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/components/ValidateCode/ValidateCodeModal.js b/src/components/ValidateCode/ValidateCodeModal.js index 032a008cb7fe..fe97e878a698 100644 --- a/src/components/ValidateCode/ValidateCodeModal.js +++ b/src/components/ValidateCode/ValidateCodeModal.js @@ -13,9 +13,6 @@ import TextLink from '../TextLink'; const propTypes = { - /** Whether the user has been signed in with the link. */ - isSuccessfullySignedIn: PropTypes.bool, - /** Code to display. */ code: PropTypes.string.isRequired, @@ -29,7 +26,6 @@ const propTypes = { }; const defaultProps = { - isSuccessfullySignedIn: false, shouldShowSignInHere: false, onSignInHereClick: () => {}, }; @@ -42,16 +38,16 @@ class ValidateCodeModal extends PureComponent { - {this.props.translate(this.props.isSuccessfullySignedIn ? 'validateCodeModal.successfulSignInTitle' : 'validateCodeModal.title')} + {this.props.translate('validateCodeModal.title')} - {this.props.translate(this.props.isSuccessfullySignedIn ? 'validateCodeModal.successfulSignInDescription' : 'validateCodeModal.description')} + {this.props.translate('validateCodeModal.description')} {this.props.shouldShowSignInHere && ( <> @@ -65,13 +61,11 @@ class ValidateCodeModal extends PureComponent { {this.props.shouldShowSignInHere ? '!' : '.'} - {!this.props.isSuccessfullySignedIn && ( - - - {this.props.code} - - - )} + + + {this.props.code} + + Date: Mon, 27 Feb 2023 15:36:38 +0200 Subject: [PATCH 04/36] Separate component for when magic link fails. --- .../ValidateCode/ExpiredValidateCodeModal.js | 78 +++++++++++++++++++ src/languages/en.js | 4 + src/languages/es.js | 4 + 3 files changed, 86 insertions(+) diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index e69de29bb2d1..1fdd4cfb5f8f 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -0,0 +1,78 @@ +import React, {PureComponent} from 'react'; +import PropTypes from 'prop-types'; +import {View} from 'react-native'; +import colors from '../../styles/colors'; +import styles from '../../styles/styles'; +import Icon from '../Icon'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import Text from '../Text'; +import * as Expensicons from '../Icon/Expensicons'; +import * as Illustrations from '../Icon/Illustrations'; +import variables from '../../styles/variables'; +import TextLink from '../TextLink'; + +const propTypes = { + + /** Whether the user can a new validate code from the current page */ + shouldShowRequestCodeLink: PropTypes.bool, + + /** Callback to be called when user clicks the request code link */ + onRequestCodeClick: PropTypes.func, + + ...withLocalizePropTypes, +}; + +const defaultProps = { + shouldShowRequestCodeLink: false, + onRequestCodeClick: () => {}, +}; + +class ExpiredValidateCodeModal extends PureComponent { + render() { + return ( + + + + + + + {this.props.translate('validateCodeModal.expiredCodeTitle')} + + + + {this.props.translate('validateCodeModal.expiredCodeDescription')} + {this.props.shouldShowRequestCodeLink + && ( + <> +
+ {this.props.translate('validateCodeModal.requestNewCode')} + {' '} + + {this.props.translate('validateCodeModal.requestNewCodeLink')} + + {'!'} + + )} +
+
+
+ + + +
+ ); + } +} + +ExpiredValidateCodeModal.propTypes = propTypes; +ExpiredValidateCodeModal.defaultProps = defaultProps; +export default withLocalize(ExpiredValidateCodeModal); diff --git a/src/languages/en.js b/src/languages/en.js index c2edea86b01e..3e2b4ab81a52 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -162,6 +162,10 @@ export default { description: 'Please enter the code using the device\nwhere it was originally requested', or: ', or', signInHere: 'just sign in here', + expiredCodeTitle: 'Magic code expired', + expiredCodeDescription: 'Go back to the original device and request a new code.', + requestNewCode: 'You can also', + requestNewCodeLink: 'request a new code here', }, iOUConfirmationList: { whoPaid: 'Who paid?', diff --git a/src/languages/es.js b/src/languages/es.js index 8c82b5e70135..816de99e19e3 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -162,6 +162,10 @@ export default { or: ', ¡o', description: 'Por favor, introduzca el código utilizando el dispositivo\nen el que se solicitó originalmente', signInHere: 'simplemente inicia sesión aquí', + expiredCodeTitle: 'Código mágico caducado', + expiredCodeDescription: 'Vuelve al dispositivo original y solicita un nuevo código.', + requestNewCode: 'También puede', + requestNewCodeLink: 'solicitar un nuevo código aquí', }, iOUConfirmationList: { whoPaid: '¿Quién pago?', From abcf1f96ffd250fe01603f813339249273ec4043 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 27 Feb 2023 17:13:17 +0200 Subject: [PATCH 05/36] Correct Spanish translations after feedback from native speakers. --- src/languages/es.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.js b/src/languages/es.js index 816de99e19e3..717f4d86abed 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -164,7 +164,7 @@ export default { signInHere: 'simplemente inicia sesión aquí', expiredCodeTitle: 'Código mágico caducado', expiredCodeDescription: 'Vuelve al dispositivo original y solicita un nuevo código.', - requestNewCode: 'También puede', + requestNewCode: '¡También puedes', requestNewCodeLink: 'solicitar un nuevo código aquí', }, iOUConfirmationList: { From 42dbf4b04a4696ecfcc6cd9ab906022b691db577 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 27 Feb 2023 22:53:04 +0200 Subject: [PATCH 06/36] Update illustration in the modal shown when magic link fails. --- .../todd-behind-cloud.svg | 58 +++++++++++++++++++ src/components/Icon/Illustrations.js | 2 + .../ValidateCode/ExpiredValidateCodeModal.js | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 assets/images/product-illustrations/todd-behind-cloud.svg diff --git a/assets/images/product-illustrations/todd-behind-cloud.svg b/assets/images/product-illustrations/todd-behind-cloud.svg new file mode 100644 index 000000000000..6281ce0ef727 --- /dev/null +++ b/assets/images/product-illustrations/todd-behind-cloud.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.js b/src/components/Icon/Illustrations.js index 3a038d66ca72..f5243f469b8d 100644 --- a/src/components/Icon/Illustrations.js +++ b/src/components/Icon/Illustrations.js @@ -19,6 +19,7 @@ import RocketBlue from '../../../assets/images/product-illustrations/rocket--blu import RocketOrange from '../../../assets/images/product-illustrations/rocket--orange.svg'; import TadaYellow from '../../../assets/images/product-illustrations/tada--yellow.svg'; import TadaBlue from '../../../assets/images/product-illustrations/tada--blue.svg'; +import ToddBehindCloud from '../../../assets/images/product-illustrations/todd-behind-cloud.svg'; import GpsTrackOrange from '../../../assets/images/product-illustrations/gps-track--orange.svg'; import ShieldYellow from '../../../assets/images/simple-illustrations/simple-illustration__shield.svg'; import MoneyReceipts from '../../../assets/images/simple-illustrations/simple-illustration__money-receipts.svg'; @@ -60,6 +61,7 @@ export { RocketOrange, TadaYellow, TadaBlue, + ToddBehindCloud, GpsTrackOrange, ShieldYellow, MoneyReceipts, diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index 1fdd4cfb5f8f..7718fd0259b5 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -36,7 +36,7 @@ class ExpiredValidateCodeModal extends PureComponent {
From 7483f9a84da102a860d4e273003937ed52db1688 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 27 Feb 2023 23:38:52 +0200 Subject: [PATCH 07/36] Show expired code modal when sign in fails. --- src/pages/ValidateLoginPage/index.website.js | 32 ++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 5704f6900e6b..dd612094cf8c 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -15,6 +15,7 @@ import * as Session from '../../libs/actions/Session'; import Permissions from '../../libs/Permissions'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import AbracadabraModal from '../../components/ValidateCode/AbracadabraModal'; +import ExpiredValidateCodeModal from '../../components/ValidateCode/ExpiredValidateCodeModal'; const propTypes = { /** The accountID and validateCode are passed via the URL */ @@ -89,18 +90,25 @@ class ValidateLoginPage extends Component { isSignInInitiated = () => !this.isAuthenticated() && lodashGet(this.props, 'credentials.login', null); render() { - return ( - this.isOnPasswordlessBeta() && !this.isSignInInitiated() && !lodashGet(this.props, 'account.isLoading', true) - ? ( - this.state.justSignedIn ? - : Session.signInWithValidateCodeAndNavigate(this.accountID(), this.validateCode())} - /> - ) - : - ); + var showExpiredCodeModal = this.props.account && !_.isEmpty(this.props.account.errors); + var showAbracadabra = this.isOnPasswordlessBeta() + && !this.isSignInInitiated() + && !lodashGet(this.props, 'account.isLoading', true) + && this.state.justSignedIn; + var showValidateCodeModal = this.isOnPasswordlessBeta() + && !this.isSignInInitiated() + && !lodashGet(this.props, 'account.isLoading', true) + && !this.state.justSignedIn; + return <> + {showExpiredCodeModal && } + {showAbracadabra && } + {showValidateCodeModal && Session.signInWithValidateCodeAndNavigate(this.accountID(), this.validateCode())} + />} + {!showExpiredCodeModal && !showAbracadabra && !showValidateCodeModal && } + ; } } From ffb52ea040e85a9ba9efb612278d8b0812a3ded1 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Feb 2023 15:32:25 +0200 Subject: [PATCH 08/36] Expired magic link modal when trying to sign in the current tab with a wrong link. --- src/pages/ValidateLoginPage/index.website.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index dd612094cf8c..f8d9a1b66e93 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -16,6 +16,8 @@ import Permissions from '../../libs/Permissions'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import AbracadabraModal from '../../components/ValidateCode/AbracadabraModal'; import ExpiredValidateCodeModal from '../../components/ValidateCode/ExpiredValidateCodeModal'; +import Navigation from '../../libs/Navigation/Navigation'; +import ROUTES from '../../ROUTES'; const propTypes = { /** The accountID and validateCode are passed via the URL */ @@ -57,10 +59,13 @@ class ValidateLoginPage extends Component { } componentDidUpdate(prevProps) { - if (!(prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode)) { - return; + if (prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode) { + if (!lodashGet(this.props, 'credentials.login', null) && lodashGet(this.props, 'credentials.accountID', null)) { + Navigation.navigate(ROUTES.REPORT); + } else { + this.setState({justSignedIn: true}); + } } - this.setState({justSignedIn: true}); } /** @@ -98,14 +103,15 @@ class ValidateLoginPage extends Component { var showValidateCodeModal = this.isOnPasswordlessBeta() && !this.isSignInInitiated() && !lodashGet(this.props, 'account.isLoading', true) - && !this.state.justSignedIn; + && !this.state.justSignedIn + && _.isEmpty(this.props.account.errors); return <> {showExpiredCodeModal && } {showAbracadabra && } {showValidateCodeModal && Session.signInWithValidateCodeAndNavigate(this.accountID(), this.validateCode())} + onSignInHereClick={() => Session.signInWithValidateCode(this.accountID(), this.validateCode())} />} {!showExpiredCodeModal && !showAbracadabra && !showValidateCodeModal && } ; From 61a15e8da69b909cb9dfe43d1e1dbf5cdd032086 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Feb 2023 15:33:14 +0200 Subject: [PATCH 09/36] Cleanup unused action in Session. --- src/libs/actions/Session/index.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 0e98da31fc0c..c2393a2f11fc 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -17,8 +17,6 @@ import * as API from '../../API'; import * as NetworkStore from '../../Network/NetworkStore'; import * as Report from '../Report'; import DateUtils from '../../DateUtils'; -import Navigation from '../../Navigation/Navigation'; -import ROUTES from '../../../ROUTES'; let credentials = {}; Onyx.connect({ @@ -336,18 +334,12 @@ function signInWithValidateCode(accountID, validateCode) { }, ]; - // This is temporary for now. Server should login with the accountID and validateCode API.write('SigninUser', { validateCode, accountID, }, {optimisticData, successData, failureData}); } -function signInWithValidateCodeAndNavigate(accountID, validateCode) { - signInWithValidateCode(accountID, validateCode); - Navigation.navigate(ROUTES.HOME); -} - /** * User forgot the password so let's send them the link to reset their password */ @@ -567,7 +559,6 @@ export { updatePasswordAndSignin, signIn, signInWithValidateCode, - signInWithValidateCodeAndNavigate, signInWithShortLivedAuthToken, cleanupSession, signOut, From b601db1d4f47aa573c2b35881070bb2fb0189610 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Feb 2023 15:35:43 +0200 Subject: [PATCH 10/36] Add comments for a complex condition check. --- src/pages/ValidateLoginPage/index.website.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index f8d9a1b66e93..3ea7649617d7 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -61,8 +61,10 @@ class ValidateLoginPage extends Component { componentDidUpdate(prevProps) { if (prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode) { if (!lodashGet(this.props, 'credentials.login', null) && lodashGet(this.props, 'credentials.accountID', null)) { + // The user clicked the option to sign in the current tab Navigation.navigate(ROUTES.REPORT); } else { + // The sign in was initiated in another tab on the same browser this.setState({justSignedIn: true}); } } From fdca7c17ef6f2637c2fa346d9ed9000904e996fd Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 28 Feb 2023 16:16:33 +0200 Subject: [PATCH 11/36] Fix lint errors --- .../ValidateCode/ExpiredValidateCodeModal.js | 2 +- src/pages/ValidateLoginPage/index.website.js | 28 ++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index 7718fd0259b5..68d11f6a6394 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -54,7 +54,7 @@ class ExpiredValidateCodeModal extends PureComponent { {this.props.translate('validateCodeModal.requestNewCodeLink')} - {'!'} + ! )} diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 3ea7649617d7..9d1c84b82ebb 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -1,5 +1,6 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; +import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import { @@ -59,14 +60,15 @@ class ValidateLoginPage extends Component { } componentDidUpdate(prevProps) { - if (prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode) { - if (!lodashGet(this.props, 'credentials.login', null) && lodashGet(this.props, 'credentials.accountID', null)) { - // The user clicked the option to sign in the current tab - Navigation.navigate(ROUTES.REPORT); - } else { - // The sign in was initiated in another tab on the same browser - this.setState({justSignedIn: true}); - } + if (!(prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode)) { + return; + } + if (!lodashGet(this.props, 'credentials.login', null) && lodashGet(this.props, 'credentials.accountID', null)) { + // The user clicked the option to sign in the current tab + Navigation.navigate(ROUTES.REPORT); + } else { + // The sign in was initiated in another tab on the same browser + this.setState({justSignedIn: true}); } } @@ -97,17 +99,17 @@ class ValidateLoginPage extends Component { isSignInInitiated = () => !this.isAuthenticated() && lodashGet(this.props, 'credentials.login', null); render() { - var showExpiredCodeModal = this.props.account && !_.isEmpty(this.props.account.errors); - var showAbracadabra = this.isOnPasswordlessBeta() + const showExpiredCodeModal = this.props.account && !_.isEmpty(this.props.account.errors); + const showAbracadabra = this.isOnPasswordlessBeta() && !this.isSignInInitiated() && !lodashGet(this.props, 'account.isLoading', true) && this.state.justSignedIn; - var showValidateCodeModal = this.isOnPasswordlessBeta() + const showValidateCodeModal = this.isOnPasswordlessBeta() && !this.isSignInInitiated() && !lodashGet(this.props, 'account.isLoading', true) && !this.state.justSignedIn && _.isEmpty(this.props.account.errors); - return <> + return (<> {showExpiredCodeModal && } {showAbracadabra && } {showValidateCodeModal && Session.signInWithValidateCode(this.accountID(), this.validateCode())} />} {!showExpiredCodeModal && !showAbracadabra && !showValidateCodeModal && } - ; + ); } } From 9af3f8af6a8742cc79ce6073adb0e96d7616f1f5 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 1 Mar 2023 15:35:25 +0200 Subject: [PATCH 12/36] Add "request new code" link in the error modal. --- .../ValidateCode/ExpiredValidateCodeModal.js | 41 ++++++++++++++++++- src/languages/en.js | 1 + src/languages/es.js | 1 + src/libs/actions/Session/index.js | 34 +++++++++++++++ src/pages/ValidateLoginPage/index.website.js | 38 ++++++++++++----- 5 files changed, 103 insertions(+), 12 deletions(-) diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index 68d11f6a6394..dc33ccd25682 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -1,5 +1,8 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import _, {compose} from 'underscore'; +import lodashGet from 'lodash/get'; import {View} from 'react-native'; import colors from '../../styles/colors'; import styles from '../../styles/styles'; @@ -10,6 +13,8 @@ import * as Expensicons from '../Icon/Expensicons'; import * as Illustrations from '../Icon/Illustrations'; import variables from '../../styles/variables'; import TextLink from '../TextLink'; +import ONYXKEYS from '../../ONYXKEYS'; +import * as ErrorUtils from '../../libs/ErrorUtils'; const propTypes = { @@ -28,7 +33,14 @@ const defaultProps = { }; class ExpiredValidateCodeModal extends PureComponent { + render() { + const codeRequestedMessage = lodashGet(this.props, 'account.message', null); + const accountErrors = lodashGet(this.props, 'account.errors', {}); + let codeRequestedErrors; + if (Object.keys(accountErrors).length > 1) { + codeRequestedErrors = ErrorUtils.getLatestErrorMessage(this.props.account); + } return ( @@ -45,7 +57,7 @@ class ExpiredValidateCodeModal extends PureComponent { {this.props.translate('validateCodeModal.expiredCodeDescription')} - {this.props.shouldShowRequestCodeLink + {this.props.shouldShowRequestCodeLink && !codeRequestedMessage && ( <>
@@ -57,6 +69,26 @@ class ExpiredValidateCodeModal extends PureComponent { ! )} + {this.props.shouldShowRequestCodeLink && codeRequestedErrors + && ( + <> +
+
+ + {codeRequestedErrors} + + + ) + } + {this.props.shouldShowRequestCodeLink && codeRequestedMessage + && ( + <> +
+
+ {codeRequestedMessage} + + ) + }
@@ -75,4 +107,9 @@ class ExpiredValidateCodeModal extends PureComponent { ExpiredValidateCodeModal.propTypes = propTypes; ExpiredValidateCodeModal.defaultProps = defaultProps; -export default withLocalize(ExpiredValidateCodeModal); +export default compose( + withLocalize, + withOnyx({ + account: {key: ONYXKEYS.ACCOUNT}, + }), +)(ExpiredValidateCodeModal); diff --git a/src/languages/en.js b/src/languages/en.js index 3e2b4ab81a52..2c8e73c5ca6d 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -166,6 +166,7 @@ export default { expiredCodeDescription: 'Go back to the original device and request a new code.', requestNewCode: 'You can also', requestNewCodeLink: 'request a new code here', + successfulNewCodeRequest: 'Code requested. Please check your device.', }, iOUConfirmationList: { whoPaid: 'Who paid?', diff --git a/src/languages/es.js b/src/languages/es.js index 717f4d86abed..d7c92c18856c 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -166,6 +166,7 @@ export default { expiredCodeDescription: 'Vuelve al dispositivo original y solicita un nuevo código.', requestNewCode: '¡También puedes', requestNewCodeLink: 'solicitar un nuevo código aquí', + successfulNewCodeRequest: 'Código solicitado. Por favor, comprueba su dispositivo.', }, iOUConfirmationList: { whoPaid: '¿Quién pago?', diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index c2393a2f11fc..9eb1d7d5f2b9 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -139,6 +139,39 @@ function resendValidateCode(login = credentials.login) { API.write('RequestNewValidateCode', {email: login}, {optimisticData, successData, failureData}); } +/** + * Request a new validate / magic code for user to sign in automatically with the link + * + * @param {String} [login] + */ +function resendLinkWithValidateCode(login = credentials.login) { + const optimisticData = [{ + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.ACCOUNT, + value: { + isLoading: true, + message: null, + }, + }]; + const successData = [{ + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.ACCOUNT, + value: { + isLoading: false, + message: Localize.translateLocal('validateCodeModal.successfulNewCodeRequest'), + }, + }]; + const failureData = [{ + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.ACCOUNT, + value: { + isLoading: false, + message: null, + }, + }]; + API.write('RequestNewValidateCode', {email: login}, {optimisticData, successData, failureData}); +} + /** * Checks the API to see if an account exists for the given login * @@ -565,6 +598,7 @@ export { signOutAndRedirectToSignIn, resendValidationLink, resendValidateCode, + resendLinkWithValidateCode, resetPassword, resendResetPassword, clearSignInData, diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 9d1c84b82ebb..86b4af862083 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -39,6 +39,8 @@ class ValidateLoginPage extends Component { constructor(props) { super(props); + this.resendValidateCode = this.resendValidateCode.bind(this); + this.state = {justSignedIn: false}; } @@ -98,6 +100,13 @@ class ValidateLoginPage extends Component { */ isSignInInitiated = () => !this.isAuthenticated() && lodashGet(this.props, 'credentials.login', null); + /** + * Trigger the reset validate code flow + */ + resendValidateCode() { + Session.resendLinkWithValidateCode(); + } + render() { const showExpiredCodeModal = this.props.account && !_.isEmpty(this.props.account.errors); const showAbracadabra = this.isOnPasswordlessBeta() @@ -109,16 +118,25 @@ class ValidateLoginPage extends Component { && !lodashGet(this.props, 'account.isLoading', true) && !this.state.justSignedIn && _.isEmpty(this.props.account.errors); - return (<> - {showExpiredCodeModal && } - {showAbracadabra && } - {showValidateCodeModal && Session.signInWithValidateCode(this.accountID(), this.validateCode())} - />} - {!showExpiredCodeModal && !showAbracadabra && !showValidateCodeModal && } - ); + return ( + <> + {showExpiredCodeModal && ( + + )} + {showAbracadabra && } + {showValidateCodeModal && ( + Session.signInWithValidateCode(this.accountID(), this.validateCode())} + /> + )} + {!showExpiredCodeModal && !showAbracadabra && !showValidateCodeModal && } + + ); } } From 2ec71acd103d6a4a089737d2b127bac7d1c771a2 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 1 Mar 2023 16:25:38 +0200 Subject: [PATCH 13/36] Fix lint and style errors. --- assets/images/expensify-wordmark.svg | 39 ++++++++---------- .../ValidateCode/ExpiredValidateCodeModal.js | 41 +++++++++---------- .../ValidateCode/ValidateCodeModal.js | 2 +- src/styles/styles.js | 6 ++- src/styles/variables.js | 3 +- 5 files changed, 45 insertions(+), 46 deletions(-) diff --git a/assets/images/expensify-wordmark.svg b/assets/images/expensify-wordmark.svg index 73018497030b..69fbcbae6743 100644 --- a/assets/images/expensify-wordmark.svg +++ b/assets/images/expensify-wordmark.svg @@ -1,26 +1,23 @@ - + + viewBox="0 0 78 19" style="enable-background:new 0 0 78 19;" xml:space="preserve"> - - - - - - - - - + + + + + + + + + diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index dc33ccd25682..32cfa2a96504 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -33,12 +33,11 @@ const defaultProps = { }; class ExpiredValidateCodeModal extends PureComponent { - render() { const codeRequestedMessage = lodashGet(this.props, 'account.message', null); const accountErrors = lodashGet(this.props, 'account.errors', {}); let codeRequestedErrors; - if (Object.keys(accountErrors).length > 1) { + if (_.keys(accountErrors).length > 1) { codeRequestedErrors = ErrorUtils.getLatestErrorMessage(this.props.account); } return ( @@ -69,27 +68,25 @@ class ExpiredValidateCodeModal extends PureComponent { ! )} - {this.props.shouldShowRequestCodeLink && codeRequestedErrors - && ( - <> -
-
- - {codeRequestedErrors} - - - ) - } - {this.props.shouldShowRequestCodeLink && codeRequestedMessage - && ( - <> -
-
- {codeRequestedMessage} - - ) - } + {this.props.shouldShowRequestCodeLink && codeRequestedErrors + && ( + +
+
+ + {codeRequestedErrors} + +
+ )} + {this.props.shouldShowRequestCodeLink && codeRequestedMessage + && ( + +
+
+ {codeRequestedMessage} +
+ )}
diff --git a/src/components/ValidateCode/ValidateCodeModal.js b/src/components/ValidateCode/ValidateCodeModal.js index fe97e878a698..eb2f4835b224 100644 --- a/src/components/ValidateCode/ValidateCodeModal.js +++ b/src/components/ValidateCode/ValidateCodeModal.js @@ -62,7 +62,7 @@ class ValidateCodeModal extends PureComponent { - + {this.props.code} diff --git a/src/styles/styles.js b/src/styles/styles.js index 0f8457940c87..24422c3fac54 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2911,12 +2911,16 @@ const styles = { paddingBottom: 45, }, - magicCodeDigits: { + validateCodeDigits: { color: themeColors.text, fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeXXLarge, letterSpacing: 4, }, + validateCodeMessage: { + width: variables.modalContentMaxWidth, + textAlign: 'center', + }, }; export default styles; diff --git a/src/styles/variables.js b/src/styles/variables.js index 0e91a707d00a..272cecefc024 100644 --- a/src/styles/variables.js +++ b/src/styles/variables.js @@ -97,5 +97,6 @@ export default { modalTopIconHeight: 164, modalTopBigIconHeight: 244, modalWordmarkWidth: 154, - modalWordmarkHeight: 34, + modalWordmarkHeight: 37, + modalContentMaxWidth: 360, }; From 7c4cd9a62f60d53b64d3c6a5be9f0e853c121370 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 3 Mar 2023 17:13:42 +0200 Subject: [PATCH 14/36] Make it work for accounts with 2FA --- src/pages/ValidateLoginPage/index.website.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 86b4af862083..20b8ce03d236 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -62,16 +62,19 @@ class ValidateLoginPage extends Component { } componentDidUpdate(prevProps) { - if (!(prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode)) { - return; - } if (!lodashGet(this.props, 'credentials.login', null) && lodashGet(this.props, 'credentials.accountID', null)) { // The user clicked the option to sign in the current tab - Navigation.navigate(ROUTES.REPORT); - } else { - // The sign in was initiated in another tab on the same browser - this.setState({justSignedIn: true}); + if (lodashGet(this.props, 'account.requiresTwoFactorAuth', false)) { + Navigation.navigate(ROUTES.HOME); + } else { + Navigation.navigate(ROUTES.REPORT); + } + return; } + if (!(prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode)) { + return; + } + this.setState({justSignedIn: true}); } /** @@ -110,7 +113,6 @@ class ValidateLoginPage extends Component { render() { const showExpiredCodeModal = this.props.account && !_.isEmpty(this.props.account.errors); const showAbracadabra = this.isOnPasswordlessBeta() - && !this.isSignInInitiated() && !lodashGet(this.props, 'account.isLoading', true) && this.state.justSignedIn; const showValidateCodeModal = this.isOnPasswordlessBeta() From 61e702e87befefcf849682b90a185a3db13c6d0a Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 6 Mar 2023 14:09:08 +0200 Subject: [PATCH 15/36] Try fixing the intermediary screen on iOS. --- src/pages/ValidateLoginPage/index.website.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 20b8ce03d236..ff9d4856be6c 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -40,6 +40,7 @@ class ValidateLoginPage extends Component { super(props); this.resendValidateCode = this.resendValidateCode.bind(this); + this.signInWithValidateCode = this.signInWithValidateCode.bind(this); this.state = {justSignedIn: false}; } @@ -71,10 +72,10 @@ class ValidateLoginPage extends Component { } return; } - if (!(prevProps.credentials && !prevProps.credentials.validateCode && this.props.credentials.validateCode)) { + if (!(!lodashGet(prevProps, 'credentials.validateCode', null) && lodashGet(this.props, 'credentials.validateCode', null))) { return; } - this.setState({justSignedIn: true}); + setTimeout(() => this.setState({justSignedIn: true}), 500); } /** @@ -103,13 +104,14 @@ class ValidateLoginPage extends Component { */ isSignInInitiated = () => !this.isAuthenticated() && lodashGet(this.props, 'credentials.login', null); - /** - * Trigger the reset validate code flow - */ resendValidateCode() { Session.resendLinkWithValidateCode(); } + signInWithValidateCode() { + Session.signInWithValidateCode(this.accountID(), this.validateCode()); + } + render() { const showExpiredCodeModal = this.props.account && !_.isEmpty(this.props.account.errors); const showAbracadabra = this.isOnPasswordlessBeta() @@ -133,7 +135,7 @@ class ValidateLoginPage extends Component { Session.signInWithValidateCode(this.accountID(), this.validateCode())} + onSignInHereClick={this.signInWithValidateCode} /> )} {!showExpiredCodeModal && !showAbracadabra && !showValidateCodeModal && } From c63438a86ee911284d3cd1719ab827376a2dfb54 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 7 Mar 2023 14:21:53 +0200 Subject: [PATCH 16/36] Work in progress. --- src/libs/Permissions.js | 3 +- src/libs/actions/Session/index.js | 20 ++++--- src/pages/ValidateLoginPage/index.website.js | 57 ++++++++++++-------- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js index 68f1a493f158..be5f45a48b4c 100644 --- a/src/libs/Permissions.js +++ b/src/libs/Permissions.js @@ -91,7 +91,8 @@ function canUsePolicyExpenseChat(betas) { * @returns {Boolean} */ function canUsePasswordlessLogins(betas) { - return _.contains(betas, CONST.BETAS.PASSWORDLESS) || _.contains(betas, CONST.BETAS.ALL); + return true; + // return _.contains(betas, CONST.BETAS.PASSWORDLESS) || _.contains(betas, CONST.BETAS.ALL); } export default { diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 9eb1d7d5f2b9..542890a06d8e 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -337,15 +337,18 @@ function signInWithValidateCode(accountID, validateCode) { isLoading: true, }, }, + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.CREDENTIALS, + value: {signedWithLink: true}, + }, ]; const successData = [ { onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.ACCOUNT, - value: { - isLoading: false, - }, + value: {isLoading: false}, }, { onyxMethod: CONST.ONYX.METHOD.MERGE, @@ -361,13 +364,16 @@ function signInWithValidateCode(accountID, validateCode) { { onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.ACCOUNT, - value: { - isLoading: false, - }, + value: {isLoading: false}, + }, + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.CREDENTIALS, + value: {signedWithLink: null}, }, ]; - API.write('SigninUser', { + API.write('SigninUserWithLink', { validateCode, accountID, }, {optimisticData, successData, failureData}); diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index ff9d4856be6c..73b36a8d2f8b 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import {withOnyx} from 'react-native-onyx'; +import Onyx, {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import { propTypes as validateLinkPropTypes, @@ -42,7 +42,7 @@ class ValidateLoginPage extends Component { this.resendValidateCode = this.resendValidateCode.bind(this); this.signInWithValidateCode = this.signInWithValidateCode.bind(this); - this.state = {justSignedIn: false}; + this.state = {modal: null}; } componentDidMount() { @@ -59,7 +59,10 @@ class ValidateLoginPage extends Component { // - AND the user has initiated the sign in process in another tab if (this.isOnPasswordlessBeta() && !this.isAuthenticated() && this.isSignInInitiated()) { Session.signInWithValidateCode(this.accountID(), this.validateCode()); + return; } + + this.showPasswordlessModal(false); } componentDidUpdate(prevProps) { @@ -72,10 +75,8 @@ class ValidateLoginPage extends Component { } return; } - if (!(!lodashGet(prevProps, 'credentials.validateCode', null) && lodashGet(this.props, 'credentials.validateCode', null))) { - return; - } - setTimeout(() => this.setState({justSignedIn: true}), 500); + const justSignedIn = !lodashGet(prevProps, 'credentials.accountID', null) && lodashGet(this.props, 'credentials.accountID', null); + this.showPasswordlessModal(justSignedIn); } /** @@ -96,7 +97,7 @@ class ValidateLoginPage extends Component { /** * @returns {Boolean} */ - isAuthenticated = () => Boolean(lodashGet(this.props, 'session.authToken', null)); + isAuthenticated = () => Boolean(lodashGet(this.props, 'credentials.autoGeneratedLogin', null)); /** * Whether SignIn was initiated on the current browser. @@ -112,33 +113,48 @@ class ValidateLoginPage extends Component { Session.signInWithValidateCode(this.accountID(), this.validateCode()); } + showPasswordlessModal(justSignedIn) { + if (!this.isOnPasswordlessBeta()) { + return; + } + if (this.state.modal === 'abracadabra') { + return; + } + if (justSignedIn) { + this.setState({modal: 'abracadabra'}); + Onyx.merge(ONYXKEYS.CREDENTIALS, {signedWithLink: null}); + return; + } + if (!_.isEmpty(lodashGet(this.props, 'account.errors', {})) && this.state.modal != 'expiredCode') { + this.setState({modal: 'expiredCode'}); + return; + } + if ( + (this.isAuthenticated() || !lodashGet(this.props, 'credentials.login', null)) + && !lodashGet(this.props, 'credentials.signedWithLink', null) + && this.state.modal != 'validateCode') { + this.setState({modal: 'validateCode'}); + } + } + render() { - const showExpiredCodeModal = this.props.account && !_.isEmpty(this.props.account.errors); - const showAbracadabra = this.isOnPasswordlessBeta() - && !lodashGet(this.props, 'account.isLoading', true) - && this.state.justSignedIn; - const showValidateCodeModal = this.isOnPasswordlessBeta() - && !this.isSignInInitiated() - && !lodashGet(this.props, 'account.isLoading', true) - && !this.state.justSignedIn - && _.isEmpty(this.props.account.errors); return ( <> - {showExpiredCodeModal && ( + {this.state.modal === 'expiredCode' && ( )} - {showAbracadabra && } - {showValidateCodeModal && ( + {this.state.modal === 'abracadabra' && } + {this.state.modal === 'validateCode' && ( )} - {!showExpiredCodeModal && !showAbracadabra && !showValidateCodeModal && } + {!this.state.modal && } ); } @@ -153,6 +169,5 @@ export default compose( account: {key: ONYXKEYS.ACCOUNT}, betas: {key: ONYXKEYS.BETAS}, credentials: {key: ONYXKEYS.CREDENTIALS}, - session: {key: ONYXKEYS.SESSION}, }), )(ValidateLoginPage); From a8ceacaa29b13c59f9970b5f6f00e248715d5e4e Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 10 Mar 2023 19:34:53 +0200 Subject: [PATCH 17/36] Revert undesired changes in Permissions.js --- src/libs/Permissions.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/Permissions.js b/src/libs/Permissions.js index be5f45a48b4c..68f1a493f158 100644 --- a/src/libs/Permissions.js +++ b/src/libs/Permissions.js @@ -91,8 +91,7 @@ function canUsePolicyExpenseChat(betas) { * @returns {Boolean} */ function canUsePasswordlessLogins(betas) { - return true; - // return _.contains(betas, CONST.BETAS.PASSWORDLESS) || _.contains(betas, CONST.BETAS.ALL); + return _.contains(betas, CONST.BETAS.PASSWORDLESS) || _.contains(betas, CONST.BETAS.ALL); } export default { From 7632b8eafba8f6e35e8b687126ea0e6c05beab7d Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 10 Mar 2023 20:27:08 +0200 Subject: [PATCH 18/36] Fix authentication when sign in with link and 2fa --- src/libs/actions/Session/index.js | 5 +++-- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 542890a06d8e..a66dd84157bc 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -327,7 +327,7 @@ function signIn(password, validateCode, twoFactorAuthCode) { API.write('SigninUser', params, {optimisticData, successData, failureData}); } -function signInWithValidateCode(accountID, validateCode) { +function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { const optimisticData = [ { onyxMethod: CONST.ONYX.METHOD.MERGE, @@ -374,8 +374,9 @@ function signInWithValidateCode(accountID, validateCode) { ]; API.write('SigninUserWithLink', { - validateCode, accountID, + validateCode, + twoFactorAuthCode, }, {optimisticData, successData, failureData}); } diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index abc753e30ee4..ec8e5f444178 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -5,6 +5,7 @@ import { import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import lodashGet from 'lodash/get'; import styles from '../../../styles/styles'; import Button from '../../../components/Button'; import Text from '../../../components/Text'; @@ -175,7 +176,12 @@ class BaseValidateCodeForm extends React.Component { formError: {}, }); - Session.signIn('', this.state.validateCode, this.state.twoFactorAuthCode); + const accountID = lodashGet(this.props, 'credentials.accountID', null); + if (accountID) { + Session.signInWithValidateCode(accountID, this.state.validateCode, this.state.twoFactorAuthCode); + } else { + Session.signIn('', this.state.validateCode, this.state.twoFactorAuthCode); + } } render() { From 939d5074b43f5344c0d21582997e68fd66dc12f1 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 13 Mar 2023 23:40:13 +0200 Subject: [PATCH 19/36] Cleanup ValidateLoginPage --- src/CONST.js | 7 ++ src/libs/actions/Session/index.js | 18 +++- src/pages/ValidateLoginPage/index.website.js | 90 ++++---------------- 3 files changed, 36 insertions(+), 79 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 418d5bc1ad6b..7a84125f9477 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -26,6 +26,13 @@ const CONST = { MIN_SIZE: 240, }, + AUTO_AUTH_STATE: { + NOT_STARTED: 'not-started', + SIGNING_IN: 'signing-in', + JUST_SIGNED_IN: 'just-signed-in', + FAILED: 'failed', + }, + AVATAR_MAX_ATTACHMENT_SIZE: 6291456, AVATAR_ALLOWED_EXTENSIONS: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'], diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index d0ab05fd2c43..6492d3edf886 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -332,8 +332,8 @@ function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { }, { onyxMethod: CONST.ONYX.METHOD.MERGE, - key: ONYXKEYS.CREDENTIALS, - value: {signedWithLink: true}, + key: ONYXKEYS.SESSION, + value: {autoAuthState: CONST.AUTO_AUTH_STATE.SIGNING_IN}, }, ]; @@ -351,6 +351,11 @@ function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { validateCode, }, }, + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: ONYXKEYS.SESSION, + value: {autoAuthState: CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN}, + }, ]; const failureData = [ @@ -361,8 +366,8 @@ function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { }, { onyxMethod: CONST.ONYX.METHOD.MERGE, - key: ONYXKEYS.CREDENTIALS, - value: {signedWithLink: null}, + key: ONYXKEYS.SESSION, + value: {autoAuthState: CONST.AUTO_AUTH_STATE.FAILED}, }, ]; @@ -373,6 +378,10 @@ function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { }, {optimisticData, successData, failureData}); } +function initAutoAuthState() { + Onyx.merge(ONYXKEYS.SESSION, {autoAuthState: CONST.AUTO_AUTH_STATE.NOT_STARTED}); +} + /** * User forgot the password so let's send them the link to reset their password */ @@ -592,6 +601,7 @@ export { updatePasswordAndSignin, signIn, signInWithValidateCode, + initAutoAuthState, signInWithShortLivedAuthToken, cleanupSession, signOut, diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 73b36a8d2f8b..c10fa9da09e1 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -19,6 +19,7 @@ import AbracadabraModal from '../../components/ValidateCode/AbracadabraModal'; import ExpiredValidateCodeModal from '../../components/ValidateCode/ExpiredValidateCodeModal'; import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; +import CONST from '../../CONST'; const propTypes = { /** The accountID and validateCode are passed via the URL */ @@ -39,50 +40,40 @@ class ValidateLoginPage extends Component { constructor(props) { super(props); - this.resendValidateCode = this.resendValidateCode.bind(this); - this.signInWithValidateCode = this.signInWithValidateCode.bind(this); - - this.state = {modal: null}; + this.state = {autoAuth: null}; } componentDidMount() { // Validate login if // - The user is not on passwordless beta - if (!this.isOnPasswordlessBeta()) { + if (!Permissions.canUsePasswordlessLogins(this.props.betas)) { User.validateLogin(this.accountID(), this.validateCode()); return; } + Session.initAutoAuthState(); + // Sign in if // - The user is on the passwordless beta // - AND the user is not authenticated // - AND the user has initiated the sign in process in another tab - if (this.isOnPasswordlessBeta() && !this.isAuthenticated() && this.isSignInInitiated()) { + if (!lodashGet(this.props, 'session.authToken',null) && lodashGet(this.props, 'credentials.login', null)) { Session.signInWithValidateCode(this.accountID(), this.validateCode()); return; } - - this.showPasswordlessModal(false); } componentDidUpdate(prevProps) { if (!lodashGet(this.props, 'credentials.login', null) && lodashGet(this.props, 'credentials.accountID', null)) { + // The user clicked the option to sign in the current tab - if (lodashGet(this.props, 'account.requiresTwoFactorAuth', false)) { - Navigation.navigate(ROUTES.HOME); - } else { - Navigation.navigate(ROUTES.REPORT); - } - return; + Navigation.navigate(ROUTES.HOME); } - const justSignedIn = !lodashGet(prevProps, 'credentials.accountID', null) && lodashGet(this.props, 'credentials.accountID', null); - this.showPasswordlessModal(justSignedIn); } - /** - * @returns {Boolean} - */ - isOnPasswordlessBeta = () => Permissions.canUsePasswordlessLogins(this.props.betas); + getAutoAuthState() { + return lodashGet(this.props, 'session.autoAuthState', CONST.AUTO_AUTH_STATE.NOT_STARTED); + } /** * @returns {String} @@ -94,67 +85,16 @@ class ValidateLoginPage extends Component { */ validateCode = () => lodashGet(this.props.route.params, 'validateCode', ''); - /** - * @returns {Boolean} - */ - isAuthenticated = () => Boolean(lodashGet(this.props, 'credentials.autoGeneratedLogin', null)); - - /** - * Whether SignIn was initiated on the current browser. - * @returns {Boolean} - */ - isSignInInitiated = () => !this.isAuthenticated() && lodashGet(this.props, 'credentials.login', null); - - resendValidateCode() { - Session.resendLinkWithValidateCode(); - } - - signInWithValidateCode() { - Session.signInWithValidateCode(this.accountID(), this.validateCode()); - } - - showPasswordlessModal(justSignedIn) { - if (!this.isOnPasswordlessBeta()) { - return; - } - if (this.state.modal === 'abracadabra') { - return; - } - if (justSignedIn) { - this.setState({modal: 'abracadabra'}); - Onyx.merge(ONYXKEYS.CREDENTIALS, {signedWithLink: null}); - return; - } - if (!_.isEmpty(lodashGet(this.props, 'account.errors', {})) && this.state.modal != 'expiredCode') { - this.setState({modal: 'expiredCode'}); - return; - } - if ( - (this.isAuthenticated() || !lodashGet(this.props, 'credentials.login', null)) - && !lodashGet(this.props, 'credentials.signedWithLink', null) - && this.state.modal != 'validateCode') { - this.setState({modal: 'validateCode'}); - } - } - render() { return ( <> - {this.state.modal === 'expiredCode' && ( - - )} - {this.state.modal === 'abracadabra' && } - {this.state.modal === 'validateCode' && ( + {this.getAutoAuthState === CONST.AUTO_AUTH_STATE.FAILED && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && ( )} - {!this.state.modal && } ); } @@ -166,8 +106,8 @@ ValidateLoginPage.defaultProps = defaultProps; export default compose( withLocalize, withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, betas: {key: ONYXKEYS.BETAS}, credentials: {key: ONYXKEYS.CREDENTIALS}, + session: {key: ONYXKEYS.SESSION}, }), )(ValidateLoginPage); From 3b66d81f23d4afe1a131377fff71cfd48c3c459c Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 13 Mar 2023 23:42:12 +0200 Subject: [PATCH 20/36] Code style consistency. --- src/pages/ValidateLoginPage/index.website.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index c10fa9da09e1..3bc3c4aa1cca 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -47,7 +47,7 @@ class ValidateLoginPage extends Component { // Validate login if // - The user is not on passwordless beta if (!Permissions.canUsePasswordlessLogins(this.props.betas)) { - User.validateLogin(this.accountID(), this.validateCode()); + User.validateLogin(this.getAccountID(), this.getValidateCode()); return; } @@ -58,7 +58,7 @@ class ValidateLoginPage extends Component { // - AND the user is not authenticated // - AND the user has initiated the sign in process in another tab if (!lodashGet(this.props, 'session.authToken',null) && lodashGet(this.props, 'credentials.login', null)) { - Session.signInWithValidateCode(this.accountID(), this.validateCode()); + Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode()); return; } } @@ -78,12 +78,16 @@ class ValidateLoginPage extends Component { /** * @returns {String} */ - accountID = () => lodashGet(this.props.route.params, 'accountID', ''); + getAccountID() { + return lodashGet(this.props.route.params, 'accountID', ''); + } /** * @returns {String} */ - validateCode = () => lodashGet(this.props.route.params, 'validateCode', ''); + getValidateCode(){ + return lodashGet(this.props.route.params, 'validateCode', ''); + } render() { return ( @@ -92,7 +96,7 @@ class ValidateLoginPage extends Component { {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && ( )} From 6900b039b2fed50abb95abd495723bee67ded097 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 14 Mar 2023 19:09:22 +0200 Subject: [PATCH 21/36] Finally found a way to handle the unmount-remount of the page due to switching from PublicScreens-AuthScreen --- src/libs/actions/Session/index.js | 10 ++++++++-- src/pages/ValidateLoginPage/index.website.js | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 6492d3edf886..01df27062c73 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -378,8 +378,14 @@ function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { }, {optimisticData, successData, failureData}); } -function initAutoAuthState() { - Onyx.merge(ONYXKEYS.SESSION, {autoAuthState: CONST.AUTO_AUTH_STATE.NOT_STARTED}); +function initAutoAuthState(cachedAutoAuthState) { + Onyx.merge( + ONYXKEYS.SESSION, + { + autoAuthState: cachedAutoAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN ? + CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN : CONST.AUTO_AUTH_STATE.NOT_STARTED + } + ); } /** diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 3bc3c4aa1cca..67087ae11af8 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -51,7 +51,7 @@ class ValidateLoginPage extends Component { return; } - Session.initAutoAuthState(); + Session.initAutoAuthState(lodashGet(this.props, 'session.autoAuthState', null)); // Sign in if // - The user is on the passwordless beta @@ -99,6 +99,7 @@ class ValidateLoginPage extends Component { code={this.getValidateCode()} /> )} + {this.getAutoAuthState() == CONST.AUTO_AUTH_STATE.SIGNING_IN && } ); } From 73aa4124bb7cac3ae2346bd2d776057dfc37ab18 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 14 Mar 2023 19:18:30 +0200 Subject: [PATCH 22/36] Add js doc to explain how autoAuthState initialization works --- src/libs/actions/Session/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 01df27062c73..bf0511507f3d 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -378,6 +378,16 @@ function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { }, {optimisticData, successData, failureData}); } +/** + * Initializes the state of the automatic authentication when the user clicks on a magic link. + * + * This method is called in componentDidMount event of the lifecycle. + * When the user gets authenticated, the component is unmounted and then remounted + * when AppNavigator switches from PublicScreens to AuthScreens. + * That's the reason why autoAuthState initialization is skipped while the last state is SIGNING_IN. + * + * @param {string} cachedAutoAuthState + */ function initAutoAuthState(cachedAutoAuthState) { Onyx.merge( ONYXKEYS.SESSION, From 02c753d8bbfbe543b178abc06ffa3895ccc52649 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 14 Mar 2023 23:38:47 +0200 Subject: [PATCH 23/36] Add "sign in here" option. --- .../ValidateCode/ValidateCodeModal.js | 39 ++++++++++------ src/pages/ValidateLoginPage/index.website.js | 44 +++++++++++-------- src/pages/signin/SignInPage.js | 3 +- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/components/ValidateCode/ValidateCodeModal.js b/src/components/ValidateCode/ValidateCodeModal.js index eb2f4835b224..854cdf9cdbfe 100644 --- a/src/components/ValidateCode/ValidateCodeModal.js +++ b/src/components/ValidateCode/ValidateCodeModal.js @@ -1,5 +1,8 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; +import {compose} from 'underscore'; +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import {View} from 'react-native'; import colors from '../../styles/colors'; import styles from '../../styles/styles'; @@ -10,27 +13,31 @@ import * as Expensicons from '../Icon/Expensicons'; import * as Illustrations from '../Icon/Illustrations'; import variables from '../../styles/variables'; import TextLink from '../TextLink'; +import ONYXKEYS from '../../ONYXKEYS'; +import * as Session from '../../libs/actions/Session'; const propTypes = { /** Code to display. */ code: PropTypes.string.isRequired, - /** Whether the user can get signed straight in the App from the current page */ - shouldShowSignInHere: PropTypes.bool, - - /** Callback to be called when user clicks the Sign in here link */ - onSignInHereClick: PropTypes.func, + /** The ID of the account to which the code belongs. */ + accountID: PropTypes.string.isRequired, ...withLocalizePropTypes, }; -const defaultProps = { - shouldShowSignInHere: false, - onSignInHereClick: () => {}, -}; - class ValidateCodeModal extends PureComponent { + constructor(props) { + super(props); + + this.signInHere = this.signInHere.bind(this); + } + + signInHere() { + Session.signInWithValidateCode(this.props.accountID, this.props.code); + } + render() { return ( @@ -48,12 +55,12 @@ class ValidateCodeModal extends PureComponent { {this.props.translate('validateCodeModal.description')} - {this.props.shouldShowSignInHere + {!lodashGet(this.props, 'session.authToken', null) && ( <> {this.props.translate('validateCodeModal.or')} {' '} - + {this.props.translate('validateCodeModal.signInHere')} @@ -81,5 +88,9 @@ class ValidateCodeModal extends PureComponent { } ValidateCodeModal.propTypes = propTypes; -ValidateCodeModal.defaultProps = defaultProps; -export default withLocalize(ValidateCodeModal); +export default compose( + withLocalize, + withOnyx({ + session: {key: ONYXKEYS.SESSION}, + }), +)(ValidateCodeModal); diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 67087ae11af8..825c5211db8b 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import Onyx, {withOnyx} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import { propTypes as validateLinkPropTypes, @@ -20,6 +20,7 @@ import ExpiredValidateCodeModal from '../../components/ValidateCode/ExpiredValid import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; +import { InteractionManager } from 'react-native'; const propTypes = { /** The accountID and validateCode are passed via the URL */ @@ -37,12 +38,6 @@ const defaultProps = { }; class ValidateLoginPage extends Component { - constructor(props) { - super(props); - - this.state = {autoAuth: null}; - } - componentDidMount() { // Validate login if // - The user is not on passwordless beta @@ -51,26 +46,40 @@ class ValidateLoginPage extends Component { return; } - Session.initAutoAuthState(lodashGet(this.props, 'session.autoAuthState', null)); + const isSignedIn = Boolean(lodashGet(this.props, 'session.authToken', null)); + const cachedAutoAuthState = lodashGet(this.props, 'session.autoAuthState', null); + if (isSignedIn && cachedAutoAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN) { + + // The user clicked the option to sign in the current tab + Navigation.navigate(ROUTES.REPORT); + return; + } + Session.initAutoAuthState(cachedAutoAuthState); // Sign in if // - The user is on the passwordless beta // - AND the user is not authenticated // - AND the user has initiated the sign in process in another tab - if (!lodashGet(this.props, 'session.authToken',null) && lodashGet(this.props, 'credentials.login', null)) { + if (!isSignedIn && lodashGet(this.props, 'credentials.login', null)) { Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode()); - return; } } - componentDidUpdate(prevProps) { - if (!lodashGet(this.props, 'credentials.login', null) && lodashGet(this.props, 'credentials.accountID', null)) { - + componentDidUpdate() { + if ( + !lodashGet(this.props, 'credentials.login', null) + && lodashGet(this.props, 'credentials.accountID', null) + && lodashGet(this.props, 'account.requiresTwoFactorAuth', false) + ) { + // The user clicked the option to sign in the current tab - Navigation.navigate(ROUTES.HOME); + Navigation.navigate(ROUTES.REPORT); } } + /** + * @returns {String} + */ getAutoAuthState() { return lodashGet(this.props, 'session.autoAuthState', CONST.AUTO_AUTH_STATE.NOT_STARTED); } @@ -94,11 +103,7 @@ class ValidateLoginPage extends Component { <> {this.getAutoAuthState === CONST.AUTO_AUTH_STATE.FAILED && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && ( - - )} + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && } {this.getAutoAuthState() == CONST.AUTO_AUTH_STATE.SIGNING_IN && } ); @@ -111,6 +116,7 @@ ValidateLoginPage.defaultProps = defaultProps; export default compose( withLocalize, withOnyx({ + account: {key: ONYXKEYS.ACCOUNT}, betas: {key: ONYXKEYS.BETAS}, credentials: {key: ONYXKEYS.CREDENTIALS}, session: {key: ONYXKEYS.SESSION}, diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index 323c166b3ec4..f8c1ccaffa0b 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -1,6 +1,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; import {SafeAreaView} from 'react-native-safe-area-context'; import ONYXKEYS from '../../ONYXKEYS'; @@ -90,7 +91,7 @@ class SignInPage extends Component { // We will only know this after a user signs in successfully, without their 2FA code welcomeText = this.props.translate('validateCodeForm.enterAuthenticatorCode'); } else { - const userLogin = Str.removeSMSDomain(this.props.credentials.login); + const userLogin = Str.removeSMSDomain(lodashGet(this.props, 'credentials.login', '')); welcomeText = this.props.account.validated ? this.props.translate('welcomeText.welcomeBackEnterMagicCode', {login: userLogin}) : this.props.translate('welcomeText.welcomeEnterMagicCode', {login: userLogin}); From c84620d8db6d5cf2ea53fe3505c0f3bc0e7d3b6a Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 00:02:51 +0200 Subject: [PATCH 24/36] Add expired validate code modal. --- .../ValidateCode/ExpiredValidateCodeModal.js | 37 ++++++++++--------- src/pages/ValidateLoginPage/index.website.js | 8 ++-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index 32cfa2a96504..ee22cf777ee2 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -1,5 +1,4 @@ import React, {PureComponent} from 'react'; -import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _, {compose} from 'underscore'; import lodashGet from 'lodash/get'; @@ -15,24 +14,28 @@ import variables from '../../styles/variables'; import TextLink from '../TextLink'; import ONYXKEYS from '../../ONYXKEYS'; import * as ErrorUtils from '../../libs/ErrorUtils'; +import * as Session from '../../libs/actions/Session'; const propTypes = { - /** Whether the user can a new validate code from the current page */ - shouldShowRequestCodeLink: PropTypes.bool, - - /** Callback to be called when user clicks the request code link */ - onRequestCodeClick: PropTypes.func, - ...withLocalizePropTypes, }; -const defaultProps = { - shouldShowRequestCodeLink: false, - onRequestCodeClick: () => {}, -}; - class ExpiredValidateCodeModal extends PureComponent { + constructor(props) { + super(props); + + this.requestNewCode = this.requestNewCode.bind(this); + } + + shouldShowRequestCodeLink() { + return Boolean(lodashGet(this.props, 'credentials.login', null)); + } + + requestNewCode() { + Session.resendValidateCode(); + } + render() { const codeRequestedMessage = lodashGet(this.props, 'account.message', null); const accountErrors = lodashGet(this.props, 'account.errors', {}); @@ -56,20 +59,20 @@ class ExpiredValidateCodeModal extends PureComponent { {this.props.translate('validateCodeModal.expiredCodeDescription')} - {this.props.shouldShowRequestCodeLink && !codeRequestedMessage + {this.shouldShowRequestCodeLink() && !codeRequestedMessage && ( <>
{this.props.translate('validateCodeModal.requestNewCode')} {' '} - + {this.props.translate('validateCodeModal.requestNewCodeLink')} ! )}
- {this.props.shouldShowRequestCodeLink && codeRequestedErrors + {this.shouldShowRequestCodeLink() && codeRequestedErrors && (
@@ -79,7 +82,7 @@ class ExpiredValidateCodeModal extends PureComponent {
)} - {this.props.shouldShowRequestCodeLink && codeRequestedMessage + {this.shouldShowRequestCodeLink() && codeRequestedMessage && (
@@ -103,10 +106,10 @@ class ExpiredValidateCodeModal extends PureComponent { } ExpiredValidateCodeModal.propTypes = propTypes; -ExpiredValidateCodeModal.defaultProps = defaultProps; export default compose( withLocalize, withOnyx({ account: {key: ONYXKEYS.ACCOUNT}, + credentials: {key: ONYXKEYS.CREDENTIALS}, }), )(ExpiredValidateCodeModal); diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 825c5211db8b..0b8a75bf0486 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -20,7 +20,6 @@ import ExpiredValidateCodeModal from '../../components/ValidateCode/ExpiredValid import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; -import { InteractionManager } from 'react-native'; const propTypes = { /** The accountID and validateCode are passed via the URL */ @@ -48,7 +47,8 @@ class ValidateLoginPage extends Component { const isSignedIn = Boolean(lodashGet(this.props, 'session.authToken', null)); const cachedAutoAuthState = lodashGet(this.props, 'session.autoAuthState', null); - if (isSignedIn && cachedAutoAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN) { + const login = lodashGet(this.props, 'credentials.login', null); + if (!login && isSignedIn && cachedAutoAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN) { // The user clicked the option to sign in the current tab Navigation.navigate(ROUTES.REPORT); @@ -60,7 +60,7 @@ class ValidateLoginPage extends Component { // - The user is on the passwordless beta // - AND the user is not authenticated // - AND the user has initiated the sign in process in another tab - if (!isSignedIn && lodashGet(this.props, 'credentials.login', null)) { + if (!isSignedIn && login) { Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode()); } } @@ -101,7 +101,7 @@ class ValidateLoginPage extends Component { render() { return ( <> - {this.getAutoAuthState === CONST.AUTO_AUTH_STATE.FAILED && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.FAILED && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && } {this.getAutoAuthState() == CONST.AUTO_AUTH_STATE.SIGNING_IN && } From e191d90b47ec53f589d71e9575e5b89b4c077a6d Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 00:04:33 +0200 Subject: [PATCH 25/36] Better comment. --- src/pages/ValidateLoginPage/index.website.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 0b8a75bf0486..3c201eda5113 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -56,11 +56,9 @@ class ValidateLoginPage extends Component { } Session.initAutoAuthState(cachedAutoAuthState); - // Sign in if - // - The user is on the passwordless beta - // - AND the user is not authenticated - // - AND the user has initiated the sign in process in another tab if (!isSignedIn && login) { + + // The user has initiated the sign in process on the same browser, in another tab. Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode()); } } From a6fee31036df5d1d2e1a87e9514db99985e36409 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 00:21:16 +0200 Subject: [PATCH 26/36] Add Tfa required modal. --- .../ValidateCode/TfaRequiredModal.js | 52 +++++++++++++++++++ src/languages/en.js | 2 + src/languages/es.js | 2 + src/pages/ValidateLoginPage/index.website.js | 5 +- 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/components/ValidateCode/TfaRequiredModal.js diff --git a/src/components/ValidateCode/TfaRequiredModal.js b/src/components/ValidateCode/TfaRequiredModal.js new file mode 100644 index 000000000000..ea6eca64b927 --- /dev/null +++ b/src/components/ValidateCode/TfaRequiredModal.js @@ -0,0 +1,52 @@ +import React, {PureComponent} from 'react'; +import {View} from 'react-native'; +import colors from '../../styles/colors'; +import styles from '../../styles/styles'; +import Icon from '../Icon'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import Text from '../Text'; +import * as Expensicons from '../Icon/Expensicons'; +import * as Illustrations from '../Icon/Illustrations'; +import variables from '../../styles/variables'; + +const propTypes = { + + ...withLocalizePropTypes, +}; + +class TfaRequiredModal extends PureComponent { + render() { + return ( + + + + + + + {this.props.translate('validateCodeModal.tfaRequiredTitle')} + + + + {this.props.translate('validateCodeModal.tfaRequiredDescription')} + + + + + + + + ); + } +} + +TfaRequiredModal.propTypes = propTypes; +export default withLocalize(TfaRequiredModal); \ No newline at end of file diff --git a/src/languages/en.js b/src/languages/en.js index 7bb91480b533..8877686ab90b 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -170,6 +170,8 @@ export default { requestNewCode: 'You can also', requestNewCodeLink: 'request a new code here', successfulNewCodeRequest: 'Code requested. Please check your device.', + tfaRequiredTitle: 'Two factor authentication required', + tfaRequiredDescription: 'Please enter the two-factor authentication code where you are trying to sign in', }, iOUConfirmationList: { whoPaid: 'Who paid?', diff --git a/src/languages/es.js b/src/languages/es.js index 8c11939194d5..d3a6ca74f29c 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -169,6 +169,8 @@ export default { requestNewCode: '¡También puedes', requestNewCodeLink: 'solicitar un nuevo código aquí', successfulNewCodeRequest: 'Código solicitado. Por favor, comprueba su dispositivo.', + tfaRequiredTitle: 'Two factor authentication required', + tfaRequiredDescription: 'Please enter the two-factor authentication code where you are trying to sign in', }, iOUConfirmationList: { whoPaid: '¿Quién pago?', diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 3c201eda5113..871c371e12ef 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -20,6 +20,7 @@ import ExpiredValidateCodeModal from '../../components/ValidateCode/ExpiredValid import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; +import TfaRequiredModal from '../../components/ValidateCode/TfaRequiredModal'; const propTypes = { /** The accountID and validateCode are passed via the URL */ @@ -97,10 +98,12 @@ class ValidateLoginPage extends Component { } render() { + const isTfaRequired = lodashGet(this.props, 'account.requiresTwoFactorAuth', false); return ( <> {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.FAILED && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && !isTfaRequired && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && isTfaRequired && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && } {this.getAutoAuthState() == CONST.AUTO_AUTH_STATE.SIGNING_IN && } From 4393c6f0344285699e1c45583ed10cd1429028a8 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 00:36:49 +0200 Subject: [PATCH 27/36] Fix lint errors. --- .../ValidateCode/TfaRequiredModal.js | 2 +- src/libs/actions/Session/index.js | 12 ++--- src/pages/ValidateLoginPage/index.website.js | 18 ++++---- src/styles/styles.js | 46 ------------------- 4 files changed, 15 insertions(+), 63 deletions(-) diff --git a/src/components/ValidateCode/TfaRequiredModal.js b/src/components/ValidateCode/TfaRequiredModal.js index ea6eca64b927..2585584544b6 100644 --- a/src/components/ValidateCode/TfaRequiredModal.js +++ b/src/components/ValidateCode/TfaRequiredModal.js @@ -49,4 +49,4 @@ class TfaRequiredModal extends PureComponent { } TfaRequiredModal.propTypes = propTypes; -export default withLocalize(TfaRequiredModal); \ No newline at end of file +export default withLocalize(TfaRequiredModal); diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index bf0511507f3d..42492432b230 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -380,21 +380,21 @@ function signInWithValidateCode(accountID, validateCode, twoFactorAuthCode) { /** * Initializes the state of the automatic authentication when the user clicks on a magic link. - * + * * This method is called in componentDidMount event of the lifecycle. * When the user gets authenticated, the component is unmounted and then remounted * when AppNavigator switches from PublicScreens to AuthScreens. * That's the reason why autoAuthState initialization is skipped while the last state is SIGNING_IN. - * - * @param {string} cachedAutoAuthState + * + * @param {string} cachedAutoAuthState */ function initAutoAuthState(cachedAutoAuthState) { Onyx.merge( ONYXKEYS.SESSION, { - autoAuthState: cachedAutoAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN ? - CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN : CONST.AUTO_AUTH_STATE.NOT_STARTED - } + autoAuthState: cachedAutoAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN + ? CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN : CONST.AUTO_AUTH_STATE.NOT_STARTED, + }, ); } diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 871c371e12ef..f05d58eb7443 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -1,6 +1,5 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; -import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import { @@ -50,18 +49,18 @@ class ValidateLoginPage extends Component { const cachedAutoAuthState = lodashGet(this.props, 'session.autoAuthState', null); const login = lodashGet(this.props, 'credentials.login', null); if (!login && isSignedIn && cachedAutoAuthState === CONST.AUTO_AUTH_STATE.SIGNING_IN) { - // The user clicked the option to sign in the current tab Navigation.navigate(ROUTES.REPORT); return; } Session.initAutoAuthState(cachedAutoAuthState); - if (!isSignedIn && login) { - - // The user has initiated the sign in process on the same browser, in another tab. - Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode()); + if (isSignedIn || !login) { + return; } + + // The user has initiated the sign in process on the same browser, in another tab. + Session.signInWithValidateCode(this.getAccountID(), this.getValidateCode()); } componentDidUpdate() { @@ -70,7 +69,6 @@ class ValidateLoginPage extends Component { && lodashGet(this.props, 'credentials.accountID', null) && lodashGet(this.props, 'account.requiresTwoFactorAuth', false) ) { - // The user clicked the option to sign in the current tab Navigation.navigate(ROUTES.REPORT); } @@ -93,7 +91,7 @@ class ValidateLoginPage extends Component { /** * @returns {String} */ - getValidateCode(){ + getValidateCode() { return lodashGet(this.props.route.params, 'validateCode', ''); } @@ -104,8 +102,8 @@ class ValidateLoginPage extends Component { {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.FAILED && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && !isTfaRequired && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && isTfaRequired && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && } - {this.getAutoAuthState() == CONST.AUTO_AUTH_STATE.SIGNING_IN && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.SIGNING_IN && } ); } diff --git a/src/styles/styles.js b/src/styles/styles.js index 0cb0a8f387e2..340c79a37c75 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -3004,52 +3004,6 @@ const styles = { justifyContent: 'space-between', }, - emojiReactionBubble: { - paddingVertical: 2, - paddingHorizontal: 8, - borderRadius: 28, - backgroundColor: themeColors.border, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', - alignSelf: 'flex-start', - marginTop: 8, - marginRight: 4, - }, - - emojiReactionText: { - fontSize: 12, - lineHeight: 20, - textAlignVertical: 'center', - userSelect: 'none', - WebkitUserSelect: 'none', - }, - reactionCounterText: { - fontSize: 11, - marginLeft: 4, - fontWeight: 'bold', - color: themeColors.textLight, - userSelect: 'none', - WebkitUserSelect: 'none', - }, - - fontColorReactionLabel: { - color: '#586A64', - }, - - reactionEmojiTitle: { - fontSize: variables.iconSizeLarge, - lineHeight: variables.iconSizeXLarge, - }, - - quickReactionsContainer: { - gap: 12, - flexDirection: 'row', - paddingHorizontal: 25, - paddingVertical: 12, - justifyContent: 'space-between', - }, - validateCodeDigits: { color: themeColors.text, fontFamily: fontFamily.EXP_NEUE, From c2ac666293aa4277bd00102a17e5863e3dfb9b16 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 00:39:03 +0200 Subject: [PATCH 28/36] Remove unnecessary change in BaseValidationCodeForm.js --- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index ec8e5f444178..2e9b81d02189 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -176,7 +176,7 @@ class BaseValidateCodeForm extends React.Component { formError: {}, }); - const accountID = lodashGet(this.props, 'credentials.accountID', null); + const accountID = lodashGet(this.props, 'credentials.accountID'); if (accountID) { Session.signInWithValidateCode(accountID, this.state.validateCode, this.state.twoFactorAuthCode); } else { From 7266f071a45711199099cc74e9fb300c8a0944e6 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 00:44:22 +0200 Subject: [PATCH 29/36] Linter prefers early return :shrug: --- src/pages/ValidateLoginPage/index.website.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index f05d58eb7443..533b37a3480f 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -65,13 +65,15 @@ class ValidateLoginPage extends Component { componentDidUpdate() { if ( - !lodashGet(this.props, 'credentials.login', null) - && lodashGet(this.props, 'credentials.accountID', null) - && lodashGet(this.props, 'account.requiresTwoFactorAuth', false) + lodashGet(this.props, 'credentials.login', null) + || !lodashGet(this.props, 'credentials.accountID', null) + || !lodashGet(this.props, 'account.requiresTwoFactorAuth', false) ) { - // The user clicked the option to sign in the current tab - Navigation.navigate(ROUTES.REPORT); + return; } + + // The user clicked the option to sign in the current tab + Navigation.navigate(ROUTES.REPORT); } /** From 8e95a5feed29fed575ae07dc14c8252da47dafbc Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 21:21:32 +0200 Subject: [PATCH 30/36] Add correct Spanish translations for tfa required modal. --- src/languages/en.js | 2 +- src/languages/es.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 8877686ab90b..405e264eb3fc 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -171,7 +171,7 @@ export default { requestNewCodeLink: 'request a new code here', successfulNewCodeRequest: 'Code requested. Please check your device.', tfaRequiredTitle: 'Two factor authentication required', - tfaRequiredDescription: 'Please enter the two-factor authentication code where you are trying to sign in', + tfaRequiredDescription: 'Please enter the two-factor authentication code where you are trying to sign in.', }, iOUConfirmationList: { whoPaid: 'Who paid?', diff --git a/src/languages/es.js b/src/languages/es.js index d3a6ca74f29c..4f83e2a83c94 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -162,15 +162,15 @@ export default { successfulSignInDescription: 'Vuelve a la pestaña original para continuar.', title: 'Aquí está tu código mágico', or: ', ¡o', - description: 'Por favor, introduzca el código utilizando el dispositivo\nen el que se solicitó originalmente', + description: 'Por favor, introduce el código utilizando el dispositivo\nen el que se solicitó originalmente', signInHere: 'simplemente inicia sesión aquí', expiredCodeTitle: 'Código mágico caducado', expiredCodeDescription: 'Vuelve al dispositivo original y solicita un nuevo código.', requestNewCode: '¡También puedes', requestNewCodeLink: 'solicitar un nuevo código aquí', successfulNewCodeRequest: 'Código solicitado. Por favor, comprueba su dispositivo.', - tfaRequiredTitle: 'Two factor authentication required', - tfaRequiredDescription: 'Please enter the two-factor authentication code where you are trying to sign in', + tfaRequiredTitle: 'Se requiere autenticación de dos factores', + tfaRequiredDescription: 'Por favor, introduce el código de autenticación de dos factores donde estás intentando iniciar sesión.', }, iOUConfirmationList: { whoPaid: '¿Quién pago?', @@ -474,7 +474,7 @@ export default { invalidName: 'Por favor ingresa un nombre válido', addressZipCode: 'Por favor ingresa un código postal válido', debitCardNumber: 'Ingresa un número de tarjeta de débito válido', - expirationDate: 'Por favor introduzca una fecha de vencimiento válida', + expirationDate: 'Por favor introduce una fecha de vencimiento válida', securityCode: 'Ingresa un código de seguridad válido', addressStreet: 'Ingresa una dirección de facturación válida que no sea un apartado postal', addressState: 'Por favor seleccione un estado', @@ -694,7 +694,7 @@ export default { routingNumber: 'Ingresa un número de ruta válido', accountNumber: 'Ingresa un número de cuenta válido', companyType: 'Ingresa un tipo de compañía válido', - tooManyAttempts: 'Debido a la gran cantidad de intentos de inicio de sesión, esta opción se ha desactivado temporalmente durante 24 horas. Vuelve a intentarlo más tarde o introduzca los detalles manualmente.', + tooManyAttempts: 'Debido a la gran cantidad de intentos de inicio de sesión, esta opción se ha desactivado temporalmente durante 24 horas. Vuelve a intentarlo más tarde o introduce los detalles manualmente.', address: 'Ingresa una dirección válida', dob: 'Ingresa una fecha de nacimiento válida', age: 'Debe ser mayor de 18 años', From 2ff6376fd70d1f036c199574fa77180cacf17a09 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 21:33:32 +0200 Subject: [PATCH 31/36] Final shape for TfaRequiredModal --- assets/images/product-illustrations/safe.svg | 119 ++++++++++++++++++ src/components/Icon/Illustrations.js | 2 + .../ValidateCode/TfaRequiredModal.js | 2 +- src/languages/en.js | 4 +- src/languages/es.js | 4 +- 5 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 assets/images/product-illustrations/safe.svg diff --git a/assets/images/product-illustrations/safe.svg b/assets/images/product-illustrations/safe.svg new file mode 100644 index 000000000000..db2ac0707f7f --- /dev/null +++ b/assets/images/product-illustrations/safe.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.js b/src/components/Icon/Illustrations.js index f5243f469b8d..167d8e90f29f 100644 --- a/src/components/Icon/Illustrations.js +++ b/src/components/Icon/Illustrations.js @@ -17,6 +17,7 @@ import ReceiptsSearchYellow from '../../../assets/images/product-illustrations/r import ReceiptYellow from '../../../assets/images/product-illustrations/receipt--yellow.svg'; import RocketBlue from '../../../assets/images/product-illustrations/rocket--blue.svg'; import RocketOrange from '../../../assets/images/product-illustrations/rocket--orange.svg'; +import SafeBlue from '../../../assets/images/product-illustrations/safe.svg'; import TadaYellow from '../../../assets/images/product-illustrations/tada--yellow.svg'; import TadaBlue from '../../../assets/images/product-illustrations/tada--blue.svg'; import ToddBehindCloud from '../../../assets/images/product-illustrations/todd-behind-cloud.svg'; @@ -59,6 +60,7 @@ export { ReceiptYellow, RocketBlue, RocketOrange, + SafeBlue, TadaYellow, TadaBlue, ToddBehindCloud, diff --git a/src/components/ValidateCode/TfaRequiredModal.js b/src/components/ValidateCode/TfaRequiredModal.js index 2585584544b6..6155fed1f1d9 100644 --- a/src/components/ValidateCode/TfaRequiredModal.js +++ b/src/components/ValidateCode/TfaRequiredModal.js @@ -23,7 +23,7 @@ class TfaRequiredModal extends PureComponent {
diff --git a/src/languages/en.js b/src/languages/en.js index 405e264eb3fc..a0e086cdd6cb 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -170,8 +170,8 @@ export default { requestNewCode: 'You can also', requestNewCodeLink: 'request a new code here', successfulNewCodeRequest: 'Code requested. Please check your device.', - tfaRequiredTitle: 'Two factor authentication required', - tfaRequiredDescription: 'Please enter the two-factor authentication code where you are trying to sign in.', + tfaRequiredTitle: 'Two factor authentication\nrequired', + tfaRequiredDescription: 'Please enter the two-factor authentication code\nwhere you are trying to sign in.', }, iOUConfirmationList: { whoPaid: 'Who paid?', diff --git a/src/languages/es.js b/src/languages/es.js index 4f83e2a83c94..d8849f7d6b94 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -169,8 +169,8 @@ export default { requestNewCode: '¡También puedes', requestNewCodeLink: 'solicitar un nuevo código aquí', successfulNewCodeRequest: 'Código solicitado. Por favor, comprueba su dispositivo.', - tfaRequiredTitle: 'Se requiere autenticación de dos factores', - tfaRequiredDescription: 'Por favor, introduce el código de autenticación de dos factores donde estás intentando iniciar sesión.', + tfaRequiredTitle: 'Se requiere autenticación\nde dos factores', + tfaRequiredDescription: 'Por favor, introduce el código de autenticación de dos factores\ndonde estás intentando iniciar sesión.', }, iOUConfirmationList: { whoPaid: '¿Quién pago?', From 6a80fe536220287671e3a05dd94aec84b47c83fe Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Wed, 15 Mar 2023 22:19:16 +0200 Subject: [PATCH 32/36] Show abracadabra after inserting tfa code. --- src/components/ValidateCode/TfaRequiredModal.js | 2 +- src/pages/ValidateLoginPage/index.website.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/ValidateCode/TfaRequiredModal.js b/src/components/ValidateCode/TfaRequiredModal.js index 6155fed1f1d9..22aacc0fc64d 100644 --- a/src/components/ValidateCode/TfaRequiredModal.js +++ b/src/components/ValidateCode/TfaRequiredModal.js @@ -22,7 +22,7 @@ class TfaRequiredModal extends PureComponent { diff --git a/src/pages/ValidateLoginPage/index.website.js b/src/pages/ValidateLoginPage/index.website.js index 533b37a3480f..8d34495fd801 100644 --- a/src/pages/ValidateLoginPage/index.website.js +++ b/src/pages/ValidateLoginPage/index.website.js @@ -99,11 +99,12 @@ class ValidateLoginPage extends Component { render() { const isTfaRequired = lodashGet(this.props, 'account.requiresTwoFactorAuth', false); + const isSignedIn = Boolean(lodashGet(this.props, 'session.authToken', null)); return ( <> {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.FAILED && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && !isTfaRequired && } - {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && isTfaRequired && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && (!isTfaRequired || isSignedIn) && } + {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.JUST_SIGNED_IN && isTfaRequired && !isSignedIn && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.NOT_STARTED && } {this.getAutoAuthState() === CONST.AUTO_AUTH_STATE.SIGNING_IN && } From c3ec252541a466718ffa09d8f2d17f5c8a9da7d6 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 20 Mar 2023 21:05:47 +0200 Subject: [PATCH 33/36] Remove unnecessary line breaks. --- src/components/ValidateCode/ExpiredValidateCodeModal.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index ee22cf777ee2..543b3417b0cd 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -77,9 +77,7 @@ class ExpiredValidateCodeModal extends PureComponent {

- {codeRequestedErrors} -
)} {this.shouldShowRequestCodeLink() && codeRequestedMessage From ae59b49c11433712aaaf50c082a5ca8ebcd0e3de Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 20 Mar 2023 23:56:40 +0200 Subject: [PATCH 34/36] Remove unnecesary line break in TfaRequiredModal.js --- src/components/ValidateCode/TfaRequiredModal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ValidateCode/TfaRequiredModal.js b/src/components/ValidateCode/TfaRequiredModal.js index 22aacc0fc64d..5ac7802edc7a 100644 --- a/src/components/ValidateCode/TfaRequiredModal.js +++ b/src/components/ValidateCode/TfaRequiredModal.js @@ -10,7 +10,6 @@ import * as Illustrations from '../Icon/Illustrations'; import variables from '../../styles/variables'; const propTypes = { - ...withLocalizePropTypes, }; From 76abf002b532b7113247437be02409d279b123ae Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 20 Mar 2023 23:57:21 +0200 Subject: [PATCH 35/36] Remove unnecesary line break in AbracadabraModal.js --- src/components/ValidateCode/AbracadabraModal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ValidateCode/AbracadabraModal.js b/src/components/ValidateCode/AbracadabraModal.js index 28e3653f2f6b..fd54fdd7db7b 100644 --- a/src/components/ValidateCode/AbracadabraModal.js +++ b/src/components/ValidateCode/AbracadabraModal.js @@ -10,7 +10,6 @@ import * as Illustrations from '../Icon/Illustrations'; import variables from '../../styles/variables'; const propTypes = { - ...withLocalizePropTypes, }; From 13460c87fe6c9522981e4d80f5d5a65d0ff5d740 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 20 Mar 2023 23:58:02 +0200 Subject: [PATCH 36/36] Remove unnecesary line break in ExpiredValidateCodeModal.js --- src/components/ValidateCode/ExpiredValidateCodeModal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ValidateCode/ExpiredValidateCodeModal.js b/src/components/ValidateCode/ExpiredValidateCodeModal.js index 543b3417b0cd..5a5fa65dc7e2 100644 --- a/src/components/ValidateCode/ExpiredValidateCodeModal.js +++ b/src/components/ValidateCode/ExpiredValidateCodeModal.js @@ -17,7 +17,6 @@ import * as ErrorUtils from '../../libs/ErrorUtils'; import * as Session from '../../libs/actions/Session'; const propTypes = { - ...withLocalizePropTypes, };