diff --git a/src/actions/auth/reducer.actions.js b/src/actions/auth/reducer.actions.js
index c5f9f9a..41a4f49 100644
--- a/src/actions/auth/reducer.actions.js
+++ b/src/actions/auth/reducer.actions.js
@@ -8,7 +8,7 @@ import {
CLEAR_EMAIL_VERIFICATION_SUCCESS,
SET_EMAIL_VERIFICATION_ERROR,
CLEAR_EMAIL_VERIFICATION_ERROR,
- SET_SELF_USER,
+ SET_SELF_USER_TOKEN,
SET_FORM_EMAIL,
SET_FORM_PASSWORD,
SET_FORM_PASSWORD_CONFIRMATION,
@@ -103,9 +103,9 @@ export function clearEmailVerificationSuccess() {
};
}
-export function setSelfUser(selfUser) {
+export function setSelfUserToken(selfUser) {
return {
- type: SET_SELF_USER,
+ type: SET_SELF_USER_TOKEN,
data: selfUser
};
}
diff --git a/src/actions/auth/saga.actions.js b/src/actions/auth/saga.actions.js
index 523877a..b33eb3e 100644
--- a/src/actions/auth/saga.actions.js
+++ b/src/actions/auth/saga.actions.js
@@ -1,18 +1,33 @@
import {
SEND_REGISTER_REQUEST,
- SEND_EMAIL_VERIFICATION_REQUEST
+ SEND_EMAIL_VERIFICATION_REQUEST,
+ SEND_LOGIN_REQUEST,
+ SEND_LOGOUT_REQUEST
} from "../../constants/auth.constants";
-export function sendRegisterRequest(postbody) {
+export function sendRegisterRequest(postBody) {
return {
type: SEND_REGISTER_REQUEST,
- data: postbody
+ data: postBody
};
}
-export function sendEmailVerificationRequest(postbody) {
+export function sendEmailVerificationRequest(postBody) {
return {
type: SEND_EMAIL_VERIFICATION_REQUEST,
- data: postbody
+ data: postBody
};
}
+
+export function sendLoginRequest(postBody) {
+ return {
+ type: SEND_LOGIN_REQUEST,
+ data: postBody
+ };
+}
+
+export function sendLogoutRequest() {
+ return {
+ type: SEND_LOGOUT_REQUEST
+ }
+}
diff --git a/src/api/auth.api.js b/src/api/auth.api.js
index 769e935..0b9e349 100644
--- a/src/api/auth.api.js
+++ b/src/api/auth.api.js
@@ -11,15 +11,33 @@ export function registerUser(email, password1, password2) {
email,
password1,
password2
- }).then(response => {
- return Promise.resolve(response);
- });
+ }).then(resp => Promise.resolve(resp));
}
+/**
+ * Function wrapping POST request for email validation
+ * @param {string} emailKey - key for email validation
+ */
export function verifyEmail(emailKey) {
return post("/rest-auth/registration/verify-email/", {
key: emailKey
- }).then(response => {
- return Promise.resolve(response);
- });
+ }).then(resp => Promise.resolve(resp));
+}
+
+/**
+ * Function wrapping POST request for user login
+ * @param {string} email - email of user to login
+ * @param {string} password - password of user to login
+ */
+export function loginUser(email, password) {
+ return post("/rest-auth/login/", { email, password }).then(resp =>
+ Promise.resolve(resp)
+ );
+}
+
+/**
+ * Function wrapping POST request for user logout
+ */
+export function logoutUser() {
+ return post("/rest-auth/logout/").then(resp => Promise.resolve(resp));
}
diff --git a/src/api/baseApi.js b/src/api/baseApi.js
index 1fe3634..a614810 100644
--- a/src/api/baseApi.js
+++ b/src/api/baseApi.js
@@ -9,7 +9,7 @@ const localStorage = global.process && process.env.NODE_ENV === "test"
: global.window.localStorage;
function headers() {
- const token = localStorage.getItem("token") || "";
+ const token = localStorage.getItem("userToken") || "";
return {
Accept: "application/json",
diff --git a/src/components/App.jsx b/src/components/App.jsx
index 30a3ebf..e444629 100644
--- a/src/components/App.jsx
+++ b/src/components/App.jsx
@@ -3,7 +3,9 @@ import { Route, Switch } from "react-router-dom";
import Login from "./Auth/Login";
import Register from "./Auth/Register";
+import Settings from "./Auth/Settings";
import VerifyEmail from "./Auth/VerifyEmail";
+import PrivateRoute from "./Shared/PrivateRoute";
import About from "./Static/About";
import Footer from "./Static/Footer";
import Home from "./Static/Home";
@@ -31,6 +33,7 @@ class App extends Component {
path="/auth/verify-email/:emailKey"
component={VerifyEmail}
/>
+
diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx
index ddc9ebd..793ecc1 100644
--- a/src/components/Auth/Login.jsx
+++ b/src/components/Auth/Login.jsx
@@ -1,16 +1,111 @@
import React, { Component } from "react";
-import { Container } from "semantic-ui-react";
+import { connect } from "react-redux";
+import { Redirect } from "react-router-dom";
+import { Container, Form, Header, Message } from "semantic-ui-react";
+
+import {
+ clearAuthRequestError,
+ clearAuthRequestSuccess,
+ setFormEmail,
+ setFormPassword
+} from "../../actions/auth/reducer.actions";
+import { sendLoginRequest } from "../../actions/auth/saga.actions";
+import Error from "../Shared/Error";
class Login extends Component {
+ constructor(props) {
+ super(props);
+ this.props.dispatch(clearAuthRequestError());
+ this.props.dispatch(clearAuthRequestSuccess());
+ }
+
+ changeEmail = event => {
+ this.props.dispatch(setFormEmail(event.target.value));
+ };
+
+ changePassword = event => {
+ this.props.dispatch(setFormPassword(event.target.value));
+ };
+
+ onSubmitLogin = event => {
+ event.preventDefault();
+ const { dispatch, email, password } = this.props;
+ dispatch(sendLoginRequest({ email, password }));
+ };
+
render() {
- return ;
+ const {
+ isSendingAuthRequest,
+ authRequestError,
+ authRequestSuccess,
+ email,
+ password,
+ userToken
+ } = this.props;
+ if (userToken) return ;
+ return (
+
+ );
}
}
-const LoginView = () => (
+function mapStateToProps(state) {
+ return { ...state.auth };
+}
+
+const LoginView = ({
+ isSendingAuthRequest,
+ authRequestError,
+ authRequestSuccess,
+ email,
+ password,
+ changeEmail,
+ changePassword,
+ onSubmitLogin
+}) => (
- Login
+
+
+
+
+
+
+
+
+
+
+
+ Login successful!
+ Redirecting you now...
+
+ Login
+
);
-export default Login;
+export default connect(mapStateToProps)(Login);
diff --git a/src/components/Auth/Register.jsx b/src/components/Auth/Register.jsx
index 3ac7730..7c4788e 100644
--- a/src/components/Auth/Register.jsx
+++ b/src/components/Auth/Register.jsx
@@ -1,5 +1,6 @@
import React, { Component } from "react";
import { connect } from "react-redux";
+import { Redirect } from "react-router-dom";
import { Container, Form, Header, Message } from "semantic-ui-react";
import {
@@ -50,8 +51,10 @@ class Register extends Component {
authRequestSuccess,
email,
password,
- passwordConfirmation
+ passwordConfirmation,
+ userToken
} = this.props;
+ if (userToken) return ;
return (
;
+ }
+}
+
+function mapStateToProps(state) {
+ return { ...state.auth };
+}
+
+const SettingsView = () => (
+
+ Settings
+ todo, change password
+
+);
+
+export default connect(mapStateToProps)(Settings);
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx
index dd168e9..018e8dc 100644
--- a/src/components/Navbar.jsx
+++ b/src/components/Navbar.jsx
@@ -1,28 +1,61 @@
import React, { Component } from "react";
+import { connect } from "react-redux";
import { Link } from "react-router-dom";
-import { Menu } from "semantic-ui-react";
+import { Dropdown, Menu } from "semantic-ui-react";
+
+import { sendLogoutRequest } from "../actions/auth/saga.actions";
class Navbar extends Component {
+ dispatchLogoutRequest = () => {
+ this.props.dispatch(sendLogoutRequest());
+ };
+
render() {
+ const { userToken } = this.props;
return (
-
+
);
}
}
-export default Navbar;
+function mapStateToProps(state) {
+ return { ...state.auth };
+}
+
+const NavbarView = ({ isAuthenticated, dispatchLogoutRequest }) => (
+
+);
+
+export default connect(mapStateToProps)(Navbar);
diff --git a/src/components/Shared/PrivateRoute.jsx b/src/components/Shared/PrivateRoute.jsx
new file mode 100644
index 0000000..e7b9e62
--- /dev/null
+++ b/src/components/Shared/PrivateRoute.jsx
@@ -0,0 +1,29 @@
+import PropTypes from "prop-types";
+import React from "react";
+import { connect } from "react-redux";
+import { Redirect, Route } from "react-router-dom";
+
+const propTypes = {
+ userToken: PropTypes.string,
+ component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired
+};
+
+const PrivateRoute = ({ userToken, component, ...rest }) => {
+ return (
+ {
+ if (!!userToken) return React.createElement(component, props);
+ return ;
+ }}
+ />
+ );
+};
+
+PrivateRoute.propTypes = propTypes;
+
+const mapStateToProps = state => ({
+ userToken: state.auth.userToken
+});
+
+export default connect(mapStateToProps)(PrivateRoute);
diff --git a/src/constants/auth.constants.js b/src/constants/auth.constants.js
index c40e6d2..1861b04 100644
--- a/src/constants/auth.constants.js
+++ b/src/constants/auth.constants.js
@@ -9,7 +9,7 @@ export const CLEAR_EMAIL_VERIFICATION_SUCCESS =
"CLEAR_EMAIL_VERIFICATION_SUCCESS";
export const SET_EMAIL_VERIFICATION_ERROR = "SET_EMAIL_VERIFICATION_ERROR";
export const CLEAR_EMAIL_VERIFICATION_ERROR = "CLEAR_EMAIL_VERIFICATION_ERROR";
-export const SET_SELF_USER = "SET_SELF_USER";
+export const SET_SELF_USER_TOKEN = "SET_SELF_USER_TOKEN";
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";
@@ -19,3 +19,5 @@ export const SET_FORM_EMAIL_VERIFICATION = "SET_FORM_EMAIL_VERIFICATION";
export const SEND_REGISTER_REQUEST = "SEND_REGISTER_REQUEST";
export const SEND_EMAIL_VERIFICATION_REQUEST =
"SEND_EMAIL_VERIFICATION_REQUEST";
+export const SEND_LOGIN_REQUEST = "SEND_LOGIN_REQUEST";
+export const SEND_LOGOUT_REQUEST = "SEND_LOGOUT_REQUEST";
diff --git a/src/reducers/authReducer.js b/src/reducers/authReducer.js
index 6bce905..9772bed 100644
--- a/src/reducers/authReducer.js
+++ b/src/reducers/authReducer.js
@@ -8,20 +8,32 @@ import {
CLEAR_EMAIL_VERIFICATION_ERROR,
SET_EMAIL_VERIFICATION_SUCCESS,
CLEAR_EMAIL_VERIFICATION_SUCCESS,
- SET_SELF_USER,
+ SET_SELF_USER_TOKEN,
SET_FORM_EMAIL,
SET_FORM_PASSWORD,
SET_FORM_PASSWORD_CONFIRMATION,
SET_FORM_EMAIL_VERIFICATION
} from "../constants/auth.constants";
+/**
+ * Set the user's auth token, and return the value
+ * @param {string} newToken
+ */
+function me(newToken) {
+ if (typeof newToken === "string") {
+ localStorage.setItem("userToken", newToken);
+ }
+ const userToken = localStorage.getItem("userToken");
+ return userToken ? userToken : "";
+}
+
const initialState = {
isSendingAuthRequest: false,
authRequestError: "",
authRequestSuccess: "",
emailVerificationRequestError: "",
emailVerificationRequestSuccess: "",
- currentUser: {},
+ userToken: me(null),
email: "",
password: "",
passwordConfirmation: "",
@@ -75,10 +87,10 @@ function authReducer(state = initialState, action) {
...state,
emailVerificationRequestSuccess: ""
};
- case SET_SELF_USER:
+ case SET_SELF_USER_TOKEN:
return {
...state,
- currentUser: action.data
+ userToken: me(action.data)
};
case SET_FORM_EMAIL:
return {
diff --git a/src/sagas/auth.sagas.js b/src/sagas/auth.sagas.js
index e730650..04ac07c 100644
--- a/src/sagas/auth.sagas.js
+++ b/src/sagas/auth.sagas.js
@@ -12,9 +12,15 @@ import {
setFormEmail,
setFormPassword,
setFormPasswordConfirmation,
- setFormEmailVerification
+ setFormEmailVerification,
+ setSelfUserToken
} from "../actions/auth/reducer.actions";
-import { registerUser, verifyEmail } from "../api/auth.api";
+import {
+ registerUser,
+ verifyEmail,
+ loginUser,
+ logoutUser
+} from "../api/auth.api";
function* registerUserCall(postBody) {
yield effects.put(isSendingAuthRequest(true));
@@ -42,12 +48,29 @@ function* verifyEmailCall(postBody) {
}
}
+function* loginUserCall(postBody) {
+ yield effects.put(isSendingAuthRequest(true));
+ const { email, password } = postBody;
+ try {
+ return yield effects.call(loginUser, email, password);
+ } catch (exception) {
+ yield effects.put(setAuthRequestError(exception));
+ return false;
+ } finally {
+ yield effects.put(isSendingAuthRequest(false));
+ }
+}
+
+function* logoutUserCall() {
+ yield effects.call(logoutUser);
+}
+
export function* registerUserFlow(request) {
yield effects.put(clearAuthRequestSuccess());
yield effects.put(clearAuthRequestError());
- const wasSucessful = yield effects.call(registerUserCall, request.data);
- if (wasSucessful) {
- yield effects.put(setAuthRequestSuccess(wasSucessful));
+ const wasSuccessful = yield effects.call(registerUserCall, request.data);
+ if (wasSuccessful) {
+ yield effects.put(setAuthRequestSuccess(wasSuccessful));
yield effects.put(clearAuthRequestError());
yield effects.put(setFormEmail(""));
yield effects.put(setFormPassword(""));
@@ -58,10 +81,31 @@ export function* registerUserFlow(request) {
export function* verifyEmailFlow(request) {
yield effects.put(clearEmailVerificationSuccess());
yield effects.put(clearEmailVerificationError());
- const wasSucessful = yield effects.call(verifyEmailCall, request.data);
- if (wasSucessful) {
- yield effects.put(setEmailVerificationSuccess(wasSucessful));
+ const wasSuccessful = yield effects.call(verifyEmailCall, request.data);
+ if (wasSuccessful) {
+ yield effects.put(setEmailVerificationSuccess(wasSuccessful));
yield effects.put(clearEmailVerificationError());
yield effects.put(setFormEmailVerification(""));
}
}
+
+export function* loginUserFlow(request) {
+ yield effects.put(clearAuthRequestSuccess());
+ yield effects.put(clearAuthRequestError());
+ const wasSuccessful = yield effects.call(loginUserCall, request.data);
+ if (wasSuccessful) {
+ yield effects.put(setSelfUserToken(wasSuccessful.key));
+ yield effects.put(setAuthRequestSuccess(wasSuccessful));
+ yield effects.put(clearAuthRequestError());
+ yield effects.put(setFormEmail(""));
+ yield effects.put(setFormPassword(""));
+ yield effects.put(setFormPasswordConfirmation(""));
+ }
+}
+
+export function* logoutUserFlow(request) {
+ yield effects.put(clearAuthRequestSuccess());
+ yield effects.put(clearAuthRequestError());
+ yield effects.call(logoutUserCall);
+ yield effects.put(setSelfUserToken(""));
+}
diff --git a/src/sagas/index.js b/src/sagas/index.js
index 6d852db..82c31b0 100644
--- a/src/sagas/index.js
+++ b/src/sagas/index.js
@@ -1,8 +1,20 @@
import { takeLatest } from "redux-saga/effects";
-import { SEND_REGISTER_REQUEST, SEND_EMAIL_VERIFICATION_REQUEST } from "../constants/auth.constants";
-import { registerUserFlow, verifyEmailFlow } from "./auth.sagas";
+import {
+ SEND_REGISTER_REQUEST,
+ SEND_EMAIL_VERIFICATION_REQUEST,
+ SEND_LOGIN_REQUEST,
+ SEND_LOGOUT_REQUEST
+} from "../constants/auth.constants";
+import {
+ registerUserFlow,
+ verifyEmailFlow,
+ loginUserFlow,
+ logoutUserFlow
+} from "./auth.sagas";
export default function* rootSaga() {
yield takeLatest(SEND_REGISTER_REQUEST, registerUserFlow);
yield takeLatest(SEND_EMAIL_VERIFICATION_REQUEST, verifyEmailFlow);
+ yield takeLatest(SEND_LOGIN_REQUEST, loginUserFlow);
+ yield takeLatest(SEND_LOGOUT_REQUEST, logoutUserFlow);
}