Skip to content

Commit 2566173

Browse files
committed
Migrate NewPasswordView to final-form
1 parent 97139d3 commit 2566173

File tree

5 files changed

+108
-118
lines changed

5 files changed

+108
-118
lines changed

client/modules/User/actions.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,17 +220,20 @@ export function validateResetPasswordToken(token) {
220220
};
221221
}
222222

223-
export function updatePassword(token, formValues) {
224-
return (dispatch) => {
223+
export function updatePassword(formValues, token) {
224+
return dispatch => new Promise((resolve, reject) =>
225225
apiClient.post(`/reset-password/${token}`, formValues)
226226
.then((response) => {
227227
dispatch(loginUserSuccess(response.data));
228228
browserHistory.push('/');
229+
resolve();
229230
})
230-
.catch(() => dispatch({
231-
type: ActionTypes.INVALID_RESET_PASSWORD_TOKEN
231+
.catch(() => {
232+
dispatch({
233+
type: ActionTypes.INVALID_RESET_PASSWORD_TOKEN
234+
});
235+
reject();
232236
}));
233-
};
234237
}
235238

236239
export function updateSettingsSuccess(user) {
Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,77 @@
11
import PropTypes from 'prop-types';
22
import React from 'react';
3-
import { withTranslation } from 'react-i18next';
4-
import { domOnlyProps } from '../../../utils/reduxFormUtils';
3+
import { Form, Field } from 'react-final-form';
4+
import { useDispatch } from 'react-redux';
5+
import { useTranslation } from 'react-i18next';
6+
import { validateNewPassword } from '../../../utils/reduxFormUtils';
7+
import { updatePassword } from '../actions';
58
import Button from '../../../common/Button';
69

710
function NewPasswordForm(props) {
8-
const {
9-
fields: { password, confirmPassword }, handleSubmit, submitting, invalid, pristine,
10-
t
11-
} = props;
11+
const { resetPasswordToken } = props;
12+
const { t } = useTranslation();
13+
const dispatch = useDispatch();
14+
15+
function onSubmit(formProps) {
16+
return dispatch(updatePassword(formProps, resetPasswordToken));
17+
}
18+
1219
return (
13-
<form
14-
className="form"
15-
onSubmit={handleSubmit(props.updatePassword.bind(this, props.params.reset_password_token))}
20+
<Form
21+
fields={['password', 'confirmPassword']}
22+
validate={validateNewPassword}
23+
onSubmit={onSubmit}
1624
>
17-
<p className="form__field">
18-
<label htmlFor="password" className="form__label">{t('NewPasswordForm.Title')}</label>
19-
<input
20-
className="form__input"
21-
aria-label={t('NewPasswordForm.TitleARIA')}
22-
type="password"
23-
id="Password"
24-
{...domOnlyProps(password)}
25-
/>
26-
{password.touched && password.error && (
27-
<span className="form-error">{password.error}</span>
28-
)}
29-
</p>
30-
<p className="form__field">
31-
<label htmlFor="confirm password" className="form__label">{t('NewPasswordForm.ConfirmPassword')}</label>
32-
<input
33-
className="form__input"
34-
type="password"
35-
aria-label={t('NewPasswordForm.ConfirmPasswordARIA')}
36-
id="confirm password"
37-
{...domOnlyProps(confirmPassword)}
38-
/>
39-
{confirmPassword.touched && confirmPassword.error && (
40-
<span className="form-error">{confirmPassword.error}</span>
41-
)}
42-
</p>
43-
<Button type="submit" disabled={submitting || invalid || pristine}>{t('NewPasswordForm.SubmitSetNewPassword')}</Button>
44-
</form>
25+
{({
26+
handleSubmit, submitting, invalid, pristine
27+
}) => (
28+
<form
29+
className="form"
30+
onSubmit={handleSubmit}
31+
>
32+
<Field name="password">
33+
{field => (
34+
<p className="form__field">
35+
<label htmlFor="password" className="form__label">{t('NewPasswordForm.Title')}</label>
36+
<input
37+
className="form__input"
38+
aria-label={t('NewPasswordForm.TitleARIA')}
39+
type="password"
40+
id="Password"
41+
{...field.input}
42+
/>
43+
{field.meta.touched && field.meta.error && (
44+
<span className="form-error">{field.meta.error}</span>
45+
)}
46+
</p>
47+
)}
48+
</Field>
49+
<Field name="confirmPassword">
50+
{field => (
51+
<p className="form__field">
52+
<label htmlFor="confirm password" className="form__label">{t('NewPasswordForm.ConfirmPassword')}</label>
53+
<input
54+
className="form__input"
55+
type="password"
56+
aria-label={t('NewPasswordForm.ConfirmPasswordARIA')}
57+
id="confirm password"
58+
{...field.input}
59+
/>
60+
{field.meta.touched && field.meta.error && (
61+
<span className="form-error">{field.meta.error}</span>
62+
)}
63+
</p>
64+
)}
65+
</Field>
66+
<Button type="submit" disabled={submitting || invalid || pristine}>{t('NewPasswordForm.SubmitSetNewPassword')}</Button>
67+
</form>
68+
)}
69+
</Form>
4570
);
4671
}
4772

4873
NewPasswordForm.propTypes = {
49-
fields: PropTypes.shape({
50-
password: PropTypes.objectOf(PropTypes.shape()).isRequired,
51-
confirmPassword: PropTypes.objectOf(PropTypes.shape()).isRequired,
52-
}).isRequired,
53-
handleSubmit: PropTypes.func.isRequired,
54-
updatePassword: PropTypes.func.isRequired,
55-
submitting: PropTypes.bool,
56-
invalid: PropTypes.bool,
57-
pristine: PropTypes.bool,
58-
params: PropTypes.shape({
59-
reset_password_token: PropTypes.string,
60-
}).isRequired,
61-
t: PropTypes.func.isRequired
62-
};
63-
64-
NewPasswordForm.defaultProps = {
65-
invalid: false,
66-
pristine: true,
67-
submitting: false,
74+
resetPasswordToken: PropTypes.string.isRequired,
6875
};
6976

70-
export default withTranslation()(NewPasswordForm);
77+
export default NewPasswordForm;

client/modules/User/components/ResetPasswordForm.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ function ResetPasswordForm(props) {
1111
const resetPasswordInitiate = useSelector(state => state.user.resetPasswordInitiate);
1212
const dispatch = useDispatch();
1313

14-
function submitInitiateResetPassword() {
15-
dispatch(initiateResetPassword());
14+
function submitInitiateResetPassword(formProps) {
15+
dispatch(initiateResetPassword(formProps));
1616
}
1717

1818
return (
Lines changed: 20 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
import PropTypes from 'prop-types';
2-
import React from 'react';
3-
import { reduxForm } from 'redux-form';
2+
import React, { useEffect } from 'react';
43
import classNames from 'classnames';
5-
import { bindActionCreators } from 'redux';
4+
import { useDispatch, useSelector } from 'react-redux';
65
import { Helmet } from 'react-helmet';
7-
import { withTranslation } from 'react-i18next';
8-
import i18next from 'i18next';
6+
import { useTranslation } from 'react-i18next';
97
import NewPasswordForm from '../components/NewPasswordForm';
10-
import * as UserActions from '../actions';
8+
import { validateResetPasswordToken } from '../actions';
119
import Nav from '../../../components/Nav';
1210

1311
function NewPasswordView(props) {
12+
const { t } = useTranslation();
13+
const resetPasswordToken = props.params.reset_password_token;
14+
const resetPasswordInvalid = useSelector(state => state.user.resetPasswordInvalid);
15+
const dispatch = useDispatch();
16+
17+
useEffect(() => {
18+
dispatch(validateResetPasswordToken(resetPasswordToken));
19+
}, [resetPasswordToken]);
20+
1421
const newPasswordClass = classNames({
1522
'new-password': true,
16-
'new-password--invalid': props.user.resetPasswordInvalid,
23+
'new-password--invalid': resetPasswordInvalid,
1724
'form-container': true,
1825
'user': true
1926
});
@@ -22,13 +29,13 @@ function NewPasswordView(props) {
2229
<Nav layout="dashboard" />
2330
<div className={newPasswordClass}>
2431
<Helmet>
25-
<title>{props.t('NewPasswordView.Title')}</title>
32+
<title>{t('NewPasswordView.Title')}</title>
2633
</Helmet>
2734
<div className="form-container__content">
28-
<h2 className="form-container__title">{props.t('NewPasswordView.Description')}</h2>
29-
<NewPasswordForm {...props} />
35+
<h2 className="form-container__title">{t('NewPasswordView.Description')}</h2>
36+
<NewPasswordForm resetPasswordToken={resetPasswordToken} />
3037
<p className="new-password__invalid">
31-
{props.t('NewPasswordView.TokenInvalidOrExpired')}
38+
{t('NewPasswordView.TokenInvalidOrExpired')}
3239
</p>
3340
</div>
3441
</div>
@@ -39,43 +46,7 @@ function NewPasswordView(props) {
3946
NewPasswordView.propTypes = {
4047
params: PropTypes.shape({
4148
reset_password_token: PropTypes.string,
42-
}).isRequired,
43-
validateResetPasswordToken: PropTypes.func.isRequired,
44-
user: PropTypes.shape({
45-
resetPasswordInvalid: PropTypes.bool
46-
}).isRequired,
47-
t: PropTypes.func.isRequired
49+
}).isRequired
4850
};
4951

50-
function validate(formProps) {
51-
const errors = {};
52-
53-
if (!formProps.password) {
54-
errors.password = i18next.t('NewPasswordView.EmptyPassword');
55-
}
56-
if (!formProps.confirmPassword) {
57-
errors.confirmPassword = i18next.t('NewPasswordView.PasswordConfirmation');
58-
}
59-
60-
if (formProps.password !== formProps.confirmPassword) {
61-
errors.password = i18next.t('NewPasswordView.PasswordMismatch');
62-
}
63-
64-
return errors;
65-
}
66-
67-
function mapStateToProps(state) {
68-
return {
69-
user: state.user
70-
};
71-
}
72-
73-
function mapDispatchToProps(dispatch) {
74-
return bindActionCreators(UserActions, dispatch);
75-
}
76-
77-
export default withTranslation()(reduxForm({
78-
form: 'new-password',
79-
fields: ['password', 'confirmPassword'],
80-
validate
81-
}, mapStateToProps, mapDispatchToProps)(NewPasswordView));
52+
export default NewPasswordView;

client/utils/reduxFormUtils.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,7 @@ export function validateLogin(formProps) {
6262
return errors;
6363
}
6464

65-
export function validateSignup(formProps) {
66-
const errors = {};
67-
68-
validateNameEmail(formProps, errors);
69-
65+
function validatePasswords(formProps, errors) {
7066
if (!formProps.password) {
7167
errors.password = i18n.t('ReduxFormUtils.errorEmptyPassword');
7268
}
@@ -80,6 +76,19 @@ export function validateSignup(formProps) {
8076
if (formProps.password !== formProps.confirmPassword && formProps.confirmPassword) {
8177
errors.confirmPassword = i18n.t('ReduxFormUtils.errorPasswordMismatch');
8278
}
79+
}
80+
81+
export function validateNewPassword(formProps) {
82+
const errors = {};
83+
validatePasswords(formProps, errors);
84+
return errors;
85+
}
86+
87+
export function validateSignup(formProps) {
88+
const errors = {};
89+
90+
validateNameEmail(formProps, errors);
91+
validatePasswords(formProps, errors);
8392

8493
return errors;
8594
}

0 commit comments

Comments
 (0)