Added change username form

This commit is contained in:
SleepWalker 2016-05-02 16:13:18 +03:00
parent 9df4819905
commit 739cb0f2b6
8 changed files with 245 additions and 1 deletions

View File

@ -49,6 +49,7 @@ export default class Profile extends Component {
</div>
<ProfileField
link="/profile/change-username"
label={<Message {...messages.nickname} />}
value={user.username}
warningMessage={user.hasMojangUsernameCollision ? (
@ -62,8 +63,8 @@ export default class Profile extends Component {
/>
<ProfileField
label={<Message {...messages.password} />}
link="/profile/change-password"
label={<Message {...messages.password} />}
value={<Message {...messages.changedAt} values={{
at: (<Relative value={user.passwordChangedAt * 1000} />)
}} />}

View File

@ -0,0 +1,86 @@
import React, { Component, PropTypes } from 'react';
import { FormattedMessage as Message } from 'react-intl';
import { Link } from 'react-router';
import Helmet from 'react-helmet';
import { Input, Button, Form, FormModel } from 'components/ui/form';
import styles from 'components/profile/profileForm.scss';
import messages from './ChangeUsername.messages';
export default class ChangeUsername extends Component {
static displayName = 'ChangeUsername';
static propTypes = {
username: PropTypes.string.isRequired,
form: PropTypes.instanceOf(FormModel),
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired
};
static get defaultProps() {
return {
form: new FormModel()
};
}
render() {
const {form, username} = this.props;
return (
<Form onSubmit={this.onFormSubmit}
form={form}
>
<div className={styles.contentWithBackButton}>
<Link className={styles.backButton} to="/" />
<div className={styles.form}>
<div className={styles.formBody}>
<Message {...messages.changeUsernameTitle}>
{(pageTitle) => (
<h3 className={styles.title}>
<Helmet title={pageTitle} />
{pageTitle}
</h3>
)}
</Message>
<div className={styles.formRow}>
<p className={styles.description}>
<Message {...messages.changeUsernameDescription} />
</p>
</div>
<div className={styles.formRow}>
<Input {...form.bindField('username')}
value={username}
onChange={this.onUsernameChange}
required
skin="light"
/>
</div>
<div className={styles.formRow}>
<p className={styles.description}>
<Message {...messages.changeUsernameWarning} />
</p>
</div>
</div>
<Button color="green" block label={messages.changeUsernameButton} />
</div>
</div>
</Form>
);
}
onUsernameChange = (event) => {
this.props.onChange(event.target.value);
};
onFormSubmit = () => {
this.props.onSubmit(this.props.form);
};
}

View File

@ -0,0 +1,24 @@
import { defineMessages } from 'react-intl';
export default defineMessages({
changeUsernameTitle: {
id: 'changeUsernameTitle',
defaultMessage: 'Change nickname'
// defaultMessage: 'Смена никнейма'
},
changeUsernameDescription: {
id: 'changeUsernameDescription',
defaultMessage: 'You can change your nickname to any arbitrary value. Remember that it is not recommended to take a nickname of already existing Mojang account.'
// defaultMessage: 'Вы можете сменить свой никнейм на любое допустимое значение. Помните о том, что не рекомендуется занимать никнеймы пользователей Mojang.'
},
changeUsernameWarning: {
id: 'changeUsernameWarning',
defaultMessage: 'Be careful: if you playing on the server with nickname binding, then after changing nickname you may lose all your progress.'
// defaultMessage: 'Будьте внимательны: если вы играли на сервере с привязкой по нику, то после смены ника вы можете утратить весь свой прогресс.'
},
changeUsernameButton: {
id: 'changeUsernameButton',
defaultMessage: 'Change nickname'
// defaultMessage: 'Сменить никнейм'
}
});

View File

@ -0,0 +1,115 @@
import React, { Component, PropTypes } from 'react';
import accounts from 'services/api/accounts';
import { FormModel } from 'components/ui/form';
import ChangeUsername from 'components/profile/changeUsername/ChangeUsername';
import PasswordRequestForm from 'components/profile/passwordRequestForm/PasswordRequestForm';
class ChangeUsernamePage extends Component {
static displayName = 'ChangeUsernamePage';
static propTypes = {
username: PropTypes.string.isRequired,
updateUsername: PropTypes.func.isRequired, // updates username in state
changeUsername: PropTypes.func.isRequired // saves username to backend
};
form = new FormModel();
componentWillMount() {
this.setState({
actualUsername: this.props.username
});
}
componentWillUnmount() {
this.props.updateUsername(this.state.actualUsername);
}
render() {
return (
<ChangeUsername form={this.form}
onSubmit={this.onSubmit}
onChange={this.onUsernameChange}
username={this.props.username}
/>
);
}
onUsernameChange = (username) => {
this.props.updateUsername(username);
};
onSubmit = () => {
this.props.changeUsername(this.form).then(() => {
console.log('update to', this.props.username)
this.setState({
actualUsername: this.props.username
});
});
};
}
import { connect } from 'react-redux';
import { routeActions } from 'react-router-redux';
import { register as registerPopup, create as createPopup } from 'components/ui/popup/actions';
import { updateUser } from 'components/user/actions';
function goToProfile() {
return routeActions.push('/');
}
export default connect((state) => ({
username: state.user.username
}), {
updateUsername: (username) => {
return updateUser({username});
},
changeUsername: (form) => {
return (dispatch) => accounts.changeUsername(form.serialize())
.catch((resp) => {
// prevalidate user input, because requestPassword popup will block the
// entire form from input, so it must be valid
if (resp.errors) {
Reflect.deleteProperty(resp.errors, 'password');
if (Object.keys(resp.errors).length) {
form.setErrors(resp.errors);
return Promise.reject(resp);
}
}
return Promise.resolve();
})
.then(() => {
return new Promise((resolve) => {
// TODO: судя по всему registerPopup было явно лишним. Надо еще раз
// обдумать API и переписать
dispatch(registerPopup('requestPassword', PasswordRequestForm));
dispatch(createPopup('requestPassword', (props) => ({
form,
onSubmit: () => {
// TODO: hide this logic in action
accounts.changeUsername(form.serialize())
.catch((resp) => {
if (resp.errors) {
form.setErrors(resp.errors);
}
return Promise.reject(resp);
})
.then(() => {
dispatch(updateUser({
username: form.value('username')
}));
})
.then(resolve)
.then(props.onClose)
.then(() => dispatch(goToProfile()));
}
})));
});
})
;
}
})(ChangeUsernamePage);

View File

@ -7,6 +7,7 @@ import AuthPage from 'pages/auth/AuthPage';
import ProfilePage from 'pages/profile/ProfilePage';
import ProfileChangePasswordPage from 'pages/profile/ChangePasswordPage';
import ProfileChangeUsernamePage from 'pages/profile/ChangeUsernamePage';
import { authenticate } from 'components/user/actions';
@ -55,6 +56,7 @@ export default function routesFactory(store) {
<Route path="profile" component={ProfilePage}>
<Route path="change-password" component={ProfileChangePasswordPage} />
<Route path="change-username" component={ProfileChangeUsernamePage} />
</Route>
</Route>
);

View File

@ -15,5 +15,15 @@ export default {
'/api/accounts/change-password',
{password, newPassword, newRePassword, logoutAll}
);
},
changeUsername({
username = '',
password = ''
}) {
return request.post(
'/api/accounts/change-username',
{username, password}
);
}
};

View File

@ -31,6 +31,7 @@ const errorsMap = {
),
'error.username_required': () => <Message {...messages.usernameRequired} />,
'error.username_not_available': () => <Message {...messages.usernameUnavailable} />,
'error.email_required': () => <Message {...messages.emailRequired} />,
'error.email_invalid': () => <Message {...messages.emailInvalid} />,
'error.email_is_tempmail': () => <Message {...messages.emailIsTempmail} />,

View File

@ -46,6 +46,11 @@ export default defineMessages({
defaultMessage: 'Username is required'
},
usernameUnavailable: {
id: 'usernameUnavailable',
defaultMessage: 'This username is already taken'
},
emailRequired: {
id: 'emailRequired',
defaultMessage: 'Email is required'