#100: form loading state for profile

This commit is contained in:
SleepWalker 2016-05-27 23:04:17 +03:00
parent 808f239286
commit c51bd17259
9 changed files with 78 additions and 18 deletions

View File

@ -22,6 +22,7 @@
"intl": "^1.2.2",
"intl-format-cache": "^2.0.4",
"intl-messageformat": "^1.1.0",
"promise.prototype.finally": "^1.0.1",
"react": "^15.0.0",
"react-dom": "^15.0.0",
"react-helmet": "^2.3.1",

View File

@ -108,6 +108,7 @@ export default class ChangeEmail extends Component {
<Button
color="violet"
type="submit"
block
label={this.isLastStep() ? messages.changeEmailButton : messages.sendEmailButton}
onClick={this.onSubmit}

View File

@ -88,7 +88,7 @@ export default class ChangePassword extends Component {
</div>
</div>
<Button color="green" block label={messages.changePasswordButton} />
<Button color="green" block label={messages.changePasswordButton} type="submit" />
</div>
</div>
</Form>

View File

@ -1,7 +1,6 @@
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';
@ -69,7 +68,7 @@ export default class ChangeUsername extends Component {
</div>
</div>
<Button color="green" block label={messages.changeUsernameButton} />
<Button color="green" block label={messages.changeUsernameButton} type="submit" />
</div>
</div>
</Form>

View File

@ -18,7 +18,7 @@ export default class PasswordRequestForm extends Component {
const {form} = this.props;
return (
<Form onSubmit={this.onSubmit}
<Form onSubmit={this.onFormSubmit}
form={form}
>
<h2>
@ -34,12 +34,12 @@ export default class PasswordRequestForm extends Component {
icon="key"
placeholder={messages.pleaseEnterPassword}
/>
<Button color="green" label="OK" block />
<Button color="green" label="OK" block type="submit" />
</Form>
);
}
onSubmit = () => {
onFormSubmit = () => {
this.props.onSubmit(this.props.form);
};
}

View File

@ -29,19 +29,42 @@ export default class Form extends Component {
};
state = {
isTouched: false
isTouched: false,
isLoading: this.props.isLoading || false
};
componentWillMount() {
if (this.props.form) {
this.props.form.addLoadingListener(this.onLoading);
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.id !== this.props.id) {
this.setState({
isTouched: false
});
}
if (typeof nextProps.isLoading !== 'undefined') {
this.setState({
isLoading: nextProps.isLoading
});
}
if (nextProps.form && nextProps.form !== this.props.form) {
throw new Error('The FormModel instance should not be changed during component lifetime');
}
}
componentWillUnmount() {
if (this.props.form) {
this.props.form.removeLoadingListener(this.onLoading);
}
}
render() {
const {isLoading} = this.props;
const {isLoading} = this.state;
return (
<form
@ -100,4 +123,6 @@ export default class Form extends Component {
this.props.onInvalid(errors);
}
};
onLoading = (isLoading) => this.setState({isLoading});
}

View File

@ -3,6 +3,7 @@ import FormInputComponent from './FormInputComponent';
export default class FormModel {
fields = {};
errors = {};
handlers = [];
/**
* Connects form with React's component
@ -83,4 +84,31 @@ export default class FormModel {
return acc;
}, {});
}
/**
* Bind handler to listen for form loading state change
*
* @param {Function} fn
*/
addLoadingListener(fn) {
this.removeLoadingListener(fn);
this.handlers.push(fn);
}
/**
* Remove form loading state handler
*
* @param {Function} fn
*/
removeLoadingListener(fn) {
this.handlers = this.handlers.filter((handler) => handler !== fn);
}
beginLoading() {
this.handlers.forEach((fn) => fn(true));
}
endLoading() {
this.handlers.forEach((fn) => fn(false));
}
}

View File

@ -1,4 +1,5 @@
import 'babel-polyfill';
import 'promise.prototype.finally';
import React from 'react';
import ReactDOM from 'react-dom';

View File

@ -50,8 +50,9 @@ export default connect(null, {
return routeActions.push('/');
},
fetchUserData,
onSubmit: ({form, sendData}) => (dispatch) =>
sendData()
onSubmit: ({form, sendData}) => (dispatch) => {
form.beginLoading();
return sendData()
.catch((resp) => {
const requirePassword = resp.errors && !!resp.errors.password;
@ -73,20 +74,24 @@ export default connect(null, {
dispatch(createPopup(PasswordRequestForm, (props) => ({
form,
onSubmit: () => {
form.beginLoading();
sendData()
.catch((resp) => {
if (resp.errors) {
form.setErrors(resp.errors);
}
.catch((resp) => {
if (resp.errors) {
form.setErrors(resp.errors);
}
return Promise.reject(resp);
})
.then(resolve)
.then(props.onClose);
return Promise.reject(resp);
})
.then(resolve)
.then(props.onClose)
.finally(() => form.endLoading());
}
})));
} else {
resolve();
}
}))
.finally(() => form.endLoading());
}
})(ProfilePage);