From f70e2326c0571dfc000b046e28c77c51e0d63da3 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Wed, 17 May 2017 00:09:47 -0700 Subject: [PATCH 1/3] Fixed source maps --- webpack.config.dev.js | 15 ++++++++++----- webpack.config.prod.js | 38 ++++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/webpack.config.dev.js b/webpack.config.dev.js index 84f09e3c..2fb71a17 100644 --- a/webpack.config.dev.js +++ b/webpack.config.dev.js @@ -1,3 +1,4 @@ +/* eslint-disable */ var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -5,7 +6,7 @@ const commonWebpackConfig = require('./webpack.config'); const developmentWebpackConfig = Object.assign(commonWebpackConfig, { cache: true, - devtool: 'eval', + devtool: 'eval-source-map', entry: [ 'babel-polyfill', 'babel-regenerator-runtime', @@ -18,11 +19,15 @@ developmentWebpackConfig.module.loaders.push({ test: /\.s?css$/, loader: 'style-loader!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass?sourceMap', }); -developmentWebpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin()); +developmentWebpackConfig.plugins.push( + new webpack.optimize.OccurenceOrderPlugin() +); developmentWebpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); developmentWebpackConfig.plugins.push(new webpack.NoErrorsPlugin()); -developmentWebpackConfig.plugins.push(new HtmlWebpackPlugin({ - title: 'Atlas', -})); +developmentWebpackConfig.plugins.push( + new HtmlWebpackPlugin({ + title: 'Atlas', + }) +); module.exports = developmentWebpackConfig; diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 6c7630ba..1c460696 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -1,18 +1,15 @@ +/* eslint-disable */ var path = require('path'); var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require("extract-text-webpack-plugin"); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); commonWebpackConfig = require('./webpack.config'); productionWebpackConfig = Object.assign(commonWebpackConfig, { cache: true, devtool: 'cheap-module-source-map', - entry: [ - 'babel-polyfill', - 'babel-regenerator-runtime', - './frontend/index', - ], + entry: ['babel-polyfill', 'babel-regenerator-runtime', './frontend/index'], output: { path: path.join(__dirname, 'dist'), filename: 'bundle.[hash].js', @@ -21,25 +18,34 @@ productionWebpackConfig = Object.assign(commonWebpackConfig, { }); productionWebpackConfig.module.loaders.push({ test: /\.s?css$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass?sourceMap') + loader: ExtractTextPlugin.extract( + 'style-loader', + 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass?sourceMap' + ), }); -productionWebpackConfig.plugins.push(new HtmlWebpackPlugin({ - template: 'server/static/index.html' -})); -productionWebpackConfig.plugins.push(new ExtractTextPlugin('styles.[hash].css')); -productionWebpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin()); +productionWebpackConfig.plugins.push( + new HtmlWebpackPlugin({ + template: 'server/static/index.html', + }) +); +productionWebpackConfig.plugins.push( + new ExtractTextPlugin('styles.[hash].css') +); +productionWebpackConfig.plugins.push( + new webpack.optimize.OccurenceOrderPlugin() +); productionWebpackConfig.plugins.push( new webpack.optimize.UglifyJsPlugin({ compress: { - warnings: false - } + warnings: false, + }, }) ); productionWebpackConfig.plugins.push( new webpack.DefinePlugin({ 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } + NODE_ENV: JSON.stringify('production'), + }, }) ); From b25298c8f198e0426e9da15b06cf1608a447bb6a Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Wed, 17 May 2017 00:11:13 -0700 Subject: [PATCH 2/3] upgraded to react router v4 --- .../components/AtlasPreview/AtlasPreview.js | 2 +- .../components/DocumentLink/DocumentLink.js | 2 +- .../DocumentPreview/DocumentPreview.js | 2 +- .../components/DropdownMenu/DropdownMenu.js | 35 ++--- frontend/components/Layout/Layout.js | 56 +++++--- frontend/components/Sidebar/Sidebar.js | 5 +- frontend/components/Tree/Node.js | 6 +- frontend/components/Tree/UiTree.js | 2 + frontend/index.js | 125 ++++++++---------- frontend/scenes/Atlas/Atlas.js | 9 +- frontend/scenes/Dashboard/Dashboard.js | 2 +- .../components/Breadcrumbs/Breadcrumbs.js | 2 +- frontend/scenes/Error404/Error404.js | 2 +- frontend/scenes/ErrorAuth/ErrorAuth.js | 2 +- frontend/scenes/Flatpage/Flatpage.js | 13 +- frontend/scenes/Home/Home.js | 10 +- frontend/scenes/Search/Search.js | 19 +-- frontend/scenes/SlackAuth/SlackAuth.js | 66 +++++---- frontend/stores/UserStore.js | 26 ++-- frontend/utils/ApiClient.js | 17 ++- frontend/utils/History.js | 4 - package.json | 4 +- yarn.lock | 64 ++++++--- 23 files changed, 245 insertions(+), 230 deletions(-) delete mode 100644 frontend/utils/History.js diff --git a/frontend/components/AtlasPreview/AtlasPreview.js b/frontend/components/AtlasPreview/AtlasPreview.js index e760e24f..8665df1b 100644 --- a/frontend/components/AtlasPreview/AtlasPreview.js +++ b/frontend/components/AtlasPreview/AtlasPreview.js @@ -1,7 +1,7 @@ // @flow import React from 'react'; import { observer } from 'mobx-react'; -import Link from 'react-router/lib/Link'; +import { Link } from 'react-router-dom'; import DocumentLink from './components/DocumentLink'; diff --git a/frontend/components/AtlasPreview/components/DocumentLink/DocumentLink.js b/frontend/components/AtlasPreview/components/DocumentLink/DocumentLink.js index e83ef42b..97991b3c 100644 --- a/frontend/components/AtlasPreview/components/DocumentLink/DocumentLink.js +++ b/frontend/components/AtlasPreview/components/DocumentLink/DocumentLink.js @@ -3,7 +3,7 @@ import React from 'react'; import { observer } from 'mobx-react'; import moment from 'moment'; -import Link from 'react-router/lib/Link'; +import { Link } from 'react-router-dom'; import styles from './DocumentLink.scss'; diff --git a/frontend/components/DocumentPreview/DocumentPreview.js b/frontend/components/DocumentPreview/DocumentPreview.js index 0e0777da..52870f06 100644 --- a/frontend/components/DocumentPreview/DocumentPreview.js +++ b/frontend/components/DocumentPreview/DocumentPreview.js @@ -1,7 +1,7 @@ // @flow import React from 'react'; import { toJS } from 'mobx'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import styles from './DocumentPreview.scss'; diff --git a/frontend/components/DropdownMenu/DropdownMenu.js b/frontend/components/DropdownMenu/DropdownMenu.js index 3455ab3c..1b6e5c9f 100644 --- a/frontend/components/DropdownMenu/DropdownMenu.js +++ b/frontend/components/DropdownMenu/DropdownMenu.js @@ -1,31 +1,20 @@ // @flow import React from 'react'; -import { browserHistory } from 'react-router'; import styles from './DropdownMenu.scss'; -class MenuItem extends React.Component { - onClick = () => { - if (this.props.to) { - browserHistory.push(this.props.to); - } else { - this.props.onClick(); - } - }; - - render() { - return ( -
- {this.props.children} -
- ); - } -} - -MenuItem.propTypes = { - onClick: React.PropTypes.func, - to: React.PropTypes.string, - children: React.PropTypes.node.isRequired, +const MenuItem = ({ + onClick, + children, +}: { + onClick?: Function, + children?: React.Element, +}) => { + return ( +
+ {children} +
+ ); }; // diff --git a/frontend/components/Layout/Layout.js b/frontend/components/Layout/Layout.js index 4c204c95..9ee33a01 100644 --- a/frontend/components/Layout/Layout.js +++ b/frontend/components/Layout/Layout.js @@ -1,6 +1,6 @@ // @flow import React from 'react'; -import { browserHistory, Link } from 'react-router'; +import { Link, withRouter } from 'react-router-dom'; import Helmet from 'react-helmet'; import styled from 'styled-components'; import { observer, inject } from 'mobx-react'; @@ -9,7 +9,9 @@ import keydown from 'react-keydown'; import classNames from 'classnames/bind'; import searchIcon from 'assets/icons/search.svg'; import { Flex } from 'reflexbox'; +import { textColor } from 'styles/constants.scss'; import styles from './Layout.scss'; + import DropdownMenu, { MenuItem } from 'components/DropdownMenu'; import LoadingIndicator from 'components/LoadingIndicator'; import UserStore from 'stores/UserStore'; @@ -17,6 +19,7 @@ import UserStore from 'stores/UserStore'; const cx = classNames.bind(styles); type Props = { + history: Object, children?: ?React.Element, actions?: ?React.Element, title?: ?React.Element, @@ -36,19 +39,23 @@ type Props = { @keydown(['/', 't']) search() { - // if (!this.props.user) return; - _.defer(() => browserHistory.push('/search')); + if (!this.props.user) return; + _.defer(() => this.props.history.push('/search')); } @keydown(['d']) dashboard() { - // if (!this.props.user) return; - _.defer(() => browserHistory.push('/')); + if (!this.props.user) return; + _.defer(() => this.props.history.push('/')); } render() { const user = this.props.user; + const handleLogout = () => { + user.logout(() => this.props.history.push('/')); + }; + return (
{this.props.search && -
- Search -
+ +
+ Search +
+
} }> - Settings - - Keyboard shortcuts - - API - Logout + + Settings + + + + Keyboard shortcuts + + + + API + + Logout } @@ -118,4 +132,8 @@ const Avatar = styled.img` border-radius: 50%; `; -export default inject('user')(Layout); +const MenuLink = styled(Link)` + color: ${textColor}; +`; + +export default withRouter(inject('user')(Layout)); diff --git a/frontend/components/Sidebar/Sidebar.js b/frontend/components/Sidebar/Sidebar.js index 72044dd5..d4a9f58b 100644 --- a/frontend/components/Sidebar/Sidebar.js +++ b/frontend/components/Sidebar/Sidebar.js @@ -1,6 +1,7 @@ // @flow import React from 'react'; import { observer } from 'mobx-react'; +import { withRouter } from 'react-router-dom'; import { Flex } from 'reflexbox'; import Tree from 'components/Tree'; @@ -18,6 +19,7 @@ type Props = { navigationTree: Object, onNavigationUpdate: Function, onNodeCollapse: Function, + history: Object, }; @observer class Sidebar extends React.Component { @@ -47,6 +49,7 @@ type Props = { allowUpdates={this.store.isEditing} onChange={this.props.onNavigationUpdate} onCollapse={this.props.onNodeCollapse} + history={this.props.history} /> @@ -79,4 +82,4 @@ type Props = { } } -export default Sidebar; +export default withRouter(Sidebar); diff --git a/frontend/components/Tree/Node.js b/frontend/components/Tree/Node.js index 6b06f3c7..ba8157a3 100644 --- a/frontend/components/Tree/Node.js +++ b/frontend/components/Tree/Node.js @@ -1,6 +1,6 @@ /* eslint-disable */ import React from 'react'; -import history from 'utils/History'; + import styles from './Tree.scss'; import classNames from 'classnames/bind'; const cx = classNames.bind(styles); @@ -60,6 +60,7 @@ class Node extends React.Component { paddingLeft={this.props.paddingLeft} onCollapse={this.props.onCollapse} onDragStart={this.props.onDragStart} + history={this.props.history} /> ); })} @@ -77,8 +78,7 @@ class Node extends React.Component { onClick = () => { const index = this.props.index; const node = index.node; - - if (!this.isModifying()) history.push(node.url); + if (!this.isModifying()) this.props.history.push(node.url); }; render() { diff --git a/frontend/components/Tree/UiTree.js b/frontend/components/Tree/UiTree.js index c0c97bc0..02d774d3 100644 --- a/frontend/components/Tree/UiTree.js +++ b/frontend/components/Tree/UiTree.js @@ -13,6 +13,7 @@ export default React.createClass({ paddingLeft: React.PropTypes.number, onCollapse: React.PropTypes.func, allowUpdates: React.PropTypes.bool, + history: React.PropTypes.object, }, getDefaultProps() { @@ -93,6 +94,7 @@ export default React.createClass({ onDragStart={this.dragStart} onCollapse={this.toggleCollapse} dragging={dragging && dragging.id} + history={this.props.history} />
); diff --git a/frontend/index.js b/frontend/index.js index 2c4aff64..d08aaf8e 100644 --- a/frontend/index.js +++ b/frontend/index.js @@ -2,10 +2,13 @@ import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'mobx-react'; -import Router from 'react-router/lib/Router'; -import Route from 'react-router/lib/Route'; -import IndexRoute from 'react-router/lib/IndexRoute'; -import History from 'utils/History'; +import { + BrowserRouter as Router, + Switch, + Route, + Redirect, +} from 'react-router-dom'; +import { Flex } from 'reflexbox'; import stores from 'stores'; @@ -34,80 +37,66 @@ if (__DEV__) { DevTools = require('mobx-react-devtools').default; // eslint-disable-line global-require } -function requireAuth(nextState, replace) { - if (!stores.user.authenticated) { - replace({ - pathname: '/', - state: { nextPathname: nextState.location.pathname }, - }); +type AuthProps = { + children?: React.Element, +}; + +const Auth = ({ children }: AuthProps) => { + if (stores.user.authenticated) { + return {children}; + } else { + return ; } -} +}; + +const notFoundSearch = () => ; +const KeyboardShortcuts = () => ( + +); +const Api = () => ; +const DocumentEdit = () => ; +const DocumentNew = () => ; +const DocumentNewChild = () => ; render(
- - - + + + - - - - - - + + + + + + + + - - + + - - - + + + - + + - - - - - + + + + + {DevTools && } diff --git a/frontend/scenes/Atlas/Atlas.js b/frontend/scenes/Atlas/Atlas.js index ee294095..5fc61133 100644 --- a/frontend/scenes/Atlas/Atlas.js +++ b/frontend/scenes/Atlas/Atlas.js @@ -1,7 +1,6 @@ // @flow import React from 'react'; import { observer } from 'mobx-react'; -import { browserHistory } from 'react-router'; import keydown from 'react-keydown'; import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; import _ from 'lodash'; @@ -21,6 +20,8 @@ import styles from './Atlas.scss'; type Props = { params: Object, + history: Object, + match: Object, keydown: Object, }; @@ -30,11 +31,11 @@ class Atlas extends React.Component { props: Props; componentDidMount = () => { - const { id } = this.props.params; + const { id } = this.props.match.params; store.fetchCollection(id, data => { // Forward directly to root document 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) => { if (event) event.preventDefault(); - store.collection && browserHistory.push(`${store.collection.url}/new`); + store.collection && this.props.history.push(`${store.collection.url}/new`); }; render() { diff --git a/frontend/scenes/Dashboard/Dashboard.js b/frontend/scenes/Dashboard/Dashboard.js index 0a3af592..5e094468 100644 --- a/frontend/scenes/Dashboard/Dashboard.js +++ b/frontend/scenes/Dashboard/Dashboard.js @@ -1,7 +1,7 @@ // @flow import React from 'react'; import { observer, inject } from 'mobx-react'; -import { withRouter } from 'react-router'; +import { withRouter } from 'react-router-dom'; import { Flex } from 'reflexbox'; import DashboardStore from './DashboardStore'; diff --git a/frontend/scenes/Document/components/Breadcrumbs/Breadcrumbs.js b/frontend/scenes/Document/components/Breadcrumbs/Breadcrumbs.js index 6e1f9dbd..14e59899 100644 --- a/frontend/scenes/Document/components/Breadcrumbs/Breadcrumbs.js +++ b/frontend/scenes/Document/components/Breadcrumbs/Breadcrumbs.js @@ -1,6 +1,6 @@ // @flow import React from 'react'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import type { Document, NavigationNode } from 'types'; type Props = { diff --git a/frontend/scenes/Error404/Error404.js b/frontend/scenes/Error404/Error404.js index 89b3eb2e..bc1c767f 100644 --- a/frontend/scenes/Error404/Error404.js +++ b/frontend/scenes/Error404/Error404.js @@ -1,6 +1,6 @@ // @flow import React from 'react'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import Layout from 'components/Layout'; import CenteredContent from 'components/CenteredContent'; diff --git a/frontend/scenes/ErrorAuth/ErrorAuth.js b/frontend/scenes/ErrorAuth/ErrorAuth.js index 40f8447b..e5746fe6 100644 --- a/frontend/scenes/ErrorAuth/ErrorAuth.js +++ b/frontend/scenes/ErrorAuth/ErrorAuth.js @@ -1,6 +1,6 @@ // @flow import React from 'react'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import Layout from 'components/Layout'; import CenteredContent from 'components/CenteredContent'; diff --git a/frontend/scenes/Flatpage/Flatpage.js b/frontend/scenes/Flatpage/Flatpage.js index 240f264c..0abd0b53 100644 --- a/frontend/scenes/Flatpage/Flatpage.js +++ b/frontend/scenes/Flatpage/Flatpage.js @@ -1,5 +1,5 @@ // @flow -import React, { PropTypes } from 'react'; +import React from 'react'; import { observer } from 'mobx-react'; import Layout, { Title } from 'components/Layout'; @@ -8,13 +8,16 @@ import { DocumentHtml } from 'components/Document'; import { convertToMarkdown } from 'utils/markdown'; +type Props = { + title: string, + content: string, +}; + @observer class Flatpage extends React.Component { - static propTypes = { - route: PropTypes.object, - }; + props: Props; render() { - const { title, content } = this.props.route; + const { title, content } = this.props; return ( { - if (this.props.user.authenticated) { - browserHistory.replace('/dashboard'); - } - }; - get notifications(): React.Element[] { const notifications = []; const { state } = this.props.location; @@ -46,6 +40,8 @@ export default class Home extends React.Component { return ( + {this.props.user.authenticated && } + {showLandingPageCopy &&
diff --git a/frontend/scenes/Search/Search.js b/frontend/scenes/Search/Search.js index b8839db2..44291f8b 100644 --- a/frontend/scenes/Search/Search.js +++ b/frontend/scenes/Search/Search.js @@ -13,8 +13,7 @@ import CenteredContent from 'components/CenteredContent'; import DocumentPreview from 'components/DocumentPreview'; type Props = { - route: Object, - routeParams: Object, + notFound: ?boolean, }; @observer class Search extends React.Component { @@ -26,20 +25,6 @@ type Props = { 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() { const search = _.debounce(searchTerm => { this.store.search(searchTerm); @@ -54,7 +39,7 @@ type Props = { loading={this.store.isFetching} > - {this.viewNotFound && + {this.props.notFound &&

Not Found

We're unable to find the page you're accessing.

diff --git a/frontend/scenes/SlackAuth/SlackAuth.js b/frontend/scenes/SlackAuth/SlackAuth.js index dd3f800f..e29af7bf 100644 --- a/frontend/scenes/SlackAuth/SlackAuth.js +++ b/frontend/scenes/SlackAuth/SlackAuth.js @@ -1,51 +1,65 @@ // @flow import React from 'react'; +import { Redirect } from 'react-router'; +import queryString from 'query-string'; import { observer, inject } from 'mobx-react'; -import { browserHistory } from 'react-router'; import { client } from 'utils/ApiClient'; +import UserStore from 'stores/UserStore'; + +type Props = { + user: UserStore, + location: Object, +}; + @inject('user') @observer class SlackAuth extends React.Component { - static propTypes = { - user: React.PropTypes.object.isRequired, - location: React.PropTypes.object.isRequired, - route: React.PropTypes.object.isRequired, - }; + props: Props; - // $FlowIssue wtf + state: { redirectTo: string }; + + // $FlowFixMe not sure why this breaks componentDidMount = async () => { - const { error, code, state } = this.props.location.query; + const { error, code, state } = queryString.parse( + this.props.location.search + ); if (error) { if (error === 'access_denied') { // User selected "Deny" access on Slack OAuth - browserHistory.push('/'); + this.setState({ redirectTo: '/dashboard' }); } else { - browserHistory.push('/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'); + this.setState({ redirectTo: '/auth/error' }); } } else { - // Regular Slack authentication - const redirectTo = sessionStorage.getItem('redirectTo'); - sessionStorage.removeItem('redirectTo'); + if (this.props.location.pathname === '/auth/slack/commands') { + // User adding webhook integrations + 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() { - return
; + return ( +
+ {this.state.redirectTo && } +
+ ); } } diff --git a/frontend/stores/UserStore.js b/frontend/stores/UserStore.js index b002e82f..30a90845 100644 --- a/frontend/stores/UserStore.js +++ b/frontend/stores/UserStore.js @@ -1,7 +1,6 @@ // @flow import { observable, action, computed } from 'mobx'; import invariant from 'invariant'; -import { browserHistory } from 'react-router'; import { client } from 'utils/ApiClient'; import type { User, Team } from 'types'; @@ -33,10 +32,10 @@ class UserStore { /* Actions */ - @action logout = () => { + @action logout = (cb: Function) => { this.user = null; this.token = null; - browserHistory.push('/'); + cb(); }; @action getOauthState = () => { @@ -45,22 +44,20 @@ class UserStore { return this.oauthState; }; - @action authWithSlack = async ( - code: string, - state: string, - redirectTo: ?string - ) => { + @action authWithSlack = async (code: string, state: string) => { if (state !== this.oauthState) { - browserHistory.push('/auth-error'); - return; + return { + success: false, + }; } let res; try { res = await client.post('/auth.slack', { code }); } catch (e) { - browserHistory.push('/auth-error'); - return; + return { + success: false, + }; } invariant( @@ -70,7 +67,10 @@ class UserStore { this.user = res.data.user; this.team = res.data.team; this.token = res.data.accessToken; - browserHistory.replace(redirectTo || '/'); + + return { + success: true, + }; }; constructor() { diff --git a/frontend/utils/ApiClient.js b/frontend/utils/ApiClient.js index 841979a9..4c304cc6 100644 --- a/frontend/utils/ApiClient.js +++ b/frontend/utils/ApiClient.js @@ -1,6 +1,5 @@ // @flow import _ from 'lodash'; -import { browserHistory } from 'react-router'; import stores from 'stores'; type Options = { @@ -63,15 +62,15 @@ class ApiClient { return response; } - // Handle 404 - if (response.status === 404) { - return browserHistory.push('/404'); - } + // // Handle 404 + // if (response.status === 404) { + // // return browserHistory.push('/404'); + // } - // Handle 401, log out user - if (response.status === 401) { - return stores.user.logout(); - } + // // Handle 401, log out user + // if (response.status === 401) { + // return stores.user.logout(); + // } // Handle failed responses const error = {}; diff --git a/frontend/utils/History.js b/frontend/utils/History.js deleted file mode 100644 index 76b01758..00000000 --- a/frontend/utils/History.js +++ /dev/null @@ -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; diff --git a/package.json b/package.json index da27c903..fd574c93 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "react-helmet": "3.1.0", "react-keydown": "^1.7.3", "react-portal": "^3.1.0", - "react-router": "2.8.0", + "react-router-dom": "^4.1.1", "redis": "^2.6.2", "redis-lock": "^0.1.0", "reflexbox": "^2.2.3", @@ -187,4 +187,4 @@ "react-addons-test-utils": "^15.3.1", "react-test-renderer": "^15.3.1" } -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e55054f1..076a6f53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2120,7 +2120,7 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" 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" 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" warning "^2.0.0" -history@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/history/-/history-2.1.2.tgz#4aa2de897a0e4867e4539843be6ecdb2986bfdec" +history@^4.5.1, history@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.6.1.tgz#911cf8eb65728555a94f2b12780a0c531a14d2fd" dependencies: - deep-equal "^1.0.0" - invariant "^2.0.0" - query-string "^3.0.0" - warning "^2.0.0" + invariant "^2.2.1" + loose-envify "^1.2.0" + resolve-pathname "^2.0.0" + value-equal "^0.2.0" + warning "^3.0.0" hoek@2.x.x: version "2.16.3" @@ -5435,12 +5436,18 @@ longest@^1.0.1: version "1.0.1" 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" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.2.0.tgz#69a65aad3de542cf4ee0f4fe74e8e33c709ccb0f" dependencies: 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: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -6414,7 +6421,7 @@ path-root@^0.1.1: dependencies: 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" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.6.0.tgz#4c59cfeab5e360a2657b180730a4bb4582ecec5b" dependencies: @@ -6929,12 +6936,6 @@ qs@~6.4.0: version "6.4.0" 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: version "4.3.4" 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" react-deep-force-update "^1.0.0" -react-router@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-2.8.0.tgz#cbc629fede81d96d0598c2bae1e348e7d03f6c17" +react-router-dom@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.1.1.tgz#3021ade1f2c160af97cf94e25594c5f294583025" 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" - invariant "^2.2.1" - loose-envify "^1.2.0" + invariant "^2.2.2" + loose-envify "^1.3.1" + path-to-regexp "^1.5.3" + prop-types "^15.5.4" warning "^3.0.0" react-side-effect@1.0.2: @@ -7447,6 +7459,10 @@ resolve-from@^1.0.0: version "1.0.1" 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: version "1.1.7" 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" 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: version "1.1.0" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" From 451b6ad058eb440d54eee0881e6e144e577369c9 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Wed, 17 May 2017 20:31:33 -0700 Subject: [PATCH 3/3] Router fixes --- frontend/scenes/Document/Document.js | 60 +++++++++++---------- frontend/scenes/Document/DocumentStore.js | 17 ++++-- frontend/scenes/Document/components/Menu.js | 9 ++-- 3 files changed, 50 insertions(+), 36 deletions(-) diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js index 2eea552b..26704de5 100644 --- a/frontend/scenes/Document/Document.js +++ b/frontend/scenes/Document/Document.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import get from 'lodash/get'; import { observer } from 'mobx-react'; -import { browserHistory, withRouter } from 'react-router'; +import { withRouter } from 'react-router'; import { Flex } from 'reflexbox'; import DocumentStore from './DocumentStore'; @@ -19,10 +19,12 @@ Are you sure you want to discard them? `; type Props = { - route: Object, - router: Object, - params: Object, + match: Object, + history: Object, keydown: Object, + editDocument?: boolean, + newChildDocument?: boolean, + editDocument?: boolean, }; @withRouter @@ -33,39 +35,39 @@ class Document extends Component { constructor(props: Props) { super(props); - this.store = new DocumentStore({}); + this.store = new DocumentStore({ history: this.props.history }); } componentDidMount = () => { - if (this.props.route.newDocument) { - this.store.collectionId = this.props.params.id; + if (this.props.newDocument) { + this.store.collectionId = this.props.match.params.id; this.store.newDocument = true; - } else if (this.props.route.editDocument) { - this.store.documentId = this.props.params.id; + } else if (this.props.editDocument) { + this.store.documentId = this.props.match.params.id; this.store.fetchDocument(); - } else if (this.props.route.newChildDocument) { - this.store.documentId = this.props.params.id; + } else if (this.props.newChildDocument) { + this.store.documentId = this.props.match.params.id; this.store.newChildDocument = true; this.store.fetchDocument(); } else { - this.store.documentId = this.props.params.id; + this.store.documentId = this.props.match.params.id; this.store.newDocument = false; this.store.fetchDocument(); } - // Prevent user from accidentally leaving with unsaved changes - const remove = this.props.router.setRouteLeaveHook(this.props.route, () => { - if (this.store.hasPendingChanges) { - return confirm(DISCARD_CHANGES); - } - remove(); - return null; - }); + // // Prevent user from accidentally leaving with unsaved changes + // const remove = this.props.router.setRouteLeaveHook(this.props.route, () => { + // if (this.store.hasPendingChanges) { + // return confirm(DISCARD_CHANGES); + // } + // remove(); + // return null; + // }); }; onEdit = () => { const url = `${this.store.document.url}/edit`; - browserHistory.push(url); + this.props.history.push(url); }; onSave = (options: { redirect?: boolean } = {}) => { @@ -85,20 +87,22 @@ class Document extends Component { }; onCancel = () => { - browserHistory.goBack(); + this.props.history.goBack(); }; render() { - const { route } = this.props; - const isNew = route.newDocument || route.newChildDocument; - const isEditing = route.editDocument; + const isNew = this.props.newDocument || this.props.newChildDocument; + const isEditing = this.props.editDocument; const title = ( ); - const titleText = `${get(this.store, 'document.collection.name')} - ${get(this.store, 'document.title')}`; + + const titleText = + this.store.document && + `${get(this.store, 'document.collection.name')} - ${get(this.store, 'document.title')}`; const actions = ( @@ -107,7 +111,7 @@ class Document extends Component { ? : Edit} @@ -140,7 +144,7 @@ class Document extends Component { onChange={this.store.updateText} onSave={this.onSave} onCancel={this.onCancel} - readOnly={!this.props.route.editDocument} + readOnly={!this.props.editDocument} />} ); diff --git a/frontend/scenes/Document/DocumentStore.js b/frontend/scenes/Document/DocumentStore.js index 608331c3..9a5aa2da 100644 --- a/frontend/scenes/Document/DocumentStore.js +++ b/frontend/scenes/Document/DocumentStore.js @@ -1,6 +1,5 @@ // @flow import { observable, action, computed, toJS } from 'mobx'; -import { browserHistory } from 'react-router'; import get from 'lodash/get'; import invariant from 'invariant'; import { client } from 'utils/ApiClient'; @@ -23,6 +22,10 @@ const parseHeader = text => { return ''; }; +type Options = { + history: Object, +}; + class DocumentStore { @observable collapsedNodes: string[] = []; @observable documentId = null; @@ -38,6 +41,8 @@ class DocumentStore { @observable isSaving: boolean = false; @observable isUploading: boolean = false; + history: Object; + /* Computed */ @computed get isCollection(): boolean { @@ -136,7 +141,7 @@ class DocumentStore { const { url } = res.data; this.hasPendingChanges = false; - if (redirect) browserHistory.push(url); + if (redirect) this.history.push(url); } catch (e) { console.error('Something went wrong'); } @@ -162,7 +167,7 @@ class DocumentStore { const { url } = res.data; this.hasPendingChanges = false; - if (redirect) browserHistory.push(url); + if (redirect) this.history.push(url); } catch (e) { console.error('Something went wrong'); } @@ -174,7 +179,7 @@ class DocumentStore { try { await client.post('/documents.delete', { id: this.documentId }); - browserHistory.push(this.document.collection.id); + this.history.push(this.document.collection.url); } catch (e) { console.error('Something went wrong'); } @@ -192,6 +197,10 @@ class DocumentStore { @action updateUploading = (uploading: boolean) => { this.isUploading = uploading; }; + + constructor(options: Options) { + this.history = options.history; + } } export default DocumentStore; diff --git a/frontend/scenes/Document/components/Menu.js b/frontend/scenes/Document/components/Menu.js index 665a941c..38e493d8 100644 --- a/frontend/scenes/Document/components/Menu.js +++ b/frontend/scenes/Document/components/Menu.js @@ -2,13 +2,14 @@ import React, { Component } from 'react'; import invariant from 'invariant'; import get from 'lodash/get'; -import { browserHistory } from 'react-router'; +import { withRouter } from 'react-router-dom'; import { observer } from 'mobx-react'; import type { Document as DocumentType } from 'types'; import DropdownMenu, { MenuItem, MoreIcon } from 'components/DropdownMenu'; import DocumentStore from '../DocumentStore'; type Props = { + history: Object, document: DocumentType, collectionTree: ?Object, store: DocumentStore, @@ -19,12 +20,12 @@ type Props = { onCreateDocument = () => { invariant(this.props.collectionTree, 'collectionTree is not available'); - browserHistory.push(`${this.props.collectionTree.url}/new`); + this.props.history.push(`${this.props.collectionTree.url}/new`); }; onCreateChild = () => { invariant(this.props.document, 'Document is not available'); - browserHistory.push(`${this.props.document.url}/new`); + this.props.history.push(`${this.props.document.url}/new`); }; onDelete = () => { @@ -75,4 +76,4 @@ type Props = { } } -export default Menu; +export default withRouter(Menu);