Finished functionally complete client registration workflow
This commit is contained in:
parent
aaad6ea3eb
commit
415aea74a3
|
@ -11,6 +11,15 @@ Generated using [create-react-app](https://github.com/facebookincubator/create-r
|
|||
|
||||
Now you can visit `localhost:3000` from your browser.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The environment variables are embedded during the build time. For more information, please refer to the [docs](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables.
|
||||
|
||||
| Environment Variable | Default Value | Description |
|
||||
| ------------------------- |:-------------------------:| -------------------:|
|
||||
| `REACT_APP_API_ENDPOINT` | `"http://localhost:8000"` | Server API endpoint |
|
||||
| `REACT_APP_REDUX_LOGGING` | `` | Set for Redux Log |
|
||||
|
||||
## Testing
|
||||
|
||||
To test the react app, call `yarn test`.
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"axios": "^0.16.2",
|
||||
"localStorage": "^1.0.3",
|
||||
"react": "^15.6.1",
|
||||
"react-dom": "^15.6.1",
|
||||
"react-redux": "^5.0.6",
|
||||
|
|
91
src/actions/auth/reducer.actions.js
Normal file
91
src/actions/auth/reducer.actions.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
import {
|
||||
IS_SENDING_AUTH_REQUEST,
|
||||
SET_AUTH_REQUEST_ERROR,
|
||||
CLEAR_AUTH_REQUEST_ERROR,
|
||||
SET_AUTH_REQUEST_SUCCESS,
|
||||
CLEAR_AUTH_REQUEST_SUCCESS,
|
||||
SET_SELF_USER,
|
||||
SET_FORM_EMAIL,
|
||||
SET_FORM_PASSWORD,
|
||||
SET_FORM_PASSWORD_CONFIRMATION
|
||||
} from "../../constants/auth.constants";
|
||||
import { parseError } from "../common.actions";
|
||||
|
||||
export function isSendingAuthRequest(sendingRequest) {
|
||||
return {
|
||||
type: IS_SENDING_AUTH_REQUEST,
|
||||
data: sendingRequest
|
||||
};
|
||||
}
|
||||
|
||||
export function setAuthRequestError(exception) {
|
||||
let rawError = parseError(exception);
|
||||
if (rawError.email) {
|
||||
rawError["Email"] = rawError.email;
|
||||
delete rawError["email"];
|
||||
}
|
||||
if (rawError.password1) {
|
||||
rawError["Password"] = rawError.password1;
|
||||
delete rawError["password1"];
|
||||
}
|
||||
if (rawError.password2) {
|
||||
rawError["Password Confirmation"] = rawError.password2;
|
||||
delete rawError["password2"];
|
||||
}
|
||||
if (rawError.non_field_errors) {
|
||||
rawError["Non Field Errors"] = rawError.non_field_errors;
|
||||
delete rawError["non_field_errors"];
|
||||
}
|
||||
|
||||
return {
|
||||
type: SET_AUTH_REQUEST_ERROR,
|
||||
data: parseError(exception)
|
||||
};
|
||||
}
|
||||
|
||||
export function clearAuthRequestError() {
|
||||
return {
|
||||
type: CLEAR_AUTH_REQUEST_ERROR
|
||||
};
|
||||
}
|
||||
|
||||
export function setAuthRequestSuccess(response) {
|
||||
return {
|
||||
type: SET_AUTH_REQUEST_SUCCESS,
|
||||
data: response.detail || response
|
||||
}
|
||||
}
|
||||
|
||||
export function clearAuthRequestSuccess() {
|
||||
return {
|
||||
type: CLEAR_AUTH_REQUEST_SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
export function setSelfUser(selfUser) {
|
||||
return {
|
||||
type: SET_SELF_USER,
|
||||
data: selfUser
|
||||
};
|
||||
}
|
||||
|
||||
export function setFormEmail(email) {
|
||||
return {
|
||||
type: SET_FORM_EMAIL,
|
||||
data: email
|
||||
};
|
||||
}
|
||||
|
||||
export function setFormPassword(password) {
|
||||
return {
|
||||
type: SET_FORM_PASSWORD,
|
||||
data: password
|
||||
};
|
||||
}
|
||||
|
||||
export function setFormPasswordConfirmation(passwordConfirmation) {
|
||||
return {
|
||||
type: SET_FORM_PASSWORD_CONFIRMATION,
|
||||
data: passwordConfirmation
|
||||
};
|
||||
}
|
10
src/actions/auth/saga.actions.js
Normal file
10
src/actions/auth/saga.actions.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import {
|
||||
SEND_REGISTER_REQUEST
|
||||
} from "../../constants/auth.constants";
|
||||
|
||||
export function sendRegisterRequest(postbody) {
|
||||
return {
|
||||
type: SEND_REGISTER_REQUEST,
|
||||
data: postbody
|
||||
}
|
||||
}
|
13
src/actions/common.actions.js
Normal file
13
src/actions/common.actions.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* Given an exception return the list of errors, the singular error, or generic error
|
||||
* @param {object|string} exception - axios returned exception
|
||||
*/
|
||||
export function parseError(exception) {
|
||||
let response = exception.response || {};
|
||||
let data = response.data || {};
|
||||
let err = "" + exception;
|
||||
if (response.status) {
|
||||
err = `${response.status} ${response.statusText}`;
|
||||
}
|
||||
return data || err
|
||||
}
|
17
src/api/auth.api.js
Normal file
17
src/api/auth.api.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { post } from "./baseApi";
|
||||
|
||||
/**
|
||||
* Function wrapping POST request for user registration
|
||||
* @param {string} email - email of user to register
|
||||
* @param {string} password1 - password of user to register
|
||||
* @param {string} password2 - server side password confirmation
|
||||
*/
|
||||
export function registerUser(email, password1, password2) {
|
||||
return post("/rest-auth/registration/", {
|
||||
email,
|
||||
password1,
|
||||
password2
|
||||
}).then(response => {
|
||||
return Promise.resolve(response);
|
||||
});
|
||||
}
|
52
src/api/baseApi.js
Normal file
52
src/api/baseApi.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import axios from "axios";
|
||||
|
||||
const API_ENDPOINT = process.env.REACT_APP_API_ENDPOINT;
|
||||
|
||||
// If testing, use localStorage polyfill, else use browser localStorage
|
||||
const localStorage = global.process && process.env.NODE_ENV === "test"
|
||||
? // eslint-disable-next-line import/no-extraneous-dependencies
|
||||
require("localStorage")
|
||||
: global.window.localStorage;
|
||||
|
||||
function headers() {
|
||||
const token = localStorage.getItem("token") || "";
|
||||
|
||||
return {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Token: ${token}`
|
||||
};
|
||||
}
|
||||
|
||||
const apiInstance = axios.create({
|
||||
baseURL: API_ENDPOINT,
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
export function get(url, params = {}) {
|
||||
return apiInstance
|
||||
.get(url, {params, headers: headers()})
|
||||
.then(response => response.data)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
export function post(url, data) {
|
||||
return apiInstance
|
||||
.post(url, data, {headers: headers()})
|
||||
.then(response => response.data)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
export function patch(url, data) {
|
||||
return apiInstance
|
||||
.patch(url, data, {headers: headers()})
|
||||
.then(response => response.data)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
export function del(url) {
|
||||
return apiInstance
|
||||
.delete(url, {headers: headers()})
|
||||
.then(response => response.data)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
|
@ -1,23 +1,32 @@
|
|||
import React, { Component } from "react";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
|
||||
import Login from "./Auth/Login";
|
||||
import Register from "./Auth/Register";
|
||||
import About from "./Static/About";
|
||||
import Footer from "./Static/Footer";
|
||||
import Home from "./Static/Home";
|
||||
import NoMatch from "./Static/NoMatch";
|
||||
import Navbar from "./Navbar";
|
||||
import Footer from "./Footer";
|
||||
import Home from "./Home";
|
||||
import About from "./About";
|
||||
import Topics from "./Topics";
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
const footSmash = {
|
||||
display: "flex",
|
||||
minHeight: "calc(100vh - 1px)",
|
||||
flexDirection: "column"
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Navbar />
|
||||
<div style={{ display: "flex", minHeight: "calc(100vh - 1px)", flexDirection: "column" }}>
|
||||
<div style={footSmash}>
|
||||
<div style={{ flex: "1" }}>
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route path="/about" component={About} />
|
||||
<Route path="/topics" component={Topics} />
|
||||
<Route path="/auth/login" component={Login} />
|
||||
<Route path="/auth/register" component={Register} />
|
||||
<Route component={NoMatch} />
|
||||
</Switch>
|
||||
</div>
|
||||
<Footer />
|
||||
|
|
16
src/components/Auth/Login.jsx
Normal file
16
src/components/Auth/Login.jsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React, { Component } from "react";
|
||||
import { Container } from "semantic-ui-react";
|
||||
|
||||
class Login extends Component {
|
||||
render() {
|
||||
return <LoginView />;
|
||||
}
|
||||
}
|
||||
|
||||
const LoginView = () => (
|
||||
<Container>
|
||||
<p>Login</p>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default Login;
|
135
src/components/Auth/Register.jsx
Normal file
135
src/components/Auth/Register.jsx
Normal file
|
@ -0,0 +1,135 @@
|
|||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Container, Form, Header, Message } from "semantic-ui-react";
|
||||
|
||||
import {
|
||||
clearAuthRequestError,
|
||||
setFormEmail,
|
||||
setFormPassword,
|
||||
setFormPasswordConfirmation
|
||||
} from "../../actions/auth/reducer.actions";
|
||||
import { sendRegisterRequest } from "../../actions/auth/saga.actions";
|
||||
import Error from "../Shared/Error";
|
||||
|
||||
class Register extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.props.dispatch(clearAuthRequestError());
|
||||
}
|
||||
|
||||
changeEmail = event => {
|
||||
this.props.dispatch(setFormEmail(event.target.value));
|
||||
};
|
||||
|
||||
changePassword = event => {
|
||||
this.props.dispatch(setFormPassword(event.target.value));
|
||||
};
|
||||
|
||||
changePasswordConfirmation = event => {
|
||||
this.props.dispatch(setFormPasswordConfirmation(event.target.value));
|
||||
};
|
||||
|
||||
onSubmitRegistration = event => {
|
||||
event.preventDefault();
|
||||
const { dispatch, email, password, passwordConfirmation } = this.props;
|
||||
dispatch(
|
||||
sendRegisterRequest({
|
||||
email,
|
||||
password1: password,
|
||||
password2: passwordConfirmation
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
isSendingAuthRequest,
|
||||
authRequestError,
|
||||
authRequestSuccess,
|
||||
email,
|
||||
password,
|
||||
passwordConfirmation
|
||||
} = this.props;
|
||||
return (
|
||||
<RegisterView
|
||||
isSendingAuthRequest={isSendingAuthRequest}
|
||||
authRequestError={authRequestError}
|
||||
authRequestSuccess={authRequestSuccess}
|
||||
email={email}
|
||||
password={password}
|
||||
passwordConfirmation={passwordConfirmation}
|
||||
changeEmail={this.changeEmail}
|
||||
changePassword={this.changePassword}
|
||||
changePasswordConfirmation={this.changePasswordConfirmation}
|
||||
onSubmitRegistration={this.onSubmitRegistration}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return { ...state.auth };
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional view component for Register logic
|
||||
*/
|
||||
const RegisterView = ({
|
||||
isSendingAuthRequest,
|
||||
authRequestError,
|
||||
authRequestSuccess,
|
||||
email,
|
||||
password,
|
||||
passwordConfirmation,
|
||||
changeEmail,
|
||||
changePassword,
|
||||
changePasswordConfirmation,
|
||||
onSubmitRegistration
|
||||
}) => (
|
||||
<Container>
|
||||
<Header>Register</Header>
|
||||
<Form
|
||||
loading={isSendingAuthRequest}
|
||||
onSubmit={onSubmitRegistration}
|
||||
error={!!authRequestError}
|
||||
success={!!authRequestSuccess}
|
||||
>
|
||||
<Form.Field>
|
||||
<label>Email</label>
|
||||
<input
|
||||
placeholder="bob@gmail.com"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={changeEmail}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Password</label>
|
||||
<input
|
||||
placeholder="••••••••"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={changePassword}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Password Confirmation</label>
|
||||
<input
|
||||
placeholder="••••••••"
|
||||
type="password"
|
||||
value={passwordConfirmation}
|
||||
onChange={changePasswordConfirmation}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Error header="Register failed!" error={authRequestError} />
|
||||
<Message
|
||||
success
|
||||
header="Registration Sent"
|
||||
content="A confirmation email has been sent to confirm your registration."
|
||||
/>
|
||||
<Form.Button>Submit</Form.Button>
|
||||
</Form>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default connect(mapStateToProps)(Register);
|
|
@ -12,9 +12,14 @@ class Navbar extends Component {
|
|||
<Menu.Item as={Link} to="/about">
|
||||
About
|
||||
</Menu.Item>
|
||||
<Menu.Item as={Link} to="/topics">
|
||||
Topics
|
||||
<Menu.Menu position="right">
|
||||
<Menu.Item as={Link} to="/auth/login">
|
||||
Login
|
||||
</Menu.Item>
|
||||
<Menu.Item as={Link} to="/auth/register">
|
||||
Register
|
||||
</Menu.Item>
|
||||
</Menu.Menu>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
|
41
src/components/Shared/Error.jsx
Normal file
41
src/components/Shared/Error.jsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
import { Message } from "semantic-ui-react";
|
||||
|
||||
const propTypes = {
|
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
header: PropTypes.string
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
error: "",
|
||||
header: ""
|
||||
};
|
||||
|
||||
const Error = ({ error, header }) => {
|
||||
if (typeof error === "string") {
|
||||
const hasError = !!error;
|
||||
if (hasError) {
|
||||
return <Message error={hasError} header={header} content={error} />;
|
||||
}
|
||||
} else if (typeof error === "object" && Object.keys(error).length > 0) {
|
||||
const hasError = !!Object.keys(error);
|
||||
if (hasError) {
|
||||
return (
|
||||
<Message
|
||||
error={hasError}
|
||||
header={header}
|
||||
list={Object.keys(error).map(p => (
|
||||
<Message.Item key={p}> {p}: {error[p]}</Message.Item>
|
||||
))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Error.propTypes = propTypes;
|
||||
Error.defaultProps = defaultProps;
|
||||
|
||||
export default Error;
|
13
src/components/Static/NoMatch.jsx
Normal file
13
src/components/Static/NoMatch.jsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Container } from "semantic-ui-react";
|
||||
|
||||
const NoMatch = ({ location }) => (
|
||||
<Container>
|
||||
<h3>Page not found!</h3>
|
||||
<p>No match found for <code>{location.pathname}</code></p>
|
||||
<p><Link to="/">Go to the home page →</Link></p>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default NoMatch;
|
|
@ -1,41 +0,0 @@
|
|||
import React from "react";
|
||||
import { Route, Link } from "react-router-dom";
|
||||
import { Container } from "semantic-ui-react";
|
||||
|
||||
const Topics = ({ match }) => (
|
||||
<Container>
|
||||
<h2>Topics</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<Link to={`${match.url}/rendering`}>
|
||||
Rendering with React
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={`${match.url}/components`}>
|
||||
Components
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={`${match.url}/props-v-state`}>
|
||||
Props v. State
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<Route path={`${match.url}/:topicId`} component={Topic} />
|
||||
<Route
|
||||
exact
|
||||
path={match.url}
|
||||
render={() => <h3>Please select a topic.</h3>}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
||||
const Topic = ({ match }) => (
|
||||
<div>
|
||||
<h3>{match.params.topicId}</h3>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Topics;
|
13
src/constants/auth.constants.js
Normal file
13
src/constants/auth.constants.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Reducer Auth Action Constants
|
||||
export const IS_SENDING_AUTH_REQUEST = "IS_SENDING_AUTH_REQUEST";
|
||||
export const SET_AUTH_REQUEST_ERROR = "SET_AUTH_REQUEST_ERROR";
|
||||
export const CLEAR_AUTH_REQUEST_ERROR = "CLEAR_AUTH_REQUEST_ERROR";
|
||||
export const SET_AUTH_REQUEST_SUCCESS = "SET_AUTH_REQUEST_SUCCESS";
|
||||
export const CLEAR_AUTH_REQUEST_SUCCESS = "CLEAR_AUTH_REQUEST_SUCCESS";
|
||||
export const SET_SELF_USER = "SET_SELF_USER";
|
||||
export const SET_FORM_EMAIL = "SET_FORM_EMAIL";
|
||||
export const SET_FORM_PASSWORD = "SET_FORM_PASSWORD";
|
||||
export const SET_FORM_PASSWORD_CONFIRMATION = "SET_FORM_PASSWORD_CONFIRMATION";
|
||||
|
||||
// Saga Auth Action Constants
|
||||
export const SEND_REGISTER_REQUEST = "SEND_REGISTER_REQUEST";
|
15
src/index.js
15
src/index.js
|
@ -1,26 +1,15 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { createStore, applyMiddleware } from "redux";
|
||||
import createSagaMiddleware from "redux-saga";
|
||||
import { Provider } from "react-redux";
|
||||
import { createLogger } from "redux-logger";
|
||||
|
||||
import rootReducer from "./reducers";
|
||||
import rootSaga from "./sagas";
|
||||
import configureStore from "./store";
|
||||
import App from "./components/App";
|
||||
import { unregister } from "./registerServiceWorker";
|
||||
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
const debugLogger = createLogger();
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
applyMiddleware(debugLogger, sagaMiddleware)
|
||||
);
|
||||
const store = configureStore();
|
||||
const supportsHistory = "pushState" in window.history;
|
||||
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
|
||||
ReactDOM.render(
|
||||
|
|
|
@ -1,7 +1,72 @@
|
|||
const initialState = {};
|
||||
import {
|
||||
IS_SENDING_AUTH_REQUEST,
|
||||
SET_AUTH_REQUEST_ERROR,
|
||||
CLEAR_AUTH_REQUEST_ERROR,
|
||||
SET_AUTH_REQUEST_SUCCESS,
|
||||
CLEAR_AUTH_REQUEST_SUCCESS,
|
||||
SET_SELF_USER,
|
||||
SET_FORM_EMAIL,
|
||||
SET_FORM_PASSWORD,
|
||||
SET_FORM_PASSWORD_CONFIRMATION
|
||||
} from "../constants/auth.constants";
|
||||
|
||||
const initialState = {
|
||||
isSendingAuthRequest: false,
|
||||
authRequestError: "",
|
||||
authRequestSuccess: "",
|
||||
currentUser: {},
|
||||
email: "",
|
||||
password: "",
|
||||
passwordConfirmation: ""
|
||||
};
|
||||
|
||||
function authReducer(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case IS_SENDING_AUTH_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
isSendingAuthRequest: action.data
|
||||
};
|
||||
case SET_AUTH_REQUEST_ERROR:
|
||||
return {
|
||||
...state,
|
||||
authRequestError: action.data
|
||||
};
|
||||
case CLEAR_AUTH_REQUEST_ERROR:
|
||||
return {
|
||||
...state,
|
||||
authRequestError: ""
|
||||
};
|
||||
case SET_AUTH_REQUEST_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
authRequestSuccess: action.data
|
||||
};
|
||||
case CLEAR_AUTH_REQUEST_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
authRequestSuccess: ""
|
||||
};
|
||||
case SET_SELF_USER:
|
||||
return {
|
||||
...state,
|
||||
currentUser: action.data
|
||||
};
|
||||
case SET_FORM_EMAIL:
|
||||
return {
|
||||
...state,
|
||||
email: action.data
|
||||
};
|
||||
case SET_FORM_PASSWORD:
|
||||
return {
|
||||
...state,
|
||||
password: action.data
|
||||
};
|
||||
case SET_FORM_PASSWORD_CONFIRMATION:
|
||||
return {
|
||||
...state,
|
||||
passwordConfirmation: action.data
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
39
src/sagas/auth.sagas.js
Normal file
39
src/sagas/auth.sagas.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { effects } from "redux-saga";
|
||||
import {
|
||||
isSendingAuthRequest,
|
||||
setAuthRequestError,
|
||||
setAuthRequestSuccess,
|
||||
clearAuthRequestError,
|
||||
setFormEmail,
|
||||
setFormPassword,
|
||||
setFormPasswordConfirmation
|
||||
} from "../actions/auth/reducer.actions";
|
||||
import { registerUser } from "../api/auth.api";
|
||||
|
||||
/**
|
||||
* Saga for registering a new user.
|
||||
* @param {*} postBody
|
||||
*/
|
||||
function* registerUserCall(postBody) {
|
||||
yield effects.put(isSendingAuthRequest(true));
|
||||
const { email, password1, password2 } = postBody;
|
||||
try {
|
||||
return yield effects.call(registerUser, email, password1, password2);
|
||||
} catch (exception) {
|
||||
yield effects.put(setAuthRequestError(exception));
|
||||
return false;
|
||||
} finally {
|
||||
yield effects.put(isSendingAuthRequest(false));
|
||||
}
|
||||
}
|
||||
|
||||
export function* registerUserFlow(request) {
|
||||
const wasSucessful = yield effects.call(registerUserCall, request.data);
|
||||
if (wasSucessful) {
|
||||
yield effects.put(setAuthRequestSuccess(wasSucessful));
|
||||
yield effects.put(clearAuthRequestError());
|
||||
yield effects.put(setFormEmail(""));
|
||||
yield effects.put(setFormPassword(""));
|
||||
yield effects.put(setFormPasswordConfirmation(""));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
import { takeLatest } from "redux-saga/effects";
|
||||
import { SEND_REGISTER_REQUEST } from "../constants/auth.constants";
|
||||
import { registerUserFlow } from "./auth.sagas";
|
||||
|
||||
export default function* rootSaga() {
|
||||
|
||||
yield takeLatest(SEND_REGISTER_REQUEST, registerUserFlow);
|
||||
}
|
||||
|
|
22
src/store/index.js
Normal file
22
src/store/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { createStore, applyMiddleware, compose } from "redux";
|
||||
import createSagaMiddleware from "redux-saga";
|
||||
import { createLogger } from "redux-logger";
|
||||
import rootReducer from "../reducers";
|
||||
import rootSaga from "../sagas";
|
||||
|
||||
export default function configureStore(initialState = {}) {
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
const middlewares = [sagaMiddleware];
|
||||
if (process.env.NODE_ENV === "development" && process.env.REACT_APP_REDUX_LOGGING) {
|
||||
middlewares.push(createLogger());
|
||||
}
|
||||
|
||||
const enhancers = [applyMiddleware(...middlewares)];
|
||||
const store = createStore(rootReducer, initialState, compose(...enhancers));
|
||||
|
||||
// Extensions
|
||||
store.asyncReducers = {}; // Async reducer registry
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
return store;
|
||||
}
|
19
yarn.lock
19
yarn.lock
|
@ -316,6 +316,13 @@ aws4@^1.2.1:
|
|||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
|
||||
|
||||
axios@^0.16.2:
|
||||
version "0.16.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d"
|
||||
dependencies:
|
||||
follow-redirects "^1.2.3"
|
||||
is-buffer "^1.1.5"
|
||||
|
||||
axobject-query@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-0.1.0.tgz#62f59dbc59c9f9242759ca349960e7a2fe3c36c0"
|
||||
|
@ -1757,7 +1764,7 @@ date-now@^0.1.4:
|
|||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||
|
||||
debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.6.0, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8:
|
||||
debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.4.5, debug@^2.6.0, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8:
|
||||
version "2.6.8"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
|
||||
dependencies:
|
||||
|
@ -2592,6 +2599,12 @@ flatten@^1.0.2:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
||||
|
||||
follow-redirects@^1.2.3:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.4.tgz#355e8f4d16876b43f577b0d5ce2668b9723214ea"
|
||||
dependencies:
|
||||
debug "^2.4.5"
|
||||
|
||||
for-in@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
|
@ -3881,6 +3894,10 @@ loader-utils@^1.0.2, loader-utils@^1.1.0:
|
|||
emojis-list "^2.0.0"
|
||||
json5 "^0.5.0"
|
||||
|
||||
localStorage@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/localStorage/-/localStorage-1.0.3.tgz#e6b89a57bb760a156a38cc87e0f2550f6ed413d8"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
|
|
Loading…
Reference in New Issue
Block a user