Moving redirects to declarative method

This commit is contained in:
Tom Moor 2019-01-19 00:23:39 -08:00
parent d21dd710bb
commit 13501b6d76
17 changed files with 105 additions and 115 deletions

View File

@ -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<Props> {
@observable redirectTo: ?string;
onUploadImage = async (file: File) => {
const result = await uploadFile(file);
return result.url;
@ -42,7 +47,7 @@ class Editor extends React.Component<Props> {
}
}
this.props.history.push(navigateTo);
this.redirectTo = navigateTo;
} else {
window.open(href, '_blank');
}
@ -69,6 +74,8 @@ class Editor extends React.Component<Props> {
};
render() {
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
return (
<RichMarkdownEditor
ref={this.props.forwardedRef}

View File

@ -1,8 +1,7 @@
// @flow
import * as React from 'react';
import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { observer } from 'mobx-react';
import styled from 'styled-components';
import Input, { LabelText, Outline } from 'components/Input';
@ -11,13 +10,11 @@ type Props = {
minHeight?: number,
maxHeight?: number,
readOnly?: boolean,
history: *,
ui: *,
};
@observer
class InputRich extends React.Component<Props> {
@observable editorComponent;
@observable editorComponent: *;
componentDidMount() {
this.loadEditor();
@ -63,4 +60,4 @@ const StyledOutline = styled(Outline)`
}
`;
export default inject('ui')(withRouter(InputRich));
export default InputRich;

View File

@ -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<Props> {
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<Props> {
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<Props> {
const showSidebar = auth.authenticated && user && team;
if (auth.isSuspended) return <ErrorSuspended />;
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
return (
<Container column auto>
@ -122,4 +127,4 @@ const Content = styled(Flex)`
`};
`;
export default withRouter(inject('auth', 'ui', 'documents')(Layout));
export default inject('auth', 'ui', 'documents')(Layout);

View File

@ -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);

View File

@ -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<Props> {
/>
</Section>
<Section>
<Collections
history={this.props.history}
location={this.props.location}
onCreateCollection={this.handleCreateCollection}
/>
<Collections onCreateCollection={this.handleCreateCollection} />
</Section>
</Scrollable>
</Flex>
@ -106,4 +98,4 @@ class MainSidebar extends React.Component<Props> {
}
}
export default withRouter(inject('documents', 'auth', 'ui')(MainSidebar));
export default inject('documents', 'auth', 'ui')(MainSidebar);

View File

@ -12,7 +12,6 @@ import UiStore from 'stores/UiStore';
type Props = {
children: React.Node,
history: Object,
location: Location,
ui: UiStore,
};

View File

@ -62,7 +62,6 @@ class CollectionLink extends React.Component<Props> {
exact={false}
menu={
<CollectionMenu
history={history}
collection={collection}
onOpen={() => (this.menuOpen = true)}
onClose={() => (this.menuOpen = false)}

View File

@ -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<Props> {
}
}
export default inject('collections', 'ui', 'documents')(Collections);
export default inject('collections', 'ui', 'documents')(
withRouter(Collections)
);

View File

@ -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) {
<Router>
<React.Fragment>
<ScrollToTop>
<ScrollToAnchor>
<Routes />
</ScrollToAnchor>
<Routes />
</ScrollToTop>
<Toasts />
</React.Fragment>

View File

@ -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<Props> {
@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<Props> {
};
render() {
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
return (
<DropdownMenu
style={{ marginRight: 10, marginTop: -10 }}
@ -69,4 +73,4 @@ class AccountMenu extends React.Component<Props> {
}
}
export default withRouter(inject('ui', 'auth')(AccountMenu));
export default inject('ui', 'auth')(AccountMenu);

View File

@ -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<Props> {
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<Props> {
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<Props> {
};
render() {
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
const { collection, label, onOpen, onClose } = this.props;
return (

View File

@ -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<Props> {
@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<Props> {
};
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<Props> {
};
render() {
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
const { document, label, className, showPrint, auth } = this.props;
const canShareDocuments = auth.team && auth.team.sharing;
@ -136,4 +140,4 @@ class DocumentMenu extends React.Component<Props> {
}
}
export default withRouter(inject('ui', 'auth')(DocumentMenu));
export default inject('ui', 'auth')(DocumentMenu);

View File

@ -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<Props> {
@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 <Redirect to={this.redirectTo} />;
const { label, document, ...rest } = this.props;
const { collection } = document;
return (
@ -45,4 +50,4 @@ class NewChildDocumentMenu extends React.Component<Props> {
}
}
export default withRouter(NewChildDocumentMenu);
export default NewChildDocumentMenu;

View File

@ -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<Props> {
@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<Props> {
};
render() {
const { collections, label, history, ...rest } = this.props;
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
const { collections, label, ...rest } = this.props;
return (
<DropdownMenu
@ -55,4 +60,4 @@ class NewDocumentMenu extends React.Component<Props> {
}
}
export default withRouter(inject('collections')(NewDocumentMenu));
export default inject('collections')(NewDocumentMenu);

View File

@ -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<Props> {
@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<Props> {
};
render() {
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
const { share, label, onOpen, onClose } = this.props;
return (
@ -61,4 +65,4 @@ class ShareMenu extends React.Component<Props> {
}
}
export default withRouter(inject('shares', 'ui')(ShareMenu));
export default inject('shares', 'ui')(ShareMenu);

View File

@ -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<Props> {
@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<Props> {
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<Props> {
return (
<Actions align="center" justify="flex-end">
<Action>
<CollectionMenu
history={this.props.history}
collection={this.collection}
/>
<CollectionMenu collection={this.collection} />
</Action>
<Separator />
<Action>
@ -117,16 +114,11 @@ class CollectionScene extends React.Component<Props> {
);
}
renderNotFound() {
return <Search notFound />;
}
render() {
const { documents } = this.props;
if (!this.isFetching && !this.collection) {
return this.renderNotFound();
}
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
if (!this.isFetching && !this.collection) return <Search notFound />;
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);

View File

@ -376,7 +376,6 @@ class DocumentScene extends React.Component<Props> {
onCancel={this.onDiscard}
readOnly={!this.isEditing}
toc={!revision}
history={this.props.history}
ui={this.props.ui}
schema={schema}
/>