Merge pull request #871 from outline/less-with-router
Reduce withRouter usage
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
import * as React from 'react';
|
||||
import { observable } from 'mobx';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import { omit } from 'lodash';
|
||||
import invariant from 'invariant';
|
||||
import importFile from 'utils/importFile';
|
||||
import Dropzone from 'react-dropzone';
|
||||
@@ -18,11 +18,11 @@ type Props = {
|
||||
rejectClassName?: string,
|
||||
documents: DocumentsStore,
|
||||
disabled: boolean,
|
||||
history: Object,
|
||||
};
|
||||
|
||||
const GlobalStyles = createGlobalStyle`
|
||||
export const GlobalStyles = createGlobalStyle`
|
||||
.activeDropZone {
|
||||
border-radius: 4px;
|
||||
background: ${props => props.theme.slateDark};
|
||||
svg { fill: ${props => props.theme.white}; }
|
||||
}
|
||||
@@ -35,6 +35,7 @@ const GlobalStyles = createGlobalStyle`
|
||||
@observer
|
||||
class DropToImport extends React.Component<Props> {
|
||||
@observable isImporting: boolean = false;
|
||||
@observable redirectTo: ?string;
|
||||
|
||||
onDropAccepted = async (files = []) => {
|
||||
this.isImporting = true;
|
||||
@@ -59,7 +60,7 @@ class DropToImport extends React.Component<Props> {
|
||||
});
|
||||
|
||||
if (redirect) {
|
||||
this.props.history.push(doc.url);
|
||||
this.redirectTo = doc.url;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -68,16 +69,15 @@ class DropToImport extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const props = omit(
|
||||
this.props,
|
||||
'history',
|
||||
'documentId',
|
||||
'collectionId',
|
||||
'documents',
|
||||
'disabled',
|
||||
'menuOpen'
|
||||
);
|
||||
const {
|
||||
documentId,
|
||||
collectionId,
|
||||
documents,
|
||||
disabled,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
|
||||
if (this.props.disabled) return this.props.children;
|
||||
|
||||
return (
|
||||
@@ -88,9 +88,8 @@ class DropToImport extends React.Component<Props> {
|
||||
disableClick
|
||||
disablePreview
|
||||
multiple
|
||||
{...props}
|
||||
{...rest}
|
||||
>
|
||||
<GlobalStyles />
|
||||
{this.isImporting && <LoadingIndicator />}
|
||||
{this.props.children}
|
||||
</Dropzone>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
@@ -12,18 +12,16 @@ import Flex from 'shared/components/Flex';
|
||||
import { documentEditUrl, homeUrl, searchUrl } from 'utils/routeHelpers';
|
||||
|
||||
import { LoadingIndicatorBar } from 'components/LoadingIndicator';
|
||||
import { GlobalStyles } from 'components/DropToImport';
|
||||
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 +34,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 +61,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 +75,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>
|
||||
@@ -98,6 +104,7 @@ class Layout extends React.Component<Props> {
|
||||
</Content>
|
||||
</Flex>
|
||||
<Modals ui={ui} />
|
||||
<GlobalStyles />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -122,4 +129,4 @@ const Content = styled(Flex)`
|
||||
`};
|
||||
`;
|
||||
|
||||
export default withRouter(inject('auth', 'ui', 'documents')(Layout));
|
||||
export default inject('auth', 'ui', 'documents')(Layout);
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -12,7 +12,6 @@ import UiStore from 'stores/UiStore';
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
history: Object,
|
||||
location: Location,
|
||||
ui: UiStore,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@ import DropToImport from 'components/DropToImport';
|
||||
import Flex from 'shared/components/Flex';
|
||||
|
||||
type Props = {
|
||||
history: Object,
|
||||
collection: Collection,
|
||||
ui: UiStore,
|
||||
activeDocument: ?Document,
|
||||
@@ -25,19 +24,12 @@ class CollectionLink extends React.Component<Props> {
|
||||
@observable menuOpen = false;
|
||||
|
||||
render() {
|
||||
const {
|
||||
history,
|
||||
collection,
|
||||
activeDocument,
|
||||
prefetchDocument,
|
||||
ui,
|
||||
} = this.props;
|
||||
const { collection, activeDocument, prefetchDocument, ui } = this.props;
|
||||
const expanded = collection.id === ui.activeCollectionId;
|
||||
|
||||
return (
|
||||
<DropToImport
|
||||
key={collection.id}
|
||||
history={history}
|
||||
collectionId={collection.id}
|
||||
activeClassName="activeDropZone"
|
||||
>
|
||||
@@ -62,7 +54,6 @@ class CollectionLink extends React.Component<Props> {
|
||||
exact={false}
|
||||
menu={
|
||||
<CollectionMenu
|
||||
history={history}
|
||||
collection={collection}
|
||||
onOpen={() => (this.menuOpen = true)}
|
||||
onClose={() => (this.menuOpen = false)}
|
||||
@@ -73,7 +64,6 @@ class CollectionLink extends React.Component<Props> {
|
||||
{collection.documents.map(document => (
|
||||
<DocumentLink
|
||||
key={document.id}
|
||||
history={history}
|
||||
document={document}
|
||||
activeDocument={activeDocument}
|
||||
prefetchDocument={prefetchDocument}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -10,7 +10,6 @@ import { type NavigationNode } from 'types';
|
||||
|
||||
type Props = {
|
||||
document: NavigationNode,
|
||||
history: Object,
|
||||
activeDocument: ?Document,
|
||||
activeDocumentRef?: (?HTMLElement) => *,
|
||||
prefetchDocument: (documentId: string) => Promise<void>,
|
||||
@@ -34,7 +33,6 @@ class DocumentLink extends React.Component<Props> {
|
||||
activeDocumentRef,
|
||||
prefetchDocument,
|
||||
depth,
|
||||
history,
|
||||
} = this.props;
|
||||
|
||||
const isActiveDocument =
|
||||
@@ -55,11 +53,7 @@ class DocumentLink extends React.Component<Props> {
|
||||
ref={isActiveDocument ? activeDocumentRef : undefined}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
>
|
||||
<DropToImport
|
||||
history={history}
|
||||
documentId={document.id}
|
||||
activeClassName="activeDropZone"
|
||||
>
|
||||
<DropToImport documentId={document.id} activeClassName="activeDropZone">
|
||||
<SidebarLink
|
||||
to={{
|
||||
pathname: document.url,
|
||||
@@ -74,7 +68,6 @@ class DocumentLink extends React.Component<Props> {
|
||||
{document.children.map(childDocument => (
|
||||
<DocumentLink
|
||||
key={childDocument.id}
|
||||
history={history}
|
||||
document={childDocument}
|
||||
activeDocument={activeDocument}
|
||||
prefetchDocument={prefetchDocument}
|
||||
|
||||
@@ -9,6 +9,7 @@ import Team from '../models/Team';
|
||||
|
||||
type Props = {
|
||||
team: Team,
|
||||
disabled: boolean,
|
||||
};
|
||||
|
||||
@observer
|
||||
@@ -20,7 +21,8 @@ class TipInvite extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { team } = this.props;
|
||||
const { team, disabled } = this.props;
|
||||
if (disabled) return null;
|
||||
|
||||
return (
|
||||
<Tip id="subdomain-invite">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,24 @@ import {
|
||||
|
||||
type Props = {
|
||||
label: React.Node,
|
||||
history: Object,
|
||||
ui: UiStore,
|
||||
auth: AuthStore,
|
||||
};
|
||||
|
||||
@observer
|
||||
class AccountMenu extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
|
||||
componentDidUpdate() {
|
||||
this.redirectTo = undefined;
|
||||
}
|
||||
|
||||
handleOpenKeyboardShortcuts = () => {
|
||||
this.props.ui.setActiveModal('keyboard-shortcuts');
|
||||
};
|
||||
|
||||
handleOpenSettings = () => {
|
||||
this.props.history.push('/settings');
|
||||
this.redirectTo = '/settings';
|
||||
};
|
||||
|
||||
handleLogout = () => {
|
||||
@@ -35,6 +41,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 +77,4 @@ class AccountMenu extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(inject('ui', 'auth')(AccountMenu));
|
||||
export default inject('ui', 'auth')(AccountMenu);
|
||||
|
||||
@@ -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,16 @@ type Props = {
|
||||
class CollectionMenu extends React.Component<Props> {
|
||||
file: ?HTMLInputElement;
|
||||
@observable permissionsModalOpen: boolean = false;
|
||||
@observable redirectTo: ?string;
|
||||
|
||||
componentDidUpdate() {
|
||||
this.redirectTo = undefined;
|
||||
}
|
||||
|
||||
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 +56,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 +90,8 @@ class CollectionMenu extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
|
||||
|
||||
const { collection, label, onOpen, onClose } = this.props;
|
||||
|
||||
return (
|
||||
|
||||
@@ -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,17 @@ type Props = {
|
||||
|
||||
@observer
|
||||
class DocumentMenu extends React.Component<Props> {
|
||||
@observable redirectTo: ?string;
|
||||
|
||||
componentDidUpdate() {
|
||||
this.redirectTo = undefined;
|
||||
}
|
||||
|
||||
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 +42,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 +82,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 +144,4 @@ class DocumentMenu extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(inject('ui', 'auth')(DocumentMenu));
|
||||
export default inject('ui', 'auth')(DocumentMenu);
|
||||
|
||||
@@ -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,32 @@ 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;
|
||||
|
||||
componentDidUpdate() {
|
||||
this.redirectTo = undefined;
|
||||
}
|
||||
|
||||
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 +54,4 @@ class NewChildDocumentMenu extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(NewChildDocumentMenu);
|
||||
export default NewChildDocumentMenu;
|
||||
|
||||
@@ -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,19 @@ 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;
|
||||
|
||||
componentDidUpdate() {
|
||||
this.redirectTo = undefined;
|
||||
}
|
||||
|
||||
handleNewDocument = collection => {
|
||||
this.props.history.push(newDocumentUrl(collection));
|
||||
this.redirectTo = newDocumentUrl(collection);
|
||||
};
|
||||
|
||||
onOpen = () => {
|
||||
@@ -28,7 +35,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 +64,4 @@ class NewDocumentMenu extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(inject('collections')(NewDocumentMenu));
|
||||
export default inject('collections')(NewDocumentMenu);
|
||||
|
||||
@@ -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,22 @@ 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;
|
||||
|
||||
componentDidUpdate() {
|
||||
this.redirectTo = undefined;
|
||||
}
|
||||
|
||||
handleGoToDocument = (ev: SyntheticEvent<*>) => {
|
||||
ev.preventDefault();
|
||||
this.props.history.push(this.props.share.documentUrl);
|
||||
this.redirectTo = this.props.share.documentUrl;
|
||||
};
|
||||
|
||||
handleRevoke = (ev: SyntheticEvent<*>) => {
|
||||
@@ -38,6 +44,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 +69,4 @@ class ShareMenu extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(inject('shares', 'ui')(ShareMenu));
|
||||
export default inject('shares', 'ui')(ShareMenu);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -41,6 +41,7 @@ import Revision from 'models/Revision';
|
||||
|
||||
import schema from './schema';
|
||||
|
||||
let EditorImport;
|
||||
const AUTOSAVE_DELAY = 3000;
|
||||
const IS_DIRTY_DELAY = 500;
|
||||
const MARK_AS_VIEWED_AFTER = 3000;
|
||||
@@ -69,7 +70,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
viewTimeout: TimeoutID;
|
||||
getEditorText: () => string;
|
||||
|
||||
@observable editorComponent;
|
||||
@observable editorComponent = EditorImport;
|
||||
@observable document: ?Document;
|
||||
@observable revision: ?Revision;
|
||||
@observable newDocument: ?Document;
|
||||
@@ -167,10 +168,13 @@ class DocumentScene extends React.Component<Props> {
|
||||
}
|
||||
|
||||
if (!this.revision) {
|
||||
// Update url to match the current one
|
||||
this.props.history.replace(
|
||||
updateDocumentUrl(props.match.url, document.url)
|
||||
const canonicalUrl = updateDocumentUrl(
|
||||
props.match.url,
|
||||
document.url
|
||||
);
|
||||
if (this.props.location.pathname !== canonicalUrl) {
|
||||
this.props.history.replace(canonicalUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -181,8 +185,11 @@ class DocumentScene extends React.Component<Props> {
|
||||
};
|
||||
|
||||
loadEditor = async () => {
|
||||
const EditorImport = await import('./components/Editor');
|
||||
this.editorComponent = EditorImport.default;
|
||||
if (this.editorComponent) return;
|
||||
|
||||
const Imported = await import('./components/Editor');
|
||||
EditorImport = Imported.default;
|
||||
this.editorComponent = EditorImport;
|
||||
};
|
||||
|
||||
get isEditing() {
|
||||
@@ -357,7 +364,6 @@ class DocumentScene extends React.Component<Props> {
|
||||
isSaving={this.isSaving}
|
||||
isPublishing={this.isPublishing}
|
||||
savingIsDisabled={!document.allowSave}
|
||||
history={this.props.history}
|
||||
onDiscard={this.onDiscard}
|
||||
onSave={this.onSave}
|
||||
/>
|
||||
@@ -376,7 +382,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}
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as React from 'react';
|
||||
import { throttle } from 'lodash';
|
||||
import { observable } from 'mobx';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import { NewDocumentIcon } from 'outline-icons';
|
||||
@@ -33,7 +34,6 @@ type Props = {
|
||||
publish?: boolean,
|
||||
autosave?: boolean,
|
||||
}) => *,
|
||||
history: Object,
|
||||
auth: AuthStore,
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@ type Props = {
|
||||
class Header extends React.Component<Props> {
|
||||
@observable isScrolled = false;
|
||||
@observable showShareModal = false;
|
||||
@observable redirectTo: ?string;
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
@@ -57,7 +58,7 @@ class Header extends React.Component<Props> {
|
||||
handleScroll = throttle(this.updateIsScrolled, 50);
|
||||
|
||||
handleEdit = () => {
|
||||
this.props.history.push(documentEditUrl(this.props.document));
|
||||
this.redirectTo = documentEditUrl(this.props.document);
|
||||
};
|
||||
|
||||
handleSave = () => {
|
||||
@@ -86,6 +87,8 @@ class Header extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
|
||||
|
||||
const {
|
||||
document,
|
||||
isEditing,
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
"react-waypoint": "^7.3.1",
|
||||
"redis": "^2.6.2",
|
||||
"redis-lock": "^0.1.0",
|
||||
"rich-markdown-editor": "^8.0.0",
|
||||
"rich-markdown-editor": "^8.0.2",
|
||||
"safestart": "1.1.0",
|
||||
"sequelize": "4.28.6",
|
||||
"sequelize-cli": "^2.7.0",
|
||||
@@ -166,7 +166,7 @@
|
||||
"string-hash": "^1.1.0",
|
||||
"string-replace-to-array": "^1.0.3",
|
||||
"style-loader": "^0.18.2",
|
||||
"styled-components": "4.0.3",
|
||||
"styled-components": "^4.1.3",
|
||||
"styled-components-breakpoint": "^1.0.1",
|
||||
"styled-components-grid": "^2.2.0",
|
||||
"styled-normalize": "^2.2.1",
|
||||
|
||||
42
yarn.lock
42
yarn.lock
@@ -39,6 +39,12 @@
|
||||
dependencies:
|
||||
"@babel/types" "7.0.0-beta.31"
|
||||
|
||||
"@babel/helper-module-imports@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d"
|
||||
dependencies:
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@babel/template@7.0.0-beta.31":
|
||||
version "7.0.0-beta.31"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.31.tgz#577bb29389f6c497c3e7d014617e7d6713f68bda"
|
||||
@@ -77,15 +83,19 @@
|
||||
lodash "^4.17.10"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@emotion/is-prop-valid@^0.6.8":
|
||||
version "0.6.8"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz#68ad02831da41213a2089d2cab4e8ac8b30cbd85"
|
||||
"@emotion/is-prop-valid@^0.7.3":
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz#a6bf4fa5387cbba59d44e698a4680f481a8da6cc"
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.6.6"
|
||||
"@emotion/memoize" "0.7.1"
|
||||
|
||||
"@emotion/memoize@^0.6.6":
|
||||
version "0.6.6"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.6.6.tgz#004b98298d04c7ca3b4f50ca2035d4f60d2eed1b"
|
||||
"@emotion/memoize@0.7.1":
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.1.tgz#e93c13942592cf5ef01aa8297444dc192beee52f"
|
||||
|
||||
"@emotion/unitless@^0.7.0":
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.3.tgz#6310a047f12d21a1036fb031317219892440416f"
|
||||
|
||||
"@tommoor/remove-markdown@0.3.1":
|
||||
version "0.3.1"
|
||||
@@ -8803,9 +8813,9 @@ retry-axios@0.3.2, retry-axios@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-0.3.2.tgz#5757c80f585b4cc4c4986aa2ffd47a60c6d35e13"
|
||||
|
||||
rich-markdown-editor@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-8.0.0.tgz#d11a7b81e819bcef1680b60359b8761baeadbb01"
|
||||
rich-markdown-editor@^8.0.2:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-8.0.2.tgz#b642d8c85794eca1df4e6026b2c07f66eb7445ad"
|
||||
dependencies:
|
||||
"@wikifactory/slate-edit-blockquote" "^0.7.1"
|
||||
"@wikifactory/slate-edit-code" "^0.16.0"
|
||||
@@ -8836,7 +8846,7 @@ rich-markdown-editor@^8.0.0:
|
||||
slate-react "^0.21.15"
|
||||
slate-schema-violations "^0.1.39"
|
||||
slugify "^1.3.0"
|
||||
styled-components "4.0.3"
|
||||
styled-components "^4.1.3"
|
||||
|
||||
right-align@^0.1.1:
|
||||
version "0.1.3"
|
||||
@@ -9637,11 +9647,13 @@ styled-components-grid@^2.2.0:
|
||||
react-create-component-from-tag-prop "^1.4.0"
|
||||
styled-components-breakpoint "^2.0.2"
|
||||
|
||||
styled-components@4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.0.3.tgz#6c1a95a93857aa613fdfc26ad40899217100d8c3"
|
||||
styled-components@^4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.1.3.tgz#4472447208e618b57e84deaaeb6acd34a5e0fe9b"
|
||||
dependencies:
|
||||
"@emotion/is-prop-valid" "^0.6.8"
|
||||
"@babel/helper-module-imports" "^7.0.0"
|
||||
"@emotion/is-prop-valid" "^0.7.3"
|
||||
"@emotion/unitless" "^0.7.0"
|
||||
babel-plugin-styled-components ">= 1"
|
||||
css-to-react-native "^2.2.2"
|
||||
memoize-one "^4.0.0"
|
||||
|
||||
Reference in New Issue
Block a user