upgraded to react router v4

This commit is contained in:
Jori Lallo
2017-05-17 00:11:13 -07:00
parent f70e2326c0
commit b25298c8f1
23 changed files with 245 additions and 230 deletions

View File

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import Link from 'react-router/lib/Link'; import { Link } from 'react-router-dom';
import DocumentLink from './components/DocumentLink'; import DocumentLink from './components/DocumentLink';

View File

@ -3,7 +3,7 @@ import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import moment from 'moment'; import moment from 'moment';
import Link from 'react-router/lib/Link'; import { Link } from 'react-router-dom';
import styles from './DocumentLink.scss'; import styles from './DocumentLink.scss';

View File

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { toJS } from 'mobx'; import { toJS } from 'mobx';
import { Link } from 'react-router'; import { Link } from 'react-router-dom';
import styles from './DocumentPreview.scss'; import styles from './DocumentPreview.scss';

View File

@ -1,31 +1,20 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { browserHistory } from 'react-router';
import styles from './DropdownMenu.scss'; import styles from './DropdownMenu.scss';
class MenuItem extends React.Component { const MenuItem = ({
onClick = () => { onClick,
if (this.props.to) { children,
browserHistory.push(this.props.to); }: {
} else { onClick?: Function,
this.props.onClick(); children?: React.Element<any>,
} }) => {
}; return (
<div className={styles.menuItem} onClick={onClick}>
render() { {children}
return ( </div>
<div className={styles.menuItem} onClick={this.onClick}> );
{this.props.children}
</div>
);
}
}
MenuItem.propTypes = {
onClick: React.PropTypes.func,
to: React.PropTypes.string,
children: React.PropTypes.node.isRequired,
}; };
// //

View File

