diff --git a/app/components/Editor/Editor.js b/app/components/Editor/Editor.js index d21f3934e..2abba0e1e 100644 --- a/app/components/Editor/Editor.js +++ b/app/components/Editor/Editor.js @@ -1,5 +1,8 @@ // @flow import * as React from 'react'; +import { Redirect } from 'react-router-dom'; +import { observable } from 'mobx'; +import { observer } from 'mobx-react'; import RichMarkdownEditor from 'rich-markdown-editor'; import { uploadFile } from 'utils/uploadFile'; import isInternalUrl from 'utils/isInternalUrl'; @@ -11,11 +14,13 @@ type Props = { readOnly?: boolean, disableEmbeds?: boolean, forwardedRef: *, - history: *, ui: *, }; +@observer class Editor extends React.Component { + @observable redirectTo: ?string; + onUploadImage = async (file: File) => { const result = await uploadFile(file); return result.url; @@ -42,7 +47,7 @@ class Editor extends React.Component { } } - this.props.history.push(navigateTo); + this.redirectTo = navigateTo; } else { window.open(href, '_blank'); } @@ -69,6 +74,8 @@ class Editor extends React.Component { }; render() { + if (this.redirectTo) return ; + return ( { - @observable editorComponent; + @observable editorComponent: *; componentDidMount() { this.loadEditor(); @@ -63,4 +60,4 @@ const StyledOutline = styled(Outline)` } `; -export default inject('ui')(withRouter(InputRich)); +export default InputRich; diff --git a/app/components/Layout.js b/app/components/Layout.js index d8f223de5..45ae4c43f 100644 --- a/app/components/Layout.js +++ b/app/components/Layout.js @@ -1,10 +1,10 @@ // @flow import * as React from 'react'; -import { Switch, Route, withRouter } from 'react-router-dom'; -import type { Location } from 'react-router-dom'; +import { Switch, Route, Redirect } from 'react-router-dom'; import { Helmet } from 'react-helmet'; import styled from 'styled-components'; import breakpoint from 'styled-components-breakpoint'; +import { observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import keydown from 'react-keydown'; import Analytics from 'components/Analytics'; @@ -16,14 +16,11 @@ import Sidebar from 'components/Sidebar'; import SettingsSidebar from 'components/Sidebar/Settings'; import Modals from 'components/Modals'; import ErrorSuspended from 'scenes/ErrorSuspended'; - import AuthStore from 'stores/AuthStore'; import UiStore from 'stores/UiStore'; import DocumentsStore from 'stores/DocumentsStore'; type Props = { - history: Object, - location: Location, documents: DocumentsStore, children?: ?React.Node, actions?: ?React.Node, @@ -36,17 +33,24 @@ type Props = { @observer class Layout extends React.Component { scrollable: ?HTMLDivElement; + @observable redirectTo: ?string; + + componentDidUpdate() { + if (this.redirectTo) { + this.redirectTo = undefined; + } + } @keydown(['/', 't', 'meta+k']) goToSearch(ev) { ev.preventDefault(); ev.stopPropagation(); - this.props.history.push(searchUrl()); + this.redirectTo = searchUrl(); } @keydown('d') goToDashboard() { - this.props.history.push(homeUrl()); + this.redirectTo = homeUrl(); } @keydown('e') @@ -56,7 +60,7 @@ class Layout extends React.Component { ev.preventDefault(); ev.stopPropagation(); - this.props.history.push(documentEditUrl(activeDocument)); + this.redirectTo = documentEditUrl(activeDocument); } @keydown('shift+/') @@ -70,6 +74,7 @@ class Layout extends React.Component { const showSidebar = auth.authenticated && user && team; if (auth.isSuspended) return ; + if (this.redirectTo) return ; return ( @@ -122,4 +127,4 @@ const Content = styled(Flex)` `}; `; -export default withRouter(inject('auth', 'ui', 'documents')(Layout)); +export default inject('auth', 'ui', 'documents')(Layout); diff --git a/app/components/ScrollToAnchor.js b/app/components/ScrollToAnchor.js deleted file mode 100644 index 349ab8762..000000000 --- a/app/components/ScrollToAnchor.js +++ /dev/null @@ -1,23 +0,0 @@ -// @flow -import * as React from 'react'; -import { withRouter } from 'react-router-dom'; - -class ScrollToAnchor extends React.Component<*> { - componentDidUpdate(prevProps) { - if (this.props.location.hash === prevProps.location.hash) return; - if (window.location.hash === '') return; - - // Delay on timeout to ensure that the DOM is updated first - setImmediate(() => { - const id = window.location.hash.replace('#', ''); - const element = document.getElementById(id); - if (element) element.scrollIntoView(); - }); - } - - render() { - return this.props.children; - } -} - -export default withRouter(ScrollToAnchor); diff --git a/app/components/Sidebar/Main.js b/app/components/Sidebar/Main.js index ad72711e8..4ec967dcf 100644 --- a/app/components/Sidebar/Main.js +++ b/app/components/Sidebar/Main.js @@ -1,7 +1,5 @@ // @flow import * as React from 'react'; -import { withRouter } from 'react-router-dom'; -import type { Location } from 'react-router-dom'; import { observer, inject } from 'mobx-react'; import { HomeIcon, EditIcon, SearchIcon, StarredIcon } from 'outline-icons'; @@ -20,8 +18,6 @@ import DocumentsStore from 'stores/DocumentsStore'; import UiStore from 'stores/UiStore'; type Props = { - history: Object, - location: Location, auth: AuthStore, documents: DocumentsStore, ui: UiStore, @@ -93,11 +89,7 @@ class MainSidebar extends React.Component { />
- +
@@ -106,4 +98,4 @@ class MainSidebar extends React.Component { } } -export default withRouter(inject('documents', 'auth', 'ui')(MainSidebar)); +export default inject('documents', 'auth', 'ui')(MainSidebar); diff --git a/app/components/Sidebar/Sidebar.js b/app/components/Sidebar/Sidebar.js index 5668ccbbf..f20a07cc3 100644 --- a/app/components/Sidebar/Sidebar.js +++ b/app/components/Sidebar/Sidebar.js @@ -12,7 +12,6 @@ import UiStore from 'stores/UiStore'; type Props = { children: React.Node, - history: Object, location: Location, ui: UiStore, }; diff --git a/app/components/Sidebar/components/CollectionLink.js b/app/components/Sidebar/components/CollectionLink.js index 48b056da9..f4d322eb9 100644 --- a/app/components/Sidebar/components/CollectionLink.js +++ b/app/components/Sidebar/components/CollectionLink.js @@ -62,7 +62,6 @@ class CollectionLink extends React.Component { exact={false} menu={ (this.menuOpen = true)} onClose={() => (this.menuOpen = false)} diff --git a/app/components/Sidebar/components/Collections.js b/app/components/Sidebar/components/Collections.js index 330db016e..138291df1 100644 --- a/app/components/Sidebar/components/Collections.js +++ b/app/components/Sidebar/components/Collections.js @@ -1,6 +1,7 @@ // @flow import * as React from 'react'; import { observer, inject } from 'mobx-react'; +import { withRouter } from 'react-router-dom'; import type { Location } from 'react-router-dom'; import Flex from 'shared/components/Flex'; import { PlusIcon } from 'outline-icons'; @@ -62,4 +63,6 @@ class Collections extends React.Component { } } -export default inject('collections', 'ui', 'documents')(Collections); +export default inject('collections', 'ui', 'documents')( + withRouter(Collections) +); diff --git a/app/index.js b/app/index.js index b4336801b..10479c416 100644 --- a/app/index.js +++ b/app/index.js @@ -12,7 +12,6 @@ import 'shared/styles/prism.css'; import ErrorBoundary from 'components/ErrorBoundary'; import ScrollToTop from 'components/ScrollToTop'; -import ScrollToAnchor from 'components/ScrollToAnchor'; import Toasts from 'components/Toasts'; import Routes from './routes'; @@ -33,9 +32,7 @@ if (element) { - - - + diff --git a/app/menus/AccountMenu.js b/app/menus/AccountMenu.js index 464f292ea..bbf86d795 100644 --- a/app/menus/AccountMenu.js +++ b/app/menus/AccountMenu.js @@ -1,6 +1,7 @@ // @flow import * as React from 'react'; -import { withRouter } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; +import { observable } from 'mobx'; import { inject, observer } from 'mobx-react'; import UiStore from 'stores/UiStore'; import AuthStore from 'stores/AuthStore'; @@ -15,19 +16,20 @@ import { type Props = { label: React.Node, - history: Object, ui: UiStore, auth: AuthStore, }; @observer class AccountMenu extends React.Component { + @observable redirectTo: ?string; + handleOpenKeyboardShortcuts = () => { this.props.ui.setActiveModal('keyboard-shortcuts'); }; handleOpenSettings = () => { - this.props.history.push('/settings'); + this.redirectTo = '/settings'; }; handleLogout = () => { @@ -35,6 +37,8 @@ class AccountMenu extends React.Component { }; render() { + if (this.redirectTo) return ; + return ( { } } -export default withRouter(inject('ui', 'auth')(AccountMenu)); +export default inject('ui', 'auth')(AccountMenu); diff --git a/app/menus/CollectionMenu.js b/app/menus/CollectionMenu.js index bf6bf1ecf..5e843155e 100644 --- a/app/menus/CollectionMenu.js +++ b/app/menus/CollectionMenu.js @@ -2,6 +2,7 @@ import * as React from 'react'; import { observable } from 'mobx'; import { inject, observer } from 'mobx-react'; +import { Redirect } from 'react-router-dom'; import styled from 'styled-components'; import { MoreIcon } from 'outline-icons'; import Modal from 'components/Modal'; @@ -18,7 +19,6 @@ type Props = { label?: React.Node, onOpen?: () => *, onClose?: () => *, - history: Object, ui: UiStore, documents: DocumentsStore, collection: Collection, @@ -28,11 +28,12 @@ type Props = { class CollectionMenu extends React.Component { file: ?HTMLInputElement; @observable permissionsModalOpen: boolean = false; + @observable redirectTo: ?string; onNewDocument = (ev: SyntheticEvent<*>) => { ev.preventDefault(); - const { collection, history } = this.props; - history.push(`${collection.url}/new`); + const { collection } = this.props; + this.redirectTo = `${collection.url}/new`; }; onImportDocument = (ev: SyntheticEvent<*>) => { @@ -51,7 +52,7 @@ class CollectionMenu extends React.Component { documents: this.props.documents, collectionId: this.props.collection.id, }); - this.props.history.push(document.url); + this.redirectTo = document.url; } catch (err) { this.props.ui.showToast(err.message); } @@ -85,6 +86,8 @@ class CollectionMenu extends React.Component { }; render() { + if (this.redirectTo) return ; + const { collection, label, onOpen, onClose } = this.props; return ( diff --git a/app/menus/DocumentMenu.js b/app/menus/DocumentMenu.js index 5f12ff506..ad396d46a 100644 --- a/app/menus/DocumentMenu.js +++ b/app/menus/DocumentMenu.js @@ -1,6 +1,7 @@ // @flow import * as React from 'react'; -import { withRouter } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; +import { observable } from 'mobx'; import { inject, observer } from 'mobx-react'; import { MoreIcon } from 'outline-icons'; @@ -14,7 +15,6 @@ type Props = { ui: UiStore, auth: AuthStore, label?: React.Node, - history: Object, document: Document, className: string, showPrint?: boolean, @@ -23,11 +23,13 @@ type Props = { @observer class DocumentMenu extends React.Component { + @observable redirectTo: ?string; + handleNewChild = (ev: SyntheticEvent<*>) => { - const { history, document } = this.props; - history.push( - `${document.collection.url}/new?parentDocument=${document.id}` - ); + const { document } = this.props; + this.redirectTo = `${document.collection.url}/new?parentDocument=${ + document.id + }`; }; handleDelete = (ev: SyntheticEvent<*>) => { @@ -36,16 +38,16 @@ class DocumentMenu extends React.Component { }; handleDocumentHistory = () => { - this.props.history.push(documentHistoryUrl(this.props.document)); + this.redirectTo = documentHistoryUrl(this.props.document); }; handleMove = (ev: SyntheticEvent<*>) => { - this.props.history.push(documentMoveUrl(this.props.document)); + this.redirectTo = documentMoveUrl(this.props.document); }; handleDuplicate = async (ev: SyntheticEvent<*>) => { const duped = await this.props.document.duplicate(); - this.props.history.push(duped.url); + this.redirectTo = duped.url; }; handlePin = (ev: SyntheticEvent<*>) => { @@ -76,6 +78,8 @@ class DocumentMenu extends React.Component { }; render() { + if (this.redirectTo) return ; + const { document, label, className, showPrint, auth } = this.props; const canShareDocuments = auth.team && auth.team.sharing; @@ -136,4 +140,4 @@ class DocumentMenu extends React.Component { } } -export default withRouter(inject('ui', 'auth')(DocumentMenu)); +export default inject('ui', 'auth')(DocumentMenu); diff --git a/app/menus/NewChildDocumentMenu.js b/app/menus/NewChildDocumentMenu.js index 1a98c1152..f9f75c3c6 100644 --- a/app/menus/NewChildDocumentMenu.js +++ b/app/menus/NewChildDocumentMenu.js @@ -1,6 +1,8 @@ // @flow import * as React from 'react'; -import { withRouter } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; +import { observable } from 'mobx'; +import { observer } from 'mobx-react'; import { MoreIcon } from 'outline-icons'; import { newDocumentUrl } from 'utils/routeHelpers'; @@ -9,25 +11,28 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; type Props = { label?: React.Node, - history: Object, document: Document, }; +@observer class NewChildDocumentMenu extends React.Component { + @observable redirectTo: ?string; + handleNewDocument = () => { - const { history, document } = this.props; - history.push(newDocumentUrl(document.collection)); + this.redirectTo = newDocumentUrl(this.props.document.collection); }; handleNewChild = () => { - const { history, document } = this.props; - history.push( - `${document.collection.url}/new?parentDocument=${document.id}` - ); + const { document } = this.props; + this.redirectTo = `${document.collection.url}/new?parentDocument=${ + document.id + }`; }; render() { - const { label, document, history, ...rest } = this.props; + if (this.redirectTo) return ; + + const { label, document, ...rest } = this.props; const { collection } = document; return ( @@ -45,4 +50,4 @@ class NewChildDocumentMenu extends React.Component { } } -export default withRouter(NewChildDocumentMenu); +export default NewChildDocumentMenu; diff --git a/app/menus/NewDocumentMenu.js b/app/menus/NewDocumentMenu.js index 5d5a64c23..ed568323d 100644 --- a/app/menus/NewDocumentMenu.js +++ b/app/menus/NewDocumentMenu.js @@ -1,7 +1,8 @@ // @flow import * as React from 'react'; -import { withRouter } from 'react-router-dom'; -import { inject } from 'mobx-react'; +import { observable } from 'mobx'; +import { inject, observer } from 'mobx-react'; +import { Redirect } from 'react-router-dom'; import { MoreIcon, CollectionIcon, PrivateCollectionIcon } from 'outline-icons'; import { newDocumentUrl } from 'utils/routeHelpers'; @@ -10,13 +11,15 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; type Props = { label?: React.Node, - history: Object, collections: CollectionsStore, }; +@observer class NewDocumentMenu extends React.Component { + @observable redirectTo: ?string; + handleNewDocument = collection => { - this.props.history.push(newDocumentUrl(collection)); + this.redirectTo = newDocumentUrl(collection); }; onOpen = () => { @@ -28,7 +31,9 @@ class NewDocumentMenu extends React.Component { }; render() { - const { collections, label, history, ...rest } = this.props; + if (this.redirectTo) return ; + + const { collections, label, ...rest } = this.props; return ( { } } -export default withRouter(inject('collections')(NewDocumentMenu)); +export default inject('collections')(NewDocumentMenu); diff --git a/app/menus/ShareMenu.js b/app/menus/ShareMenu.js index d2c147d13..114abc033 100644 --- a/app/menus/ShareMenu.js +++ b/app/menus/ShareMenu.js @@ -1,12 +1,12 @@ // @flow import * as React from 'react'; -import { withRouter } from 'react-router-dom'; -import { inject } from 'mobx-react'; +import { Redirect } from 'react-router-dom'; +import { inject, observer } from 'mobx-react'; +import { observable } from 'mobx'; import { MoreIcon } from 'outline-icons'; import CopyToClipboard from 'components/CopyToClipboard'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; - import SharesStore from 'stores/SharesStore'; import UiStore from 'stores/UiStore'; import Share from 'models/Share'; @@ -15,16 +15,18 @@ type Props = { label?: React.Node, onOpen?: () => *, onClose: () => *, - history: Object, shares: SharesStore, ui: UiStore, share: Share, }; +@observer class ShareMenu extends React.Component { + @observable redirectTo: ?string; + handleGoToDocument = (ev: SyntheticEvent<*>) => { ev.preventDefault(); - this.props.history.push(this.props.share.documentUrl); + this.redirectTo = this.props.share.documentUrl; }; handleRevoke = (ev: SyntheticEvent<*>) => { @@ -38,6 +40,8 @@ class ShareMenu extends React.Component { }; render() { + if (this.redirectTo) return ; + const { share, label, onOpen, onClose } = this.props; return ( @@ -61,4 +65,4 @@ class ShareMenu extends React.Component { } } -export default withRouter(inject('shares', 'ui')(ShareMenu)); +export default inject('shares', 'ui')(ShareMenu); diff --git a/app/scenes/Collection.js b/app/scenes/Collection.js index 526bb43db..dfe2c0078 100644 --- a/app/scenes/Collection.js +++ b/app/scenes/Collection.js @@ -2,7 +2,7 @@ import * as React from 'react'; import { observable } from 'mobx'; import { observer, inject } from 'mobx-react'; -import { withRouter, Link, Switch, Route } from 'react-router-dom'; +import { Redirect, Link, Switch, Route } from 'react-router-dom'; import styled from 'styled-components'; import { @@ -42,7 +42,6 @@ type Props = { ui: UiStore, documents: DocumentsStore, collections: CollectionsStore, - history: Object, match: Object, }; @@ -51,6 +50,7 @@ class CollectionScene extends React.Component { @observable collection: ?Collection; @observable isFetching: boolean = true; @observable permissionsModalOpen: boolean = false; + @observable redirectTo: ?string; componentDidMount() { this.loadContent(this.props.match.params.id); @@ -85,7 +85,7 @@ class CollectionScene extends React.Component { ev.preventDefault(); if (this.collection) { - this.props.history.push(`${this.collection.url}/new`); + this.redirectTo = `${this.collection.url}/new`; } }; @@ -102,10 +102,7 @@ class CollectionScene extends React.Component { return ( - + @@ -117,16 +114,11 @@ class CollectionScene extends React.Component { ); } - renderNotFound() { - return ; - } - render() { const { documents } = this.props; - if (!this.isFetching && !this.collection) { - return this.renderNotFound(); - } + if (this.redirectTo) return ; + if (!this.isFetching && !this.collection) return ; const pinnedDocuments = this.collection ? documents.pinnedInCollection(this.collection.id) @@ -296,6 +288,4 @@ const Wrapper = styled(Flex)` margin: 10px 0; `; -export default withRouter( - inject('collections', 'documents', 'ui')(CollectionScene) -); +export default inject('collections', 'documents', 'ui')(CollectionScene); diff --git a/app/scenes/Document/Document.js b/app/scenes/Document/Document.js index 9f6d49364..c13cb5eb1 100644 --- a/app/scenes/Document/Document.js +++ b/app/scenes/Document/Document.js @@ -376,7 +376,6 @@ class DocumentScene extends React.Component { onCancel={this.onDiscard} readOnly={!this.isEditing} toc={!revision} - history={this.props.history} ui={this.props.ui} schema={schema} />