diff --git a/package.json b/package.json
index a418328..04edcef 100644
--- a/package.json
+++ b/package.json
@@ -66,7 +66,6 @@
"express-session": "^1.12.1",
"file-loader": "^0.8.5",
"invariant": "^2.2.0",
- "history": "1.17.0",
"hoist-non-react-statics": "^1.0.3",
"less": "^2.5.3",
"less-loader": "^2.2.1",
@@ -74,19 +73,19 @@
"map-props": "^1.0.0",
"piping": "^0.3.0",
"pretty-error": "^1.2.0",
- "query-string": "^3.0.0",
"react": "^0.14.2",
"react-bootstrap": "^0.28.1",
"react-helmet": "^2.2.0",
"react-dom": "^0.14.1",
"react-inline-css": "^2.0.0",
"react-redux": "^4.0.0",
- "react-router": "1.0.3",
- "react-router-bootstrap": "^0.19.3",
+ "react-router": "2.0.0",
+ "react-router-bootstrap": "^0.20.1",
+ "react-router-redux": "^3.0.0",
"redux": "^3.0.4",
+ "redux-async-connect": "andresgutgon/redux-async-connect#feature/deferred-first-class-citizen",
"redux-form": "^3.0.12",
- "redux-router": "1.0.0-beta5",
- "scroll-behavior": "^0.3.0",
+ "scroll-behavior": "^0.3.2",
"serialize-javascript": "^1.1.2",
"serve-favicon": "^2.3.0",
"superagent": "^1.4.0",
diff --git a/src/client.js b/src/client.js
index 718269e..003305b 100755
--- a/src/client.js
+++ b/src/client.js
@@ -4,27 +4,34 @@
import 'babel/polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
-import createHistory from 'history/lib/createBrowserHistory';
-import useScroll from 'scroll-behavior/lib/useStandardScroll';
import createStore from './redux/create';
import ApiClient from './helpers/ApiClient';
import {Provider} from 'react-redux';
-import {reduxReactRouter, ReduxRouter} from 'redux-router';
+
+import { Router, browserHistory } from 'react-router';
+import { ReduxAsyncConnect } from 'redux-async-connect';
+import useScroll from 'scroll-behavior/lib/useStandardScroll';
import getRoutes from './routes';
-import makeRouteHooksSafe from './helpers/makeRouteHooksSafe';
const client = new ApiClient();
-// Three differnt types of scroll behavior available.
-// Documented here: https://github.com/rackt/scroll-behavior
-const scrollableHistory = useScroll(createHistory);
-
+const history = useScroll(() => browserHistory)();
const dest = document.getElementById('content');
-const store = createStore(reduxReactRouter, makeRouteHooksSafe(getRoutes), scrollableHistory, client, window.__data);
+const store = createStore(history, client, window.__data);
const component = (
-
+
+
+ }
+ history={history}
+ >
+ {getRoutes(store)}
+
);
ReactDOM.render(
diff --git a/src/containers/App/App.js b/src/containers/App/App.js
index bd5b351..6d40587 100755
--- a/src/containers/App/App.js
+++ b/src/containers/App/App.js
@@ -1,31 +1,41 @@
import React, { Component, PropTypes } from 'react';
-import { bindActionCreators } from 'redux';
import { IndexLink } from 'react-router';
import { LinkContainer } from 'react-router-bootstrap';
-import { Navbar, Nav, NavItem } from 'react-bootstrap';
+import Navbar from 'react-bootstrap/lib/Navbar';
+import Nav from 'react-bootstrap/lib/Nav';
+import NavItem from 'react-bootstrap/lib/NavItem';
import { connect } from 'react-redux';
import Helmet from 'react-helmet';
import { isLoaded as isAuthLoaded, load as loadAuth, logout } from 'redux/modules/auth';
-import { pushState } from 'redux-router';
-import connectData from 'helpers/connectData';
+import { routeActions } from 'react-router-redux';
+import { asyncConnect } from 'redux-async-connect';
+
import config from '../../config';
-function fetchData(getState, dispatch) {
- if (!isAuthLoaded(getState())) {
- return dispatch(loadAuth());
- }
-}
+@asyncConnect([{
+ promise: (options) => {
+ const {
+ store: { dispatch, getState },
+ } = options;
+ const promises = [];
-@connectData(fetchData)
+ if (!isAuthLoaded(getState())) {
+ promises.push(dispatch(loadAuth()));
+ }
+
+ return Promise.all(promises);
+ }
+}])
@connect(
- state => ({user: state.auth.user}),
- dispatch => bindActionCreators({logout, pushState}, dispatch))
+ state => ({user: state.auth.user}),
+ {logout, push: routeActions.push}
+)
export default class App extends Component {
static propTypes = {
children: PropTypes.object.isRequired,
user: PropTypes.object,
logout: PropTypes.func.isRequired,
- pushState: PropTypes.func.isRequired
+ push: PropTypes.func.isRequired
};
static contextTypes = {
@@ -33,10 +43,12 @@ export default class App extends Component {
};
componentWillReceiveProps(nextProps) {
+ const { push } = this.props;
+
if (!this.props.user && nextProps.user) {
- this.props.pushState(null, '/groups');
+ push('/groups');
} else if (this.props.user && !nextProps.user) {
- this.props.pushState(null, '/login');
+ push('/login');
// Full real page reload to clean local data
window.location.reload();
}
diff --git a/src/containers/DevTools/DevTools.js b/src/containers/DevTools/DevTools.js
index ca930ca..feb12d8 100644
--- a/src/containers/DevTools/DevTools.js
+++ b/src/containers/DevTools/DevTools.js
@@ -5,8 +5,8 @@ import DockMonitor from 'redux-devtools-dock-monitor';
export default createDevTools(
+ toggleVisibilityKey="ctrl+H"
+ changePositionKey="ctrl+Q">
);
diff --git a/src/containers/Group/Base.js b/src/containers/Group/Base.js
index a03dbea..45e4fe8 100644
--- a/src/containers/Group/Base.js
+++ b/src/containers/Group/Base.js
@@ -1,64 +1,63 @@
import _ from 'underscore';
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
+import { asyncConnect } from 'redux-async-connect';
-import connectData from 'helpers/connectData';
import { loadEntity as loadGroup } from 'redux/modules/groups/groups';
import { load as loadUsers } from 'redux/modules/users/users';
import { load as loadMemberships } from 'redux/modules/groups/memberships';
-import { load as loadInvitations } from 'redux/modules/invitations/list';
import { membersWithUserSelector } from 'selectors/members';
-function fetchData(getState, dispatch, location, params) {
- const {
- groupsReducer: {groups: { byId }},
- usersReducer: { users },
- membershipsReducer: { memberships },
- } = getState();
- const promises = [];
- const id = params.id;
- const group = byId[id];
-
- promises.push(dispatch(loadInvitations(id)));
-
- if (!users.entities.length) {
- promises.push(dispatch(loadUsers()));
- }
-
- if (!memberships.entities.length) {
- promises.push(dispatch(loadMemberships()));
- }
-
-
- if (!group) {
- promises.push(dispatch(loadGroup(id)));
- }
-
- return Promise.all(promises);
-}
-
function groupSelector(state) {
return {
...membersWithUserSelector(state),
- invitations: state.invitationsReducer.invitations,
user: state.auth.user,
};
}
-@connectData(fetchData)
+@asyncConnect([{
+ promise: (options) => {
+ const {
+ store: { dispatch, getState },
+ params,
+ } = options;
+
+ const {
+ groupsReducer: {groups: { byId }},
+ usersReducer: { users },
+ membershipsReducer: { memberships },
+ } = getState();
+ const promises = [];
+ const id = params.id;
+ const group = byId[id];
+
+ if (!users.entities.length) {
+ promises.push(dispatch(loadUsers()));
+ }
+
+ if (!memberships.entities.length) {
+ promises.push(dispatch(loadMemberships()));
+ }
+
+ if (!group) {
+ promises.push(dispatch(loadGroup(id)));
+ }
+
+ return Promise.all(promises);
+ }
+}])
@connect(groupSelector, {})
export default class GroupBase extends Component {
static propTypes = {
children: PropTypes.object.isRequired,
groups: PropTypes.object.isRequired,
user: PropTypes.object,
- invitations: PropTypes.object.isRequired,
members: PropTypes.object.isRequired,
params: PropTypes.object.isRequired,
}
render() {
- const { user, invitations, groups, members, params: { id }} = this.props;
+ const { user, groups, members, params: { id }} = this.props;
const group = groups.byId[id];
const membersOfGroup = members.byGroupID[group.id] || [];
@@ -74,7 +73,6 @@ export default class GroupBase extends Component {
group: group,
currentUser: currentUser,
members: membersOfGroup,
- invitations: invitations,
}
)}
diff --git a/src/containers/Group/Members/Invitations/index.js b/src/containers/Group/Members/Invitations/index.js
index 696e15a..40a8ae8 100644
--- a/src/containers/Group/Members/Invitations/index.js
+++ b/src/containers/Group/Members/Invitations/index.js
@@ -9,9 +9,9 @@ import { isRole } from 'helpers/entities/member';
const mapStateToProps = (state) => ({
bulkErrors: state.bulkInvitationsReducer.bulkErrors,
+ invitations: state.invitationsReducer.invitations,
invitationStatusByID: state.invitationsReducer.invitationStatusByID,
});
-
const mapDispatchToProps = { initialize, send, sendBulk };
@connect(mapStateToProps, mapDispatchToProps)
@@ -61,7 +61,7 @@ export default class GroupMembersInvitations extends Component {
invitationStatusByID,
group,
} = this.props;
- const invitationsByGroup = invitations.byGroupID[group.id];
+ const invitationsByGroup = invitations.byGroupID[group.id] || [];
const isAdmin = isRole(currentUser, 'admin');
const invitationsList = invitationsByGroup.map((invitation) => {
diff --git a/src/containers/Group/Members/index.js b/src/containers/Group/Members/index.js
index 9929174..1d9ba9d 100644
--- a/src/containers/Group/Members/index.js
+++ b/src/containers/Group/Members/index.js
@@ -1,24 +1,36 @@
import React, {Component, PropTypes} from 'react';
-import DocumentMeta from 'react-document-meta';
+import Helmet from 'react-helmet';
+import { asyncConnect } from 'redux-async-connect';
+import { load as loadInvitations } from 'redux/modules/invitations/list';
import Invitations from './Invitations';
import List from './List';
+@asyncConnect([{
+ deferred: true,
+ promise: (options) => {
+ const {
+ store: { dispatch },
+ params: { id },
+ } = options;
+
+ return dispatch(loadInvitations(id));
+ },
+}])
export default class GroupMembers extends Component {
static propTypes = {
group: PropTypes.object,
currentUser: PropTypes.object,
members: PropTypes.array,
- invitations: PropTypes.object,
}
render() {
- const { currentUser, group, members, invitations } = this.props;
+ const { currentUser, group, members } = this.props;
return (
-
+
Miembros
@@ -27,7 +39,6 @@ export default class GroupMembers extends Component {
diff --git a/src/containers/Invitation/Complete.js b/src/containers/Invitation/Complete.js
index e287c9c..e9e0340 100644
--- a/src/containers/Invitation/Complete.js
+++ b/src/containers/Invitation/Complete.js
@@ -1,52 +1,46 @@
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {initialize} from 'redux-form';
-import DocumentMeta from 'react-document-meta';
+import Helmet from 'react-helmet';
import * as invitationCompleteActions from 'redux/modules/invitations/complete';
import CompleteSignupForm from 'components/forms/signup/Complete';
@connect(
state => ({
validInvitation: state.completeInvitationReducer.validInvitation,
+ user: state.auth.user,
completeInvitationErrors: state.completeInvitationReducer.completeInvitationErrors
}),
{initialize, complete: invitationCompleteActions.complete})
export default class InvitationComplete extends Component {
static propTypes = {
initialize: PropTypes.func.isRequired,
+ user: PropTypes.object,
complete: PropTypes.func.isRequired,
params: PropTypes.object,
completeInvitationErrors: PropTypes.object,
validInvitation: PropTypes.bool,
history: PropTypes.object,
token: PropTypes.string
- }
-
- static onEnter(nextState, replaceState, cb) {
- const context = this.context;
- const token = nextState.params.token;
-
- function goHome() {
- replaceState(null, '/');
- }
+ };
- function signupOrHome() {
- const {completeInvitationReducer: {validInvitation}} = context.getState();
+ static contextTypes = {
+ router: React.PropTypes.object.isRequired
+ };
- if (!validInvitation) {
- goHome();
- }
+ componentWillMount() {
+ const { validInvitation, user } = this.props;
- cb();
+ if (!validInvitation || user) {
+ this.context.router.replace('/');
}
+ }
- if (token) {
- context.dispatch(invitationCompleteActions.checkInvitation(token))
- .then(signupOrHome);
- } else {
- goSignup();
- cb();
- }
+ static reduxAsyncConnect(params, store) {
+ const { dispatch } = store;
+ const { token } = params;
+
+ return dispatch(invitationCompleteActions.checkInvitation(token));
}
handleSubmit(data) {
@@ -57,7 +51,7 @@ export default class InvitationComplete extends Component {
return Promise.reject(errors);
}
- this.props.history.pushState(null, '/groups');
+ this.props.history.push('/groups');
return Promise.resolve({});
});
}
@@ -67,7 +61,7 @@ export default class InvitationComplete extends Component {
-
+
Finaliza el registro
-
+
Crea un grupo
diff --git a/src/containers/Signup/Complete.js b/src/containers/Signup/Complete.js
index f09b548..a2f4681 100644
--- a/src/containers/Signup/Complete.js
+++ b/src/containers/Signup/Complete.js
@@ -1,7 +1,7 @@
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {initialize} from 'redux-form';
-import DocumentMeta from 'react-document-meta';
+import Helmet from 'react-helmet';
import * as signupCompleteActions from 'redux/modules/signup/complete';
import CompleteSignupForm from 'components/forms/signup/Complete';
@@ -22,31 +22,23 @@ export default class Complete extends Component {
token: PropTypes.string
}
- static onEnter(nextState, replaceState, cb) {
- const context = this.context;
- const token = nextState.params.token;
+ static contextTypes = {
+ router: React.PropTypes.object.isRequired
+ };
- function goSignup() {
- replaceState(null, '/signup');
- }
-
- function signupOrCreate() {
- const {signupCompleteReducer: {validSignup}} = context.getState();
-
- if (!validSignup) {
- goSignup();
- }
+ componentWillMount() {
+ const { validSignup } = this.props;
- cb();
+ if (!validSignup) {
+ this.context.router.replace('/signup');
}
+ }
- if (token) {
- context.dispatch(signupCompleteActions.checkSignup(token))
- .then(signupOrCreate);
- } else {
- goSignup();
- cb();
- }
+ static reduxAsyncConnect(params, store) {
+ const { dispatch } = store;
+ const { token } = params;
+
+ return dispatch(signupCompleteActions.checkSignup(token));
}
handleSubmit(data) {
@@ -67,7 +59,7 @@ export default class Complete extends Component {
-
+
Finaliza el registro
-
+
Signup
{successMessage}
diff --git a/src/helpers/Html.js b/src/helpers/Html.js
index 4929a6a..57b1c80 100644
--- a/src/helpers/Html.js
+++ b/src/helpers/Html.js
@@ -17,7 +17,7 @@ export default class Html extends Component {
assets: PropTypes.object,
component: PropTypes.object,
store: PropTypes.object
- }
+ };
render() {
const {assets, component, store} = this.props;
diff --git a/src/helpers/connectData.js b/src/helpers/connectData.js
deleted file mode 100644
index 4529529..0000000
--- a/src/helpers/connectData.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React, { Component } from 'react';
-import hoistStatics from 'hoist-non-react-statics';
-
-/*
- Note:
- When this decorator is used, it MUST be the first (outermost) decorator.
- Otherwise, we cannot find and call the fetchData and fetchDataDeffered methods.
- */
-
-export default function connectData(fetchData, fetchDataDeferred) {
-
- return function wrapWithFetchData(WrappedComponent) {
- class ConnectData extends Component {
- render() {
- return ;
- }
- }
-
- ConnectData.fetchData = fetchData;
- ConnectData.fetchDataDeferred = fetchDataDeferred;
-
- return hoistStatics(ConnectData, WrappedComponent);
- };
-}
diff --git a/src/helpers/getDataDependencies.js b/src/helpers/getDataDependencies.js
deleted file mode 100755
index 37f4c13..0000000
--- a/src/helpers/getDataDependencies.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * 1. Skip holes in route component chain and
- * only consider components that implement
- * fetchData or fetchDataDeferred
- *
- * 2. Pull out fetch data methods
- *
- * 3. Call fetch data methods and gather promises
- */
-export default (components, getState, dispatch, location, params, deferred) => {
- const methodName = deferred ? 'fetchDataDeferred' : 'fetchData';
-
- return components
- .filter((component) => component && component[methodName]) // 1
- .map((component) => component[methodName]) // 2
- .map((fetchData) =>
- fetchData(getState, dispatch, location, params)); // 3
-};
diff --git a/src/helpers/getStatusFromRoutes.js b/src/helpers/getStatusFromRoutes.js
deleted file mode 100644
index a942cd2..0000000
--- a/src/helpers/getStatusFromRoutes.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * Return the status code from the last matched route with a status property.
- *
- * @param matchedRoutes
- * @returns {Number|undefined}
- */
-export default (matchedRoutes) => {
- return matchedRoutes.reduce((prev, cur) => cur.status || prev, null);
-};
diff --git a/src/helpers/makeRouteHooksSafe.js b/src/helpers/makeRouteHooksSafe.js
deleted file mode 100644
index 7c9f8c5..0000000
--- a/src/helpers/makeRouteHooksSafe.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { createRoutes } from 'react-router/lib/RouteUtils';
-
-// Wrap the hooks so they don't fire if they're called before
-// the store is initialised. This only happens when doing the first
-// client render of a route that has an onEnter hook
-function makeHooksSafe(routes, store) {
- if (Array.isArray(routes)) {
- return routes.map((route) => makeHooksSafe(route, store));
- }
-
- const onEnter = routes.onEnter;
-
- if (onEnter) {
- routes.onEnter = function safeOnEnter(...args) {
- try {
- store.getState();
- } catch (err) {
- if (onEnter.length === 3) {
- args[2]();
- }
-
- // There's no store yet so ignore the hook
- return;
- }
-
- onEnter.apply(null, args);
- };
- }
-
- if (routes.childRoutes) {
- makeHooksSafe(routes.childRoutes, store);
- }
-
- if (routes.indexRoute) {
- makeHooksSafe(routes.indexRoute, store);
- }
-
- return routes;
-}
-
-export default function makeRouteHooksSafe(_getRoutes) {
- return (store) => makeHooksSafe(createRoutes(_getRoutes(store)), store);
-}
diff --git a/src/redux/create.js b/src/redux/create.js
index f124229..0f52727 100644
--- a/src/redux/create.js
+++ b/src/redux/create.js
@@ -1,30 +1,31 @@
import { createStore as _createStore, applyMiddleware, compose } from 'redux';
import createMiddleware from './middleware/clientMiddleware';
-import transitionMiddleware from './middleware/transitionMiddleware';
+import { syncHistory } from 'react-router-redux';
-export default function createStore(reduxReactRouter, getRoutes, createHistory, client, data) {
- const middleware = [createMiddleware(client), transitionMiddleware];
+export default function createStore(history, client, data) {
+ // Sync dispatched route actions to the history
+ const reduxRouterMiddleware = syncHistory(history);
+
+ const middleware = [createMiddleware(client), reduxRouterMiddleware];
let finalCreateStore;
if (__DEVELOPMENT__ && __CLIENT__ && __DEVTOOLS__) {
const { persistState } = require('redux-devtools');
const DevTools = require('../containers/DevTools/DevTools');
-
finalCreateStore = compose(
applyMiddleware(...middleware),
window.devToolsExtension ? window.devToolsExtension() : DevTools.instrument(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(_createStore);
-
} else {
finalCreateStore = applyMiddleware(...middleware)(_createStore);
}
- finalCreateStore = reduxReactRouter({ getRoutes, createHistory })(finalCreateStore);
-
const reducer = require('./modules/reducer');
const store = finalCreateStore(reducer, data);
+ reduxRouterMiddleware.listenForReplays(store);
+
if (__DEVELOPMENT__ && module.hot) {
module.hot.accept('./modules/reducer', () => {
store.replaceReducer(require('./modules/reducer'));
diff --git a/src/redux/middleware/clientMiddleware.js b/src/redux/middleware/clientMiddleware.js
index c9cb4a3..e1d9b8c 100644
--- a/src/redux/middleware/clientMiddleware.js
+++ b/src/redux/middleware/clientMiddleware.js
@@ -5,7 +5,13 @@ export default function clientMiddleware(client) {
return action(dispatch, getState);
}
- const { promise, types, requestData, ...rest } = action;
+ const {
+ promise,
+ types,
+ requestData,
+ ...rest,
+ } = action;
+
if (!promise) {
return next(action);
}
@@ -16,13 +22,16 @@ export default function clientMiddleware(client) {
next({...rest, type: REQUEST, requestData: data});
- return promise(client).then(
+ const actionPromise = promise(client);
+ actionPromise.then(
(result) => next({...rest, result, type: SUCCESS}),
(error) => next({...rest, error, type: FAILURE})
).catch((error)=> {
console.error('MIDDLEWARE ERROR:', error);
next({...rest, error, type: FAILURE});
});
+
+ return actionPromise;
};
};
}
diff --git a/src/redux/middleware/transitionMiddleware.js b/src/redux/middleware/transitionMiddleware.js
deleted file mode 100644
index 13993bd..0000000
--- a/src/redux/middleware/transitionMiddleware.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {ROUTER_DID_CHANGE} from 'redux-router/lib/constants';
-import getDataDependencies from '../../helpers/getDataDependencies';
-
-const locationsAreEqual = (locA, locB) => {
- return (locA.pathname === locB.pathname) && (locA.search === locB.search);
-};
-
-export default ({getState, dispatch}) => next => action => {
- if (action.type === ROUTER_DID_CHANGE) {
- if (getState().router && locationsAreEqual(action.payload.location, getState().router.location)) {
- return next(action);
- }
-
- const {components, location, params} = action.payload;
- const promise = new Promise((resolve) => {
-
- const doTransition = () => {
- next(action);
- Promise.all(getDataDependencies(components, getState, dispatch, location, params, true))
- .then(resolve, resolve);
- };
-
- Promise.all(getDataDependencies(components, getState, dispatch, location, params))
- .then(doTransition, doTransition);
- });
-
- if (__SERVER__) {
- // router state is null until ReduxRouter is created so we can use this to store
- // our promise to let the server know when it can render
- getState().router = promise;
- }
-
- return promise;
- }
-
- return next(action);
-};
diff --git a/src/redux/modules/reducer.js b/src/redux/modules/reducer.js
index 84b100a..9cb3e86 100644
--- a/src/redux/modules/reducer.js
+++ b/src/redux/modules/reducer.js
@@ -13,10 +13,12 @@ import groupsReducer from './groups/groups';
import {reducer as form} from 'redux-form';
import info from './info';
import widgets from './widgets';
-import { routerStateReducer } from 'redux-router';
+import { routeReducer } from 'react-router-redux';
+import {reducer as reduxAsyncConnect} from 'redux-async-connect';
export default combineReducers({
- router: routerStateReducer,
+ routing: routeReducer,
+ reduxAsyncConnect,
auth,
signupCreateReducer,
signupCompleteReducer,
diff --git a/src/routes.js b/src/routes.js
index 37916e7..ea9c99a 100755
--- a/src/routes.js
+++ b/src/routes.js
@@ -24,7 +24,7 @@ import {
} from 'containers';
export default (store) => {
- const requireLogin = (nextState, replaceState, cb) => {
+ const requireLogin = (nextState, replace, cb) => {
/**
* Check if user must be refetched.
* Cases:
@@ -45,7 +45,7 @@ export default (store) => {
const { auth: { user }} = store.getState();
if (!user) {
// oops, not logged in, so can't be here!
- replaceState(null, '/login');
+ replace('/login');
}
cb();
}
@@ -59,12 +59,12 @@ export default (store) => {
}
};
- const redirectToGroups = (nextState, replaceState, cb) => {
+ const redirectToGroups = (nextState, replace, cb) => {
function goToGroups() {
const {auth: { user }} = store.getState();
if (user) {
- replaceState(null, '/groups');
+ replace('/groups');
}
cb();
@@ -78,7 +78,7 @@ export default (store) => {
}
};
- const redirectToGroupsDetail = (nextState, replaceState, cb) => {
+ const redirectToGroupsDetail = (nextState, replace, cb) => {
function getGroupIds() {
const {membershipsReducer: {memberships: {entities}}} = store.getState();
return _.uniq(_.pluck(entities, 'group_id'));
@@ -88,9 +88,9 @@ export default (store) => {
const groupIds = getGroupIds();
if (groupIds.length === 0) {
- replaceState(null, '/onboarding');
+ replace('/onboarding');
} else if (groupIds.length === 1) {
- replaceState(null, `/groups/${groupIds[0]}`);
+ replace(`/groups/${groupIds[0]}`);
}
cb();
@@ -108,12 +108,12 @@ export default (store) => {
-
+
{/* Routes signup */}
-
+
diff --git a/src/server.js b/src/server.js
index f18d095..00dded0 100755
--- a/src/server.js
+++ b/src/server.js
@@ -14,13 +14,12 @@ import Html from './helpers/Html';
import PrettyError from 'pretty-error';
import http from 'http';
-import {ReduxRouter} from 'redux-router';
-import createHistory from 'history/lib/createMemoryHistory';
-import {reduxReactRouter, match} from 'redux-router/server';
+import { match } from 'react-router';
+import { ReduxAsyncConnect, loadOnServer } from 'redux-async-connect';
+import createHistory from 'react-router/lib/createMemoryHistory';
+
import {Provider} from 'react-redux';
-import qs from 'query-string';
import getRoutes from './routes';
-import getStatusFromRoutes from './helpers/getStatusFromRoutes';
const pretty = new PrettyError();
const app = new Express();
@@ -102,7 +101,8 @@ app.use((req, res, next) => {
webpackIsomorphicTools.refresh();
}
const client = new ApiClient(req);
- const store = createStore(reduxReactRouter, getRoutes, createHistory, client);
+ const history = createHistory(req.originalUrl);
+ const store = createStore(history, client);
function hydrateOnClient() {
res.send('\n' +
@@ -114,47 +114,38 @@ app.use((req, res, next) => {
return;
}
- store.dispatch(match(req.originalUrl, (error, redirectLocation, routerState) => {
+
+ match({ history, routes: getRoutes(store), location: req.originalUrl }, (error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (error) {
console.error('ROUTER ERROR:', pretty.render(error));
res.status(500);
hydrateOnClient();
- } else if (!routerState) {
- res.status(500);
- hydrateOnClient();
- } else {
- // Workaround redux-router query string issue:
- // https://github.com/rackt/redux-router/issues/106
- if (routerState.location.search && !routerState.location.query) {
- routerState.location.query = qs.parse(routerState.location.search);
- }
-
- store.getState().router.then(() => {
+ } else if (renderProps) {
+ loadOnServer({...renderProps, store, helpers: {client}}).then(() => {
const component = (
-
+
);
- const status = getStatusFromRoutes(routerState.routes);
-
- if (status) {
- res.status(status);
- }
+ res.status(200);
+ global.navigator = {userAgent: req.headers['user-agent']};
- res.send('\n' +
- ReactDOM.renderToString()
+ res.send(
+ '\n' +
+ ReactDOM.renderToString(
+
+ )
);
-
- }).catch((err) => {
- console.error('DATA FETCHING ERROR:', pretty.render(err));
- res.status(500);
- hydrateOnClient();
});
+ } else {
+ res.status(404).send('Not found');
}
- }));
+ });
});
if (config.port) {