@ -1,6 +1,6 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { browserHistory, Link } from 'react-router'; import { Link, withRouter } from 'react-router-dom';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import styled from 'styled-components'; import styled from 'styled-components';
import { observer, inject } from 'mobx-react'; import { observer, inject } from 'mobx-react';
@ -9,7 +9,9 @@ import keydown from 'react-keydown';
import classNames from 'classnames/bind'; import classNames from 'classnames/bind';
import searchIcon from 'assets/icons/search.svg'; import searchIcon from 'assets/icons/search.svg';
import { Flex } from 'reflexbox'; import { Flex } from 'reflexbox';
import { textColor } from 'styles/constants.scss';
import styles from './Layout.scss'; import styles from './Layout.scss';
import DropdownMenu, { MenuItem } from 'components/DropdownMenu'; import DropdownMenu, { MenuItem } from 'components/DropdownMenu';
import LoadingIndicator from 'components/LoadingIndicator'; import LoadingIndicator from 'components/LoadingIndicator';
import UserStore from 'stores/UserStore'; import UserStore from 'stores/UserStore';
@ -17,6 +19,7 @@ import UserStore from 'stores/UserStore';
const cx = classNames.bind(styles); const cx = classNames.bind(styles);
type Props = { type Props = {
history: Object,
children?: ?React.Element<any>, children?: ?React.Element<any>,
actions?: ?React.Element<any>, actions?: ?React.Element<any>,
title?: ?React.Element<any>, title?: ?React.Element<any>,
@ -36,19 +39,23 @@ type Props = {
@keydown(['/', 't']) @keydown(['/', 't'])
search() { search() {
// if (!this.props.user) return; if (!this.props.user) return;
_.defer(() => browserHistory.push('/search')); _.defer(() => this.props.history.push('/search'));
} }
@keydown(['d']) @keydown(['d'])
dashboard() { dashboard() {
// if (!this.props.user) return; if (!this.props.user) return;
_.defer(() => browserHistory.push('/')); _.defer(() => this.props.history.push('/'));
} }
render() { render() {
const user = this.props.user; const user = this.props.user;
const handleLogout = () => {
user.logout(() => this.props.history.push('/'));
};
return ( return (
<div className={styles.container}> <div className={styles.container}>
<Helmet <Helmet
@ -83,21 +90,28 @@ type Props = {
<Flex> <Flex>
{this.props.search && {this.props.search &&
<Flex> <Flex>
<div <Link to="/search">
onClick={this.search} <div className={styles.search} title="Search (/)">
className={styles.search} <img
title="Search (/)" src={searchIcon}
> alt="Search"
<img src={searchIcon} alt="Search" /> />
</div> </div>
</Link>
</Flex>} </Flex>}
<DropdownMenu label={<Avatar src={user.user.avatarUrl} />}> <DropdownMenu label={<Avatar src={user.user.avatarUrl} />}>
<MenuItem to="/settings">Settings</MenuItem> <MenuLink to="/settings">
<MenuItem to="/keyboard-shortcuts"> <MenuItem>Settings</MenuItem>
Keyboard shortcuts </MenuLink>
</MenuItem> <MenuLink to="/keyboard-shortcuts">
<MenuItem to="/developers">API</MenuItem> <MenuItem>
<MenuItem onClick={user.logout}>Logout</MenuItem> Keyboard shortcuts
</MenuItem>
</MenuLink>
<MenuLink to="/developers">
<MenuItem>API</MenuItem>
</MenuLink>
<MenuItem onClick={handleLogout}>Logout</MenuItem>
</DropdownMenu> </DropdownMenu>
</Flex>} </Flex>}
</Flex> </Flex>
@ -118,4 +132,8 @@ const Avatar = styled.img`
border-radius: 50%; border-radius: 50%;
`; `;
export default inject('user')(Layout); const MenuLink = styled(Link)`
color: ${textColor};
`;
export default withRouter(inject('user')(Layout));

View File

@ -1,6 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { Flex } from 'reflexbox'; import { Flex } from 'reflexbox';
import Tree from 'components/Tree'; import Tree from 'components/Tree';
@ -18,6 +19,7 @@ type Props = {
navigationTree: Object, navigationTree: Object,
onNavigationUpdate: Function, onNavigationUpdate: Function,
onNodeCollapse: Function, onNodeCollapse: Function,
history: Object,
}; };
@observer class Sidebar extends React.Component { @observer class Sidebar extends React.Component {
@ -47,6 +49,7 @@ type Props = {
allowUpdates={this.store.isEditing} allowUpdates={this.store.isEditing}
onChange={this.props.onNavigationUpdate} onChange={this.props.onNavigationUpdate}
onCollapse={this.props.onNodeCollapse} onCollapse={this.props.onNodeCollapse}
history={this.props.history}
/> />
</Flex> </Flex>
<Flex auto className={styles.actions}> <Flex auto className={styles.actions}>
@ -79,4 +82,4 @@ type Props = {
} }
} }
export default Sidebar; export default withRouter(Sidebar);

View File

@ -1,6 +1,6 @@
/* eslint-disable */ /* eslint-disable */
import React from 'react'; import React from 'react';
import history from 'utils/History';
import styles from './Tree.scss'; import styles from './Tree.scss';
import classNames from 'classnames/bind'; import classNames from 'classnames/bind';
const cx = classNames.bind(styles); const cx = classNames.bind(styles);
@ -60,6 +60,7 @@ class Node extends React.Component {
paddingLeft={this.props.paddingLeft} paddingLeft={this.props.paddingLeft}
onCollapse={this.props.onCollapse} onCollapse={this.props.onCollapse}
onDragStart={this.props.onDragStart} onDragStart={this.props.onDragStart}
history={this.props.history}
/> />
); );
})} })}
@ -77,8 +78,7 @@ class Node extends React.Component {
onClick = () => { onClick = () => {
const index = this.props.index; const index = this.props.index;
const node = index.node; const node = index.node;
if (!this.isModifying()) this.props.history.push(node.url);
if (!this.isModifying()) history.push(node.url);
}; };
render() { render() {

View File

@ -13,6 +13,7 @@ export default React.createClass({
paddingLeft: React.PropTypes.number, paddingLeft: React.PropTypes.number,
onCollapse: React.PropTypes.func, onCollapse: React.PropTypes.func,
allowUpdates: React.PropTypes.bool, allowUpdates: React.PropTypes.bool,
history: React.PropTypes.object,
}, },
getDefaultProps() { getDefaultProps() {
@ -93,6 +94,7 @@ export default React.createClass({
onDragStart={this.dragStart} onDragStart={this.dragStart}
onCollapse={this.toggleCollapse} onCollapse={this.toggleCollapse}
dragging={dragging && dragging.id} dragging={dragging && dragging.id}
history={this.props.history}
/> />
</div> </div>
); );

View File

@ -2,10 +2,13 @@
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import { Provider } from 'mobx-react'; import { Provider } from 'mobx-react';
import Router from 'react-router/lib/Router'; import {
import Route from 'react-router/lib/Route'; BrowserRouter as Router,
import IndexRoute from 'react-router/lib/IndexRoute'; Switch,
import History from 'utils/History'; Route,
Redirect,
} from 'react-router-dom';
import { Flex } from 'reflexbox';
import stores from 'stores'; import stores from 'stores';
@ -34,80 +37,66 @@ if (__DEV__) {
DevTools = require('mobx-react-devtools').default; // eslint-disable-line global-require DevTools = require('mobx-react-devtools').default; // eslint-disable-line global-require
} }
function requireAuth(nextState, replace) { type AuthProps = {
if (!stores.user.authenticated) { children?: React.Element<any>,
replace({ };
pathname: '/',
state: { nextPathname: nextState.location.pathname }, const Auth = ({ children }: AuthProps) => {
}); if (stores.user.authenticated) {
return <Flex auto>{children}</Flex>;
} else {
return <Redirect to="/" />;
} }
} };
const notFoundSearch = () => <Search notFound />;
const KeyboardShortcuts = () => (
<Flatpage title="Keyboard shortcuts" content={flatpages.keyboard} />
);
const Api = () => <Flatpage title="API" content={flatpages.api} />;
const DocumentEdit = () => <Document editDocument />;
const DocumentNew = () => <Document newDocument />;
const DocumentNewChild = () => <Document newChildDocument />;
render( render(
<div style={{ display: 'flex', flex: 1, height: '100%' }}> <div style={{ display: 'flex', flex: 1, height: '100%' }}>
<Provider {...stores}> <Provider {...stores}>
<Router history={History}> <Router>
<Route path="/"> <Switch>
<IndexRoute component={Home} /> <Route exact path="/" component={Home} />
<Route <Auth>
path="/dashboard" <Switch>
component={Dashboard} <Route exact path="/dashboard" component={Dashboard} />
onEnter={requireAuth} <Route exact path="/collections/:id" component={Atlas} />
/> <Route exact path="/d/:id" component={Document} />
<Route <Route exact path="/d/:id/edit" component={DocumentEdit} />
path="/collections/:id" <Route
component={Atlas} exact
onEnter={requireAuth} path="/collections/:id/new"
/> component={DocumentNew}
<Route />
path="/collections/:id/new" <Route exact path="/d/:id/new" component={DocumentNewChild} />
component={Document}
onEnter={requireAuth}
newDocument
/>
<Route path="/d/:id" component={Document} onEnter={requireAuth} />
<Route
path="/d/:id/edit"
component={Document}
onEnter={requireAuth}
editDocument
/>
<Route
path="/d/:id/new"
component={Document}
onEnter={requireAuth}
newChildDocument
/>
<Route path="/search" component={Search} onEnter={requireAuth} /> <Route exact path="/search" component={Search} />
<Route path="/settings" component={Settings} onEnter={requireAuth} /> <Route exact path="/settings" component={Settings} />
<Route path="/auth/slack" component={SlackAuth} /> <Route exact path="/auth/slack" component={SlackAuth} />
<Route <Route exact path="/auth/slack/commands" component={SlackAuth} />
path="/auth/slack/commands" <Route exact path="/auth/error" component={ErrorAuth} />
component={SlackAuth}
apiPath="/auth.slackCommands"
/>
<Route path="/auth/error" component={ErrorAuth} />
<Flatpage <Route
path="/keyboard-shortcuts" exact
component={Flatpage} path="/keyboard-shortcuts"
title="Keyboard shortcuts" component={KeyboardShortcuts}
content={flatpages.keyboard} />
/> <Route exact path="/developers" component={Api} />
<Flatpage <Route path="/404" component={Error404} />
path="/developers" <Route component={notFoundSearch} />
component={Flatpage} </Switch>
title="API" </Auth>
content={flatpages.api} </Switch>
/>
<Route path="/404" component={Error404} />
<Route path="*" component={Search} sceneType="notFound" />
</Route>
</Router> </Router>
</Provider> </Provider>
{DevTools && <DevTools position={{ bottom: 0, right: 0 }} />} {DevTools && <DevTools position={{ bottom: 0, right: 0 }} />}

View File

@ -1,7 +1,6 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { browserHistory } from 'react-router';
import keydown from 'react-keydown'; import keydown from 'react-keydown';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import _ from 'lodash'; import _ from 'lodash';
@ -21,6 +20,8 @@ import styles from './Atlas.scss';
type Props = { type Props = {
params: Object, params: Object,
history: Object,
match: Object,
keydown: Object, keydown: Object,
}; };
@ -30,11 +31,11 @@ class Atlas extends React.Component {
props: Props; props: Props;
componentDidMount = () => { componentDidMount = () => {
const { id } = this.props.params; const { id } = this.props.match.params;
store.fetchCollection(id, data => { store.fetchCollection(id, data => {
// Forward directly to root document // Forward directly to root document
if (data.type === 'atlas') { if (data.type === 'atlas') {
browserHistory.replace(data.navigationTree.url); this.props.history.replace(data.navigationTree.url);
} }
}); });
}; };
@ -50,7 +51,7 @@ class Atlas extends React.Component {
onCreate = (event: Event) => { onCreate = (event: Event) => {
if (event) event.preventDefault(); if (event) event.preventDefault();
store.collection && browserHistory.push(`${store.collection.url}/new`); store.collection && this.props.history.push(`${store.collection.url}/new`);
}; };
render() { render() {

View File

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { observer, inject } from 'mobx-react'; import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router-dom';
import { Flex } from 'reflexbox'; import { Flex } from 'reflexbox';
import DashboardStore from './DashboardStore'; import DashboardStore from './DashboardStore';

View File

@ -1,6 +1,6 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router-dom';
import type { Document, NavigationNode } from 'types'; import type { Document, NavigationNode } from 'types';
type Props = { type Props = {

View File

@ -1,6 +1,6 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router-dom';
import Layout from 'components/Layout'; import Layout from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';

View File

@ -1,6 +1,6 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router-dom';
import Layout from 'components/Layout'; import Layout from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';

View File

@ -1,5 +1,5 @@
// @flow // @flow
import React, { PropTypes } from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import Layout, { Title } from 'components/Layout'; import Layout, { Title } from 'components/Layout';
@ -8,13 +8,16 @@ import { DocumentHtml } from 'components/Document';
import { convertToMarkdown } from 'utils/markdown'; import { convertToMarkdown } from 'utils/markdown';
type Props = {
title: string,
content: string,
};
@observer class Flatpage extends React.Component { @observer class Flatpage extends React.Component {
static propTypes = { props: Props;
route: PropTypes.object,
};
render() { render() {
const { title, content } = this.props.route; const { title, content } = this.props;
return ( return (
<Layout <Layout

View File

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { observer, inject } from 'mobx-react'; import { observer, inject } from 'mobx-react';
import { browserHistory } from 'react-router'; import { Redirect } from 'react-router';
import { Flex } from 'reflexbox'; import { Flex } from 'reflexbox';
import Layout from 'components/Layout'; import Layout from 'components/Layout';
@ -19,12 +19,6 @@ export default class Home extends React.Component {
location: React.PropTypes.object.isRequired, location: React.PropTypes.object.isRequired,
}; };
componentDidMount = () => {
if (this.props.user.authenticated) {
browserHistory.replace('/dashboard');
}
};
get notifications(): React.Element<any>[] { get notifications(): React.Element<any>[] {
const notifications = []; const notifications = [];
const { state } = this.props.location; const { state } = this.props.location;
@ -46,6 +40,8 @@ export default class Home extends React.Component {
return ( return (
<Flex auto> <Flex auto>
<Layout notifications={this.notifications}> <Layout notifications={this.notifications}>
{this.props.user.authenticated && <Redirect to="/dashboard" />}
<CenteredContent> <CenteredContent>
{showLandingPageCopy && {showLandingPageCopy &&
<div className={styles.intro}> <div className={styles.intro}>

View File

@ -13,8 +13,7 @@ import CenteredContent from 'components/CenteredContent';
import DocumentPreview from 'components/DocumentPreview'; import DocumentPreview from 'components/DocumentPreview';
type Props = { type Props = {
route: Object, notFound: ?boolean,
routeParams: Object,
}; };
@observer class Search extends React.Component { @observer class Search extends React.Component {
@ -26,20 +25,6 @@ type Props = {
this.store = new SearchStore(); this.store = new SearchStore();
} }
componentDidMount = () => {
const { splat } = this.props.routeParams;
if (this.viewNotFound) {
let searchTerm = _.last(splat.split('/'));
searchTerm = searchTerm.split(/[\s-]+/gi).join(' ');
this.store.search(searchTerm);
}
};
get viewNotFound(): boolean {
const { sceneType } = this.props.route;
return sceneType === 'notFound';
}
render() { render() {
const search = _.debounce(searchTerm => { const search = _.debounce(searchTerm => {
this.store.search(searchTerm); this.store.search(searchTerm);
@ -54,7 +39,7 @@ type Props = {
loading={this.store.isFetching} loading={this.store.isFetching}
> >
<CenteredContent> <CenteredContent>
{this.viewNotFound && {this.props.notFound &&
<div> <div>
<h1>Not Found</h1> <h1>Not Found</h1>
<p>We're unable to find the page you're accessing.</p> <p>We're unable to find the page you're accessing.</p>

View File

@ -1,51 +1,65 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { Redirect } from 'react-router';
import queryString from 'query-string';
import { observer, inject } from 'mobx-react'; import { observer, inject } from 'mobx-react';
import { browserHistory } from 'react-router';
import { client } from 'utils/ApiClient'; import { client } from 'utils/ApiClient';
import UserStore from 'stores/UserStore';
type Props = {
user: UserStore,
location: Object,
};
@inject('user') @inject('user')
@observer @observer
class SlackAuth extends React.Component { class SlackAuth extends React.Component {
static propTypes = { props: Props;
user: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired,
route: React.PropTypes.object.isRequired,
};
// $FlowIssue wtf state: { redirectTo: string };
// $FlowFixMe not sure why this breaks
componentDidMount = async () => { componentDidMount = async () => {
const { error, code, state } = this.props.location.query; const { error, code, state } = queryString.parse(
this.props.location.search
);
if (error) { if (error) {
if (error === 'access_denied') { if (error === 'access_denied') {
// User selected "Deny" access on Slack OAuth // User selected "Deny" access on Slack OAuth
browserHistory.push('/'); this.setState({ redirectTo: '/dashboard' });
} else { } else {
browserHistory.push('/auth/error'); this.setState({ redirectTo: '/auth/error' });
}
// $FlowIssue wtf
return;
}
if (this.props.route.apiPath) {
try {
await client.post(this.props.route.apiPath, { code });
browserHistory.replace('/dashboard');
} catch (e) {
browserHistory.push('/auth-error');
} }
} else { } else {
// Regular Slack authentication if (this.props.location.pathname === '/auth/slack/commands') {
const redirectTo = sessionStorage.getItem('redirectTo'); // User adding webhook integrations
sessionStorage.removeItem('redirectTo'); try {
await client.post('/auth.slackCommands', { code });
this.setState({ redirectTo: '/dashboard' });
} catch (e) {
this.setState({ redirectTo: '/auth/error' });
}
} else {
// Regular Slack authentication
const redirectTo = sessionStorage.getItem('redirectTo');
sessionStorage.removeItem('redirectTo');
this.props.user.authWithSlack(code, state, redirectTo); const { success } = await this.props.user.authWithSlack(code, state);
success
? this.setState({ redirectTo: redirectTo || '/dashboard' })
: this.setState({ redirectTo: '/auth/error' });
}
} }
}; };
render() { render() {
return <div />; return (
<div>
{this.state.redirectTo && <Redirect to={this.state.redirectTo} />}
</div>
);
} }
} }

View File

@ -1,7 +1,6 @@
// @flow // @flow
import { observable, action, computed } from 'mobx'; import { observable, action, computed } from 'mobx';
import invariant from 'invariant'; import invariant from 'invariant';
import { browserHistory } from 'react-router';
import { client } from 'utils/ApiClient'; import { client } from 'utils/ApiClient';
import type { User, Team } from 'types'; import type { User, Team } from 'types';
@ -33,10 +32,10 @@ class UserStore {
/* Actions */ /* Actions */
@action logout = () => { @action logout = (cb: Function) => {
this.user = null; this.user = null;
this.token = null; this.token = null;
browserHistory.push('/'); cb();
}; };
@action getOauthState = () => { @action getOauthState = () => {
@ -45,22 +44,20 @@ class UserStore {
return this.oauthState; return this.oauthState;
}; };
@action authWithSlack = async ( @action authWithSlack = async (code: string, state: string) => {
code: string,
state: string,
redirectTo: ?string
) => {
if (state !== this.oauthState) { if (state !== this.oauthState) {
browserHistory.push('/auth-error'); return {
return; success: false,
};
} }
let res; let res;
try { try {
res = await client.post('/auth.slack', { code }); res = await client.post('/auth.slack', { code });
} catch (e) { } catch (e) {
browserHistory.push('/auth-error'); return {
return; success: false,
};
} }
invariant( invariant(
@ -70,7 +67,10 @@ class UserStore {
this.user = res.data.user; this.user = res.data.user;
this.team = res.data.team; this.team = res.data.team;
this.token = res.data.accessToken; this.token = res.data.accessToken;
browserHistory.replace(redirectTo || '/');
return {
success: true,
};
}; };
constructor() { constructor() {

View File

@ -1,6 +1,5 @@
// @flow // @flow
import _ from 'lodash'; import _ from 'lodash';
import { browserHistory } from 'react-router';
import stores from 'stores'; import stores from 'stores';
type Options = { type Options = {
@ -63,15 +62,15 @@ class ApiClient {
return response; return response;
} }
// Handle 404 // // Handle 404
if (response.status === 404) { // if (response.status === 404) {
return browserHistory.push('/404'); // // return browserHistory.push('/404');
} // }
// Handle 401, log out user // // Handle 401, log out user
if (response.status === 401) { // if (response.status === 401) {
return stores.user.logout(); // return stores.user.logout();
} // }
// Handle failed responses // Handle failed responses
const error = {}; const error = {};

View File

@ -1,4 +0,0 @@
// @flow
// https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md
import browserHistory from 'react-router/lib/browserHistory';
export default browserHistory;

View File

@ -141,7 +141,7 @@
"react-helmet": "3.1.0", "react-helmet": "3.1.0",
"react-keydown": "^1.7.3", "react-keydown": "^1.7.3",
"react-portal": "^3.1.0", "react-portal": "^3.1.0",
"react-router": "2.8.0", "react-router-dom": "^4.1.1",
"redis": "^2.6.2", "redis": "^2.6.2",
"redis-lock": "^0.1.0", "redis-lock": "^0.1.0",
"reflexbox": "^2.2.3", "reflexbox": "^2.2.3",
@ -187,4 +187,4 @@
"react-addons-test-utils": "^15.3.1", "react-addons-test-utils": "^15.3.1",
"react-test-renderer": "^15.3.1" "react-test-renderer": "^15.3.1"
} }
} }

View File

@ -2120,7 +2120,7 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
deep-equal@1.0.1, deep-equal@^1.0.0, deep-equal@~1.0.0: deep-equal@1.0.1, deep-equal@~1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
@ -3689,14 +3689,15 @@ history@3.0.0:
query-string "^4.1.0" query-string "^4.1.0"
warning "^2.0.0" warning "^2.0.0"
history@^2.1.2: history@^4.5.1, history@^4.6.0:
version "2.1.2" version "4.6.1"
resolved "https://registry.yarnpkg.com/history/-/history-2.1.2.tgz#4aa2de897a0e4867e4539843be6ecdb2986bfdec" resolved "https://registry.yarnpkg.com/history/-/history-4.6.1.tgz#911cf8eb65728555a94f2b12780a0c531a14d2fd"
dependencies: dependencies:
deep-equal "^1.0.0" invariant "^2.2.1"
invariant "^2.0.0" loose-envify "^1.2.0"
query-string "^3.0.0" resolve-pathname "^2.0.0"
warning "^2.0.0" value-equal "^0.2.0"
warning "^3.0.0"
hoek@2.x.x: hoek@2.x.x:
version "2.16.3" version "2.16.3"
@ -5435,12 +5436,18 @@ longest@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0: loose-envify@^1.0.0, loose-envify@^1.1.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.2.0.tgz#69a65aad3de542cf4ee0f4fe74e8e33c709ccb0f" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.2.0.tgz#69a65aad3de542cf4ee0f4fe74e8e33c709ccb0f"
dependencies: dependencies:
js-tokens "^1.0.1" js-tokens "^1.0.1"
loose-envify@^1.2.0, loose-envify@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
js-tokens "^3.0.0"
loud-rejection@^1.0.0: loud-rejection@^1.0.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@ -6414,7 +6421,7 @@ path-root@^0.1.1:
dependencies: dependencies:
path-root-regex "^0.1.0" path-root-regex "^0.1.0"
path-to-regexp@^1.1.1: path-to-regexp@^1.1.1, path-to-regexp@^1.5.3:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.6.0.tgz#4c59cfeab5e360a2657b180730a4bb4582ecec5b" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.6.0.tgz#4c59cfeab5e360a2657b180730a4bb4582ecec5b"
dependencies: dependencies:
@ -6929,12 +6936,6 @@ qs@~6.4.0:
version "6.4.0" version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
query-string@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-3.0.3.tgz#ae2e14b4d05071d4e9b9eb4873c35b0dcd42e638"
dependencies:
strict-uri-encode "^1.0.0"
query-string@^4.1.0, query-string@^4.3.4: query-string@^4.1.0, query-string@^4.3.4:
version "4.3.4" version "4.3.4"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
@ -7056,14 +7057,25 @@ react-proxy@^1.1.7:
lodash "^4.6.1" lodash "^4.6.1"
react-deep-force-update "^1.0.0" react-deep-force-update "^1.0.0"
react-router@2.8.0: react-router-dom@^4.1.1:
version "2.8.0" version "4.1.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-2.8.0.tgz#cbc629fede81d96d0598c2bae1e348e7d03f6c17" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.1.1.tgz#3021ade1f2c160af97cf94e25594c5f294583025"
dependencies: dependencies:
history "^2.1.2" history "^4.5.1"
loose-envify "^1.3.1"
prop-types "^15.5.4"
react-router "^4.1.1"
react-router@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.1.1.tgz#d448f3b7c1b429a6fbb03395099949c606b1fe95"
dependencies:
history "^4.6.0"
hoist-non-react-statics "^1.2.0" hoist-non-react-statics "^1.2.0"
invariant "^2.2.1" invariant "^2.2.2"
loose-envify "^1.2.0" loose-envify "^1.3.1"
path-to-regexp "^1.5.3"
prop-types "^15.5.4"
warning "^3.0.0" warning "^3.0.0"
react-side-effect@1.0.2: react-side-effect@1.0.2:
@ -7447,6 +7459,10 @@ resolve-from@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
resolve-pathname@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.1.0.tgz#e8358801b86b83b17560d4e3c382d7aef2100944"
resolve@1.1.7, resolve@1.1.x, resolve@^1.0.0, resolve@^1.1.6, resolve@^1.1.7: resolve@1.1.7, resolve@1.1.x, resolve@^1.0.0, resolve@^1.1.6, resolve@^1.1.7:
version "1.1.7" version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
@ -8615,6 +8631,10 @@ validator@5.2.0, validator@^5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-5.2.0.tgz#e66fb3ec352348c1f7232512328738d8d66a9689" resolved "https://registry.yarnpkg.com/validator/-/validator-5.2.0.tgz#e66fb3ec352348c1f7232512328738d8d66a9689"
value-equal@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.2.1.tgz#c220a304361fce6994dbbedaa3c7e1a1b895871d"
vary@^1.0.0, vary@~1.1.0: vary@^1.0.0, vary@~1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140"