Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow redirect to MagicLink after Invalid Validation Code #5150

Merged
merged 25 commits into from
Jan 3, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b10f53f
Allow redirect to MagicLink after Invalid Validation Code
akshayasalvi Sep 6, 2021
0a6362e
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Sep 11, 2021
a1ce081
Added login handling along with navigation
akshayasalvi Sep 11, 2021
0ecd7f7
Updated default value to null
akshayasalvi Sep 12, 2021
3f1ad9e
Changed translation key to flag and move translation to parent component
akshayasalvi Sep 15, 2021
745efbb
Added truthy check
akshayasalvi Sep 15, 2021
103bbc5
Change the condition for translation message
akshayasalvi Sep 16, 2021
1e77d15
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Oct 27, 2021
f278059
Moved the validation message to Link form component
akshayasalvi Oct 27, 2021
8e4f703
Corrected import
akshayasalvi Oct 27, 2021
a1af035
Changed the validation key
akshayasalvi Oct 27, 2021
f25e31f
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Nov 7, 2021
418c8f6
Added `validateCodeExpired` handling for expired links
akshayasalvi Nov 7, 2021
f42b310
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Nov 9, 2021
46cd66d
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Dec 1, 2021
4b4f0ce
Changed unvalidated account flag to validation expired
akshayasalvi Dec 1, 2021
e486081
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Dec 6, 2021
cf0fe90
Rolledback unvalidated account changed and fixed validation link message
akshayasalvi Dec 6, 2021
587948f
Changed keys for welcomeText and resend formflag
akshayasalvi Dec 6, 2021
0bfaf85
Added comment for login var
akshayasalvi Dec 6, 2021
361f097
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Dec 23, 2021
b1ba7fa
Updated comment and setting error null
akshayasalvi Dec 23, 2021
c51f126
Merge branch 'main' of github-personal:akshayasalvi/App into validati…
akshayasalvi Dec 27, 2021
c11c337
Removed the `validate: true` in setpassword
akshayasalvi Dec 28, 2021
6180d9e
Reordered conditions for correct validateExpiry message
akshayasalvi Dec 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/WelcomeText.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const WelcomeText = (props) => {
return (
<>
<Text style={[textSize, styles.textStrong, styles.mb1]}>
{props.translate('welcomeText.phrase1')}
{props.translate('welcomeText.welcome')}
</Text>
<Text style={[textSize]}>
{props.translate('welcomeText.phrase2')}
Expand Down
5 changes: 3 additions & 2 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ export default {
hello: 'Hello',
phoneCountryCode: '1',
welcomeText: {
phrase1: 'Welcome to the New Expensify! Enter your phone number or email to continue.',
welcome: 'Welcome to the New Expensify! Enter your phone number or email to continue.',
phrase2: 'Money talks. And now that chat and payments are in one place, it\'s also easy.',
phrase3: 'Your payments get to you as fast as you can get your point across.',
phrase4: 'Welcome back to the New Expensify! Please enter your password.',
welcomeBack: 'Welcome back to the New Expensify! Please enter your password.',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of these names, especially when i read it in code like so,

`welcomeText.${showPasswordForm ? 'welcomeBack' : 'welcome'}`

Since its confusing to me why if showPasswordForm is true do we want welcomeBack versus welcome. Plus variables should be named after their intention of like what they store and not their parent/class names.

So in this case welcome, wants the phone or email so i would suggest - enterPhoneOrEmail. This becomes welcomeText.enterPhoneOrEmail which is a lot cleaner as welcomeText.welcome is redundant.
As for welcomeBack, it wants the password so i would suggest - enterPassword.

So now the logic becomes,

`welcomeText.${showPasswordForm ? 'enterPassword' : 'enterPhoneOrEmail'}`

which is a lot more verbose and cleaner imo.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a NAB - not a blocker.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. In general, feels like the names for these translation keys is a bit all over the place. Wondering if we can maybe come up with some standard guidance for how to name them and then get everyone to apply it in a consistent way?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we can maybe come up with some standard guidance for how to name them and then get everyone to apply it in a consistent way?

Yes, love it.

},
reportActionCompose: {
addAction: 'Actions',
Expand Down Expand Up @@ -428,6 +428,7 @@ export default {
linkHasBeenResent: 'Link has been re-sent',
weSentYouMagicSignInLink: ({login}) => `We've sent a magic sign in link to ${login}. Check your Inbox and your Spam folder and wait 5-10 minutes before trying again.`,
resendLink: 'Resend link',
validationCodeFailedMessage: 'It looks like there was an error with your validation link or it has expired.',
unvalidatedAccount: 'This account exists but isn\'t validated, please check your inbox for your magic link.',
newAccount: ({login, loginType}) => `Welcome ${login}, it's always great to see a new face around here! Please check your ${loginType} for a magic link to validate your account.`,
},
Expand Down
5 changes: 3 additions & 2 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ export default {
hello: 'Hola',
phoneCountryCode: '34',
welcomeText: {
phrase1: 'Con el Nuevo Expensify, chat y pagos son lo mismo.',
welcome: 'Con el Nuevo Expensify, chat y pagos son lo mismo.',
phrase2: 'El dinero habla. Y ahora que chat y pagos están en un mismo lugar, es también fácil.',
phrase3: 'Tus pagos llegan tan rápido como tus mensajes.',
phrase4: '¡Bienvenido de vuelta al Nuevo Expensify! Por favor, introduce tu contraseña.',
welcomeBack: '¡Bienvenido de vuelta al Nuevo Expensify! Por favor, introduce tu contraseña.',
},
reportActionCompose: {
addAction: 'Acción',
Expand Down Expand Up @@ -428,6 +428,7 @@ export default {
linkHasBeenResent: 'El enlace se ha reenviado',
weSentYouMagicSignInLink: ({login}) => `Hemos enviado un enlace mágico de inicio de sesión a ${login}. Verifica tu bandeja de entrada y tu carpeta de correo no deseado y espera de 5 a 10 minutos antes de intentarlo de nuevo.`,
resendLink: 'Reenviar enlace',
validationCodeFailedMessage: 'Parece que hubo un error con el enlace de validación o ha caducado.',
unvalidatedAccount: 'Esta cuenta existe pero no está validada, por favor busca el enlace mágico en tu bandeja de entrada',
newAccount: ({login, loginType}) => `¡Bienvenido ${login}, es genial ver una cara nueva por aquí! En tu ${loginType} encontrarás un enlace para validar tu cuenta, por favor, revísalo`,
},
Expand Down
16 changes: 13 additions & 3 deletions src/libs/actions/Session/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ function fetchAccountDetails(login) {
validated: response.validated,
closed: response.isClosed,
forgotPassword: false,
validateCodeExpired: false,
});

if (!response.accountExists) {
Expand Down Expand Up @@ -281,7 +282,7 @@ function resetPassword() {
Onyx.merge(ONYXKEYS.ACCOUNT, {loading: true, forgotPassword: true});
API.ResetPassword({email: credentials.login})
.finally(() => {
Onyx.merge(ONYXKEYS.ACCOUNT, {loading: false});
Onyx.merge(ONYXKEYS.ACCOUNT, {loading: false, validateCodeExpired: false});
});
}

Expand All @@ -295,7 +296,7 @@ function resetPassword() {
* @param {String} accountID
*/
function setPassword(password, validateCode, accountID) {
Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true});
Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true, validateCodeExpired: false});

API.SetPassword({
password,
Expand All @@ -316,7 +317,16 @@ function setPassword(password, validateCode, accountID) {
return;
}

Onyx.merge(ONYXKEYS.ACCOUNT, {error: Localize.translateLocal('setPasswordPage.accountNotValidated')});
const login = lodashGet(response, 'data.email', null);
Onyx.merge(ONYXKEYS.ACCOUNT, {
validated: true, accountExists: true, validateCodeExpired: true, error: Localize.translateLocal('setPasswordPage.accountNotValidated'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return true for validated? The validateCode is expired so they are not validated...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block gets called when validation code is expired and the account is validated. This flow comes from ValidateEmail, only if the Account Validated.

if (responseValidate.title === CONST.PASSWORD_PAGE.ERROR.ALREADY_VALIDATED) {

Hence, I've updated validated: true

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That explanation doesn't make sense to me. We are setting an error here that says:

"We were unable to validate your account."

And the account is being set as validated: true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I explained it right. Let me give it another short. In ResendValidtionForm we have the following checks:

  1. isNewAccount
  2. isOldUnvalidatedAccount -> This gives the error about "Account exists but not validated"
  3. validateCodeExpired - Handled by this PR, which shows validationCodeFailedMessage message
  4. The default weSentYouMagicSignInLink magic link text.

Now the two instances of accountNotValidated translation key were added during the n6-hold merge, which I haven't changed. If you see the setPassword function call is called only after we get the error as Email is already validated. Hence I am setting validated: true.

In short if I ask, if I remove the error: Localize.translateLocal('setPasswordPage.accountNotValidated') from the SetPassword function and just retain it in the line 433.

Onyx.merge(ONYXKEYS.ACCOUNT, {
                validated: true, accountExists: true, validateCodeExpired: true,
}); // remove error from here.

Retain this:

Onyx.merge(ONYXKEYS.SESSION, {error: 'setPasswordPage.accountNotValidated'});

Does the above change make sense?

Copy link
Contributor Author

@akshayasalvi akshayasalvi Dec 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the above change and tested it. It is working fine and guessing this should now cover all cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the above change make sense?

It makes sense as a quick solution. However, based on the full conversation in this PR so far - it isn't the one we want and will introduce inconsistency with how we handle errors.

Please try to ask clarifying questions before proposing alternatives, because I'm not sure if we are seeing eye to eye on the problem. I will try to be as specific as possible so we can move on from this PR...

The issue is with setting validated: true.

It seems the only reason we need to set this here is so that the correct error will show here:

const isNewAccount = !this.props.account.accountExists;
const isOldUnvalidatedAccount = this.props.account.accountExists && !this.props.account.validated;
const isSMSLogin = Str.isSMSLogin(this.props.credentials.login);
const login = isSMSLogin ? this.props.toLocalPhone(Str.removeSMSDomain(this.props.credentials.login)) : this.props.credentials.login;
const loginType = (isSMSLogin ? this.props.translate('common.phone') : this.props.translate('common.email')).toLowerCase();
let message = '';
if (isNewAccount) {
message = this.props.translate('resendValidationForm.newAccount', {
login,
loginType,
});
} else if (isOldUnvalidatedAccount) {
message = this.props.translate('resendValidationForm.unvalidatedAccount');
} else if (this.props.account.validateCodeExpired) {
message = this.props.translate('resendValidationForm.validationCodeFailedMessage');

The isOldUnvalidatedAccount variable needs to be false and I suspect that's only reason we are setting it to true. But it is not the correct state for the account. We must change the logic that shows the errors not the state of the account.

});

// This login is set with email received from the API so that the resend link can work if the user has hit a direct URL
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
if (login) {
Onyx.merge(ONYXKEYS.CREDENTIALS, {login});
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
}
Navigation.navigate(ROUTES.HOME);
})
.finally(() => {
Onyx.merge(ONYXKEYS.ACCOUNT, {loading: false});
Expand Down
2 changes: 2 additions & 0 deletions src/pages/signin/ResendValidationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class ResendValidationForm extends React.Component {
});
} else if (isOldUnvalidatedAccount) {
message = this.props.translate('resendValidationForm.unvalidatedAccount');
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
} else if (this.props.account.validateCodeExpired) {
message = this.props.translate('resendValidationForm.validationCodeFailedMessage');
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
} else {
message = this.props.translate('resendValidationForm.weSentYouMagicSignInLink', {
login,
Expand Down
16 changes: 11 additions & 5 deletions src/pages/signin/SignInPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
import ONYXKEYS from '../../ONYXKEYS';
import styles from '../../styles/styles';
import updateUnread from '../../libs/UnreadIndicatorUpdater/updateUnread/index';
Expand Down Expand Up @@ -59,9 +60,12 @@ class SignInPage extends Component {
// - A login has not been entered yet
const showLoginForm = !this.props.credentials.login;

const validateCodeExpired = lodashGet(this.props.account, 'validateCodeExpired', false);

const validAccount = this.props.account.accountExists
&& this.props.account.validated
&& !this.props.account.forgotPassword;
&& !this.props.account.forgotPassword
&& !validateCodeExpired;

// Show the password form if
// - A login has been entered
Expand All @@ -76,21 +80,23 @@ class SignInPage extends Component {
// - A login has been entered
// - AND a GitHub username has been entered OR they already have access to this app
// - AND an account did not exist or is not validated for that login
const showResendValidationLinkForm = this.props.credentials.login && !validAccount;
const shouldShowResendValidationLinkForm = this.props.credentials.login && !validAccount;

const welcomeText = this.props.translate(`welcomeText.${showPasswordForm ? 'phrase4' : 'phrase1'}`);
const welcomeText = shouldShowResendValidationLinkForm
? ''
: this.props.translate(`welcomeText.${showPasswordForm ? 'welcomeBack' : 'welcome'}`);

return (
<SafeAreaView style={[styles.signInPage]}>
<SignInPageLayout
welcomeText={welcomeText}
shouldShowWelcomeText={showLoginForm || showPasswordForm || !showResendValidationLinkForm}
shouldShowWelcomeText={showLoginForm || showPasswordForm || !shouldShowResendValidationLinkForm}
>
{/* LoginForm and PasswordForm must use the isVisible prop. This keeps them mounted, but visually hidden
so that password managers can access the values. Conditionally rendering these components will break this feature. */}
<LoginForm isVisible={showLoginForm} />
<PasswordForm isVisible={showPasswordForm} />
{showResendValidationLinkForm && <ResendValidationForm />}
{shouldShowResendValidationLinkForm && <ResendValidationForm />}
</SignInPageLayout>
</SafeAreaView>
);
Expand Down