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 NewPasswordForm from './settings/NewPasswordForm';

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

this.state = {
password: '',
formError: null,
isFormValid: false,
};
}

/**
* Validate the form and then submit it
*/
validateAndSubmitForm() {
if (!this.state.password.trim()) {
this.setState({
formError: this.props.translate('setPasswordPage.passwordCannotBeBlank'),
});
if (!this.state.isFormValid) {
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})}
<NewPasswordForm
password={this.state.password}
updatePassword={password => this.setState({password})}
updateIsFormValid={isValid => this.setState({isFormValid: isValid})}
anthony-hull marked this conversation as resolved.
Show resolved Hide resolved
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
138 changes: 138 additions & 0 deletions src/pages/settings/NewPasswordForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React from 'react';
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 = {
/** String to control the first password box in the form */
password: PropTypes.string.isRequired,

/** Function to update the first password box in the form */
updatePassword: PropTypes.func.isRequired,

/** Callback function called with boolean value for if the password form is valid */
updateIsFormValid: PropTypes.func.isRequired,

/** Callback function for when form is submitted */
onSubmitEditing: PropTypes.func.isRequired,
...withLocalizePropTypes,
};

class NewPasswordForm extends React.Component {
constructor(props) {
super(props);

this.state = {
confirmNewPassword: '',
passwordHintError: false,
shouldShowPasswordConfirmError: false,
};
}

componentDidUpdate(prevProps, prevState) {
const eitherPasswordChanged = (this.props.password !== prevProps.password)
|| this.state.confirmNewPassword !== prevState.confirmNewPassword;
if (eitherPasswordChanged) {
this.props.updateIsFormValid(this.isValidForm());
}
}

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

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

isValidPassword() {
return this.props.password.match(CONST.PASSWORD_COMPLEXITY_REGEX_STRING);
}

doPasswordsMatch() {
return this.props.password === this.state.confirmNewPassword;
}

isValidForm() {
return this.isValidPassword() && this.doPasswordsMatch();
}

showPasswordMatchError() {
return (
!this.doPasswordsMatch()
&& this.state.shouldShowPasswordConfirmError
&& this.state.confirmNewPassword
);
}

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

return (
<>
<View style={styles.mb6}>
<Text style={[styles.mb1, styles.formLabel]}>
{`${this.props.translate('setPasswordPage.enterPassword')}`}
</Text>
<TextInput
secureTextEntry
autoCompleteType="password"
textContentType="password"
style={styles.textInput}
value={this.state.password}
onChangeText={password => this.props.updatePassword(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>
</>
);
}
}

NewPasswordForm.propTypes = propTypes;

export default withLocalize(NewPasswordForm);