mirror of
https://github.com/usatiuk/writer.git
synced 2025-10-29 00:17:48 +01:00
nice error handling
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
@import "~@blueprintjs/core/lib/scss/variables";
|
||||
.AuthForm {
|
||||
margin: auto;
|
||||
margin-top: 10rem;
|
||||
@@ -11,10 +12,13 @@
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
#error {
|
||||
height: 1rem;
|
||||
color: $pt-intent-danger;
|
||||
}
|
||||
button.submit {
|
||||
margin-left: auto;
|
||||
justify-self: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { IAppState } from "~redux/reducers";
|
||||
interface ILoginComponentProps {
|
||||
inProgress: boolean;
|
||||
error: string;
|
||||
spinner: boolean;
|
||||
login: (username: string, password: string) => void;
|
||||
}
|
||||
|
||||
@@ -31,7 +32,9 @@ export class LoginComponent extends React.PureComponent<
|
||||
|
||||
public submit() {
|
||||
const { username, password } = this.state;
|
||||
this.props.login(username, password);
|
||||
if (!this.props.inProgress) {
|
||||
this.props.login(username, password);
|
||||
}
|
||||
}
|
||||
|
||||
public updateFields(e: React.FormEvent<HTMLInputElement>) {
|
||||
@@ -63,12 +66,13 @@ export class LoginComponent extends React.PureComponent<
|
||||
/>
|
||||
</FormGroup>
|
||||
<div className="buttons">
|
||||
<div id="errors">{this.props.error}</div>
|
||||
<div id="error">{this.props.error}</div>
|
||||
<Button
|
||||
loading={this.props.spinner}
|
||||
className="submit"
|
||||
intent="primary"
|
||||
onClick={this.submit}
|
||||
active={!this.props.inProgress}
|
||||
disabled={this.props.spinner}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
@@ -81,7 +85,11 @@ export class LoginComponent extends React.PureComponent<
|
||||
}
|
||||
|
||||
function mapStateToProps(state: IAppState) {
|
||||
return { inProgress: state.auth.inProgress, error: state.auth.error };
|
||||
return {
|
||||
inProgress: state.auth.inProgress,
|
||||
error: state.auth.formError,
|
||||
spinner: state.auth.formSpinner,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
|
||||
@@ -4,6 +4,7 @@ export enum AuthTypes {
|
||||
AUTH_START = "AUTH_START",
|
||||
AUTH_SUCCESS = "AUTH_SUCCESS",
|
||||
AUTH_FAIL = "AUTH_FAIL",
|
||||
AUTH_START_FORM_SPINNER = "AUTH_START_FORM_SPINNER",
|
||||
}
|
||||
|
||||
export interface IAuthStartActionAction extends Action {
|
||||
@@ -28,6 +29,14 @@ export interface IAuthFailureActionAction extends Action {
|
||||
};
|
||||
}
|
||||
|
||||
export interface IAuthStartFormSpinnerAction extends Action {
|
||||
type: AuthTypes.AUTH_START_FORM_SPINNER;
|
||||
}
|
||||
|
||||
export function startFormSpinner(): IAuthStartFormSpinnerAction {
|
||||
return { type: AuthTypes.AUTH_START_FORM_SPINNER };
|
||||
}
|
||||
|
||||
export function authStart(
|
||||
username: string,
|
||||
password: string,
|
||||
@@ -46,4 +55,5 @@ export function authFail(error: string): IAuthFailureActionAction {
|
||||
export type AuthAction =
|
||||
| IAuthStartActionAction
|
||||
| IAuthSuccessActionAction
|
||||
| IAuthFailureActionAction;
|
||||
| IAuthFailureActionAction
|
||||
| IAuthStartFormSpinnerAction;
|
||||
|
||||
@@ -5,13 +5,15 @@ import { AuthAction, AuthTypes } from "./actions";
|
||||
export interface IAuthState {
|
||||
jwt: string | null;
|
||||
inProgress: boolean;
|
||||
error: string | null;
|
||||
formError: string | null;
|
||||
formSpinner: boolean;
|
||||
}
|
||||
|
||||
const defaultAuthState: IAuthState = {
|
||||
jwt: null,
|
||||
inProgress: false,
|
||||
error: null,
|
||||
formError: null,
|
||||
formSpinner: false,
|
||||
};
|
||||
|
||||
export const auth: Reducer<IAuthState, AuthAction> = (
|
||||
@@ -20,14 +22,19 @@ export const auth: Reducer<IAuthState, AuthAction> = (
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case AuthTypes.AUTH_START:
|
||||
return { ...state, inProgress: true };
|
||||
return { ...defaultAuthState, inProgress: true };
|
||||
break;
|
||||
case AuthTypes.AUTH_SUCCESS:
|
||||
return { ...state, jwt: action.payload.jwt, inProgress: false };
|
||||
return {
|
||||
...defaultAuthState,
|
||||
jwt: action.payload.jwt,
|
||||
};
|
||||
break;
|
||||
case AuthTypes.AUTH_FAIL:
|
||||
return { ...defaultAuthState, error: action.payload.error };
|
||||
return { ...defaultAuthState, formError: action.payload.error };
|
||||
break;
|
||||
case AuthTypes.AUTH_START_FORM_SPINNER:
|
||||
return { ...state, formSpinner: true };
|
||||
default:
|
||||
return state;
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { delay } from "redux-saga";
|
||||
import { call, put, race, takeLatest } from "redux-saga/effects";
|
||||
import { call, cancel, fork, put, race, takeLatest } from "redux-saga/effects";
|
||||
import { login } from "~redux/api/auth";
|
||||
import { setToken } from "~redux/api/utils";
|
||||
|
||||
@@ -8,16 +8,26 @@ import {
|
||||
authSuccess,
|
||||
AuthTypes,
|
||||
IAuthStartActionAction,
|
||||
startFormSpinner,
|
||||
} from "./actions";
|
||||
|
||||
function* startSpinner() {
|
||||
yield delay(300);
|
||||
yield put(startFormSpinner());
|
||||
}
|
||||
|
||||
function* authStart(action: IAuthStartActionAction) {
|
||||
const { username, password } = action.payload;
|
||||
try {
|
||||
const spinner = yield fork(startSpinner);
|
||||
|
||||
const { response, timeout } = yield race({
|
||||
response: call(login, username, password),
|
||||
timeout: call(delay, 1000),
|
||||
timeout: call(delay, 10000),
|
||||
});
|
||||
|
||||
yield cancel(spinner);
|
||||
|
||||
if (timeout) {
|
||||
yield put(authFail("Timeout"));
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user