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

Set password #3190

Merged
merged 11 commits into from
Jun 12, 2021
3 changes: 3 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,10 @@ export default {
setPasswordPage: {
passwordCannotBeBlank: 'Password cannot be blank',
enterPassword: 'Enter a password',
confirmNewPassword: 'Confirm the password',
setPassword: 'Set Password',
passwordsDontMatch: 'Passwords must match',
newPasswordPrompt: 'Your password must have at least 8 characters,\n1 capital letter, 1 lowercase letter, 1 number.',
},
addBankAccountPage: {
enterPassword: 'Enter password',
Expand Down
38 changes: 7 additions & 31 deletions src/pages/SetPasswordPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {Component} from 'react';
import {
SafeAreaView,
Text,
TextInput,
View,
} from 'react-native';
import PropTypes from 'prop-types';
Expand All @@ -14,11 +13,10 @@ import styles from '../styles/styles';
import {setPassword} from '../libs/actions/Session';
import ONYXKEYS from '../ONYXKEYS';
import Button from '../components/Button';
import themeColors from '../styles/themes/default';
import SignInPageLayout from './signin/SignInPageLayout';
import canFocusInputOnScreenFocus from '../libs/canFocusInputOnScreenFocus';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
import NewPassword from './settings/NewPassword';

const propTypes = {
/* Onyx Props */
Expand Down Expand Up @@ -63,24 +61,17 @@ class SetPasswordPage extends Component {

this.state = {
password: '',
formError: null,
passwordIsValid: false,
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
};
}

/**
* Validate the form and then submit it
*/
validateAndSubmitForm() {
if (!this.state.password.trim()) {
this.setState({
formError: this.props.translate('setPasswordPage.passwordCannotBeBlank'),
});
if (!this.state.passwordIsValid) {
return;
}

this.setState({
formError: null,
});
setPassword(
this.state.password,
lodashGet(this.props.route, 'params.validateCode', ''),
Expand All @@ -93,20 +84,11 @@ class SetPasswordPage extends Component {
<SafeAreaView style={[styles.signInPage]}>
<SignInPageLayout>
<View style={[styles.mb4]}>
<Text style={[styles.formLabel]}>
{this.props.translate('setPasswordPage.enterPassword')}
</Text>
<TextInput
style={[styles.textInput]}
value={this.state.password}
secureTextEntry
autoCompleteType="password"
textContentType="password"
onChangeText={text => this.setState({password: text})}
<NewPassword
password={this.state.password}
setPassword={password => this.setState({password})}
passwordIsValid={isValid => this.setState({passwordIsValid: isValid})}
onSubmitEditing={this.validateAndSubmitForm}
autoCapitalize="none"
placeholderTextColor={themeColors.placeholderText}
autoFocus={canFocusInputOnScreenFocus()}
/>
</View>
<View>
Expand All @@ -119,12 +101,6 @@ class SetPasswordPage extends Component {
/>
</View>

{this.state.formError && (
<Text style={[styles.formError]}>
{this.state.formError}
</Text>
)}

{!_.isEmpty(this.props.account.error) && (
<Text style={[styles.formError]}>
{this.props.account.error}
Expand Down
131 changes: 131 additions & 0 deletions src/pages/settings/NewPassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React from 'react';
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
import PropTypes from 'prop-types';
import {TextInput, View} from 'react-native';
import Text from '../../components/Text';
import withLocalize, {
withLocalizePropTypes,
} from '../../components/withLocalize';
import CONST from '../../CONST';
import styles from '../../styles/styles';

const propTypes = {
password: PropTypes.string.isRequired,
setPassword: PropTypes.func.isRequired,
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
passwordIsValid: PropTypes.func.isRequired,
onSubmitEditing: PropTypes.func.isRequired,
Copy link
Contributor

Choose a reason for hiding this comment

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

These proptypes need comments

...withLocalizePropTypes,
};

class NewPassword extends React.Component {
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
constructor(props) {
super(props);

this.state = {
confirmNewPassword: '',
passwordHintError: false,
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
confirmPasswordError: false,
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
};
}

componentDidUpdate(prevProps, prevState) {
const eitherPasswordChanged = this.props.password
!== prevProps.password || this.state.confirmNewPassword !== prevState.confirmNewPassword;
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
if (eitherPasswordChanged) {
this.props.passwordIsValid(this.formIsValid());
}
}

onBlurNewPassword() {
if (this.state.passwordHintError) {
return;
}
if (this.props.password && !this.meetsPasswordRules()) {
this.setState({passwordHintError: true});
}
}

onBlurConfirmPassword() {
if (this.state.confirmPasswordError) {
return;
}
if (this.state.confirmNewPassword && !this.passwordsMatch()) {
this.setState({confirmPasswordError: true});
}
}

meetsPasswordRules() {
return this.props.password.match(CONST.PASSWORD_COMPLEXITY_REGEX_STRING);
}
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved

passwordsMatch() {
return this.props.password === this.state.confirmNewPassword;
}
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved

formIsValid() {
return this.meetsPasswordRules() && this.passwordsMatch();
}
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved

showPasswordMatchError() {
return (
!this.passwordsMatch()
&& this.state.confirmPasswordError
&& this.state.confirmNewPassword
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
);
}

render() {
let passwordHintStyle;
if (this.state.passwordHintError && this.props.password && !this.meetsPasswordRules()) {
passwordHintStyle = styles.formError;
}
if (this.meetsPasswordRules()) {
passwordHintStyle = styles.formSuccess;
}

return (
<>
<View style={styles.mb6}>
<Text style={[styles.mb1, styles.formLabel]}>
{`${this.props.translate('setPasswordPage.enterPassword')}*`}
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
</Text>
<TextInput
secureTextEntry
autoCompleteType="password"
textContentType="password"
style={styles.textInput}
value={this.state.password}
onChangeText={password => this.props.setPassword(password)}
onBlur={() => this.onBlurNewPassword()}
/>
<Text style={[styles.formHint, styles.mt1, passwordHintStyle]}>
{this.props.translate('setPasswordPage.newPasswordPrompt')}
</Text>
</View>
<View style={styles.mb6}>
<Text style={[styles.mb1, styles.formLabel]}>
{`${this.props.translate('setPasswordPage.confirmNewPassword')}*`}
</Text>
<TextInput
secureTextEntry
autoCompleteType="password"
textContentType="password"
style={styles.textInput}
value={this.state.confirmNewPassword}
onChangeText={confirmNewPassword => this.setState({confirmNewPassword})}
onSubmitEditing={() => this.props.onSubmitEditing()}
onBlur={() => this.onBlurConfirmPassword()}
/>
{this.showPasswordMatchError() && (
<Text style={[styles.formError, styles.mt1]}>
{this.props.translate('setPasswordPage.passwordsDontMatch')}
</Text>
)}
</View>
</>
);
}
}

NewPassword.propTypes = propTypes;

export default withLocalize(NewPassword);