Skip to content

Commit

Permalink
Merge branch 'feature/react-router-redux-integration' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
gocreating committed Oct 24, 2016
2 parents 4df8a9e + 9e79ce5 commit 5e4306d
Show file tree
Hide file tree
Showing 17 changed files with 186 additions and 98 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
"react-redux": "^4.4.5",
"react-router": "^2.3.0",
"react-router-active-component": "^4.0.0",
"react-router-redux": "^4.0.6",
"redux": "^3.5.2",
"redux-form": "^6.1.0",
"redux-thunk": "^2.1.0",
Expand Down
26 changes: 23 additions & 3 deletions src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,50 @@ import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { match, Router, browserHistory } from 'react-router';
import {
routerMiddleware,
syncHistoryWithStore,
push,
} from 'react-router-redux';
import LocaleProvider from '../common/components/utils/LocaleProvider';
import rootReducer from '../common/reducers';
import getRoutes from '../common/routes';
import setupLocale from './setupLocale';
import setupNProgress from './setupNProgress';
import setupGA from './setupGA';
import { setApiEngine } from '../common/actions/apiEngine';
import { removeCookie } from '../common/actions/cookieActions';
import ApiEngine from '../common/utils/ApiEngine';

setupNProgress();
setupLocale();
let logPageView = setupGA();
const initialState = window.__INITIAL_STATE__;
let store = createStore(rootReducer, initialState, applyMiddleware(thunk));
let store = createStore(
rootReducer,
initialState,
applyMiddleware(
routerMiddleware(browserHistory),
thunk
)
);

let apiEngine = new ApiEngine();
store.dispatch(setApiEngine(apiEngine));

let { redirect } = store.getState().cookies;
if (redirect) {
store.dispatch(push(redirect));
store.dispatch(removeCookie('redirect'));
}

