Skip to content

Commit d5ac7fd

Browse files
committed
Remove redux-form from AccountView
1 parent 0bba8ac commit d5ac7fd

File tree

4 files changed

+177
-157
lines changed

4 files changed

+177
-157
lines changed

client/modules/User/actions.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ export function updateSettingsSuccess(user) {
222222
};
223223
}
224224

225+
export function submitSettings(formValues) {
226+
return apiClient.put('/account', formValues);
227+
}
228+
225229
export function updateSettings(formValues) {
226230
return dispatch =>
227231
apiClient.put('/account', formValues)
Lines changed: 160 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,172 @@
1-
import PropTypes from 'prop-types';
21
import React from 'react';
3-
import { withTranslation } from 'react-i18next';
4-
import { domOnlyProps } from '../../../utils/reduxFormUtils';
2+
import { Form, Field } from 'react-final-form';
3+
import { useSelector, useDispatch } from 'react-redux';
4+
import { useTranslation } from 'react-i18next';
5+
import { browserHistory } from 'react-router';
56
import Button from '../../../common/Button';
7+
import { validateSettings } from '../../../utils/reduxFormUtils';
8+
import { submitSettings, initiateVerification, updateSettingsSuccess } from '../actions';
9+
import { showToast, setToastText } from '../../IDE/actions/toast';
10+
import apiClient from '../../../utils/apiClient';
611

7-
function AccountForm(props) {
8-
const {
9-
fields: {
10-
username, email, currentPassword, newPassword
11-
},
12-
user,
13-
handleSubmit,
14-
initiateVerification,
15-
submitting,
16-
invalid,
17-
pristine,
18-
t
19-
} = props;
12+
function asyncValidate(fieldToValidate, value) {
13+
if (!value || value.trim().length === 0) return `Please enter a ${fieldToValidate}.`;
14+
const queryParams = {};
15+
queryParams[fieldToValidate] = value;
16+
queryParams.check_type = fieldToValidate;
17+
return apiClient.get('/signup/duplicate_check', { params: queryParams })
18+
.then((response) => {
19+
if (response.data.exists) {
20+
return response.data.message;
21+
}
22+
return '';
23+
});
24+
}
25+
26+
function AccountForm() {
27+
const { t } = useTranslation();
28+
const user = useSelector(state => state.user);
29+
const dispatch = useDispatch();
2030

2131
const handleInitiateVerification = (evt) => {
2232
evt.preventDefault();
23-
initiateVerification();
33+
dispatch(initiateVerification());
2434
};
2535

36+
function validateUsername(username, _, meta) {
37+
if (username === user.username) return '';
38+
return asyncValidate('username', username);
39+
}
40+
41+
function validateEmail(email, _, meta) {
42+
if (email === user.email) return '';
43+
return asyncValidate('email', email);
44+
}
45+
46+
function updateSettings(formValues) {
47+
return submitSettings(formValues).then((response) => {
48+
dispatch(updateSettingsSuccess(response.data));
49+
// browserHistory.push('/');
50+
dispatch(showToast(5500));
51+
dispatch(setToastText('Toast.SettingsSaved'));
52+
})
53+
.catch((error) => {
54+
const { response } = error;
55+
Promise.reject(new Error(response.data.error));
56+
});
57+
}
58+
2659
return (
27-
<form className="form" onSubmit={handleSubmit(props.updateSettings)}>
28-
<p className="form__field">
29-
<label htmlFor="email" className="form__label">{t('AccountForm.Email')}</label>
30-
<input
31-
className="form__input"
32-
aria-label={t('AccountForm.EmailARIA')}
33-
type="text"
34-
id="email"
35-
{...domOnlyProps(email)}
36-
/>
37-
{email.touched && email.error && (
38-
<span className="form-error">{email.error}</span>
39-
)}
40-
</p>
41-
{
42-
user.verified !== 'verified' &&
43-
(
44-
<p className="form__context">
45-
<span className="form__status">{t('AccountForm.Unconfirmed')}</span>
46-
{
47-
user.emailVerificationInitiate === true ?
48-
(
49-
<span className="form__status"> {t('AccountForm.EmailSent')}</span>
50-
) :
51-
(
52-
<Button
53-
onClick={handleInitiateVerification}
54-
>{t('AccountForm.Resend')}
55-
</Button>
56-
)
57-
}
58-
</p>
59-
)
60-
}
61-
<p className="form__field">
62-
<label htmlFor="username" className="form__label">{t('AccountForm.UserName')}</label>
63-
<input
64-
className="form__input"
65-
aria-label={t('AccountForm.UserNameARIA')}
66-
type="text"
67-
id="username"
68-
defaultValue={username}
69-
{...domOnlyProps(username)}
70-
/>
71-
{username.touched && username.error && (
72-
<span className="form-error">{username.error}</span>
73-
)}
74-
</p>
75-
<p className="form__field">
76-
<label htmlFor="current password" className="form__label">{t('AccountForm.CurrentPassword')}</label>
77-
<input
78-
className="form__input"
79-
aria-label={t('AccountForm.CurrentPasswordARIA')}
80-
type="password"
81-
id="currentPassword"
82-
{...domOnlyProps(currentPassword)}
83-
/>
84-
{currentPassword.touched && currentPassword.error && (
85-
<span className="form-error">{currentPassword.error}</span>
86-
)}
87-
</p>
88-
<p className="form__field">
89-
<label htmlFor="new password" className="form__label">{t('AccountForm.NewPassword')}</label>
90-
<input
91-
className="form__input"
92-
aria-label={t('AccountForm.NewPasswordARIA')}
93-
type="password"
94-
id="newPassword"
95-
{...domOnlyProps(newPassword)}
96-
/>
97-
{newPassword.touched && newPassword.error && (
98-
<span className="form-error">{newPassword.error}</span>
99-
)}
100-
</p>
101-
<Button
102-
type="submit"
103-
disabled={submitting || invalid || pristine}
104-
>{t('AccountForm.SubmitSaveAllSettings')}
105-
</Button>
106-
</form>
60+
<Form
61+
fields={['username', 'email', 'currentPassword', 'newPassword']}
62+
validate={validateSettings}
63+
onSubmit={updateSettings}
64+
>
65+
{({
66+
handleSubmit, submitting, invalid, restart
67+
}) => (
68+
<form
69+
className="form"
70+
onSubmit={(event) => {
71+
handleSubmit(event).then(restart);
72+
}}
73+
>
74+
<Field name="email" validate={validateEmail} validateFields={[]} initialValue={user.email}>
75+
{field => (
76+
<p className="form__field">
77+
<label htmlFor="email" className="form__label">{t('AccountForm.Email')}</label>
78+
<input
79+
className="form__input"
80+
aria-label={t('AccountForm.EmailARIA')}
81+
type="text"
82+
id="email"
83+
{...field.input}
84+
/>
85+
{field.meta.touched && field.meta.error && (
86+
<span className="form-error">{field.meta.error}</span>
87+
)}
88+
</p>
89+
)}
90+
</Field>
91+
{
92+
user.verified !== 'verified' &&
93+
(
94+
<p className="form__context">
95+
<span className="form__status">{t('AccountForm.Unconfirmed')}</span>
96+
{
97+
user.emailVerificationInitiate === true ?
98+
(
99+
<span className="form__status"> {t('AccountForm.EmailSent')}</span>
100+
) :
101+
(
102+
<Button onClick={handleInitiateVerification}>
103+
{t('AccountForm.Resend')}
104+
</Button>
105+
)
106+
}
107+
</p>
108+
)
109+
}
110+
<Field name="username" validate={validateUsername} validateFields={[]} initialValue={user.username}>
111+
{field => (
112+
<p className="form__field">
113+
<label htmlFor="username" className="form__label">{t('AccountForm.UserName')}</label>
114+
<input
115+
className="form__input"
116+
aria-label={t('AccountForm.UserNameARIA')}
117+
type="text"
118+
id="username"
119+
{...field.input}
120+
/>
121+
{field.meta.touched && field.meta.error && (
122+
<span className="form-error">{field.meta.error}</span>
123+
)}
124+
</p>
125+
)}
126+
</Field>
127+
<Field name="currentPassword">
128+
{field => (
129+
<p className="form__field">
130+
<label htmlFor="current password" className="form__label">{t('AccountForm.CurrentPassword')}</label>
131+
<input
132+
className="form__input"
133+
aria-label={t('AccountForm.CurrentPasswordARIA')}
134+
type="password"
135+
id="currentPassword"
136+
{...field.input}
137+
/>
138+
{field.meta.touched && field.meta.error && (
139+
<span className="form-error">{field.meta.error}</span>
140+
)}
141+
</p>
142+
)}
143+
</Field>
144+
<Field name="newPassword">
145+
{field => (
146+
<p className="form__field">
147+
<label htmlFor="new password" className="form__label">{t('AccountForm.NewPassword')}</label>
148+
<input
149+
className="form__input"
150+
aria-label={t('AccountForm.NewPasswordARIA')}
151+
type="password"
152+
id="newPassword"
153+
{...field.input}
154+
/>
155+
{field.meta.touched && field.meta.error && (
156+
<span className="form-error">{field.meta.error}</span>
157+
)}
158+
</p>
159+
)}
160+
</Field>
161+
<Button
162+
type="submit"
163+
disabled={submitting || invalid}
164+
>{t('AccountForm.SubmitSaveAllSettings')}
165+
</Button>
166+
</form>
167+
)}
168+
</Form>
107169
);
108170
}
109171

110-
AccountForm.propTypes = {
111-
fields: PropTypes.shape({
112-
username: PropTypes.objectOf(PropTypes.shape()).isRequired,
113-
email: PropTypes.objectOf(PropTypes.shape()).isRequired,
114-
currentPassword: PropTypes.objectOf(PropTypes.shape()).isRequired,
115-
newPassword: PropTypes.objectOf(PropTypes.shape()).isRequired,
116-
}).isRequired,
117-
user: PropTypes.shape({
118-
verified: PropTypes.string.isRequired,
119-
emailVerificationInitiate: PropTypes.bool.isRequired,
120-
}).isRequired,
121-
handleSubmit: PropTypes.func.isRequired,
122-
initiateVerification: PropTypes.func.isRequired,
123-
updateSettings: PropTypes.func.isRequired,
124-
submitting: PropTypes.bool,
125-
invalid: PropTypes.bool,
126-
pristine: PropTypes.bool,
127-
t: PropTypes.func.isRequired
128-
};
129-
130-
AccountForm.defaultProps = {
131-
submitting: false,
132-
pristine: true,
133-
invalid: false,
134-
};
135-
136-
export default withTranslation()(AccountForm);
172+
export default AccountForm;

client/modules/User/pages/AccountView.jsx

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import PropTypes from 'prop-types';
22
import React from 'react';
3-
import { reduxForm } from 'redux-form';
3+
import { connect } from 'react-redux';
44
import { bindActionCreators } from 'redux';
55
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
66
import { Helmet } from 'react-helmet';
77
import { withTranslation } from 'react-i18next';
88
import { withRouter, browserHistory } from 'react-router';
99
import { parse } from 'query-string';
10-
import { updateSettings, initiateVerification, createApiKey, removeApiKey } from '../actions';
10+
import { createApiKey, removeApiKey } from '../actions';
1111
import AccountForm from '../components/AccountForm';
12-
import apiClient from '../../../utils/apiClient';
13-
import { validateSettings } from '../../../utils/reduxFormUtils';
1412
import SocialAuthButton from '../components/SocialAuthButton';
1513
import APIKeyForm from '../components/APIKeyForm';
1614
import Nav from '../../../components/Nav';
1715
import ErrorModal from '../../IDE/components/ErrorModal';
1816
import Overlay from '../../App/components/Overlay';
17+
import Toast from '../../IDE/components/Toast';
1918

2019
function SocialLoginPanel(props) {
2120
const { user } = props;
@@ -67,6 +66,7 @@ class AccountView extends React.Component {
6766
<Helmet>
6867
<title>{this.props.t('AccountView.Title')}</title>
6968
</Helmet>
69+
{this.props.toast.isVisible && <Toast />}
7070

7171
<Nav layout="dashboard" />
7272

@@ -118,48 +118,28 @@ function mapStateToProps(state) {
118118
previousPath: state.ide.previousPath,
119119
user: state.user,
120120
apiKeys: state.user.apiKeys,
121-
theme: state.preferences.theme
121+
theme: state.preferences.theme,
122+
toast: state.toast
122123
};
123124
}
124125

125126
function mapDispatchToProps(dispatch) {
126127
return bindActionCreators({
127-
updateSettings, initiateVerification, createApiKey, removeApiKey
128+
createApiKey, removeApiKey
128129
}, dispatch);
129130
}
130131

131-
function asyncValidate(formProps, dispatch, props) {
132-
const fieldToValidate = props.form._active;
133-
if (fieldToValidate) {
134-
const queryParams = {};
135-
queryParams[fieldToValidate] = formProps[fieldToValidate];
136-
queryParams.check_type = fieldToValidate;
137-
return apiClient.get('/signup/duplicate_check', { params: queryParams })
138-
.then((response) => {
139-
if (response.data.exists) {
140-
const error = {};
141-
error[fieldToValidate] = response.data.message;
142-
throw error;
143-
}
144-
});
145-
}
146-
return Promise.resolve(true).then(() => {});
147-
}
148-
149132
AccountView.propTypes = {
150133
previousPath: PropTypes.string.isRequired,
151134
theme: PropTypes.string.isRequired,
152135
t: PropTypes.func.isRequired,
153136
location: PropTypes.shape({
154137
search: PropTypes.string.isRequired,
155138
pathname: PropTypes.string.isRequired
139+
}).isRequired,
140+
toast: PropTypes.shape({
141+
isVisible: PropTypes.bool.isRequired,
156142
}).isRequired
157143
};
158144

159-
export default withTranslation()(withRouter(reduxForm({
160-
form: 'updateAllSettings',
161-
fields: ['username', 'email', 'currentPassword', 'newPassword'],
162-
validate: validateSettings,
163-
asyncValidate,
164-
asyncBlurFields: ['username', 'email', 'currentPassword']
165-
}, mapStateToProps, mapDispatchToProps)(AccountView)));
145+
export default withTranslation()(withRouter(connect(mapStateToProps, mapDispatchToProps)(AccountView)));

0 commit comments

Comments
 (0)