Auth services added & user reducer

This commit is contained in:
Andrew Revinsky (DART)
2016-02-25 21:11:38 +03:00
parent 587a9164b7
commit f887c9fefe
18 changed files with 356 additions and 74 deletions

View File

@@ -65,6 +65,7 @@
"redux": "^3.0.2",
"redux-auth": "0.0.2",
"redux-batched-subscribe": "^0.1.4",
"redux-logger": "^2.5.2",
"redux-multi": "^0.1.9",
"redux-router": "^1.0.0-beta7",
"redux-thunk": "^1.0.3",

View File

@@ -6,6 +6,7 @@ import React from "react";
import { createStore, compose, applyMiddleware, combineReducers} from "redux";
import { Provider} from "react-redux";
import thunk from "redux-thunk";
import createLogger from 'redux-logger';
import { Route, IndexRoute, Link, IndexLink } from "react-router";
import { ReduxRouter} from "redux-router";
@@ -24,6 +25,8 @@ import { reduxReactRouter as serverRouter } from "redux-router/server";
import { connect } from 'react-redux';
import { pushState } from 'redux-router';
import { requireAuthentication } from './components/AuthComponent';
//import demoButtons from "./reducers/request-test-buttons";
//import demoUi from "./reducers/demo-ui";
import Container from "./components/partials/Container";
@@ -72,59 +75,6 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
// }, 0);
//};
const requireAuth = next =>
(nextState, transition, cb) => {
debugger;
if (!auth.isLoggedIn()) {
transition.to('/login', null, {redirect: nextState.location});
return;
}
next(nextState, transition);
};
const mw = compose(requireAuth);
const requireAuthentication = (Component) => {
class AuthenticatedComponent extends React.Component {
componentWillMount() {
this.checkAuth();
}
componentWillReceiveProps(nextProps) {
this.checkAuth();
}
checkAuth() {
debugger;
if (!this.props.isAuthenticated) {
let redirectAfterLogin = this.props.location.pathname;
this.props.dispatch(pushState(null, `/signin?next=${redirectAfterLogin}`));
}
}
render() {
debugger;
return (
<div>
{this.props.isAuthenticated === true
? <Component {...this.props}/>
: null
}
</div>
)
}
}
const mapStateToProps = (state) => ({
token: state.auth.token,
userName: state.auth.userName,
isAuthenticated: state.auth.isAuthenticated
});
return connect(mapStateToProps)(AuthenticatedComponent);
};
// define app routes
// <Route path="account" component={Account} onEnter={requireAuth} />
@@ -132,12 +82,10 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
const routes = (
<Route path="/" component={App}>
<IndexRoute component={MyAccounts} />
<Route onEnter={mw} path="acc" component={Account} />
<IndexRoute component={requireAuthentication(MyAccounts)} />
<Route path="signin" component={SignIn} />
<Route path="register" component={SignUp} />
<Route path="account/:id" component={Account} />
<Route path="account/:accountId" component={requireAuthentication(Account)} />
</Route>
);
@@ -148,6 +96,7 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
// create the redux store
const store = compose(
applyMiddleware(thunk),
applyMiddleware(createLogger()),
reduxReactRouter({
createHistory: createHistoryMethod,
routes
@@ -158,7 +107,7 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
/**
* The React Router 1.0 routes for both the server and the client.
*/
return store.dispatch(reduxAuthConfigure([
return store.dispatch(((() => { debugger; })() , reduxAuthConfigure)([
{
default: {
//apiUrl: __API_URL__
@@ -189,7 +138,14 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
], {
cookies,
isServer,
currentLocation
currentLocation,
tokenFormat: {
"access-token": "{{ access-token }}",
"uid": "true"
},
initialCredentials: {
'uid': 123
}
})).then(({ redirectPath, blank } = {}) => {
// hack for material-ui server-side rendering.
// see https://github.com/callemall/material-ui/pull/2007

View File

@@ -0,0 +1,35 @@
/**
* Created by andrew on 25/02/16.
*/
import T from '../constants/ACTION_TYPES';
export function configureStart({...props} = {}) {
return {
...props,
type: T.AUTH.CONFIGURE_START
};
}
export function configureComplete({config, ...props} = {}) {
return {
...props,
type: T.AUTH.CONFIGURE_COMPLETE,
config
};
}
export function configureError({errors, ...props} = {}) {
return {
...props,
type: T.AUTH.CONFIGURE_ERROR,
errors
};
}
export function authenticateStart() {
return { type: T.AUTH.AUTHENTICATE_START };
}
export function authenticateComplete(user) {
return { type: T.AUTH.AUTHENTICATE_COMPLETE, user };
}
export function authenticateError(errors) {
return { type: T.AUTH.AUTHENTICATE_ERROR, errors };
}

View File

@@ -0,0 +1,43 @@
/**
* Created by andrew on 21/02/16.
*/
import React from 'react';
import { connect } from 'react-redux';
import { pushState } from 'redux-router';
export function requireAuthentication(Component) {
class AuthComponent extends React.Component {
componentWillMount() {
debugger;
if (!this.props.isAuthenticated) {
// redirect to login and add next param so we can redirect again after login
const redirectAfterLogin = this.props.location.pathname;
this.props.dispatch(pushState(null, `/signin?next=${redirectAfterLogin}`));
}
}
render() {
// render the component that requires auth (passed to this wrapper)
return (
<Component {...this.props} />
)
}
}
const mapStateToProps =
(state) => {
debugger;
return ({
token: state.auth.token,
//userName: state.auth.userName,
//isAuthenticated: state.auth.isAuthenticated
isAuthenticated: state.auth.getIn(['user', 'isSignedIn'])
})
};
return connect(mapStateToProps)(AuthComponent);
}

View File

@@ -4,6 +4,7 @@
import React, { PropTypes } from "react";
import { Grid, Col, Navbar, NavItem, Nav, NavbarBrand, Footer } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import { SignOutButton } from "redux-auth/bootstrap-theme";
//if (!global.__SERVER__ && !global.__TEST__) {
// require("../../styles/main.scss");
@@ -29,6 +30,9 @@ class Container extends React.Component {
<NavItem eventKey={2}>Account</NavItem>
</LinkContainer>
</Nav>
<Nav right={true}>
<SignOutButton></SignOutButton>
</Nav>
</Navbar>
<Grid className="content">

View File

@@ -4,6 +4,20 @@ export default defineActionTypes({
/*
* View model
*/
AUTH: `
CONFIGURE_START
CONFIGURE_COMPLETE
CONFIGURE_ERROR
AUTHENTICATE_START
AUTHENTICATE_COMPLETE
AUTHENTICATE_ERROR
SIGN_IN_START
SIGN_IN_COMPLETE
SIGN_IN_ERROR
SIGN_IN_FORM_UPDATE
SIGN_OUT_START
SIGN_OUT_COMPLETE
`,
DOCUMENT_LIST_VIEW: `
SET_QUERY
@@ -32,5 +46,5 @@ export default defineActionTypes({
NAVIGATION: `
START
COMPLETE
`,
`
})

View File

@@ -3,6 +3,8 @@
*/
import React, {PropTypes} from "react";
import auth from "redux-auth";
import * as BS from "react-bootstrap";
import Input from "./Input";
import ButtonLoader from "./ButtonLoader";
import { emailSignInFormUpdate, emailSignIn } from "redux-auth";
@@ -54,7 +56,6 @@ class EmailSignInForm extends React.Component {
handleSubmit (event) {
event.preventDefault();
let formData = this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "form"]).toJS();
debugger;
this.props.dispatch(emailSignIn(formData, this.getEndpoint()));
}
@@ -71,6 +72,7 @@ class EmailSignInForm extends React.Component {
className="email-sign-in-email"
label="Email"
placeholder="Email"
name="email"
disabled={disabled}
value={this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "form", "email"])}
errors={this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "errors", "email"])}

View File

@@ -7,6 +7,7 @@ import Input from "./Input";
import ButtonLoader from "./ButtonLoader";
import { emailSignUpFormUpdate, emailSignUp } from "redux-auth";
import IndexPanel from "./../../components/partials/IndexPanel";
import { customerInfoMap } from '../../entities/formToPayloadMappers';
import { Glyphicon } from "react-bootstrap";
import { connect } from "react-redux";
@@ -45,8 +46,8 @@ class EmailSignUpForm extends React.Component {
handleSubmit (event) {
event.preventDefault();
let formData = this.props.auth.getIn(["emailSignUp", this.getEndpoint(), "form"]).toJS();
debugger;
this.props.dispatch(emailSignUp(formData, this.getEndpoint()));
console.log(customerInfoMap(formData));
this.props.dispatch(emailSignUp(customerInfoMap(formData), this.getEndpoint()));
}
render () {
@@ -61,6 +62,7 @@ class EmailSignUpForm extends React.Component {
onSubmit={this.handleSubmit.bind(this)}>
<IndexPanel header="basic">
<Input type="text"
label="First name"
placeholder="First name"

View File

@@ -0,0 +1,30 @@
/**
* Created by andrew on 21/02/16.
*/
export const customerInfoMap = ({
ssn,
address1,
address2,
city, //: "Moscow"
email, //: "arevinsky@gmail.com"
fname, //: "Andrew"
lname, //: "Revinsky"
phone, //: "+79031570864"
state, //: "Kentucky"
zip //: "125315"
}) => ({
"name": {
"firstName": fname,
"lastName": lname
},
email,
ssn,
"phoneNumber": phone,
"address": {
"street1": address1,
"street2": address2,
city,
state,
"zipCode": zip
}
});

View File

@@ -0,0 +1,3 @@
/**
* Created by andrew on 25/02/16.
*/

View File

@@ -0,0 +1,3 @@
/**
* Created by andrew on 25/02/16.
*/

View File

@@ -0,0 +1,143 @@
/**
* Created by andrew on 25/02/16.
*/
import T from '../../constants/ACTION_TYPES';
const configInitialState = {
loading: true,
errors: null,
config: null
};
export const configReducer = (state = {...configInitialState}, action) => {
switch(action.type) {
case T.AUTH.CONFIGURE_START:
return {
...state,
loading: true
};
case T.AUTH.CONFIGURE_COMPLETE:
const { config } = action;
return {
...state,
loading: false,
errors: null,
config
};
case T.AUTH.CONFIGURE_ERROR:
const { errors } = action;
return {
...state,
loading: false,
errors
};
default:
return state;
}
};
const authInitialState = {
loading: false,
valid: false,
errors: null
};
export const authReducer = (state = {...authInitialState}, action) => {
switch(action.type) {
case T.AUTH.AUTHENTICATE_START: return {...state, loading: true };
case T.AUTH.AUTHENTICATE_COMPLETE: return {...state, loading: false,
errors: null,
valid: true};
case T.AUTH.AUTHENTICATE_ERROR: return {...state, loading: false,
errors: "Invalid token",
valid: false};
default: return state;
}
};
const signInInitialState = {
loading: false,
errors: null,
form: {}
};
export const signInReducer = (state = { ...signInInitialState, form: {...signInInitialState.form }}, action) => {
switch(action.type) {
case T.AUTH.SIGN_IN_START:
return {
...state,
loading: true
};
case T.AUTH.SIGN_IN_COMPLETE:
return {
...signInInitialState,
form: { ...signInInitialState.form }
};
case T.AUTH.SIGN_IN_ERROR:
const { errors } = action;
return {
...state,
loading: false,
errors
};
case T.AUTH.SIGN_IN_FORM_UPDATE:
const { key, value } = action;
return {
...state,
form: {
...state.form,
[key]: value
}
};
default: return state;
}
};
const signOutInitialState = {
loading: false,
errors: null
};
export const signOutReducer = (state = {...signOutInitialState}, action) => {
switch(action.type) {
case T.AUTH.SIGN_OUT_START:
return {
...state,
loading: true
};
case T.AUTH.SIGN_OUT_COMPLETE:
return {
...state,
loading: false,
errors: null
};
default: return state;
}
};
const userInitalState = {
attributes: null,
isSignedIn: false
};
export const userReducer = (state = {...userInitalState}, action) => {
switch(action.type) {
case T.AUTH.AUTHENTICATE_COMPLETE:
const { user } = action;
return {...state,
attributes: user,
isSignedIn: true
};
case T.AUTH.SIGN_IN_COMPLETE:
const { user } = action;
return {...state,
attributes: user.data,
isSignedIn: true
};
case T.AUTH.SIGN_OUT_COMPLETE:
case T.AUTH.AUTHENTICATE_ERROR:
return {
...userInitalState
};
default: return state;
}
};

View File

@@ -0,0 +1,3 @@
/**
* Created by andrew on 25/02/16.
*/

View File

@@ -0,0 +1,3 @@
/**
* Created by andrew on 25/02/16.
*/

View File

@@ -0,0 +1,3 @@
/**
* Created by andrew on 25/02/16.
*/

View File

@@ -0,0 +1,17 @@
/**
* Created by andrew on 25/02/16.
*/
//import * as A from "../actions/auth";
import T from '../constants/ACTION_TYPES';
import {
authenticateStart,
authenticateComplete,
authenticateError
} from '../actions/auth';
export function configure(endpoint = {}, settings = {}) {
return dispatch => {
dispatch(authenticateStart());
};
};

View File

@@ -7,6 +7,8 @@ import { connect } from "react-redux";
//import ButtonLoader from "./ButtonLoader";
import { Input } from "react-bootstrap";
import * as BS from "react-bootstrap";
import ButtonLoader from "../controls/bootstrap/ButtonLoader";
//export {bootstrap, materialUi} from "./views";
@@ -16,9 +18,20 @@ import ButtonLoader from "../controls/bootstrap/ButtonLoader";
//import { EmailSignInForm } from "redux-auth/bootstrap-theme";
import EmailSignInForm from "../controls/bootstrap/EmailSignInForm";
export class SignIn extends React.Component {
componentWillMount() {
debugger;
if (this.props.isAuthenticated) {
debugger;
//// redirect to login and add next param so we can redirect again after login
//const redirectAfterLogin = this.props.location.pathname;
//this.props.dispatch(pushState(null, `/signin?next=${redirectAfterLogin}`));
}
}
render () {
const signInProps = {
inputProps: {
@@ -31,13 +44,18 @@ export class SignIn extends React.Component {
}
};
return <EmailSignInForm />;
//return (
// <div>
// <PageHeader>Sign In First</PageHeader>
// <p>Unauthenticated users can't access the account page.</p>
// </div>
//);
return (
<BS.Well>
<PageHeader>Sign In</PageHeader>
<EmailSignInForm {...this.props} />
</BS.Well>
);
}
}
export default connect(({routes}) => ({routes}))(SignIn);
export default connect(({
routes,
auth
}) => ({
routes,
auth
}))(SignIn);

View File

@@ -5,21 +5,23 @@ import React from "react";
//import { PageHeader } from "react-bootstrap";
import { connect } from "react-redux";
import { PageHeader, OverlayTrigger, Tooltip, Row, ButtonGroup, Table } from "react-bootstrap";
import * as BS from "react-bootstrap";
import { Link, IndexLink} from "react-router";
//import { EmailSignUpForm } from "redux-auth/bootstrap-theme"
import EmailSignUpForm from "../controls/bootstrap/EmailSignUpForm";
export class SignUp extends React.Component {
render () {
return (
<div>
<PageHeader>
Register
</PageHeader>
<Row>
<BS.Well>
<EmailSignUpForm endpoint="default" />
</Row>
</BS.Well>
</div>
);