// refs:
// - <http://www.jianshu.com/p/b3ff1f53faaf>
// - <https://github.com/ryanflorence/example-react-router-server-rendering-lazy-routes>
let history = syncHistoryWithStore(browserHistory, store);
let routes = getRoutes(store);
match({
history: browserHistory,
history,
routes,
}, (error, redirectLocation, renderProps) => {
if (error) {
Expand All @@ -37,7 +57,7 @@ match({
<Provider store={store}>
<LocaleProvider>
<Router
history={browserHistory}
history={history}
onUpdate={logPageView}
{...renderProps}
>
Expand Down
9 changes: 9 additions & 0 deletions src/common/actions/routeActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { push } from 'react-router-redux';
import { setCookie } from './cookieActions';

export const redirect = (path) => {
return (dispatch) => {
dispatch(setCookie('redirect', path));
dispatch(push(path));
};
};
33 changes: 16 additions & 17 deletions src/common/components/forms/LoginForm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { Component, PropTypes } from 'react';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import { Field, reduxForm } from 'redux-form';
import Button from 'react-bootstrap/lib/Button';
// import validator from 'validator';
Expand Down Expand Up @@ -33,32 +35,31 @@ class LoginForm extends Component {
}

_login(json) {
return this.context.store.dispatch(loginUser({
return this.props.dispatch(loginUser({
token: json.token,
data: json.user,
}));
}

_handleSubmit(formData) {
return userAPI(this.context.store.getState().apiEngine)
// let { store } = this.context;
let { dispatch, apiEngine } = this.props;

return userAPI(apiEngine)
.login(formData)
.catch((err) => {
this.context.store.dispatch(pushErrors(err));
dispatch(pushErrors(err));
throw err;
})
.then((json) => {
if (json.isAuth) {
this.login(json).then(() => {
// redirect to the origin path before logging in
const { location } = this.props;
if (location && location.state && location.state.nextPathname) {
this.context.router.push(location.state.nextPathname);
} else {
this.context.router.push('/');
}
let { next } = this.props.routing.locationBeforeTransitions.query;
dispatch(push(next || '/'));
});
} else {
this.context.store.dispatch(pushErrors([{
dispatch(pushErrors([{
title: 'User Not Exists',
detail: 'You may type wrong email or password.',
}]));
Expand Down Expand Up @@ -100,12 +101,10 @@ class LoginForm extends Component {
}
};

LoginForm.contextTypes = {
store: PropTypes.object.isRequired,
router: PropTypes.any.isRequired,
};

export default reduxForm({
form: 'login',
validate,
})(LoginForm);
})(connect(state => ({
apiEngine: state.apiEngine,
routing: state.routing,
}))(LoginForm));
21 changes: 11 additions & 10 deletions src/common/components/forms/RegisterForm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { Component, PropTypes } from 'react';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import { Field, reduxForm } from 'redux-form';
import Button from 'react-bootstrap/lib/Button';
// import validator from 'validator';
Expand Down Expand Up @@ -43,14 +45,16 @@ class RegisterForm extends Component {
}

_handleSubmit(formData) {
return userAPI(this.context.store.getState().apiEngine)
let { dispatch, apiEngine } = this.props;

return userAPI(apiEngine)
.register(formData)
.catch((err) => {
this.context.store.dispatch(pushErrors(err));
dispatch(pushErrors(err));
throw err;
})
.then((json) => {
this.context.router.push('/');
dispatch(push('/'));
});
}

Expand Down Expand Up @@ -99,14 +103,11 @@ class RegisterForm extends Component {
}
};

RegisterForm.contextTypes = {
store: PropTypes.any.isRequired,
router: PropTypes.any.isRequired,
};

export default reduxForm({
form: 'register',
validate,
asyncValidate,
asyncBlurFields: ['email'],
})(RegisterForm);
})(connect(state => ({
apiEngine: state.apiEngine,
}))(RegisterForm));
20 changes: 15 additions & 5 deletions src/common/components/layouts/PageLayout.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import React from 'react';
import React, { PropTypes } from 'react';
import Grid from 'react-bootstrap/lib/Grid';
import Navigation from '../utils/Navigation';
import ErrorList from '../utils/ErrorList';

const PageLayout = ({ children, ...rest }) => (
let PageLayout = ({ hasGrid, children, ...rest }) => (
<div>
<Navigation />
<ErrorList />
<Grid {...rest}>
{children}
</Grid>
{hasGrid ? (
<Grid {...rest}>
{children}
</Grid>
) : children}
</div>
);

PageLayout.propTypes = {
hasGrid: PropTypes.bool,
};

PageLayout.defaultProps = {
hasGrid: true,
};

export default PageLayout;
12 changes: 6 additions & 6 deletions src/common/components/pages/admin/user/ListPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { push } from 'react-router-redux';
import PageHeader from 'react-bootstrap/lib/PageHeader';
import Table from 'react-bootstrap/lib/Table';
import Resources from '../../../../constants/Resources';
Expand All @@ -24,7 +24,7 @@ class ListPage extends Component {
}

componentDidUpdate(prevProps) {
let { dispatch, apiEngine, page, router, location } = this.props;
let { dispatch, apiEngine, page, location } = this.props;

if (prevProps.page.current !== page.current) {
userAPI(apiEngine)
Expand All @@ -36,10 +36,10 @@ class ListPage extends Component {
.then((json) => {
this.setState({ users: json.users });
dispatch(setPage(Resources.USER, json.page));
router.push({
dispatch(push({
pathname: location.pathname,
query: { page: json.page.current },
});
}));
});
}
}
Expand Down Expand Up @@ -78,7 +78,7 @@ class ListPage extends Component {
}
}

export default withRouter(connect(state => ({
export default connect(state => ({
apiEngine: state.apiEngine,
page: state.pages[Resources.USER] || {},
}))(ListPage));
}))(ListPage);
12 changes: 6 additions & 6 deletions src/common/components/pages/todo/ListPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { push } from 'react-router-redux';
import PageHeader from 'react-bootstrap/lib/PageHeader';
import Resources from '../../../constants/Resources';
import todoAPI from '../../../api/todo';
Expand Down Expand Up @@ -93,7 +93,7 @@ class ListPage extends Component {
}

componentDidUpdate(prevProps) {
let { dispatch, apiEngine, page, router, location } = this.props;
let { dispatch, apiEngine, page, location } = this.props;

if (prevProps.page.current !== page.current) {
todoAPI(apiEngine)
Expand All @@ -105,10 +105,10 @@ class ListPage extends Component {
.then((json) => {
dispatch(setTodo(json.todos));
dispatch(setPage(Resources.TODO, json.page));
router.push({
dispatch(push({
pathname: location.pathname,
query: { page: json.page.current },
});
}));
});
}
}
Expand Down Expand Up @@ -177,8 +177,8 @@ class ListPage extends Component {
}
};

export default withRouter(connect(state => ({
export default connect(state => ({
apiEngine: state.apiEngine,
todos: state.todos,
page: state.pages[Resources.TODO] || {},
}))(ListPage));
}))(ListPage);
78 changes: 47 additions & 31 deletions src/common/components/pages/user/LoginPage.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,58 @@
import React from 'react';
import PageHeader from 'react-bootstrap/lib/PageHeader';
import Alert from 'react-bootstrap/lib/Alert';
import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import PageLayout from '../../layouts/PageLayout';
import Head from '../../widgets/Head';
import LoginForm from '../../forms/LoginForm';

const LoginPage = (props) => (
<PageLayout>
<Head
links={[
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/5.0.0/bootstrap-social.min.css',
]}
/>
<PageHeader>Login</PageHeader>
<Grid>
<Row>
<Col md={9}>
<LoginForm location={props.location} />
</Col>
<Col md={3}>
<a
href="/auth/facebook"
className="btn btn-block btn-social btn-facebook"
>
<span className="fa fa-facebook"></span>Login with Facebook
</a>
<a
href="/auth/linkedin"
className="btn btn-block btn-social btn-linkedin"
>
<span className="fa fa-linkedin"></span>Login with LinkedIn
</a>
</Col>
</Row>
</Grid>
</PageLayout>
);
let LoginPage = ({ location }) => {
let { next } = location.query;
let search = next ? '?next=' + next : '';

return (
<PageLayout hasGrid={false}>
<Head
links={[
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-social/5.0.0/bootstrap-social.min.css',
]}
/>
<Grid>
<PageHeader>Login</PageHeader>
<Row>
<Col md={12}>
{next && (
<Alert bsStyle="warning">
<strong>Authentication Required</strong>
{' '}Please login first.
</Alert>
)}
</Col>
</Row>
<Row>
<Col md={9}>
<LoginForm />
</Col>
<Col md={3}>
<a
href={`/auth/facebook${search}`}
className="btn btn-block btn-social btn-facebook"
>
<span className="fa fa-facebook"></span>Login with Facebook
</a>
<a
href={`/auth/linkedin${search}`}
className="btn btn-block btn-social btn-linkedin"
>
<span className="fa fa-linkedin"></span>Login with LinkedIn
</a>
</Col>
</Row>
</Grid>
</PageLayout>
);
};

export default LoginPage;
Loading

0 comments on commit 5e4306d

Please sign in to comment.