diff --git a/src/actions/user/reducer.actions.js b/src/actions/user/reducer.actions.js new file mode 100644 index 0000000..ed4316b --- /dev/null +++ b/src/actions/user/reducer.actions.js @@ -0,0 +1,50 @@ +import { + IS_SENDING_USER_REQUEST, + SET_USER_REQUEST_ERROR, + CLEAR_USER_REQUEST_ERROR, + SET_USER_REQUEST_SUCCESS, + CLEAR_USER_REQUEST_SUCCESS, + SET_SELF_USER +} from "../../constants/user.constants"; +import { parseError } from "../common.actions"; + +export function isSendingUserRequest(sendingRequest) { + return { + type: IS_SENDING_USER_REQUEST, + data: sendingRequest + }; +} + +export function setUserRequestError(exception) { + let error = parseError(exception); + return { + type: SET_USER_REQUEST_ERROR, + data: error + }; +} + +export function clearUserRequestError() { + return { + type: CLEAR_USER_REQUEST_ERROR + }; +} + +export function setUserRequestSuccess(response) { + return { + type: SET_USER_REQUEST_SUCCESS, + data: response.detail || response + }; +} + +export function clearUserRequestSuccess() { + return { + type: CLEAR_USER_REQUEST_SUCCESS + }; +} + +export function setSelfUser(selfUser) { + return { + type: SET_SELF_USER, + data: selfUser + }; +} diff --git a/src/actions/user/saga.actions.js b/src/actions/user/saga.actions.js new file mode 100644 index 0000000..b7a0c81 --- /dev/null +++ b/src/actions/user/saga.actions.js @@ -0,0 +1,7 @@ +import { SEND_GET_SELF_USER_REQUEST } from "../../constants/user.constants"; + +export function sendGetSelfUserRequest() { + return { + type: SEND_GET_SELF_USER_REQUEST + }; +} diff --git a/src/api/user.api.js b/src/api/user.api.js new file mode 100644 index 0000000..bcd396c --- /dev/null +++ b/src/api/user.api.js @@ -0,0 +1,5 @@ +import { get } from "./baseApi"; + +export function getSelfUser() { + return get("/user/").then(resp => Promise.resolve(resp)); +} diff --git a/src/components/App.jsx b/src/components/App.jsx index 40fbc8f..2130649 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -7,6 +7,7 @@ import Register from "./Auth/Register"; import ResetPassword from "./Auth/ResetPassword"; import Settings from "./Auth/Settings"; import VerifyEmail from "./Auth/VerifyEmail"; +import Profile from "./User/Profile"; import PrivateRoute from "./Shared/PrivateRoute"; import About from "./Static/About"; import Footer from "./Static/Footer"; @@ -15,10 +16,6 @@ import NoMatch from "./Static/NoMatch"; import Navbar from "./Navbar"; class App extends Component { - componentDidMount() { - - } - render() { const footSmash = { display: "flex", @@ -39,13 +36,14 @@ 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 3d819c8..b842d79 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -41,7 +41,7 @@ class Login extends Component { password, userToken } = this.props; - if (userToken) return ; + if (userToken) return ; return ( ; return ( ( + + My Profile + Settings + Logout diff --git a/src/components/Shared/PrivateRoute.jsx b/src/components/Shared/PrivateRoute.jsx index d2487bd..1665d43 100644 --- a/src/components/Shared/PrivateRoute.jsx +++ b/src/components/Shared/PrivateRoute.jsx @@ -1,30 +1,66 @@ import PropTypes from "prop-types"; -import React from "react"; +import React, { Component } from "react"; import { connect } from "react-redux"; import { Redirect, Route } from "react-router-dom"; +import { Loader } from "semantic-ui-react"; + +import { sendGetSelfUserRequest } from "../../actions/user/saga.actions"; const propTypes = { - path: PropTypes.string.isRequired, userToken: PropTypes.string.isRequired, - component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired + selfUser: PropTypes.object.isRequired, + isSendingUserRequest: PropTypes.bool.isRequired, + path: PropTypes.string.isRequired, + component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]) + .isRequired, + dispatch: PropTypes.func.isRequired }; -const PrivateRoute = ({ userToken, component, ...rest }) => { - return ( - { - if (!!userToken) return React.createElement(component, props); - return ; - }} - /> - ); -}; +class PrivateRoute extends Component { + componentWillMount() { + const { dispatch, userToken, selfUser } = this.props; + // If the user token exists and the self user object isn't loaded, dispatch + if (userToken && Object.keys(selfUser).length === 0) { + dispatch(sendGetSelfUserRequest()); + } + } + + render() { + const { + userToken, + selfUser, + isSendingUserRequest, + component, + ...rest + } = this.props; + // If the user token exists and + // * self user object isn't loaded yet or + // * we are still sending user request + // show loading spinner + if ( + !!userToken && + (Object.keys(selfUser).length === 0 || isSendingUserRequest) + ) { + return ; + } + return ( + { + if (!!userToken) return React.createElement(component, props); + return ; + }} + /> + ); + } +} PrivateRoute.propTypes = propTypes; const mapStateToProps = state => ({ - userToken: state.auth.userToken + userToken: state.auth.userToken, + selfUser: state.user.selfUser, + isSendingUserRequest: state.user.isSendingUserRequest }); export default connect(mapStateToProps)(PrivateRoute); diff --git a/src/components/User/Profile.jsx b/src/components/User/Profile.jsx new file mode 100644 index 0000000..ba3ed08 --- /dev/null +++ b/src/components/User/Profile.jsx @@ -0,0 +1,47 @@ +import React, { Component } from "react"; +import { connect } from "react-redux"; +import { Card, Container, Header, Label, List } from "semantic-ui-react"; + +class Profile extends Component { + render() { + const { selfUser } = this.props; + return ; + } +} + +function mapStateToProps(state) { + return { ...state.user }; +} + +const ProfileView = ({ user }) => ( + +
Profile
+ + + {user.username || "No username!"} + {user.email || "No email!"} + + + {user.client && "Client"}{user.provider && "Provider"} + {!user.client && !user.provider && "User Registration Not Completed"} + + {user.first_name} {user.last_name} + {user.userinfo && + {Object.keys(user.userinfo).map(function(key) { + return ( + {user.userinfo[key]} + ) + })} + } + + + + + + +
+); + +export default connect(mapStateToProps)(Profile); diff --git a/src/constants/user.constants.js b/src/constants/user.constants.js new file mode 100644 index 0000000..54ee206 --- /dev/null +++ b/src/constants/user.constants.js @@ -0,0 +1,10 @@ +// Reducer User Action Constants +export const IS_SENDING_USER_REQUEST = "IS_SENDING_USER_REQUEST"; +export const SET_USER_REQUEST_ERROR = "SET_USER_REQUEST_ERROR"; +export const CLEAR_USER_REQUEST_ERROR = "CLEAR_USER_REQUEST_ERROR"; +export const SET_USER_REQUEST_SUCCESS = "SET_USER_REQUEST_SUCCESS"; +export const CLEAR_USER_REQUEST_SUCCESS = "CLEAR_USER_REQUEST_SUCCESS"; +export const SET_SELF_USER = "SET_SELF_USER"; + +// Saga User Action Constants +export const SEND_GET_SELF_USER_REQUEST = "SEND_GET_SELF_USER_REQUEST"; diff --git a/src/reducers/index.js b/src/reducers/index.js index c5a7d5a..0bc72b0 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,8 +1,10 @@ import { combineReducers } from "redux"; import authReducer from "./authReducer"; +import userReducer from "./userReducer"; const reducer = combineReducers({ - auth: authReducer + auth: authReducer, + user: userReducer }); export default reducer; diff --git a/src/reducers/userReducer.js b/src/reducers/userReducer.js new file mode 100644 index 0000000..23c7df5 --- /dev/null +++ b/src/reducers/userReducer.js @@ -0,0 +1,54 @@ +import { + IS_SENDING_USER_REQUEST, + SET_USER_REQUEST_ERROR, + CLEAR_USER_REQUEST_ERROR, + SET_USER_REQUEST_SUCCESS, + CLEAR_USER_REQUEST_SUCCESS, + SET_SELF_USER +} from "../constants/user.constants"; + +const initialState = { + isSendingUserRequest: false, + userRequestError: "", + userRequestSuccess: "", + selfUser: {} +}; + +function userReducer(state = initialState, action) { + switch (action.type) { + case IS_SENDING_USER_REQUEST: + return { + ...state, + isSendingUserRequest: action.data + }; + case SET_USER_REQUEST_ERROR: + return { + ...state, + userRequestError: action.data + }; + case CLEAR_USER_REQUEST_ERROR: + return { + ...state, + userRequestError: "" + }; + case SET_USER_REQUEST_SUCCESS: + return { + ...state, + userRequestSuccess: action.data + }; + case CLEAR_USER_REQUEST_SUCCESS: + return { + ...state, + userRequestSuccess: "" + }; + case SET_SELF_USER: + return { + ...state, + selfUser: action.data + }; + default: + return state; + } +} + +export default userReducer; diff --git a/src/sagas/auth.sagas.js b/src/sagas/auth.sagas.js index b27a609..ecb2d23 100644 --- a/src/sagas/auth.sagas.js +++ b/src/sagas/auth.sagas.js @@ -11,6 +11,7 @@ import { setFormPasswordConfirmation, setFormOldPassword } from "../actions/auth/reducer.actions"; +import { setSelfUser } from "../actions/user/reducer.actions"; import { registerUser, verifyEmail, @@ -156,6 +157,7 @@ export function* logoutUserFlow(request) { yield effects.put(clearAuthRequestError()); yield effects.call(logoutUserCall); yield effects.put(setSelfUserToken("")); + yield effects.put(setSelfUser({})); } export function* changePasswordFlow(request) { diff --git a/src/sagas/index.js b/src/sagas/index.js index 73f6d25..c51270f 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -17,6 +17,12 @@ import { forgotPasswordFlow, resetPasswordFlow, } from "./auth.sagas"; +import { + SEND_GET_SELF_USER_REQUEST +} from "../constants/user.constants"; +import { + getSelfUserFlow +} from "./user.sagas"; export default function* rootSaga() { yield takeLatest(SEND_REGISTER_REQUEST, registerUserFlow); @@ -26,4 +32,5 @@ export default function* rootSaga() { yield takeLatest(SEND_CHANGE_PASSWORD_REQUEST, changePasswordFlow); yield takeLatest(SEND_FORGOT_PASSWORD_REQUEST, forgotPasswordFlow); yield takeLatest(SEND_RESET_PASSWORD_REQUEST, resetPasswordFlow); + yield takeLatest(SEND_GET_SELF_USER_REQUEST, getSelfUserFlow); } diff --git a/src/sagas/user.sagas.js b/src/sagas/user.sagas.js new file mode 100644 index 0000000..e27d612 --- /dev/null +++ b/src/sagas/user.sagas.js @@ -0,0 +1,43 @@ +import { effects } from "redux-saga"; +import { setSelfUserToken } from "../actions/auth/reducer.actions"; +import { + isSendingUserRequest, + setUserRequestError, + setUserRequestSuccess, + clearUserRequestError, + clearUserRequestSuccess, + setSelfUser +} from "../actions/user/reducer.actions"; +import { getSelfUser } from "../api/user.api"; + +function* getSelfUserCall() { + yield effects.put(isSendingUserRequest(true)); + try { + const wasSuccessful = yield effects.call(getSelfUser); + yield effects.put(setUserRequestSuccess(wasSuccessful)); + yield effects.put(clearUserRequestError()); + // Check if the user exists, if yes set the user, otherwise force logout + if (wasSuccessful.results && wasSuccessful.results.length) { + yield effects.put(setSelfUser(wasSuccessful.results[0])); + } else { + yield effects.put(setSelfUserToken("")); + yield effects.put(setSelfUser({})); + } + return wasSuccessful; + } catch (exception) { + yield effects.put(setUserRequestError(exception)); + return false; + } finally { + yield effects.put(isSendingUserRequest(false)); + } +} + +export function* getSelfUserFlow(request) { + yield effects.put(clearUserRequestSuccess()); + yield effects.put(clearUserRequestError()); + const wasSuccessful = yield effects.call(getSelfUserCall); + if (!wasSuccessful) { + yield effects.put(setSelfUserToken("")); + yield effects.put(setSelfUser({})); + } +}