|
1 |
| -import PropTypes from 'prop-types'; |
2 | 1 | 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'; |
5 | 6 | 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'; |
6 | 11 |
|
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(); |
20 | 30 |
|
21 | 31 | const handleInitiateVerification = (evt) => {
|
22 | 32 | evt.preventDefault();
|
23 |
| - initiateVerification(); |
| 33 | + dispatch(initiateVerification()); |
24 | 34 | };
|
25 | 35 |
|
| 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 | + |
26 | 59 | 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> |
107 | 169 | );
|
108 | 170 | }
|
109 | 171 |
|
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; |
0 commit comments