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

Error modal when signin from expired magic link #15505

Merged
merged 41 commits into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9de436b
Split ValidateCodeModal different usages in multiple components.
cristipaval Feb 27, 2023
4571bc2
Separate Abracadabra component.
cristipaval Feb 27, 2023
4dfdcf0
Cleanup abracadabra related code from ValidateCodeModal component
cristipaval Feb 27, 2023
7911733
Separate component for when magic link fails.
cristipaval Feb 27, 2023
abcf1f9
Correct Spanish translations after feedback from native speakers.
cristipaval Feb 27, 2023
42dbf4b
Update illustration in the modal shown when magic link fails.
cristipaval Feb 27, 2023
7483f9a
Show expired code modal when sign in fails.
cristipaval Feb 27, 2023
ffb52ea
Expired magic link modal when trying to sign in the current tab with …
cristipaval Feb 28, 2023
61a15e8
Cleanup unused action in Session.
cristipaval Feb 28, 2023
b601db1
Add comments for a complex condition check.
cristipaval Feb 28, 2023
fdca7c1
Fix lint errors
cristipaval Feb 28, 2023
9af3f8a
Add "request new code" link in the error modal.
cristipaval Mar 1, 2023
2ec71ac
Fix lint and style errors.
cristipaval Mar 1, 2023
3497d94
Merge remote-tracking branch 'origin/main' into cristi_error-when-sig…
cristipaval Mar 1, 2023
ab1911a
Merge remote-tracking branch 'origin/main' into cristi_error-when-sig…
cristipaval Mar 3, 2023
7c4cd9a
Make it work for accounts with 2FA
cristipaval Mar 3, 2023
61e702e
Try fixing the intermediary screen on iOS.
cristipaval Mar 6, 2023
c63438a
Work in progress.
cristipaval Mar 7, 2023
177b9d2
Merge remote-tracking branch 'origin/main' into cristi_error-when-sig…
cristipaval Mar 10, 2023
a8ceaca
Revert undesired changes in Permissions.js
cristipaval Mar 10, 2023
7632b8e
Fix authentication when sign in with link and 2fa
cristipaval Mar 10, 2023
b8f6cb4
Merge remote-tracking branch 'origin/main' into cristi_error-when-sig…
cristipaval Mar 13, 2023
939d507
Cleanup ValidateLoginPage
cristipaval Mar 13, 2023
3b66d81
Code style consistency.
cristipaval Mar 13, 2023
6900b03
Finally found a way to handle the unmount-remount of the page due to …
cristipaval Mar 14, 2023
73aa412
Add js doc to explain how autoAuthState initialization works
cristipaval Mar 14, 2023
02c753d
Add "sign in here" option.
cristipaval Mar 14, 2023
c84620d
Add expired validate code modal.
cristipaval Mar 14, 2023
e191d90
Better comment.
cristipaval Mar 14, 2023
a6fee31
Add Tfa required modal.
cristipaval Mar 14, 2023
4393c6f
Fix lint errors.
cristipaval Mar 14, 2023
c2ac666
Remove unnecessary change in BaseValidationCodeForm.js
cristipaval Mar 14, 2023
7266f07
Linter prefers early return :shrug:
cristipaval Mar 14, 2023
8e95a5f
Add correct Spanish translations for tfa required modal.
cristipaval Mar 15, 2023
2ff6376
Final shape for TfaRequiredModal
cristipaval Mar 15, 2023
6a80fe5
Show abracadabra after inserting tfa code.
cristipaval Mar 15, 2023
5354ea4
Merge remote-tracking branch 'origin/main' into cristi_error-when-sig…
cristipaval Mar 20, 2023
c3ec252
Remove unnecessary line breaks.
cristipaval Mar 20, 2023
ae59b49
Remove unnecesary line break in TfaRequiredModal.js
cristipaval Mar 20, 2023
76abf00
Remove unnecesary line break in AbracadabraModal.js
cristipaval Mar 20, 2023
13460c8
Remove unnecesary line break in ExpiredValidateCodeModal.js
cristipaval Mar 20, 2023
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
39 changes: 18 additions & 21 deletions assets/images/expensify-wordmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions assets/images/product-illustrations/todd-behind-cloud.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -60,6 +61,7 @@ export {
RocketOrange,
TadaYellow,
TadaBlue,
ToddBehindCloud,
GpsTrackOrange,
ShieldYellow,
MoneyReceipts,
Expand Down
52 changes: 52 additions & 0 deletions src/components/ValidateCode/AbracadabraModal.js
Copy link
Contributor

Choose a reason for hiding this comment

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

This file is almost exactly the same as TfaRequiredModal, and has only minimal differences with the other two components in this directory. I think it would be valuable to DRY these up by creating a ValidateCodeModalLayout component, and use it in the other components you've created here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hey @roryabraham ! Thanks for the review! I do agree that we can dry up the code here. I spent a lot of time on this PR due to that challenge that I was facing (componentDidMount being called twice). I created components for each state just to simplify the code in VaidateLoginPage. But now it looks like I ended up having duplicate code. Now that I solved the aforementioned challenge, I totally do agree I can reuse the same component for multiple states. I created this follow-up issue for your suggested refactor.

Original file line number Diff line number Diff line change
@@ -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 = {

cristipaval marked this conversation as resolved.
Show resolved Hide resolved
...withLocalizePropTypes,
};

class AbracadabraModal extends PureComponent {
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB but this should be a functional component w/ memo, which is typically what we do for components that don't have state or use lifecycle methods.

render() {
return (
<View style={styles.deeplinkWrapperContainer}>
<View style={styles.deeplinkWrapperMessage}>
<View style={styles.mb2}>
<Icon
width={variables.modalTopIconWidth}
height={variables.modalTopBigIconHeight}
src={Illustrations.Abracadabra}
/>
</View>
<Text style={[styles.textHeadline, styles.textXXLarge, styles.textAlignCenter]}>
{this.props.translate('validateCodeModal.successfulSignInTitle')}
</Text>
<View style={[styles.mt2, styles.mb2]}>
<Text style={[styles.fontSizeNormal, styles.textAlignCenter]}>
{this.props.translate('validateCodeModal.successfulSignInDescription')}
</Text>
</View>
</View>
<View style={styles.deeplinkWrapperFooter}>
<Icon
width={variables.modalWordmarkWidth}
height={variables.modalWordmarkHeight}
fill={colors.green}
src={Expensicons.ExpensifyWordmark}
/>
</View>
</View>
);
}
}

AbracadabraModal.propTypes = propTypes;
export default withLocalize(AbracadabraModal);
112 changes: 112 additions & 0 deletions src/components/ValidateCode/ExpiredValidateCodeModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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';
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 ONYXKEYS from '../../ONYXKEYS';
import * as ErrorUtils from '../../libs/ErrorUtils';

const propTypes = {

cristipaval marked this conversation as resolved.
Show resolved Hide resolved
/** 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() {
const codeRequestedMessage = lodashGet(this.props, 'account.message', null);
const accountErrors = lodashGet(this.props, 'account.errors', {});
Copy link
Contributor

@youssef-lr youssef-lr Mar 21, 2023

Choose a reason for hiding this comment

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

NAB but I think we can simplify this part as getLatestErrorMessage already checks for keys and if it's empty it will return an empty string which should evaluate to false. no?

    render() {
        const codeRequestedMessage = lodashGet(this.props, 'account.message', null);
        const codeRequestedErrors = ErrorUtils.getLatestErrorMessage(this.props.account);

        return (
            ...
        )

let codeRequestedErrors;
if (_.keys(accountErrors).length > 1) {
codeRequestedErrors = ErrorUtils.getLatestErrorMessage(this.props.account);
}
return (
<View style={styles.deeplinkWrapperContainer}>
<View style={styles.deeplinkWrapperMessage}>
<View style={styles.mb2}>
<Icon
width={variables.modalTopIconWidth}
height={variables.modalTopIconHeight}
src={Illustrations.ToddBehindCloud}
/>
</View>
<Text style={[styles.textHeadline, styles.textXXLarge, styles.textAlignCenter]}>
{this.props.translate('validateCodeModal.expiredCodeTitle')}
</Text>
<View style={[styles.mt2, styles.mb2]}>
<Text style={[styles.fontSizeNormal, styles.textAlignCenter]}>
{this.props.translate('validateCodeModal.expiredCodeDescription')}
{this.props.shouldShowRequestCodeLink && !codeRequestedMessage
&& (
<>
<br />
{this.props.translate('validateCodeModal.requestNewCode')}
{' '}
<TextLink onPress={this.props.onRequestCodeClick}>
{this.props.translate('validateCodeModal.requestNewCodeLink')}
</TextLink>
!
</>
)}
</Text>
{this.props.shouldShowRequestCodeLink && codeRequestedErrors
&& (
<Text style={[styles.textDanger, styles.validateCodeMessage]}>
<br />
<br />

{codeRequestedErrors}

cristipaval marked this conversation as resolved.
Show resolved Hide resolved
</Text>
)}
{this.props.shouldShowRequestCodeLink && codeRequestedMessage
&& (
<Text style={styles.validateCodeMessage}>
<br />
<br />
{codeRequestedMessage}
</Text>
)}
</View>
</View>
<View style={styles.deeplinkWrapperFooter}>
<Icon
width={variables.modalWordmarkWidth}
height={variables.modalWordmarkHeight}
fill={colors.green}
src={Expensicons.ExpensifyWordmark}
/>
</View>
</View>
);
}
}

ExpiredValidateCodeModal.propTypes = propTypes;
ExpiredValidateCodeModal.defaultProps = defaultProps;
export default compose(
withLocalize,
withOnyx({
account: {key: ONYXKEYS.ACCOUNT},
}),
)(ExpiredValidateCodeModal);
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
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 = {

/** Whether the user has been signed in with the link. */
isSuccessfullySignedIn: PropTypes.bool,

/** Code to display. */
code: PropTypes.string.isRequired,

Expand All @@ -29,7 +26,6 @@ const propTypes = {
};

const defaultProps = {
isSuccessfullySignedIn: false,
shouldShowSignInHere: false,
onSignInHereClick: () => {},
};
Expand All @@ -42,16 +38,16 @@ class ValidateCodeModal extends PureComponent {
<View style={styles.mb2}>
<Icon
width={variables.modalTopIconWidth}
height={this.props.isSuccessfullySignedIn ? variables.modalTopBigIconHeight : variables.modalTopIconHeight}
src={this.props.isSuccessfullySignedIn ? Illustrations.Abracadabra : Illustrations.MagicCode}
height={variables.modalTopIconHeight}
src={Illustrations.MagicCode}
/>
</View>
<Text style={[styles.textHeadline, styles.textXXLarge, styles.textAlignCenter]}>
{this.props.translate(this.props.isSuccessfullySignedIn ? 'validateCodeModal.successfulSignInTitle' : 'validateCodeModal.title')}
{this.props.translate('validateCodeModal.title')}
</Text>
<View style={[styles.mt2, styles.mb2]}>
<Text style={[styles.fontSizeNormal, styles.textAlignCenter]}>
{this.props.translate(this.props.isSuccessfullySignedIn ? 'validateCodeModal.successfulSignInDescription' : 'validateCodeModal.description')}
{this.props.translate('validateCodeModal.description')}
{this.props.shouldShowSignInHere
&& (
<>
Expand All @@ -65,13 +61,11 @@ class ValidateCodeModal extends PureComponent {
{this.props.shouldShowSignInHere ? '!' : '.'}
</Text>
</View>
{!this.props.isSuccessfullySignedIn && (
<View style={styles.mt6}>
<Text style={styles.magicCodeDigits}>
{this.props.code}
</Text>
</View>
)}
<View style={styles.mt6}>
<Text style={styles.validateCodeDigits}>
{this.props.code}
</Text>
</View>
</View>
<View style={styles.deeplinkWrapperFooter}>
<Icon
Expand Down
Loading