From b57514e9d2113390ab5cf259b6e3485809f4a262 Mon Sep 17 00:00:00 2001 From: Mike Stone Date: Mon, 17 Oct 2016 09:27:22 -0400 Subject: [PATCH] Redirect users to correct location after login (#316) --- .../AuthenticatedRoutes.jsx | 9 ++++-- .../AuthenticatedRoutes.tests.jsx | 12 ++++++++ frontend/pages/LoginPage/LoginPage.jsx | 11 ++++++-- .../redux/nodes/redirectLocation/actions.js | 19 +++++++++++++ .../redux/nodes/redirectLocation/reducer.js | 18 ++++++++++++ .../nodes/redirectLocation/reducer.tests.js | 28 +++++++++++++++++++ frontend/redux/reducers.js | 2 ++ 7 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 frontend/redux/nodes/redirectLocation/actions.js create mode 100644 frontend/redux/nodes/redirectLocation/reducer.js create mode 100644 frontend/redux/nodes/redirectLocation/reducer.tests.js diff --git a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx index c7e2e92e71..3f59f7b478 100644 --- a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx +++ b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.jsx @@ -2,13 +2,16 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { isEqual } from 'lodash'; import { push } from 'react-router-redux'; + import paths from '../../router/paths'; +import { setRedirectLocation } from '../../redux/nodes/redirectLocation/actions'; export class AuthenticatedRoutes extends Component { static propTypes = { children: PropTypes.element, dispatch: PropTypes.func, loading: PropTypes.bool.isRequired, + locationBeforeTransitions: PropTypes.object, user: PropTypes.object, }; @@ -35,9 +38,10 @@ export class AuthenticatedRoutes extends Component { } redirectToLogin = () => { - const { dispatch } = this.props; + const { dispatch, locationBeforeTransitions } = this.props; const { LOGIN } = paths; + dispatch(setRedirectLocation(locationBeforeTransitions)); return dispatch(push(LOGIN)); } @@ -63,8 +67,9 @@ export class AuthenticatedRoutes extends Component { const mapStateToProps = (state) => { const { loading, user } = state.auth; + const { locationBeforeTransitions } = state.routing; - return { loading, user }; + return { loading, locationBeforeTransitions, user }; }; export default connect(mapStateToProps)(AuthenticatedRoutes); diff --git a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx index 9f72ce0e9e..a95152c641 100644 --- a/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx +++ b/frontend/components/AuthenticatedRoutes/AuthenticatedRoutes.tests.jsx @@ -30,6 +30,9 @@ describe('AuthenticatedRoutes - component', () => { force_password_reset: false, }, }, + routing: { + locationBeforeTransitions: {}, + }, }; const storeWithUserRequiringPwReset = { auth: { @@ -40,18 +43,27 @@ describe('AuthenticatedRoutes - component', () => { force_password_reset: true, }, }, + routing: { + locationBeforeTransitions: {}, + }, }; const storeWithoutUser = { auth: { loading: false, user: null, }, + routing: { + locationBeforeTransitions: {}, + }, }; const storeLoadingUser = { auth: { loading: true, user: null, }, + routing: { + locationBeforeTransitions: {}, + }, }; it('renders if there is a user in state', () => { diff --git a/frontend/pages/LoginPage/LoginPage.jsx b/frontend/pages/LoginPage/LoginPage.jsx index 622f3f25e5..84b5e4f79e 100644 --- a/frontend/pages/LoginPage/LoginPage.jsx +++ b/frontend/pages/LoginPage/LoginPage.jsx @@ -3,7 +3,9 @@ import { connect } from 'react-redux'; import { includes } from 'lodash'; import { push } from 'react-router-redux'; import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; + import { clearAuthErrors, loginUser } from '../../redux/nodes/auth/actions'; +import { clearRedirectLocation } from '../../redux/nodes/redirectLocation/actions'; import debounce from '../../utilities/debounce'; import LoginForm from '../../components/forms/LoginForm'; import LoginSuccessfulPage from '../LoginSuccessfulPage'; @@ -18,6 +20,7 @@ export class LoginPage extends Component { error: PropTypes.string, loading: PropTypes.bool, pathname: PropTypes.string, + redirectLocation: PropTypes.object, user: PropTypes.object, }; @@ -46,14 +49,16 @@ export class LoginPage extends Component { }; onSubmit = debounce((formData) => { - const { dispatch } = this.props; + const { dispatch, redirectLocation } = this.props; const { HOME } = paths; const redirectTime = 1500; return dispatch(loginUser(formData)) .then(() => { this.setState({ loginVisible: false }); setTimeout(() => { - return dispatch(push(HOME)); + const nextLocation = redirectLocation || HOME; + dispatch(clearRedirectLocation); + return dispatch(push(nextLocation)); }, redirectTime); }); }) @@ -104,10 +109,12 @@ export class LoginPage extends Component { const mapStateToProps = (state) => { const { error, loading, user } = state.auth; + const { redirectLocation } = state; return { error, loading, + redirectLocation, user, }; }; diff --git a/frontend/redux/nodes/redirectLocation/actions.js b/frontend/redux/nodes/redirectLocation/actions.js new file mode 100644 index 0000000000..0bb0367c87 --- /dev/null +++ b/frontend/redux/nodes/redirectLocation/actions.js @@ -0,0 +1,19 @@ +export const CLEAR_REDIRECT_LOCATION = 'CLEAR_REDIRECT_LOCATION'; +export const SET_REDIRECT_LOCATION = 'SET_REDIRECT_LOCATION'; + +export const clearRedirectLocation = { type: CLEAR_REDIRECT_LOCATION }; +export const setRedirectLocation = (redirectLocation) => { + return { + type: SET_REDIRECT_LOCATION, + payload: { + redirectLocation, + }, + }; +}; + +export default { + CLEAR_REDIRECT_LOCATION, + clearRedirectLocation, + SET_REDIRECT_LOCATION, + setRedirectLocation, +}; diff --git a/frontend/redux/nodes/redirectLocation/reducer.js b/frontend/redux/nodes/redirectLocation/reducer.js new file mode 100644 index 0000000000..7d5ad6fc81 --- /dev/null +++ b/frontend/redux/nodes/redirectLocation/reducer.js @@ -0,0 +1,18 @@ +import { CLEAR_REDIRECT_LOCATION, SET_REDIRECT_LOCATION } from './actions'; + +export const initialState = null; + +const reducer = (state = initialState, { type, payload }) => { + switch (type) { + case CLEAR_REDIRECT_LOCATION: + return null; + case SET_REDIRECT_LOCATION: + return { + ...payload.redirectLocation, + }; + default: + return state; + } +}; + +export default reducer; diff --git a/frontend/redux/nodes/redirectLocation/reducer.tests.js b/frontend/redux/nodes/redirectLocation/reducer.tests.js new file mode 100644 index 0000000000..02fbb441cb --- /dev/null +++ b/frontend/redux/nodes/redirectLocation/reducer.tests.js @@ -0,0 +1,28 @@ +import expect from 'expect'; + +import actions from './actions'; +import reducer, { initialState } from './reducer'; + +describe('redirectLocation - reducer', () => { + const redirectObject = { action: 'PUSH', pathname: 'admin' }; + const redirectAction = actions.setRedirectLocation(redirectObject); + + it('sets the initial state', () => { + const newState = reducer(undefined, { type: 'RANDOM_ACTION' }); + + expect(newState).toEqual(initialState); + }); + + it('sets the redirect location in state', () => { + const newState = reducer(initialState, redirectAction); + + expect(newState).toEqual(redirectObject); + }); + + it('clears the direction location in state', () => { + const state = reducer(initialState, redirectAction); + const newState = reducer(state, actions.clearRedirectLocation); + + expect(newState).toEqual(null); + }); +}); diff --git a/frontend/redux/reducers.js b/frontend/redux/reducers.js index 40d4efdc89..71b7e3f768 100644 --- a/frontend/redux/reducers.js +++ b/frontend/redux/reducers.js @@ -5,6 +5,7 @@ import auth from './nodes/auth/reducer'; import components from './nodes/components/reducer'; import entities from './nodes/entities/reducer'; import notifications from './nodes/notifications/reducer'; +import redirectLocation from './nodes/redirectLocation/reducer'; export default combineReducers({ app, @@ -12,5 +13,6 @@ export default combineReducers({ components, entities, notifications, + redirectLocation, routing: routerReducer, });