refactor: flow typing (#1012)

* fix: padding

* fix: Minor button alignment issues

* feat: Add icon to invite people button

* WIP
This commit is contained in:
Tom Moor
2019-08-08 23:09:09 -07:00
committed by GitHub
parent 7b2eea0009
commit d024d31f66
64 changed files with 207 additions and 144 deletions

View File

@ -2,7 +2,11 @@
/* global ga */
import * as React from 'react';
export default class Analytics extends React.Component<*> {
type Props = {
children?: React.Node,
};
export default class Analytics extends React.Component<Props> {
componentDidMount() {
if (!process.env.GOOGLE_ANALYTICS_ID) return;

View File

@ -66,14 +66,14 @@ class ColorPicker extends React.Component<Props> {
};
@action
focusOnCustomColor = (event: SyntheticEvent<*>) => {
focusOnCustomColor = (event: SyntheticEvent<>) => {
this.selectedColor = '';
this.customColorSelected = true;
this.fireCallback();
};
@action
setCustomColor = (event: SyntheticEvent<*>) => {
setCustomColor = (event: SyntheticEvent<HTMLElement>) => {
let target = event.target;
if (target instanceof HTMLInputElement) {
const color = target.value;

View File

@ -5,12 +5,12 @@ import copy from 'copy-to-clipboard';
type Props = {
text: string,
children?: React.Node,
onClick?: () => *,
onCopy: () => *,
onClick?: () => void,
onCopy: () => void,
};
class CopyToClipboard extends React.PureComponent<Props> {
onClick = (ev: SyntheticEvent<*>) => {
onClick = (ev: SyntheticEvent<>) => {
const { text, onCopy, children } = this.props;
const elem = React.Children.only(children);
copy(text, {

View File

@ -2,6 +2,7 @@
import * as React from 'react';
import { observable, action } from 'mobx';
import { observer, inject } from 'mobx-react';
import type { RouterHistory } from 'react-router-dom';
import styled from 'styled-components';
import Waypoint from 'react-waypoint';
import ArrowKeyNavigation from 'boundless-arrow-key-navigation';
@ -20,7 +21,7 @@ type Props = {
match: Object,
documents: DocumentsStore,
revisions: RevisionsStore,
history: Object,
history: RouterHistory,
};
@observer

View File

@ -9,10 +9,19 @@ import Flex from 'shared/components/Flex';
import Time from 'shared/components/Time';
import Avatar from 'components/Avatar';
import RevisionMenu from 'menus/RevisionMenu';
import Document from 'models/Document';
import Revision from 'models/Revision';
import { documentHistoryUrl } from 'utils/routeHelpers';
class Revision extends React.Component<*> {
type Props = {
theme: Object,
showMenu: () => void,
document: Document,
revision: Revision,
};
class RevisionListItem extends React.Component<Props> {
render() {
const { revision, document, showMenu, theme } = this.props;
@ -74,4 +83,4 @@ const Meta = styled.p`
padding: 0;
`;
export default withTheme(Revision);
export default withTheme(RevisionListItem);

View File

@ -17,7 +17,6 @@ type Props = {
showCollection?: boolean,
showPublished?: boolean,
showPin?: boolean,
ref?: *,
};
const StyledStar = withTheme(styled(({ solid, theme, ...props }) => (
@ -107,13 +106,13 @@ const SEARCH_RESULT_REGEX = /<b\b[^>]*>(.*?)<\/b>/gi;
@observer
class DocumentPreview extends React.Component<Props> {
star = (ev: SyntheticEvent<*>) => {
star = (ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();
this.props.document.star();
};
unstar = (ev: SyntheticEvent<*>) => {
unstar = (ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();
this.props.document.unstar();

View File

@ -2,7 +2,7 @@
import * as React from 'react';
import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { createGlobalStyle } from 'styled-components';
import invariant from 'invariant';
import importFile from 'utils/importFile';
@ -22,7 +22,7 @@ type Props = {
disabled: boolean,
location: Object,
match: Object,
history: Object,
history: RouterHistory,
staticContext: Object,
};

View File

@ -31,10 +31,10 @@ class DropdownMenu extends React.Component<Props> {
@observable left: number;
handleOpen = (
openPortal: (SyntheticEvent<*>) => void,
openPortal: (SyntheticEvent<>) => void,
closePortal: () => void
) => {
return (ev: SyntheticMouseEvent<*>) => {
return (ev: SyntheticMouseEvent<HTMLElement>) => {
ev.preventDefault();
const currentTarget = ev.currentTarget;
invariant(document.body, 'why you not here');

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import styled from 'styled-components';
type Props = {
onClick?: (SyntheticEvent<*>) => *,
onClick?: (SyntheticEvent<>) => void | Promise<void>,
children?: React.Node,
disabled?: boolean,
};

View File

@ -10,6 +10,7 @@ import Placeholder from 'rich-markdown-editor/lib/components/Placeholder';
import { uploadFile } from 'utils/uploadFile';
import isInternalUrl from 'utils/isInternalUrl';
import Tooltip from 'components/Tooltip';
import UiStore from 'stores/UiStore';
import Embed from './Embed';
import embeds from '../../embeds';
@ -17,8 +18,8 @@ type Props = {
defaultValue?: string,
readOnly?: boolean,
disableEmbeds?: boolean,
forwardedRef: *,
ui: *,
forwardedRef: React.Ref<RichMarkdownEditor>,
ui: UiStore,
};
@observer

View File

@ -14,7 +14,7 @@ type Props = {
@observer
class InputRich extends React.Component<Props> {
@observable editorComponent: *;
@observable editorComponent: React.ComponentType<any>;
@observable focused: boolean = false;
componentDidMount() {

View File

@ -1,9 +1,14 @@
// @flow
import * as React from 'react';
import { inject, observer } from 'mobx-react';
import UiStore from 'stores/UiStore';
type Props = {
ui: UiStore,
};
@observer
class LoadingIndicator extends React.Component<*> {
class LoadingIndicator extends React.Component<Props> {
componentDidMount() {
this.props.ui.enableProgressBar();
}

View File

@ -5,7 +5,12 @@ import { pulsate } from 'shared/styles/animations';
import { randomInteger } from 'shared/random';
import Flex from 'shared/components/Flex';
class Mask extends React.Component<*> {
type Props = {
header?: boolean,
height?: number,
};
class Mask extends React.Component<Props> {
width: number;
shouldComponentUpdate() {

View File

@ -15,7 +15,7 @@ type Props = {
children?: React.Node,
isOpen: boolean,
title?: string,
onRequestClose: () => *,
onRequestClose: () => void,
};
const GlobalStyles = createGlobalStyle`

View File

@ -11,7 +11,7 @@ import { ListPlaceholder } from 'components/LoadingPlaceholder';
type Props = {
documents: Document[],
fetch: (options: ?Object) => Promise<*>,
fetch: (options: ?Object) => Promise<void>,
options?: Object,
heading?: React.Node,
empty?: React.Node,

View File

@ -14,12 +14,12 @@ type Props = {
document?: ?Document,
collection: ?Collection,
onSuccess?: () => void,
ref?: *,
ref?: (?React.ElementRef<'div'>) => void,
};
@observer
class PathToDocument extends React.Component<Props> {
handleClick = async (ev: SyntheticEvent<*>) => {
handleClick = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { document, result, onSuccess } = this.props;
if (!document) return;

View File

@ -6,7 +6,7 @@ import UiStore from 'stores/UiStore';
type Props = {
ui: UiStore,
component: *,
component: React.ComponentType<any>,
};
class RouteSidebarHidden extends React.Component<Props> {

View File

@ -2,8 +2,14 @@
// based on: https://reacttraining.com/react-router/web/guides/scroll-restoration
import * as React from 'react';
import { withRouter } from 'react-router-dom';
import type { Location } from 'react-router-dom';
class ScrollToTop extends React.Component<*> {
type Props = {
location: Location,
children: React.Node,
};
class ScrollToTop extends React.Component<Props> {
componentDidUpdate(prevProps) {
if (this.props.location.pathname === prevProps.location.pathname) return;

View File

@ -12,7 +12,7 @@ type Props = {
class Scrollable extends React.Component<Props> {
@observable shadow: boolean = false;
handleScroll = (ev: SyntheticMouseEvent<*>) => {
handleScroll = (ev: SyntheticMouseEvent<HTMLDivElement>) => {
this.shadow = !!(this.props.shadow && ev.currentTarget.scrollTop > 0);
};

View File

@ -1,6 +1,7 @@
// @flow
import * as React from 'react';
import { observer, inject } from 'mobx-react';
import type { RouterHistory } from 'react-router-dom';
import {
DocumentIcon,
EmailIcon,
@ -25,7 +26,7 @@ import HeaderBlock from './components/HeaderBlock';
import AuthStore from 'stores/AuthStore';
type Props = {
history: Object,
history: RouterHistory,
auth: AuthStore,
};

View File

@ -18,7 +18,7 @@ type Props = {
ui: UiStore,
documents: DocumentsStore,
activeDocument: ?Document,
prefetchDocument: (id: string) => *,
prefetchDocument: (id: string) => Promise<void>,
};
@observer

View File

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import keydown from 'react-keydown';
import Flex from 'shared/components/Flex';
import { PlusIcon } from 'outline-icons';
@ -17,7 +17,7 @@ import UiStore from 'stores/UiStore';
import DocumentsStore from 'stores/DocumentsStore';
type Props = {
history: Object,
history: RouterHistory,
collections: CollectionsStore,
documents: DocumentsStore,
onCreateCollection: () => void,

View File

@ -18,7 +18,7 @@ type Props = {
documents: DocumentsStore,
collection?: Collection,
activeDocument: ?Document,
activeDocumentRef?: (?HTMLElement) => *,
activeDocumentRef?: (?HTMLElement) => void,
prefetchDocument: (documentId: string) => Promise<void>,
depth: number,
};
@ -27,7 +27,7 @@ type Props = {
class DocumentLink extends React.Component<Props> {
@observable menuOpen = false;
handleMouseEnter = (ev: SyntheticEvent<*>) => {
handleMouseEnter = (ev: SyntheticEvent<>) => {
const { node, prefetchDocument } = this.props;
ev.stopPropagation();

View File

@ -9,7 +9,7 @@ import Flex from 'shared/components/Flex';
type Props = {
to?: string | Object,
onClick?: (SyntheticEvent<*>) => *,
onClick?: (SyntheticEvent<>) => void,
children?: React.Node,
icon?: React.Node,
expanded?: boolean,
@ -43,7 +43,7 @@ class SidebarLink extends React.Component<Props> {
}
@action
handleClick = (ev: SyntheticEvent<*>) => {
handleClick = (ev: SyntheticEvent<>) => {
ev.preventDefault();
ev.stopPropagation();
this.expanded = !this.expanded;

View File

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
export default function SlackIcon(props: *) {
export default function SlackIcon() {
return (
<svg
fill="#4E5C6E"
@ -9,7 +9,6 @@ export default function SlackIcon(props: *) {
height="24px"
viewBox="0 0 24 24"
version="1.1"
{...props}
>
<path d="M7.36156352,14.1107492 C7.36156352,15.0358306 6.60586319,15.7915309 5.68078176,15.7915309 C4.75570033,15.7915309 4,15.0358306 4,14.1107492 C4,13.1856678 4.75570033,12.4299674 5.68078176,12.4299674 L7.36156352,12.4299674 L7.36156352,14.1107492 Z M8.20846906,14.1107492 C8.20846906,13.1856678 8.96416938,12.4299674 9.88925081,12.4299674 C10.8143322,12.4299674 11.5700326,13.1856678 11.5700326,14.1107492 L11.5700326,18.3192182 C11.5700326,19.2442997 10.8143322,20 9.88925081,20 C8.96416938,20 8.20846906,19.2442997 8.20846906,18.3192182 C8.20846906,18.3192182 8.20846906,14.1107492 8.20846906,14.1107492 Z M9.88925081,7.36156352 C8.96416938,7.36156352 8.20846906,6.60586319 8.20846906,5.68078176 C8.20846906,4.75570033 8.96416938,4 9.88925081,4 C10.8143322,4 11.5700326,4.75570033 11.5700326,5.68078176 L11.5700326,7.36156352 L9.88925081,7.36156352 Z M9.88925081,8.20846906 C10.8143322,8.20846906 11.5700326,8.96416938 11.5700326,9.88925081 C11.5700326,10.8143322 10.8143322,11.5700326 9.88925081,11.5700326 L5.68078176,11.5700326 C4.75570033,11.5700326 4,10.8143322 4,9.88925081 C4,8.96416938 4.75570033,8.20846906 5.68078176,8.20846906 C5.68078176,8.20846906 9.88925081,8.20846906 9.88925081,8.20846906 Z M16.6384365,9.88925081 C16.6384365,8.96416938 17.3941368,8.20846906 18.3192182,8.20846906 C19.2442997,8.20846906 20,8.96416938 20,9.88925081 C20,10.8143322 19.2442997,11.5700326 18.3192182,11.5700326 L16.6384365,11.5700326 L16.6384365,9.88925081 Z M15.7915309,9.88925081 C15.7915309,10.8143322 15.0358306,11.5700326 14.1107492,11.5700326 C13.1856678,11.5700326 12.4299674,10.8143322 12.4299674,9.88925081 L12.4299674,5.68078176 C12.4299674,4.75570033 13.1856678,4 14.1107492,4 C15.0358306,4 15.7915309,4.75570033 15.7915309,5.68078176 L15.7915309,9.88925081 Z M14.1107492,16.6384365 C15.0358306,16.6384365 15.7915309,17.3941368 15.7915309,18.3192182 C15.7915309,19.2442997 15.0358306,20 14.1107492,20 C13.1856678,20 12.4299674,19.2442997 12.4299674,18.3192182 L12.4299674,16.6384365 L14.1107492,16.6384365 Z M14.1107492,15.7915309 C13.1856678,15.7915309 12.4299674,15.0358306 12.4299674,14.1107492 C12.4299674,13.1856678 13.1856678,12.4299674 14.1107492,12.4299674 L18.3192182,12.4299674 C19.2442997,12.4299674 20,13.1856678 20,14.1107492 C20,15.0358306 19.2442997,15.7915309 18.3192182,15.7915309 L14.1107492,15.7915309 Z" />
</svg>

View File

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
export default function ZapierIcon(props: *) {
export default function ZapierIcon() {
return (
<svg
fill="#4E5C6E"
@ -9,7 +9,6 @@ export default function ZapierIcon(props: *) {
height="24px"
viewBox="0 0 24 24"
version="1.1"
{...props}
>
<path d="M14,12.00348 C13.9996,12.59796 13.89092,13.16708 13.6928,13.69244 C13.16752,13.89072 12.59816,13.99964 12.00344,14.00004 L11.99656,14.00004 C11.40216,13.99964 10.83296,13.89104 10.30768,13.69284 C10.10952,13.16764 10.0004,12.59828 10,12.00364 L10,11.99672 C10.0004,11.40224 10.10928,10.83312 10.30712,10.3078 C10.83264,10.10964 11.40192,10.0006 11.99656,10.0002 L12.00344,10.0002 C12.59816,10.0006 13.16752,10.10964 13.69276,10.3078 C13.89076,10.83316 13.99956,11.40228 13.99996,11.99676 L13.99996,12.00368 L13.99996,12.0036 L14,12.00348 Z M19.8888,10.66668 L15.21896,10.66668 L18.52096,7.36468 C18.2617173,7.00059444 17.9725547,6.65876921 17.65648,6.34276 L17.65632,6.34244 C17.340564,6.02673022 16.9990638,5.73786527 16.63536,5.47884 L13.33336,8.78084 L13.33336,4.11128 C12.894135,4.03747693 12.4495423,4.00025584 12.00416,4 L11.99568,4 C11.5503533,4.00023789 11.1058145,4.03743219 10.66664,4.1112 L10.66664,8.78108 L7.36464,5.47908 C7.00075543,5.7381962 6.65910565,6.02719699 6.34324,6.34308 L6.34204,6.34416 C6.02660762,6.65978768 5.73799769,7.00112635 5.4792,7.36464 L8.7812,10.66664 L4.1112,10.66664 C4.1112,10.66664 4.00016,11.54384 4,11.99704 L4,12.00284 C4.00016664,12.4487002 4.03736113,12.8937765 4.1112,13.33348 L8.78104,13.33348 L5.47904,16.63548 C5.99830995,17.3645251 6.63559493,18.0018101 7.36464,18.52108 L10.66664,15.21916 L10.66664,19.8888 C11.1053956,19.962476 11.5495017,19.9996699 11.9944,20 L12.00584,20 C12.4506842,19.999644 12.8947349,19.9624502 13.33344,19.8888 L13.33344,15.21892 L16.63544,18.52092 C16.9992044,18.2618792 17.3407586,17.972987 17.65656,17.65724 L17.65736,17.65664 C17.9730579,17.3408301 18.2619218,16.9992911 18.52096,16.63556 L15.21896,13.33348 L19.8888,13.33348 C19.9624671,12.8947771 19.999661,12.4507249 20,12.00588 L20,11.9944 C19.999644,11.5495558 19.9624502,11.1055051 19.8888,10.6668 L19.8888,10.66668 Z" />
</svg>

View File

@ -3,6 +3,10 @@ import * as React from 'react';
import styled, { withTheme } from 'styled-components';
import { NavLink } from 'react-router-dom';
type Props = {
theme: Object,
};
const StyledNavLink = styled(NavLink)`
position: relative;
top: 1px;
@ -21,7 +25,7 @@ const StyledNavLink = styled(NavLink)`
}
`;
function Tab(props: *) {
function Tab(props: Props) {
const activeStyle = {
paddingBottom: '5px',
borderBottom: `3px solid ${props.theme.textSecondary}`,

View File

@ -2,7 +2,7 @@
import * as React from 'react';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import styled from 'styled-components';
import { MoreIcon } from 'outline-icons';
import Modal from 'components/Modal';
@ -22,7 +22,7 @@ type Props = {
ui: UiStore,
documents: DocumentsStore,
collection: Collection,
history: Object,
history: RouterHistory,
onOpen?: () => void,
onClose?: () => void,
};
@ -33,20 +33,20 @@ class CollectionMenu extends React.Component<Props> {
@observable permissionsModalOpen: boolean = false;
@observable redirectTo: ?string;
onNewDocument = (ev: SyntheticEvent<*>) => {
onNewDocument = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { collection } = this.props;
this.props.history.push(newDocumentUrl(collection.id));
};
onImportDocument = (ev: SyntheticEvent<*>) => {
onImportDocument = (ev: SyntheticEvent<>) => {
ev.preventDefault();
// simulate a click on the file upload input element
if (this.file) this.file.click();
};
onFilePicked = async (ev: SyntheticEvent<*>) => {
onFilePicked = async (ev: SyntheticEvent<>) => {
const files = getDataTransferFiles(ev);
try {
@ -61,25 +61,25 @@ class CollectionMenu extends React.Component<Props> {
}
};
onEdit = (ev: SyntheticEvent<*>) => {
onEdit = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { collection } = this.props;
this.props.ui.setActiveModal('collection-edit', { collection });
};
onDelete = (ev: SyntheticEvent<*>) => {
onDelete = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { collection } = this.props;
this.props.ui.setActiveModal('collection-delete', { collection });
};
onExport = (ev: SyntheticEvent<*>) => {
onExport = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { collection } = this.props;
this.props.ui.setActiveModal('collection-export', { collection });
};
onPermissions = (ev: SyntheticEvent<*>) => {
onPermissions = (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.permissionsModalOpen = true;
};

View File

@ -40,12 +40,12 @@ class DocumentMenu extends React.Component<Props> {
this.redirectTo = undefined;
}
handleNewChild = (ev: SyntheticEvent<*>) => {
handleNewChild = (ev: SyntheticEvent<>) => {
const { document } = this.props;
this.redirectTo = newDocumentUrl(document.collectionId, document.id);
};
handleDelete = (ev: SyntheticEvent<*>) => {
handleDelete = (ev: SyntheticEvent<>) => {
const { document } = this.props;
this.props.ui.setActiveModal('document-delete', { document });
};
@ -54,15 +54,15 @@ class DocumentMenu extends React.Component<Props> {
this.redirectTo = documentHistoryUrl(this.props.document);
};
handleMove = (ev: SyntheticEvent<*>) => {
handleMove = (ev: SyntheticEvent<>) => {
this.redirectTo = documentMoveUrl(this.props.document);
};
handleEdit = (ev: SyntheticEvent<*>) => {
handleEdit = (ev: SyntheticEvent<>) => {
this.redirectTo = documentEditUrl(this.props.document);
};
handleDuplicate = async (ev: SyntheticEvent<*>) => {
handleDuplicate = async (ev: SyntheticEvent<>) => {
const duped = await this.props.document.duplicate();
// when duplicating, go straight to the duplicated document content
@ -70,37 +70,37 @@ class DocumentMenu extends React.Component<Props> {
this.props.ui.showToast('Document duplicated');
};
handleArchive = async (ev: SyntheticEvent<*>) => {
handleArchive = async (ev: SyntheticEvent<>) => {
await this.props.document.archive();
this.props.ui.showToast('Document archived');
};
handleRestore = async (ev: SyntheticEvent<*>) => {
handleRestore = async (ev: SyntheticEvent<>) => {
await this.props.document.restore();
this.props.ui.showToast('Document restored');
};
handlePin = (ev: SyntheticEvent<*>) => {
handlePin = (ev: SyntheticEvent<>) => {
this.props.document.pin();
};
handleUnpin = (ev: SyntheticEvent<*>) => {
handleUnpin = (ev: SyntheticEvent<>) => {
this.props.document.unpin();
};
handleStar = (ev: SyntheticEvent<*>) => {
handleStar = (ev: SyntheticEvent<>) => {
this.props.document.star();
};
handleUnstar = (ev: SyntheticEvent<*>) => {
handleUnstar = (ev: SyntheticEvent<>) => {
this.props.document.unstar();
};
handleExport = (ev: SyntheticEvent<*>) => {
handleExport = (ev: SyntheticEvent<>) => {
this.props.document.download();
};
handleShareLink = async (ev: SyntheticEvent<*>) => {
handleShareLink = async (ev: SyntheticEvent<>) => {
const { document } = this.props;
if (!document.shareUrl) await document.share();

View File

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { inject } from 'mobx-react';
import { MoreIcon } from 'outline-icons';
@ -13,9 +13,9 @@ import UiStore from 'stores/UiStore';
type Props = {
label?: React.Node,
onOpen?: () => *,
onClose: () => *,
history: Object,
onOpen?: () => void,
onClose: () => void,
history: RouterHistory,
document: Document,
revision: Revision,
className?: string,
@ -23,7 +23,7 @@ type Props = {
};
class RevisionMenu extends React.Component<Props> {
handleRestore = async (ev: SyntheticEvent<*>) => {
handleRestore = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
await this.props.document.restore(this.props.revision);
this.props.ui.showToast('Document restored');

View File

@ -13,8 +13,8 @@ import Share from 'models/Share';
type Props = {
label?: React.Node,
onOpen?: () => *,
onClose: () => *,
onOpen?: () => void,
onClose: () => void,
shares: SharesStore,
ui: UiStore,
share: Share,
@ -28,12 +28,12 @@ class ShareMenu extends React.Component<Props> {
this.redirectTo = undefined;
}
handleGoToDocument = (ev: SyntheticEvent<*>) => {
handleGoToDocument = (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.redirectTo = this.props.share.documentUrl;
};
handleRevoke = (ev: SyntheticEvent<*>) => {
handleRevoke = (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.props.shares.revoke(this.props.share);
this.props.ui.showToast('Share link revoked');

View File

@ -14,7 +14,7 @@ type Props = {
@observer
class UserMenu extends React.Component<Props> {
handlePromote = (ev: SyntheticEvent<*>) => {
handlePromote = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { user, users } = this.props;
if (
@ -29,7 +29,7 @@ class UserMenu extends React.Component<Props> {
users.promote(user);
};
handleDemote = (ev: SyntheticEvent<*>) => {
handleDemote = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { user, users } = this.props;
if (!window.confirm(`Are you want to make ${user.name} a member?`)) {
@ -38,7 +38,7 @@ class UserMenu extends React.Component<Props> {
users.demote(user);
};
handleSuspend = (ev: SyntheticEvent<*>) => {
handleSuspend = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { user, users } = this.props;
if (
@ -51,7 +51,7 @@ class UserMenu extends React.Component<Props> {
users.suspend(user);
};
handleActivate = (ev: SyntheticEvent<*>) => {
handleActivate = (ev: SyntheticEvent<>) => {
ev.preventDefault();
const { user, users } = this.props;
users.activate(user);

View File

@ -7,13 +7,13 @@ import unescape from 'shared/utils/unescape';
import BaseModel from 'models/BaseModel';
import Revision from 'models/Revision';
import User from 'models/User';
import DocumentsStore from 'stores/DocumentsStore';
type SaveOptions = { publish?: boolean, done?: boolean, autosave?: boolean };
export default class Document extends BaseModel {
isSaving: boolean;
ui: *;
store: *;
store: DocumentsStore;
collaborators: User[];
collectionId: string;
@ -37,7 +37,7 @@ export default class Document extends BaseModel {
shareUrl: ?string;
revision: number;
constructor(data?: Object = {}, store: *) {
constructor(data?: Object = {}, store: DocumentsStore) {
super(data, store);
this.updateTitle();
}
@ -54,7 +54,7 @@ export default class Document extends BaseModel {
@computed
get isStarred(): boolean {
return this.store.starredIds.get(this.id);
return !!this.store.starredIds.get(this.id);
}
@computed
@ -90,7 +90,7 @@ export default class Document extends BaseModel {
return this.store.archive(this);
};
restore = (revision: ?Revision) => {
restore = (revision: Revision) => {
return this.store.restore(this, revision);
};

View File

@ -83,7 +83,7 @@ class CollectionScene extends React.Component<Props> {
this.isFetching = false;
};
onNewDocument = (ev: SyntheticEvent<*>) => {
onNewDocument = (ev: SyntheticEvent<>) => {
ev.preventDefault();
if (this.collection) {
@ -91,7 +91,7 @@ class CollectionScene extends React.Component<Props> {
}
};
onPermissions = (ev: SyntheticEvent<*>) => {
onPermissions = (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.permissionsModalOpen = true;
};

View File

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import { homeUrl } from 'utils/routeHelpers';
@ -12,7 +12,7 @@ import CollectionsStore from 'stores/CollectionsStore';
import UiStore from 'stores/UiStore';
type Props = {
history: Object,
history: RouterHistory,
collection: Collection,
collections: CollectionsStore,
ui: UiStore,
@ -23,7 +23,7 @@ type Props = {
class CollectionDelete extends React.Component<Props> {
@observable isDeleting: boolean;
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isDeleting = true;

View File

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import Input from 'components/Input';
@ -13,7 +13,7 @@ import Collection from 'models/Collection';
import UiStore from 'stores/UiStore';
type Props = {
history: Object,
history: RouterHistory,
collection: Collection,
ui: UiStore,
onSubmit: () => void,
@ -31,7 +31,7 @@ class CollectionEdit extends React.Component<Props> {
this.description = this.props.collection.description;
}
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isSaving = true;

View File

@ -20,7 +20,7 @@ type Props = {
class CollectionExport extends React.Component<Props> {
@observable isLoading: boolean = false;
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isLoading = true;

View File

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import Button from 'components/Button';
@ -15,7 +15,7 @@ import CollectionsStore from 'stores/CollectionsStore';
import UiStore from 'stores/UiStore';
type Props = {
history: Object,
history: RouterHistory,
ui: UiStore,
collections: CollectionsStore,
onSubmit: () => void,
@ -29,7 +29,7 @@ class CollectionNew extends React.Component<Props> {
@observable private: boolean = false;
@observable isSaving: boolean;
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isSaving = true;
const collection = new Collection(

View File

@ -12,7 +12,7 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
type Props = {
user: User,
showRemove: boolean,
onRemove: () => *,
onRemove: () => void,
};
const MemberListItem = ({ user, onRemove, showRemove }: Props) => {

View File

@ -8,7 +8,7 @@ import User from 'models/User';
type Props = {
user: User,
showAdd: boolean,
onAdd: () => *,
onAdd: () => void,
};
const UserListItem = ({ user, onAdd, showAdd }: Props) => {

View File

@ -6,7 +6,7 @@ import breakpoint from 'styled-components-breakpoint';
import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import { Prompt, Route, withRouter } from 'react-router-dom';
import type { Location } from 'react-router-dom';
import type { Location, RouterHistory } from 'react-router-dom';
import keydown from 'react-keydown';
import Flex from 'shared/components/Flex';
import {
@ -59,7 +59,7 @@ Are you sure you want to discard them?
type Props = {
match: Object,
history: Object,
history: RouterHistory,
location: Location,
documents: DocumentsStore,
revisions: RevisionsStore,

View File

@ -10,7 +10,6 @@ type Props = {
document: Document,
anchor: string,
showCollection?: boolean,
ref?: *,
};
const DocumentLink = styled(Link)`

View File

@ -26,12 +26,12 @@ type Props = {
documents: DocumentsStore,
collections: CollectionsStore,
ui: UiStore,
onRequestClose: *,
onRequestClose: () => void,
};
@observer
class DocumentMove extends React.Component<Props> {
firstDocument: *;
firstDocument: ?PathToDocument;
@observable searchTerm: ?string;
@observable isSaving: boolean;

View File

@ -10,7 +10,7 @@ type Props = {
};
class DocumentEditor extends React.Component<Props> {
editor: *;
editor: ?Editor;
componentDidMount() {
if (!this.props.defaultValue) {

View File

@ -32,12 +32,12 @@ type Props = {
isPublishing: boolean,
publishingIsDisabled: boolean,
savingIsDisabled: boolean,
onDiscard: () => *,
onDiscard: () => void,
onSave: ({
done?: boolean,
publish?: boolean,
autosave?: boolean,
}) => *,
}) => void,
auth: AuthStore,
};
@ -73,7 +73,7 @@ class Header extends React.Component<Props> {
this.props.onSave({ done: true, publish: true });
};
handleShareLink = async (ev: SyntheticEvent<*>) => {
handleShareLink = async (ev: SyntheticEvent<>) => {
const { document } = this.props;
if (!document.shareUrl) await document.share();
this.showShareModal = true;

View File

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import Button from 'components/Button';
@ -12,7 +12,7 @@ import UiStore from 'stores/UiStore';
import { collectionUrl } from 'utils/routeHelpers';
type Props = {
history: Object,
history: RouterHistory,
document: Document,
documents: DocumentsStore,
ui: UiStore,
@ -23,7 +23,7 @@ type Props = {
class DocumentDelete extends React.Component<Props> {
@observable isDeleting: boolean;
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isDeleting = true;

View File

@ -11,7 +11,7 @@ import Document from 'models/Document';
type Props = {
document?: Document,
onSubmit: () => *,
onSubmit: () => void,
};
@observer

View File

@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { observable } from 'mobx';
import { inject, observer } from 'mobx-react';
import { CloseIcon } from 'outline-icons';
@ -21,7 +21,7 @@ const MAX_INVITES = 20;
type Props = {
auth: AuthStore,
users: UsersStore,
history: Object,
history: RouterHistory,
ui: UiStore,
onSubmit: () => void,
};
@ -37,7 +37,7 @@ class Invite extends React.Component<Props> {
{ email: '', name: '' },
];
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isSaving = true;

View File

@ -4,6 +4,7 @@ import ReactDOM from 'react-dom';
import keydown from 'react-keydown';
import Waypoint from 'react-waypoint';
import { withRouter, Link } from 'react-router-dom';
import type { Location, RouterHistory } from 'react-router-dom';
import { observable, action } from 'mobx';
import { observer, inject } from 'mobx-react';
import { debounce } from 'lodash';
@ -32,9 +33,9 @@ import UserFilter from './components/UserFilter';
import DateFilter from './components/DateFilter';
type Props = {
history: Object,
history: RouterHistory,
match: Object,
location: Object,
location: Location,
documents: DocumentsStore,
users: UsersStore,
notFound: ?boolean,

View File

@ -5,18 +5,18 @@ import { SearchIcon } from 'outline-icons';
import Flex from 'shared/components/Flex';
type Props = {
onChange: string => *,
onChange: string => void,
theme: Object,
};
class SearchField extends React.Component<Props> {
input: ?HTMLInputElement;
handleChange = (ev: SyntheticEvent<*>) => {
handleChange = (ev: SyntheticEvent<HTMLInputElement>) => {
this.props.onChange(ev.currentTarget.value ? ev.currentTarget.value : '');
};
focusInput = (ev: SyntheticEvent<*>) => {
focusInput = (ev: SyntheticEvent<>) => {
if (this.input) this.input.focus();
};

View File

@ -40,7 +40,7 @@ class Details extends React.Component<Props> {
clearTimeout(this.timeout);
}
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
try {

View File

@ -22,7 +22,7 @@ class Export extends React.Component<Props> {
@observable isLoading: boolean = false;
@observable isExporting: boolean = false;
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isLoading = true;

View File

@ -59,7 +59,7 @@ class Notifications extends React.Component<Props> {
this.props.notificationSettings.fetchPage();
}
handleChange = async (ev: SyntheticInputEvent<*>) => {
handleChange = async (ev: SyntheticInputEvent<>) => {
const { notificationSettings } = this.props;
const setting = notificationSettings.getByEvent(ev.target.name);

View File

@ -38,7 +38,7 @@ class Profile extends React.Component<Props> {
clearTimeout(this.timeout);
}
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
await this.props.auth.updateUser({

View File

@ -28,7 +28,7 @@ class Tokens extends React.Component<Props> {
this.name = ev.target.value;
};
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
await this.props.apiKeys.create({ name: this.name });
this.name = '';

View File

@ -4,7 +4,6 @@ import { observable } from 'mobx';
import { observer } from 'mobx-react';
import styled from 'styled-components';
import Dropzone from 'react-dropzone';
import LoadingIndicator from 'components/LoadingIndicator';
import Flex from 'shared/components/Flex';
import Modal from 'components/Modal';
@ -14,8 +13,8 @@ import { uploadFile, dataUrlToBlob } from 'utils/uploadFile';
type Props = {
children?: React.Node,
onSuccess: string => *,
onError: string => *,
onSuccess: string => void | Promise<void>,
onError: string => void,
submitText: string,
borderRadius: number,
};

View File

@ -9,7 +9,7 @@ type Props = {
event: string,
description: string,
disabled: boolean,
onChange: *,
onChange: (ev: SyntheticInputEvent<>) => void | Promise<void>,
};
const NotificationListItem = ({

View File

@ -6,7 +6,7 @@ import ApiKey from 'models/ApiKey';
type Props = {
token: ApiKey,
onDelete: (tokenId: string) => *,
onDelete: (tokenId: string) => void,
};
const TokenListItem = ({ token, onDelete }: Props) => {

View File

@ -10,14 +10,14 @@ import AuthStore from 'stores/AuthStore';
type Props = {
auth: AuthStore,
onRequestClose: () => *,
onRequestClose: () => void,
};
@observer
class UserDelete extends React.Component<Props> {
@observable isDeleting: boolean;
handleSubmit = async (ev: SyntheticEvent<*>) => {
handleSubmit = async (ev: SyntheticEvent<>) => {
ev.preventDefault();
this.isDeleting = true;

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import styled from 'styled-components';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import { inject, observer } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { withRouter, type RouterHistory } from 'react-router-dom';
import { EditIcon } from 'outline-icons';
import Flex from 'shared/components/Flex';
import HelpText from 'components/HelpText';
@ -22,8 +22,8 @@ type Props = {
user: User,
auth: AuthStore,
documents: DocumentsStore,
history: Object,
onRequestClose: () => *,
history: RouterHistory,
onRequestClose: () => void,
};
@observer

View File

@ -49,7 +49,7 @@ export default class DocumentsStore extends BaseStore<Document> {
return orderBy(this.all, 'updatedAt', 'desc');
}
createdByUser(userId: string): * {
createdByUser(userId: string): Document[] {
return orderBy(
filter(this.all, d => d.createdBy.id === userId),
'updatedAt',
@ -387,7 +387,12 @@ export default class DocumentsStore extends BaseStore<Document> {
}
@action
async update(params: *) {
async update(params: {
id: string,
title: string,
text: string,
lastRevision: number,
}) {
const document = await super.update(params);
// Because the collection object contains the url and title

View File

@ -1,5 +1,5 @@
// @flow
export default function getDataTransferFiles(event: SyntheticEvent<*>): File[] {
export default function getDataTransferFiles(event: SyntheticEvent<>): File[] {
let dataTransferItemsList = [];
// $FlowFixMe

View File

@ -1,9 +1,10 @@
// @flow
import Document from '../models/Document';
import Document from 'models/Document';
import DocumentsStore from 'stores/DocumentsStore';
type Options = {
file: File,
documents: *,
documents: DocumentsStore,
collectionId: string,
documentId?: string,
};

View File

@ -1,11 +1,36 @@
// @flow
import { sortBy } from 'lodash';
import { deburr } from 'lodash';
import naturalSort from 'natural-sort';
export default (sortableArray: *, key: string): * => {
if (!sortableArray) return [];
let keys = sortableArray.map(object => object[key]);
keys.sort(naturalSort());
return sortBy(sortableArray, object => keys.indexOf(object[key]));
type NaturalSortOptions = {
caseSensitive?: boolean,
direction?: 'asc' | 'desc',
};
const sorter = naturalSort();
function getSortByField<T: Object>(
item: T,
keyOrCallback: string | (T => string)
) {
if (typeof keyOrCallback === 'string') {
return deburr(item[keyOrCallback]);
}
return keyOrCallback(item);
}
function naturalSortBy<T>(
items: T[],
key: string | (T => string),
sortOptions?: NaturalSortOptions
): T[] {
if (!items) return [];
const sort = sortOptions ? naturalSort(sortOptions) : sorter;
return items.sort((a: any, b: any): -1 | 0 | 1 =>
sort(getSortByField(a, key), getSortByField(b, key))
);
}
export default naturalSortBy;