Блокировка формы во время обработки запроса

This commit is contained in:
SleepWalker 2016-04-02 13:58:54 +03:00
parent 0162d1270f
commit df9283400d
6 changed files with 81 additions and 14 deletions

View File

@ -25,6 +25,7 @@ class PanelTransition extends Component {
// context props
auth: PropTypes.shape({
error: PropTypes.string,
isLoading: PropTypes.bool,
login: PropTypes.shape({
login: PropTypes.string,
password: PropTypes.string
@ -142,7 +143,12 @@ class PanelTransition extends Component {
};
return (
<Form id={panelId} onSubmit={this.onFormSubmit} onInvalid={this.onFormInvalid}>
<Form
id={panelId}
onSubmit={this.onFormSubmit}
onInvalid={this.onFormInvalid}
isLoading={this.props.auth.isLoading}
>
<Panel>
<PanelHeader>
{panels.map((config) => this.getHeader(config))}

View File

@ -8,7 +8,7 @@ export function login({login = '', password = '', rememberMe = false}) {
const LOGIN_REQUIRED = 'error.login_required';
const ACTIVATION_REQUIRED = 'error.account_not_activated';
return (dispatch) =>
return wrapInLoader((dispatch) =>
request.post(
'/api/authentication/login',
{login, password, rememberMe}
@ -43,7 +43,7 @@ export function login({login = '', password = '', rememberMe = false}) {
// TODO: log unexpected errors
})
;
);
}
export function changePassword({
@ -51,7 +51,7 @@ export function changePassword({
newPassword = '',
newRePassword = ''
}) {
return (dispatch) =>
return wrapInLoader((dispatch) =>
dispatch(changeUserPassword({password, newPassword, newRePassword}))
.catch((resp) => {
if (resp.errors) {
@ -62,7 +62,7 @@ export function changePassword({
// TODO: log unexpected errors
})
;
);
}
export function register({
@ -72,7 +72,7 @@ export function register({
rePassword = '',
rulesAgreement = false
}) {
return (dispatch) =>
return wrapInLoader((dispatch) =>
request.post(
'/api/signup',
{email, username, password, rePassword, rulesAgreement}
@ -94,11 +94,11 @@ export function register({
// TODO: log unexpected errors
})
;
);
}
export function activate({key = ''}) {
return (dispatch) =>
return wrapInLoader((dispatch) =>
request.post(
'/api/signup/confirm',
{key}
@ -118,7 +118,7 @@ export function activate({key = ''}) {
// TODO: log unexpected errors
})
;
);
}
export const ERROR = 'error';
@ -141,7 +141,7 @@ export function logout() {
// TODO: move to oAuth actions?
// test request: /oauth?client_id=ely&redirect_uri=http%3A%2F%2Fely.by&response_type=code&scope=minecraft_server_session
export function oAuthValidate(oauth) {
return (dispatch) =>
return wrapInLoader((dispatch) =>
request.get(
'/api/oauth/validate',
getOAuthRequest(oauth)
@ -156,11 +156,12 @@ export function oAuthValidate(oauth) {
if (resp.statusCode === 401 && resp.error === 'accept_required') {
alert('Accept required.');
}
});
})
);
}
export function oAuthComplete(params = {}) {
return (dispatch, getState) => {
return wrapInLoader((dispatch, getState) => {
const oauth = getState().auth.oauth;
const query = request.buildQuery(getOAuthRequest(oauth));
@ -205,7 +206,7 @@ export function oAuthComplete(params = {}) {
return resp;
});
};
});
}
function getOAuthRequest(oauth) {
@ -283,3 +284,29 @@ export function setScopes(scopes) {
payload: scopes
};
}
export const SET_LOADING_STATE = 'set_loading_state';
export function setLoadingState(isLoading) {
return {
type: SET_LOADING_STATE,
payload: isLoading
};
}
function wrapInLoader(fn) {
return (dispatch, getState) => {
dispatch(setLoadingState(true));
const endLoading = () => dispatch(setLoadingState(false));
return Reflect.apply(fn, null, [dispatch, getState]).then((resp) => {
endLoading();
return resp;
}, (resp) => {
endLoading();
return Promise.reject(resp);
});
};
}

View File

@ -1,9 +1,10 @@
import { combineReducers } from 'redux';
import { ERROR, SET_CLIENT, SET_OAUTH, SET_OAUTH_RESULT, SET_SCOPES } from './actions';
import { ERROR, SET_CLIENT, SET_OAUTH, SET_OAUTH_RESULT, SET_SCOPES, SET_LOADING_STATE } from './actions';
export default combineReducers({
error,
isLoading,
client,
oauth,
scopes
@ -25,6 +26,20 @@ function error(
}
}
function isLoading(
state = false,
{type, payload = null}
) {
switch (type) {
case SET_LOADING_STATE:
return !!payload;
default:
return state;
}
}
function client(
state = null,
{type, payload = {}}

View File

@ -102,6 +102,7 @@ export class Form extends Component {
static propTypes = {
id: PropTypes.string, // and id, that uniquely identifies form contents
isLoading: PropTypes.bool,
onSubmit: PropTypes.func,
onInvalid: PropTypes.func,
children: PropTypes.oneOfType([
@ -112,6 +113,7 @@ export class Form extends Component {
static defaultProps = {
id: 'default',
isLoading: false,
onSubmit() {},
onInvalid() {}
};
@ -129,11 +131,14 @@ export class Form extends Component {
}
render() {
const {isLoading} = this.props;
return (
<form
className={classNames(
styles.form,
{
[styles.isFormLoading]: isLoading,
[styles.formTouched]: this.state.isTouched
}
)}

View File

@ -234,3 +234,17 @@
border-color: $red;
}
}
.isFormLoading {
// TODO: надо бы разнести from и input на отдельные модули,
// так как в текущем контексте isLoading немного не логичен,
// пришлось юзать isFormLoading
* {
pointer-events: none;
}
button {
background-image: url('images/loader_button.gif');
background-position: center center;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B