diff options
| author | Andrej Mihajlov <and@codeispoetry.ru> | 2017-05-24 18:09:36 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@codeispoetry.ru> | 2017-05-27 13:37:34 +0100 |
| commit | 0cd424fb094f82029f2a2e34bfd16ee8e5e87b10 (patch) | |
| tree | 010e48f769c5f687daca1499026745eaa7e961ea | |
| parent | f0eeffed6cfd055cf1aa1d23d240a0a8ded42466 (diff) | |
| download | mullvadvpn-0cd424fb094f82029f2a2e34bfd16ee8e5e87b10.tar.xz mullvadvpn-0cd424fb094f82029f2a2e34bfd16ee8e5e87b10.zip | |
Migrate to react-router v4 and react-router-redux v5-alpha
| -rw-r--r-- | app/app.js | 26 | ||||
| -rw-r--r-- | app/containers/AccountPage.js | 5 | ||||
| -rw-r--r-- | app/containers/App.js | 16 | ||||
| -rw-r--r-- | app/containers/ConnectPage.js | 5 | ||||
| -rw-r--r-- | app/containers/LoginPage.js | 3 | ||||
| -rw-r--r-- | app/containers/SelectLocationPage.js | 5 | ||||
| -rw-r--r-- | app/containers/SettingsPage.js | 5 | ||||
| -rw-r--r-- | app/routes.js | 95 | ||||
| -rw-r--r-- | app/store.js | 4 | ||||
| -rw-r--r-- | package.json | 5 |
10 files changed, 92 insertions, 77 deletions
diff --git a/app/app.js b/app/app.js index 7ce2e3418d..cab3bcb896 100644 --- a/app/app.js +++ b/app/app.js @@ -2,8 +2,8 @@ import path from 'path'; import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; -import { Router, createMemoryHistory } from 'react-router'; -import { syncHistoryWithStore } from 'react-router-redux'; +import { ConnectedRouter } from 'react-router-redux'; +import { createMemoryHistory } from 'history'; import { webFrame, ipcRenderer } from 'electron'; import log from 'electron-log'; import makeRoutes from './routes'; @@ -18,7 +18,6 @@ import { LoginState, ConnectionState, TrayIconType } from './enums'; const initialState = {}; const memoryHistory = createMemoryHistory(); const store = configureStore(initialState, memoryHistory); -const routes = makeRoutes(store); const backend = new Backend(); // reset login state if user quit the app during login @@ -35,15 +34,6 @@ if(store.getState().connect.status === ConnectionState.connecting) { })); } -// desperately trying to fix https://github.com/reactjs/react-router-redux/issues/534 -memoryHistory.replace('/'); - -const recentLocation = (store.getState().routing || {}).locationBeforeTransitions; -const routerHistory = syncHistoryWithStore(memoryHistory, store, { adjustUrlOnReplay: true }); -if(recentLocation && recentLocation.pathname) { - routerHistory.replace(recentLocation.pathname); -} - // Tray icon /** @@ -86,14 +76,6 @@ backend.on(Backend.EventType.disconnect, updateTrayIcon); // force update tray updateTrayIcon(); -// helper method for router to pass backend down the component tree -const createElement = (Component, props) => { - const newProps = { ...props, backend }; - return ( - <Component {...newProps} /> - ); -}; - const rootElement = document.querySelector(document.currentScript.getAttribute('data-container')); // disable smart pinch. @@ -112,7 +94,9 @@ ipcRenderer.send('on-browser-window-ready'); ReactDOM.render( <Provider store={ store }> - <Router history={ routerHistory } routes={ routes } createElement={ createElement } /> + <ConnectedRouter history={ memoryHistory }> + { makeRoutes(store.getState, { backend }) } + </ConnectedRouter> </Provider>, rootElement ); diff --git a/app/containers/AccountPage.js b/app/containers/AccountPage.js index bc5e3d3b73..5e695e1d4a 100644 --- a/app/containers/AccountPage.js +++ b/app/containers/AccountPage.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { push } from 'react-router-redux'; import Account from '../components/Account'; import userActions from '../actions/user'; import { shell } from 'electron'; @@ -13,8 +14,8 @@ const mapDispatchToProps = (dispatch, props) => { const { logout } = bindActionCreators(userActions, dispatch); return { onLogout: () => logout(props.backend), - onClose: () => props.router.push('/settings'), - onViewAccount: () => props.router.push('/settings/account'), + onClose: () => dispatch(push('/settings')), + onViewAccount: () => dispatch(push('/settings/account')), onExternalLink: (type) => shell.openExternal(links[type]) }; }; diff --git a/app/containers/App.js b/app/containers/App.js deleted file mode 100644 index 2b45c8ed12..0000000000 --- a/app/containers/App.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, { Component, PropTypes } from 'react'; - -export default class App extends Component { - static propTypes = { - children: PropTypes.element.isRequired - }; - - render() { - return ( - <div> - {this.props.children} - </div> - ); - } -} - diff --git a/app/containers/ConnectPage.js b/app/containers/ConnectPage.js index 5f7067b709..fdcc9463af 100644 --- a/app/containers/ConnectPage.js +++ b/app/containers/ConnectPage.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { push } from 'react-router-redux'; import { shell } from 'electron'; import { links } from '../config'; import Connect from '../components/Connect'; @@ -14,8 +15,8 @@ const mapDispatchToProps = (dispatch, props) => { const { backend } = props; return { - onSettings: () => props.router.push('/settings'), - onSelectLocation: () => props.router.push('/select-location'), + onSettings: () => dispatch(push('/settings')), + onSelectLocation: () => dispatch(push('/select-location')), onConnect: (addr) => connect(backend, addr), onCopyIP: () => copyIPAddress(), onDisconnect: () => disconnect(backend), diff --git a/app/containers/LoginPage.js b/app/containers/LoginPage.js index 571156ecb2..f5a75ca989 100644 --- a/app/containers/LoginPage.js +++ b/app/containers/LoginPage.js @@ -1,6 +1,7 @@ import { shell } from 'electron'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { push } from 'react-router-redux'; import Login from '../components/Login'; import userActions from '../actions/user'; import { LoginState } from '../enums'; @@ -11,7 +12,7 @@ const mapDispatchToProps = (dispatch, props) => { const { loginChange, login } = bindActionCreators(userActions, dispatch); const { backend } = props; return { - onSettings: () => props.router.push('/settings'), + onSettings: () => dispatch(push('/settings')), onLogin: (account) => login(backend, account), onChange: (account) => loginChange({ account }), onFirstChangeAfterFailure: () => loginChange({ status: LoginState.none, error: null }), diff --git a/app/containers/SelectLocationPage.js b/app/containers/SelectLocationPage.js index 81fbcfe203..462ffd7bb4 100644 --- a/app/containers/SelectLocationPage.js +++ b/app/containers/SelectLocationPage.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { push } from 'react-router-redux'; import SelectLocation from '../components/SelectLocation'; import settingsActions from '../actions/settings'; @@ -8,11 +9,11 @@ const mapDispatchToProps = (dispatch, props) => { const { backend } = props; const settings = bindActionCreators(settingsActions, dispatch); return { - onClose: () => props.router.push('/connect'), + onClose: () => dispatch(push('/connect')), onSelect: (preferredServer) => { const server = backend.serverInfo(preferredServer); - props.router.push('/connect'); + dispatch(push('/connect')); // add delay to let the map load setTimeout(() => { diff --git a/app/containers/SettingsPage.js b/app/containers/SettingsPage.js index fa1a98a44b..b0bc7868b0 100644 --- a/app/containers/SettingsPage.js +++ b/app/containers/SettingsPage.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import { push } from 'react-router-redux'; import Settings from '../components/Settings'; import userActions from '../actions/user'; import settingsActions from '../actions/settings'; @@ -16,8 +17,8 @@ const mapDispatchToProps = (dispatch, props) => { return { onQuit: () => remote.app.quit(), onLogout: () => logout(props.backend), - onClose: () => props.router.push('/connect'), - onViewAccount: () => props.router.push('/settings/account'), + onClose: () => dispatch(push('/connect')), + onViewAccount: () => dispatch(push('/settings/account')), onExternalLink: (type) => shell.openExternal(links[type]), onUpdateSettings: updateSettings }; diff --git a/app/routes.js b/app/routes.js index e4bdd21471..bcee8ca5e1 100644 --- a/app/routes.js +++ b/app/routes.js @@ -1,55 +1,96 @@ import React from 'react'; -import { Route, IndexRoute } from 'react-router'; - -import App from './containers/App'; +import { Switch, Route, Redirect } from 'react-router'; import LoginPage from './containers/LoginPage'; import ConnectPage from './containers/ConnectPage'; import SettingsPage from './containers/SettingsPage'; import AccountPage from './containers/AccountPage'; import SelectLocationPage from './containers/SelectLocationPage'; - import { LoginState } from './enums'; /** * Create routes * * @export - * @param {Redux.Store} store + * @param {function} getState - function to get redux state + * @param {object} componentProps - extra props to propagate across components * @returns {React.element} */ -export default function makeRoutes(store) { +export default function makeRoutes(getState, componentProps) { + + /** + * Merge props and render component + * @param {React.Component} component - component class + * @param {...} rest - props + * @private + */ + const renderMergedProps = (component, ...rest) => { + const finalProps = Object.assign({}, componentProps, ...rest); + return ( + React.createElement(component, finalProps) + ); + }; /** - * Ensures that user is redirected to /connect if logged in + * Renders public route + * Example: <PublicRoute path="/" component={ MyComponent } /> * @private */ - const ensureConnect = (nextState, replace) => { - let { user } = store.getState(); - if(user.status === LoginState.ok) { - replace('/connect'); - } + const PublicRoute = ({ component, ...rest }) => { + return ( + <Route {...rest} render={ (routeProps) => { + return renderMergedProps(component, routeProps, ...rest); + }} /> + ); }; /** - * Ensures that user is redirected to / login if not logged in + * Renders protected route that requires authentication, otherwise redirects to / + * Example: <PrivateRoute path="/protected" component={ MyComponent } /> * @private */ - const ensureLoggedIn = (nextState, replace) => { - let { user } = store.getState(); - if(user.status !== LoginState.ok) { - replace('/'); - } + const PrivateRoute = ({ component, ...rest }) => { + return ( + <Route {...rest} render={ (routeProps) => { + const { user } = getState(); + const isLoggedIn = user.status === LoginState.ok; + + if(isLoggedIn) { + return renderMergedProps(component, routeProps, ...rest); + } else { + return (<Redirect to={ '/' } />); + } + }} /> + ); + }; + + /** + * Renders login route that is only available to non-authenticated + * users. Otherwise this route redirects user to /connect. + * Example: <LoginRoute path="/login" component={ MyComponent } /> + * @private + */ + const LoginRoute = ({ component, ...rest }) => { + return ( + <Route {...rest} render={ (routeProps) => { + const { user } = getState(); + const isLoggedIn = user.status === LoginState.ok; + + if(isLoggedIn) { + return (<Redirect to={ '/connect' } />); + } else { + return renderMergedProps(component, routeProps, ...rest); + } + }} /> + ); }; return ( - <Route path="/" component={ App }> - <IndexRoute component={ LoginPage } onEnter={ ensureConnect } /> - <Route path="connect" component={ ConnectPage } onEnter={ ensureLoggedIn } /> - <Route path="settings"> - <IndexRoute component={ SettingsPage } /> - <Route path="account" component={ AccountPage } onEnter={ ensureLoggedIn } /> - </Route> - <Route path="select-location" component={ SelectLocationPage } onEnter={ ensureLoggedIn } /> - </Route> + <Switch> + <LoginRoute exact path="/" component={ LoginPage } /> + <PrivateRoute exact path="/connect" component={ ConnectPage } /> + <PublicRoute exact path="/settings" component={ SettingsPage } /> + <PrivateRoute path="/settings/account" component={ AccountPage } /> + <PrivateRoute path="/select-location" component={ SelectLocationPage } /> + </Switch> ); } diff --git a/app/store.js b/app/store.js index cbe1e3bb97..6c48cbcde8 100644 --- a/app/store.js +++ b/app/store.js @@ -1,5 +1,5 @@ import { createStore, applyMiddleware, combineReducers, compose } from 'redux'; -import { routerMiddleware, routerReducer as routing, push, replace } from 'react-router-redux'; +import { routerMiddleware, routerReducer, push, replace } from 'react-router-redux'; import persistState from 'redux-localstorage'; import thunk from 'redux-thunk'; @@ -30,7 +30,7 @@ export default function configureStore(initialState, routerHistory) { }; const reducers = { - user, connect, settings, routing + user, connect, settings, router: routerReducer }; const middlewares = [ thunk, router ]; diff --git a/package.json b/package.json index 8ad7abf859..8f41608b96 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "babel-runtime": "^6.22.0", "cheap-ruler": "^2.4.1", "electron-log": "^2.2.0", + "history": "^4.6.1", "jsonrpc-lite": "^1.2.3", "moment": "^2.17.1", "react": "^15.4.2", @@ -21,8 +22,8 @@ "react-if": "^2.1.0", "react-mapbox-gl": "^1.3.0", "react-redux": "^5.0.2", - "react-router": "^3.0.2", - "react-router-redux": "^4.0.7", + "react-router": "^4.1.1", + "react-router-redux": "5.0.0-alpha.6", "redux": "^3.0.0", "redux-actions": "^2.0.1", "redux-localstorage": "^0.4.1", |
