diff --git a/app/components/Auth.js b/app/components/Auth.js deleted file mode 100644 index 894ffb6ce..000000000 --- a/app/components/Auth.js +++ /dev/null @@ -1,75 +0,0 @@ -// @flow -import * as React from 'react'; -import { Provider, observer, inject } from 'mobx-react'; -import stores from 'stores'; -import AuthStore from 'stores/AuthStore'; -import ApiKeysStore from 'stores/ApiKeysStore'; -import UsersStore from 'stores/UsersStore'; -import CollectionsStore from 'stores/CollectionsStore'; -import IntegrationsStore from 'stores/IntegrationsStore'; -import LoadingIndicator from 'components/LoadingIndicator'; -import { isCustomSubdomain } from 'shared/utils/domains'; - -type Props = { - auth: AuthStore, - children?: React.Node, -}; - -let authenticatedStores; - -const Auth = observer(({ auth, children }: Props) => { - if (auth.authenticated) { - const { user, team } = auth; - const { hostname } = window.location; - - if (!team || !user) { - return ; - } - - // If we're authenticated but viewing a subdomain that doesn't match the - // currently authenticated team then kick the user to the teams subdomain. - if ( - process.env.SUBDOMAINS_ENABLED && - team.subdomain && - isCustomSubdomain(hostname) && - !hostname.startsWith(`${team.subdomain}.`) - ) { - window.location.href = `${team.url}${window.location.pathname}`; - return ; - } - - // Only initialize stores once. Kept in global scope because otherwise they - // will get overridden on route change - if (!authenticatedStores) { - authenticatedStores = { - integrations: new IntegrationsStore({ - ui: stores.ui, - }), - apiKeys: new ApiKeysStore(), - users: new UsersStore(), - collections: new CollectionsStore({ - ui: stores.ui, - teamId: team.id, - }), - }; - - if (window.Bugsnag) { - Bugsnag.user = { - id: user.id, - name: user.name, - teamId: team.id, - team: team.name, - }; - } - - authenticatedStores.collections.fetchPage({ limit: 100 }); - } - - return {children}; - } - - auth.logout(); - return null; -}); - -export default inject('auth')(Auth); diff --git a/app/components/Authenticated.js b/app/components/Authenticated.js new file mode 100644 index 000000000..29e43e574 --- /dev/null +++ b/app/components/Authenticated.js @@ -0,0 +1,41 @@ +// @flow +import * as React from 'react'; +import { observer, inject } from 'mobx-react'; +import AuthStore from 'stores/AuthStore'; +import LoadingIndicator from 'components/LoadingIndicator'; +import { isCustomSubdomain } from 'shared/utils/domains'; + +type Props = { + auth: AuthStore, + children?: React.Node, +}; + +const Authenticated = observer(({ auth, children }: Props) => { + if (auth.authenticated) { + const { user, team } = auth; + const { hostname } = window.location; + + if (!team || !user) { + return ; + } + + // If we're authenticated but viewing a subdomain that doesn't match the + // currently authenticated team then kick the user to the teams subdomain. + if ( + process.env.SUBDOMAINS_ENABLED && + team.subdomain && + isCustomSubdomain(hostname) && + !hostname.startsWith(`${team.subdomain}.`) + ) { + window.location.href = `${team.url}${window.location.pathname}`; + return ; + } + + return children; + } + + auth.logout(); + return null; +}); + +export default inject('auth')(Authenticated); diff --git a/app/components/DocumentHistory/DocumentHistory.js b/app/components/DocumentHistory/DocumentHistory.js index e9cab099c..5d0e0f7ef 100644 --- a/app/components/DocumentHistory/DocumentHistory.js +++ b/app/components/DocumentHistory/DocumentHistory.js @@ -7,7 +7,7 @@ import styled from 'styled-components'; import Waypoint from 'react-waypoint'; import ArrowKeyNavigation from 'boundless-arrow-key-navigation'; -import { DEFAULT_PAGINATION_LIMIT } from 'stores/DocumentsStore'; +import { DEFAULT_PAGINATION_LIMIT } from 'stores/BaseStore'; import Document from 'models/Document'; import RevisionsStore from 'stores/RevisionsStore'; diff --git a/app/components/DocumentViews/DocumentViewersStore.js b/app/components/DocumentViews/DocumentViewersStore.js deleted file mode 100644 index 336e3ae44..000000000 --- a/app/components/DocumentViews/DocumentViewersStore.js +++ /dev/null @@ -1,42 +0,0 @@ -// @flow -import { observable, action } from 'mobx'; -import invariant from 'invariant'; -import { client } from 'utils/ApiClient'; -import type { User } from 'types'; - -type View = { - user: User, - count: number, -}; - -class DocumentViewersStore { - documentId: string; - @observable viewers: Array; - @observable isFetching: boolean; - - @action - fetchViewers = async () => { - this.isFetching = true; - - try { - const res = await client.post( - '/views.list', - { - id: this.documentId, - }, - { cache: true } - ); - invariant(res && res.data, 'Data should be available'); - this.viewers = res.data.users; - } catch (e) { - console.error('Something went wrong'); - } - this.isFetching = false; - }; - - constructor(documentId: string) { - this.documentId = documentId; - } -} - -export default DocumentViewersStore; diff --git a/app/components/DocumentViews/DocumentViews.js b/app/components/DocumentViews/DocumentViews.js deleted file mode 100644 index b2c4b274c..000000000 --- a/app/components/DocumentViews/DocumentViews.js +++ /dev/null @@ -1,71 +0,0 @@ -// @flow -import * as React from 'react'; -import { observable } from 'mobx'; -import { observer } from 'mobx-react'; -import Popover from 'components/Popover'; -import styled from 'styled-components'; -import DocumentViewers from './components/DocumentViewers'; -import DocumentViewersStore from './DocumentViewersStore'; -import Flex from 'shared/components/Flex'; - -const Container = styled(Flex)` - font-size: 13px; - user-select: none; - - a { - color: #ccc; - - &:hover { - color: #aaa; - } - } -`; - -type Props = { - documentId: string, - count: number, -}; - -@observer -class DocumentViews extends React.Component { - @observable opened: boolean = false; - anchor: ?HTMLElement; - store: DocumentViewersStore; - - constructor(props: Props) { - super(props); - this.store = new DocumentViewersStore(props.documentId); - } - - openPopover = () => { - this.opened = true; - }; - - closePopover = () => { - this.opened = false; - }; - - setRef = (ref: ?HTMLElement) => { - this.anchor = ref; - }; - - render() { - return ( - - - Viewed {this.props.count} {this.props.count === 1 ? 'time' : 'times'} - - {this.opened && ( - - - - )} - - ); - } -} - -export default DocumentViews; diff --git a/app/components/DocumentViews/components/DocumentViewers/DocumentViewers.js b/app/components/DocumentViews/components/DocumentViewers/DocumentViewers.js deleted file mode 100644 index c034f4297..000000000 --- a/app/components/DocumentViews/components/DocumentViewers/DocumentViewers.js +++ /dev/null @@ -1,52 +0,0 @@ -// @flow -import * as React from 'react'; -import Flex from 'shared/components/Flex'; -import styled from 'styled-components'; -import map from 'lodash/map'; -import Avatar from 'components/Avatar'; -import Scrollable from 'components/Scrollable'; - -type Props = { - viewers: Array, - onMount: Function, -}; - -const List = styled.ul` - list-style: none; - font-size: 13px; - margin: -4px 0; - padding: 0; - - li { - padding: 4px 0; - } -`; - -const UserName = styled.span` - padding-left: 8px; -`; - -class DocumentViewers extends React.Component { - componentDidMount() { - this.props.onMount(); - } - - render() { - return ( - - - {map(this.props.viewers, view => ( -
  • - - {' '} - {view.user.name} - -
  • - ))} -
    -
    - ); - } -} - -export default DocumentViewers; diff --git a/app/components/DocumentViews/components/DocumentViewers/index.js b/app/components/DocumentViews/components/DocumentViewers/index.js deleted file mode 100644 index 86d53d674..000000000 --- a/app/components/DocumentViews/components/DocumentViewers/index.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow -import DocumentViewers from './DocumentViewers'; -export default DocumentViewers; diff --git a/app/components/DocumentViews/index.js b/app/components/DocumentViews/index.js deleted file mode 100644 index f6789427a..000000000 --- a/app/components/DocumentViews/index.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow -import DocumentViews from './DocumentViews'; -export default DocumentViews; diff --git a/app/components/DropToImport.js b/app/components/DropToImport.js index b2e861215..d5dcf5503 100644 --- a/app/components/DropToImport.js +++ b/app/components/DropToImport.js @@ -3,15 +3,15 @@ import * as React from 'react'; import { observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import { createGlobalStyle } from 'styled-components'; -import importFile from 'utils/importFile'; +import { omit } from 'lodash'; import invariant from 'invariant'; -import _ from 'lodash'; +import importFile from 'utils/importFile'; import Dropzone from 'react-dropzone'; import DocumentsStore from 'stores/DocumentsStore'; import LoadingIndicator from 'components/LoadingIndicator'; type Props = { - children?: React.Node, + children: React.Node, collectionId: string, documentId?: string, activeClassName?: string, @@ -62,15 +62,13 @@ class DropToImport extends React.Component { this.props.history.push(doc.url); } } - } catch (err) { - // TODO: show error alert. } finally { this.isImporting = false; } }; render() { - const props = _.omit( + const props = omit( this.props, 'history', 'documentId', diff --git a/app/components/Layout.js b/app/components/Layout.js index b955a89c9..b219192a2 100644 --- a/app/components/Layout.js +++ b/app/components/Layout.js @@ -124,4 +124,4 @@ const Content = styled(Flex)` `}; `; -export default withRouter(inject('user', 'auth', 'ui', 'documents')(Layout)); +export default withRouter(inject('auth', 'ui', 'documents')(Layout)); diff --git a/app/components/PaginatedDocumentList.js b/app/components/PaginatedDocumentList.js index 03ba3347b..8b4195966 100644 --- a/app/components/PaginatedDocumentList.js +++ b/app/components/PaginatedDocumentList.js @@ -4,7 +4,7 @@ import { observable, action } from 'mobx'; import { observer } from 'mobx-react'; import Waypoint from 'react-waypoint'; -import { DEFAULT_PAGINATION_LIMIT } from 'stores/DocumentsStore'; +import { DEFAULT_PAGINATION_LIMIT } from 'stores/BaseStore'; import Document from 'models/Document'; import DocumentList from 'components/DocumentList'; import { ListPlaceholder } from 'components/LoadingPlaceholder'; diff --git a/app/components/PathToDocument.js b/app/components/PathToDocument.js index 83a7ec424..e739808f1 100644 --- a/app/components/PathToDocument.js +++ b/app/components/PathToDocument.js @@ -1,12 +1,14 @@ // @flow import * as React from 'react'; import { observer } from 'mobx-react'; -import invariant from 'invariant'; import styled from 'styled-components'; import { GoToIcon } from 'outline-icons'; import Flex from 'shared/components/Flex'; import Document from 'models/Document'; +import type { DocumentPath } from 'stores/CollectionsStore'; + +const StyledGoToIcon = styled(GoToIcon)``; const ResultWrapper = styled.div` display: flex; @@ -16,8 +18,6 @@ const ResultWrapper = styled.div` cursor: default; `; -const StyledGoToIcon = styled(GoToIcon)``; - const ResultWrapperLink = styled(ResultWrapper.withComponent('a'))` height: 32px; padding-top: 3px; @@ -40,10 +40,10 @@ const ResultWrapperLink = styled(ResultWrapper.withComponent('a'))` `; type Props = { - result: Object, + result: DocumentPath, document?: Document, - onSuccess?: Function, - ref?: Function, + onSuccess?: *, + ref?: *, }; @observer @@ -51,8 +51,7 @@ class PathToDocument extends React.Component { handleClick = async (ev: SyntheticEvent<*>) => { ev.preventDefault(); const { document, result, onSuccess } = this.props; - - invariant(onSuccess && document, 'onSuccess unavailable'); + if (!document) return; if (result.type === 'document') { await document.move(result.id); @@ -64,7 +63,8 @@ class PathToDocument extends React.Component { } else { throw new Error('Not implemented yet'); } - onSuccess(); + + if (onSuccess) onSuccess(); }; render() { @@ -74,7 +74,7 @@ class PathToDocument extends React.Component { if (!result) return
    ; return ( - + {result.path .map(doc => {doc.title}) .reduce((prev, curr) => [prev, , curr])} diff --git a/app/components/Sidebar/Main.js b/app/components/Sidebar/Main.js index acebccb04..0457fa225 100644 --- a/app/components/Sidebar/Main.js +++ b/app/components/Sidebar/Main.js @@ -39,7 +39,7 @@ class MainSidebar extends React.Component { render() { const { auth, documents } = this.props; const { user, team } = auth; - if (!user || !team) return; + if (!user || !team) return null; return ( @@ -89,6 +89,4 @@ class MainSidebar extends React.Component { } } -export default withRouter( - inject('user', 'documents', 'auth', 'ui')(MainSidebar) -); +export default withRouter(inject('documents', 'auth', 'ui')(MainSidebar)); diff --git a/app/components/Sidebar/Settings.js b/app/components/Sidebar/Settings.js index 3f1c7ffa2..aa60aaf26 100644 --- a/app/components/Sidebar/Settings.js +++ b/app/components/Sidebar/Settings.js @@ -3,6 +3,7 @@ import * as React from 'react'; import { observer, inject } from 'mobx-react'; import { DocumentIcon, + EmailIcon, ProfileIcon, PadlockIcon, CodeIcon, @@ -34,7 +35,7 @@ class SettingsSidebar extends React.Component { render() { const { team, user } = this.props.auth; - if (!team || !user) return; + if (!team || !user) return null; return ( @@ -52,6 +53,9 @@ class SettingsSidebar extends React.Component { }> Profile + }> + Notifications + }> API Tokens diff --git a/app/components/Sidebar/components/CollectionLink.js b/app/components/Sidebar/components/CollectionLink.js index 8f71c5aef..2c504d70e 100644 --- a/app/components/Sidebar/components/CollectionLink.js +++ b/app/components/Sidebar/components/CollectionLink.js @@ -18,7 +18,7 @@ type Props = { collection: Collection, ui: UiStore, activeDocument: ?Document, - prefetchDocument: (id: string) => Promise, + prefetchDocument: (id: string) => *, }; @observer diff --git a/app/components/Sidebar/components/Collections.js b/app/components/Sidebar/components/Collections.js index 07af42fe4..3e1d31e47 100644 --- a/app/components/Sidebar/components/Collections.js +++ b/app/components/Sidebar/components/Collections.js @@ -24,6 +24,10 @@ type Props = { @observer class Collections extends React.Component { + componentDidMount() { + this.props.collections.fetchPage({ limit: 100 }); + } + render() { const { history, location, collections, ui, documents } = this.props; diff --git a/app/menus/AccountMenu.js b/app/menus/AccountMenu.js index 44f9e6080..464f292ea 100644 --- a/app/menus/AccountMenu.js +++ b/app/menus/AccountMenu.js @@ -14,7 +14,7 @@ import { } from '../../shared/utils/routeHelpers'; type Props = { - label?: React.Node, + label: React.Node, history: Object, ui: UiStore, auth: AuthStore, diff --git a/app/menus/CollectionMenu.js b/app/menus/CollectionMenu.js index e2103a96a..707dea4d7 100644 --- a/app/menus/CollectionMenu.js +++ b/app/menus/CollectionMenu.js @@ -40,13 +40,17 @@ class CollectionMenu extends React.Component { onFilePicked = async (ev: SyntheticEvent<*>) => { const files = getDataTransferFiles(ev); - const document = await importFile({ - file: files[0], - documents: this.props.documents, - collectionId: this.props.collection.id, - }); - this.props.history.push(document.url); + try { + const document = await importFile({ + file: files[0], + documents: this.props.documents, + collectionId: this.props.collection.id, + }); + this.props.history.push(document.url); + } catch (err) { + this.props.ui.showToast(err.message); + } }; onEdit = (ev: SyntheticEvent<*>) => { diff --git a/app/menus/RevisionMenu.js b/app/menus/RevisionMenu.js index 5e3cabac3..91cda0c10 100644 --- a/app/menus/RevisionMenu.js +++ b/app/menus/RevisionMenu.js @@ -7,7 +7,7 @@ import { MoreIcon } from 'outline-icons'; import CopyToClipboard from 'components/CopyToClipboard'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; import { documentHistoryUrl } from 'utils/routeHelpers'; -import type { Revision } from 'types'; +import Revision from 'models/Revision'; import Document from 'models/Document'; import UiStore from 'stores/UiStore'; diff --git a/app/menus/ShareMenu.js b/app/menus/ShareMenu.js index 4d222f0ed..d2c147d13 100644 --- a/app/menus/ShareMenu.js +++ b/app/menus/ShareMenu.js @@ -4,11 +4,12 @@ import { withRouter } from 'react-router-dom'; import { inject } from 'mobx-react'; import { MoreIcon } from 'outline-icons'; -import type { Share } from 'types'; import CopyToClipboard from 'components/CopyToClipboard'; +import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; + import SharesStore from 'stores/SharesStore'; import UiStore from 'stores/UiStore'; -import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; +import Share from 'models/Share'; type Props = { label?: React.Node, diff --git a/app/menus/UserMenu.js b/app/menus/UserMenu.js index a3b90f354..29ecf40b6 100644 --- a/app/menus/UserMenu.js +++ b/app/menus/UserMenu.js @@ -3,9 +3,9 @@ import * as React from 'react'; import { inject, observer } from 'mobx-react'; import { MoreIcon } from 'outline-icons'; -import UsersStore from 'stores/UsersStore'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; -import type { User } from 'types'; +import UsersStore from 'stores/UsersStore'; +import User from 'models/User'; type Props = { user: User, diff --git a/app/models/ApiKey.js b/app/models/ApiKey.js new file mode 100644 index 000000000..f0e13e6a8 --- /dev/null +++ b/app/models/ApiKey.js @@ -0,0 +1,10 @@ +// @flow +import BaseModel from './BaseModel'; + +class ApiKey extends BaseModel { + id: string; + name: string; + secret: string; +} + +export default ApiKey; diff --git a/app/models/BaseModel.js b/app/models/BaseModel.js index 11cb5f2d9..cd2dcbd23 100644 --- a/app/models/BaseModel.js +++ b/app/models/BaseModel.js @@ -1,3 +1,50 @@ // @flow -import BaseStore from 'stores/BaseStore'; -export default BaseStore; +import { set, observable } from 'mobx'; + +export default class BaseModel { + @observable id: string; + @observable isSaving: boolean; + store: *; + + constructor(fields: Object, store: *) { + set(this, fields); + this.store = store; + } + + save = async params => { + this.isSaving = true; + + try { + // ensure that the id is passed if the document has one + if (params) params = { ...params, id: this.id }; + await this.store.save(params || this.toJS()); + + // if saving is successful set the new values on the model itself + if (params) set(this, params); + return this; + } finally { + this.isSaving = false; + } + }; + + fetch = (options: *) => { + return this.store.fetch(this.id, options); + }; + + refresh = () => { + return this.fetch({ force: true }); + }; + + delete = async () => { + this.isSaving = true; + try { + return await this.store.delete(this); + } finally { + this.isSaving = false; + } + }; + + toJS = () => { + return { ...this }; + }; +} diff --git a/app/models/Collection.js b/app/models/Collection.js index 9a005e56a..f27e10bd7 100644 --- a/app/models/Collection.js +++ b/app/models/Collection.js @@ -1,26 +1,22 @@ // @flow -import { extendObservable, action, computed, runInAction } from 'mobx'; -import invariant from 'invariant'; - +import { pick } from 'lodash'; +import { action, computed } from 'mobx'; import BaseModel from 'models/BaseModel'; import Document from 'models/Document'; import { client } from 'utils/ApiClient'; -import stores from 'stores'; -import UiStore from 'stores/UiStore'; import type { NavigationNode } from 'types'; -class Collection extends BaseModel { - isSaving: boolean = false; - ui: UiStore; +export default class Collection extends BaseModel { + isSaving: boolean; - createdAt: string; - description: string; id: string; name: string; + description: string; color: string; type: 'atlas' | 'journal'; documents: NavigationNode[]; - updatedAt: string; + createdAt: ?string; + updatedAt: ?string; url: string; @computed @@ -56,103 +52,11 @@ class Collection extends BaseModel { travelDocuments(this.documents); } - @action - fetch = async () => { - try { - const res = await client.post('/collections.info', { id: this.id }); - invariant(res && res.data, 'API response should be available'); - const { data } = res; - runInAction('Collection#fetch', () => { - this.updateData(data); - }); - } catch (e) { - this.ui.showToast('Collection failed loading'); - } - - return this; + toJS = () => { + return pick(this, ['name', 'color', 'description']); }; - @action - save = async () => { - if (this.isSaving) return this; - this.isSaving = true; - - const params = { - name: this.name, - color: this.color, - description: this.description, - }; - - try { - let res; - if (this.id) { - res = await client.post('/collections.update', { - id: this.id, - ...params, - }); - } else { - res = await client.post('/collections.create', params); - } - runInAction('Collection#save', () => { - invariant(res && res.data, 'Data should be available'); - this.updateData(res.data); - }); - } catch (e) { - this.ui.showToast('Collection failed saving'); - return false; - } finally { - this.isSaving = false; - } - - return true; + export = () => { + return client.post('/collections.export', { id: this.id }); }; - - @action - delete = async () => { - try { - await client.post('/collections.delete', { id: this.id }); - this.emit('collections.delete', { id: this.id }); - return true; - } catch (e) { - this.ui.showToast('Collection failed to delete'); - } - return false; - }; - - @action - export = async () => { - await client.post('/collections.export', { id: this.id }); - }; - - @action - updateData(data: Object = {}) { - extendObservable(this, data); - } - - constructor(collection: $Shape) { - super(); - - this.updateData(collection); - this.ui = stores.ui; - - this.on('documents.delete', (data: { collectionId: string }) => { - if (data.collectionId === this.id) this.fetch(); - }); - this.on( - 'documents.update', - (data: { collectionId: string, document: Document }) => { - if (data.collectionId === this.id) { - this.updateDocument(data.document); - } - } - ); - this.on('documents.publish', (data: { collectionId: string }) => { - if (data.collectionId === this.id) this.fetch(); - }); - this.on('documents.move', (data: { collectionId: string }) => { - if (data.collectionId === this.id) this.fetch(); - }); - } } - -export default Collection; diff --git a/app/models/Collection.test.js b/app/models/Collection.test.js index 5af3f6734..32c6c012d 100644 --- a/app/models/Collection.test.js +++ b/app/models/Collection.test.js @@ -1,32 +1,12 @@ /* eslint-disable */ -import Collection from './Collection'; -const { client } = require('utils/ApiClient'); +import stores from '../stores'; describe('Collection model', () => { test('should initialize with data', () => { - const collection = new Collection({ + const collection = stores.collections.add({ id: 123, name: 'Engineering', }); expect(collection.name).toBe('Engineering'); }); - - describe('#fetch', () => { - test('should update data', async () => { - client.post = jest.fn(() => ({ - data: { - name: 'New collection', - }, - })) - - const collection = new Collection({ - id: 123, - name: 'Engineering', - }); - - await collection.fetch(); - expect(client.post).toHaveBeenCalledWith('/collections.info', { id: 123 }); - expect(collection.name).toBe('New collection'); - }); - }); }); diff --git a/app/models/Document.js b/app/models/Document.js index 3f6117373..93f907e9e 100644 --- a/app/models/Document.js +++ b/app/models/Document.js @@ -1,49 +1,57 @@ // @flow -import { extendObservable, action, runInAction, computed } from 'mobx'; +import { action, set, computed } from 'mobx'; import invariant from 'invariant'; import { client } from 'utils/ApiClient'; -import stores from 'stores'; -import parseTitle from '../../shared/utils/parseTitle'; -import unescape from '../../shared/utils/unescape'; +import parseTitle from 'shared/utils/parseTitle'; +import unescape from 'shared/utils/unescape'; -import type { NavigationNode, Revision, User } from 'types'; -import BaseModel from './BaseModel'; -import Collection from './Collection'; +import type { NavigationNode } from 'types'; +import BaseModel from 'models/BaseModel'; +import Revision from 'models/Revision'; +import User from 'models/User'; +import Collection from 'models/Collection'; type SaveOptions = { publish?: boolean, done?: boolean, autosave?: boolean }; -class Document extends BaseModel { - isSaving: boolean = false; +export default class Document extends BaseModel { + isSaving: boolean; ui: *; store: *; collaborators: User[]; - collection: $Shape; + collection: Collection; collectionId: string; firstViewedAt: ?string; lastViewedAt: ?string; - modifiedSinceViewed: ?boolean; createdAt: string; createdBy: User; updatedAt: string; updatedBy: User; - html: string; id: string; team: string; + starred: boolean; + pinned: boolean; + text: string; + title: string; emoji: string; - starred: boolean = false; - pinned: boolean = false; - text: string = ''; - title: string = ''; parentDocument: ?string; publishedAt: ?string; url: string; + urlId: string; shareUrl: ?string; views: number; revision: number; - /* Computed */ + constructor(data?: Object = {}, store: *) { + super(data, store); + this.updateTitle(); + } + + @action + updateTitle() { + set(this, parseTitle(this.text)); + } @computed get modifiedSinceViewed(): boolean { @@ -59,9 +67,8 @@ class Document extends BaseModel { if (childNode.id === this.id) { path = newPath; return; - } else { - return traveler(childNode.children, newPath); } + return traveler(childNode.children, newPath); }); }; @@ -96,44 +103,32 @@ class Document extends BaseModel { : null; } - /* Actions */ - @action share = async () => { - try { - const res = await client.post('/shares.create', { documentId: this.id }); - invariant(res && res.data, 'Document API response should be available'); - - this.shareUrl = res.data.url; - } catch (e) { - this.ui.showToast('Document failed to share'); - } + const res = await client.post('/shares.create', { documentId: this.id }); + invariant(res && res.data, 'Share data should be available'); + this.shareUrl = res.data.url; + return this.shareUrl; }; @action - restore = async (revision: Revision) => { - try { - const res = await client.post('/documents.restore', { - id: this.id, - revisionId: revision.id, - }); - runInAction('Document#save', () => { - invariant(res && res.data, 'Data should be available'); - this.updateData(res.data); - }); - } catch (e) { - this.ui.showToast('Document failed to restore'); - } + updateFromJson = data => { + set(this, data); + this.updateTitle(); + }; + + restore = (revision: Revision) => { + return this.store.restore(this, revision); }; @action pin = async () => { this.pinned = true; try { - await client.post('/documents.pin', { id: this.id }); - } catch (e) { + await this.store.pin(this); + } catch (err) { this.pinned = false; - this.ui.showToast('Document failed to pin'); + throw err; } }; @@ -141,10 +136,10 @@ class Document extends BaseModel { unpin = async () => { this.pinned = false; try { - await client.post('/documents.unpin', { id: this.id }); - } catch (e) { + await this.store.unpin(this); + } catch (err) { this.pinned = true; - this.ui.showToast('Document failed to unpin'); + throw err; } }; @@ -152,10 +147,10 @@ class Document extends BaseModel { star = async () => { this.starred = true; try { - await client.post('/documents.star', { id: this.id }); - } catch (e) { + await this.store.star(this); + } catch (err) { this.starred = false; - this.ui.showToast('Document failed star'); + throw err; } }; @@ -163,10 +158,10 @@ class Document extends BaseModel { unstar = async () => { this.starred = false; try { - await client.post('/documents.unstar', { id: this.id }); - } catch (e) { - this.starred = false; - this.ui.showToast('Document failed unstar'); + await this.store.unstar(this); + } catch (err) { + this.starred = true; + throw err; } }; @@ -178,28 +173,21 @@ class Document extends BaseModel { @action fetch = async () => { - try { - const res = await client.post('/documents.info', { id: this.id }); - invariant(res && res.data, 'Document API response should be available'); - const { data } = res; - runInAction('Document#update', () => { - this.updateData(data); - }); - } catch (e) { - this.ui.showToast('Document failed loading'); - } + const res = await client.post('/documents.info', { id: this.id }); + invariant(res && res.data, 'Data should be available'); + this.updateFromJson(res.data); }; @action save = async (options: SaveOptions) => { if (this.isSaving) return this; - const wasDraft = !this.publishedAt; const isCreating = !this.id; + const wasDraft = !this.publishedAt; this.isSaving = true; + this.updateTitle(); try { - let res; if (isCreating) { const data = { parentDocument: undefined, @@ -211,77 +199,30 @@ class Document extends BaseModel { if (this.parentDocument) { data.parentDocument = this.parentDocument; } - res = await client.post('/documents.create', data); + const document = await this.store.create(data); + return document; } else { - res = await client.post('/documents.update', { + const document = await this.store.update({ id: this.id, title: this.title, text: this.text, lastRevision: this.revision, ...options, }); + return document; } - runInAction('Document#save', () => { - invariant(res && res.data, 'Data should be available'); - this.updateData(res.data); - - if (isCreating) { - this.emit('documents.create', this); - } - - this.emit('documents.update', { - document: this, - collectionId: this.collection.id, - }); - - if (wasDraft && this.publishedAt) { - this.emit('documents.publish', { - id: this.id, - collectionId: this.collection.id, - }); - } - }); - } catch (e) { - this.ui.showToast('Document failed to save'); } finally { + if (wasDraft && options.publish) { + this.store.rootStore.collections.fetch(this.collection.id, { + force: true, + }); + } this.isSaving = false; } - - return this; }; - @action - move = async (parentDocumentId: ?string) => { - try { - const res = await client.post('/documents.move', { - id: this.id, - parentDocument: parentDocumentId, - }); - invariant(res && res.data, 'Data not available'); - this.updateData(res.data); - this.emit('documents.move', { - id: this.id, - collectionId: this.collection.id, - }); - } catch (e) { - this.ui.showToast('Error while moving the document'); - } - return; - }; - - @action - delete = async () => { - try { - await client.post('/documents.delete', { id: this.id }); - this.emit('documents.delete', { - id: this.id, - collectionId: this.collection.id, - }); - return true; - } catch (e) { - this.ui.showToast('Error while deleting the document'); - } - return false; + move = (parentDocumentId: ?string) => { + return this.store.move(this, parentDocumentId); }; duplicate = () => { @@ -289,6 +230,7 @@ class Document extends BaseModel { }; download = async () => { + // Ensure the document is upto date with latest server contents await this.fetch(); const blob = new Blob([unescape(this.text)], { type: 'text/markdown' }); @@ -301,23 +243,4 @@ class Document extends BaseModel { a.download = `${this.title}.md`; a.click(); }; - - updateData(data: Object = {}) { - if (data.text) { - const { title, emoji } = parseTitle(data.text); - data.title = title; - data.emoji = emoji; - } - extendObservable(this, data); - } - - constructor(data?: Object = {}) { - super(); - - this.updateData(data); - this.ui = stores.ui; - this.store = stores.documents; - } } - -export default Document; diff --git a/app/models/Document.test.js b/app/models/Document.test.js index f84327064..a31260ffe 100644 --- a/app/models/Document.test.js +++ b/app/models/Document.test.js @@ -1,9 +1,9 @@ /* eslint-disable */ -import Document from './Document'; +import stores from '../stores'; describe('Document model', () => { test('should initialize with data', () => { - const document = new Document({ + const document = stores.documents.add({ id: 123, text: '# Onboarding\nSome body text', }); diff --git a/app/models/Integration.js b/app/models/Integration.js index fa4a84447..6a677c44b 100644 --- a/app/models/Integration.js +++ b/app/models/Integration.js @@ -3,8 +3,6 @@ import { extendObservable, action } from 'mobx'; import BaseModel from 'models/BaseModel'; import { client } from 'utils/ApiClient'; -import stores from 'stores'; -import UiStore from 'stores/UiStore'; type Settings = { url: string, @@ -15,8 +13,6 @@ type Settings = { type Events = 'documents.create' | 'collections.create'; class Integration extends BaseModel { - ui: UiStore; - id: string; service: string; collectionId: string; @@ -25,33 +21,15 @@ class Integration extends BaseModel { @action update = async (data: Object) => { - try { - await client.post('/integrations.update', { id: this.id, ...data }); - extendObservable(this, data); - } catch (e) { - this.ui.showToast('Integration failed to update'); - } - return false; + await client.post('/integrations.update', { id: this.id, ...data }); + extendObservable(this, data); + return true; }; @action - delete = async () => { - try { - await client.post('/integrations.delete', { id: this.id }); - this.emit('integrations.delete', { id: this.id }); - return true; - } catch (e) { - this.ui.showToast('Integration failed to delete'); - } - return false; + delete = () => { + return this.store.delete(this); }; - - constructor(data?: Object = {}) { - super(); - - extendObservable(this, data); - this.ui = stores.ui; - } } export default Integration; diff --git a/app/models/NotificationSetting.js b/app/models/NotificationSetting.js new file mode 100644 index 000000000..5f305c939 --- /dev/null +++ b/app/models/NotificationSetting.js @@ -0,0 +1,9 @@ +// @flow +import BaseModel from './BaseModel'; + +class NotificationSetting extends BaseModel { + id: string; + event: string; +} + +export default NotificationSetting; diff --git a/app/models/Revision.js b/app/models/Revision.js new file mode 100644 index 000000000..ce1571f9c --- /dev/null +++ b/app/models/Revision.js @@ -0,0 +1,14 @@ +// @flow +import BaseModel from './BaseModel'; +import User from './User'; + +class Revision extends BaseModel { + id: string; + documentId: string; + title: string; + text: string; + createdAt: string; + createdBy: User; +} + +export default Revision; diff --git a/app/models/Share.js b/app/models/Share.js new file mode 100644 index 000000000..7391ded2f --- /dev/null +++ b/app/models/Share.js @@ -0,0 +1,15 @@ +// @flow +import BaseModel from './BaseModel'; +import User from './User'; + +class Share extends BaseModel { + id: string; + url: string; + documentTitle: string; + documentUrl: string; + createdBy: User; + createdAt: string; + updatedAt: string; +} + +export default Share; diff --git a/app/models/Team.js b/app/models/Team.js new file mode 100644 index 000000000..5e79ff0c6 --- /dev/null +++ b/app/models/Team.js @@ -0,0 +1,15 @@ +// @flow +import BaseModel from './BaseModel'; + +class Team extends BaseModel { + id: string; + name: string; + avatarUrl: string; + slackConnected: boolean; + googleConnected: boolean; + sharing: boolean; + subdomain: ?string; + url: string; +} + +export default Team; diff --git a/app/models/User.js b/app/models/User.js new file mode 100644 index 000000000..01b45ec0d --- /dev/null +++ b/app/models/User.js @@ -0,0 +1,15 @@ +// @flow +import BaseModel from './BaseModel'; + +class User extends BaseModel { + avatarUrl: string; + id: string; + name: string; + email: string; + username: string; + isAdmin: boolean; + isSuspended: boolean; + createdAt: string; +} + +export default User; diff --git a/app/routes.js b/app/routes.js index a97dcb02e..a7c6a2d04 100644 --- a/app/routes.js +++ b/app/routes.js @@ -11,6 +11,7 @@ import Document from 'scenes/Document'; import Search from 'scenes/Search'; import Settings from 'scenes/Settings'; import Details from 'scenes/Settings/Details'; +import Notifications from 'scenes/Settings/Notifications'; import Security from 'scenes/Settings/Security'; import People from 'scenes/Settings/People'; import Slack from 'scenes/Settings/Slack'; @@ -21,7 +22,7 @@ import Export from 'scenes/Settings/Export'; import Error404 from 'scenes/Error404'; import Layout from 'components/Layout'; -import Auth from 'components/Auth'; +import Authenticated from 'components/Authenticated'; import RouteSidebarHidden from 'components/RouteSidebarHidden'; import { matchDocumentSlug as slug } from 'utils/routeHelpers'; @@ -36,7 +37,7 @@ export default function Routes() { - + @@ -51,6 +52,11 @@ export default function Routes() { + - + ); } diff --git a/app/scenes/Collection.js b/app/scenes/Collection.js index 5d99c667c..d9dd7b879 100644 --- a/app/scenes/Collection.js +++ b/app/scenes/Collection.js @@ -54,8 +54,7 @@ class CollectionScene extends React.Component { } loadContent = async (id: string) => { - const { collections } = this.props; - const collection = collections.getById(id) || (await collections.fetch(id)); + const collection = await this.props.collections.fetch(id); if (collection) { this.props.ui.setActiveCollection(collection); @@ -103,7 +102,7 @@ class CollectionScene extends React.Component { } renderEmptyCollection() { - if (!this.collection) return; + if (!this.collection) return null; return ( diff --git a/app/scenes/CollectionDelete.js b/app/scenes/CollectionDelete.js index 6b76992e6..909dd0ee0 100644 --- a/app/scenes/CollectionDelete.js +++ b/app/scenes/CollectionDelete.js @@ -9,11 +9,13 @@ import Flex from 'shared/components/Flex'; import HelpText from 'components/HelpText'; import Collection from 'models/Collection'; import CollectionsStore from 'stores/CollectionsStore'; +import UiStore from 'stores/UiStore'; type Props = { history: Object, collection: Collection, collections: CollectionsStore, + ui: UiStore, onSubmit: () => void, }; @@ -24,14 +26,16 @@ class CollectionDelete extends React.Component { handleSubmit = async (ev: SyntheticEvent<*>) => { ev.preventDefault(); this.isDeleting = true; - const success = await this.props.collection.delete(); - if (success) { + try { + await this.props.collection.delete(); this.props.history.push(homeUrl()); this.props.onSubmit(); + } catch (err) { + this.props.ui.showToast(err.message); + } finally { + this.isDeleting = false; } - - this.isDeleting = false; }; render() { @@ -54,4 +58,4 @@ class CollectionDelete extends React.Component { } } -export default inject('collections')(withRouter(CollectionDelete)); +export default inject('collections', 'ui')(withRouter(CollectionDelete)); diff --git a/app/scenes/CollectionEdit.js b/app/scenes/CollectionEdit.js index cf1d4d87f..ab6cf953f 100644 --- a/app/scenes/CollectionEdit.js +++ b/app/scenes/CollectionEdit.js @@ -10,10 +10,12 @@ import Flex from 'shared/components/Flex'; import HelpText from 'components/HelpText'; import ColorPicker from 'components/ColorPicker'; import Collection from 'models/Collection'; +import UiStore from 'stores/UiStore'; type Props = { history: Object, collection: Collection, + ui: UiStore, onSubmit: () => void, }; @@ -33,18 +35,18 @@ class CollectionEdit extends React.Component { ev.preventDefault(); this.isSaving = true; - this.props.collection.updateData({ - name: this.name, - description: this.description, - color: this.color, - }); - const success = await this.props.collection.save(); - - if (success) { + try { + await this.props.collection.save({ + name: this.name, + description: this.description, + color: this.color, + }); this.props.onSubmit(); + } catch (err) { + this.props.ui.showToast(err.message); + } finally { + this.isSaving = false; } - - this.isSaving = false; }; handleDescriptionChange = getValue => { @@ -99,4 +101,4 @@ class CollectionEdit extends React.Component { } } -export default inject('collections')(withRouter(CollectionEdit)); +export default inject('ui')(withRouter(CollectionEdit)); diff --git a/app/scenes/CollectionExport.js b/app/scenes/CollectionExport.js index 8bfa656ed..9f4472b00 100644 --- a/app/scenes/CollectionExport.js +++ b/app/scenes/CollectionExport.js @@ -33,7 +33,7 @@ class CollectionExport extends React.Component { render() { const { collection, auth } = this.props; - if (!auth.user) return; + if (!auth.user) return null; return ( diff --git a/app/scenes/CollectionNew.js b/app/scenes/CollectionNew.js index 052dfa9fb..409f07685 100644 --- a/app/scenes/CollectionNew.js +++ b/app/scenes/CollectionNew.js @@ -11,43 +11,43 @@ import HelpText from 'components/HelpText'; import Collection from 'models/Collection'; import CollectionsStore from 'stores/CollectionsStore'; +import UiStore from 'stores/UiStore'; type Props = { history: Object, + ui: UiStore, collections: CollectionsStore, onSubmit: () => void, }; @observer class CollectionNew extends React.Component { - @observable collection: Collection; @observable name: string = ''; @observable description: string = ''; @observable color: string = ''; @observable isSaving: boolean; - constructor(props: Props) { - super(props); - this.collection = new Collection(); - } - handleSubmit = async (ev: SyntheticEvent<*>) => { ev.preventDefault(); this.isSaving = true; - this.collection.updateData({ - name: this.name, - description: this.description, - color: this.color, - }); - const success = await this.collection.save(); + const collection = new Collection( + { + name: this.name, + description: this.description, + color: this.color, + }, + this.props.collections + ); - if (success) { - this.props.collections.add(this.collection); + try { + await collection.save(); this.props.onSubmit(); - this.props.history.push(this.collection.url); + this.props.history.push(collection.url); + } catch (err) { + this.props.ui.showToast(err.message); + } finally { + this.isSaving = false; } - - this.isSaving = false; }; handleNameChange = (ev: SyntheticInputEvent<*>) => { @@ -95,4 +95,4 @@ class CollectionNew extends React.Component { } } -export default inject('collections')(withRouter(CollectionNew)); +export default inject('collections', 'ui')(withRouter(CollectionNew)); diff --git a/app/scenes/Dashboard.js b/app/scenes/Dashboard.js index ee7ba109c..2e5f2cc4f 100644 --- a/app/scenes/Dashboard.js +++ b/app/scenes/Dashboard.js @@ -23,7 +23,7 @@ type Props = { class Dashboard extends React.Component { render() { const { documents, auth } = this.props; - if (!auth.user) return; + if (!auth.user) return null; const user = auth.user.id; return ( diff --git a/app/scenes/Document/Document.js b/app/scenes/Document/Document.js index 3b07bf53c..de3ba8ae5 100644 --- a/app/scenes/Document/Document.js +++ b/app/scenes/Document/Document.js @@ -1,6 +1,6 @@ // @flow import * as React from 'react'; -import debounce from 'lodash/debounce'; +import { debounce } from 'lodash'; import styled from 'styled-components'; import breakpoint from 'styled-components-breakpoint'; import { observable } from 'mobx'; @@ -18,9 +18,7 @@ import { matchDocumentEdit, } from 'utils/routeHelpers'; import { emojiToUrl } from 'utils/emoji'; -import type { Revision } from 'types'; -import Document from 'models/Document'; import Header from './components/Header'; import DocumentMove from './components/DocumentMove'; import Branding from './components/Branding'; @@ -38,6 +36,9 @@ import UiStore from 'stores/UiStore'; import AuthStore from 'stores/AuthStore'; import DocumentsStore from 'stores/DocumentsStore'; import RevisionsStore from 'stores/RevisionsStore'; +import Document from 'models/Document'; +import Revision from 'models/Revision'; + import schema from './schema'; const AUTOSAVE_DELAY = 3000; @@ -101,6 +102,10 @@ class DocumentScene extends React.Component { this.props.ui.clearActiveDocument(); } + goToDocumentCanonical = () => { + if (this.document) this.props.history.push(this.document.url); + }; + @keydown('m') goToMove(ev) { ev.preventDefault(); @@ -121,14 +126,17 @@ class DocumentScene extends React.Component { loadDocument = async props => { if (props.newDocument) { - this.document = new Document({ - collection: { id: props.match.params.id }, - parentDocument: new URLSearchParams(props.location.search).get( - 'parentDocument' - ), - title: '', - text: '', - }); + this.document = new Document( + { + collection: { id: props.match.params.id }, + parentDocument: new URLSearchParams(props.location.search).get( + 'parentDocument' + ), + title: '', + text: '', + }, + this.props.documents + ); } else { const { shareId, revisionId } = props.match.params; @@ -140,7 +148,7 @@ class DocumentScene extends React.Component { if (revisionId) { this.revision = await this.props.revisions.fetch( props.match.params.documentSlug, - revisionId + { revisionId } ); } else { this.revision = undefined; @@ -201,7 +209,7 @@ class DocumentScene extends React.Component { // prevent autosave if nothing has changed if (options.autosave && document.text.trim() === text.trim()) return; - document.updateData({ text }); + document.text = text; if (!document.allowSave) return; // prevent autosave before anything has been written @@ -310,7 +318,12 @@ class DocumentScene extends React.Component { > } + component={() => ( + + )} /> { if (!document.collection) return null; const collection = - collections.getById(document.collection.id) || document.collection; + collections.data.get(document.collection.id) || document.collection; return ( diff --git a/app/scenes/Document/components/DocumentMove.js b/app/scenes/Document/components/DocumentMove.js index ce444122b..309a3d445 100644 --- a/app/scenes/Document/components/DocumentMove.js +++ b/app/scenes/Document/components/DocumentMove.js @@ -3,10 +3,9 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { observable, computed } from 'mobx'; import { observer, inject } from 'mobx-react'; -import { withRouter } from 'react-router-dom'; import { Search } from 'js-search'; +import { first, last } from 'lodash'; import ArrowKeyNavigation from 'boundless-arrow-key-navigation'; -import _ from 'lodash'; import styled from 'styled-components'; import Modal from 'components/Modal'; @@ -20,11 +19,10 @@ import DocumentsStore from 'stores/DocumentsStore'; import CollectionsStore, { type DocumentPath } from 'stores/CollectionsStore'; type Props = { - match: Object, - history: Object, document: Document, documents: DocumentsStore, collections: CollectionsStore, + onRequestClose: *, }; @observer @@ -44,7 +42,7 @@ class DocumentMove extends React.Component { const indexeableDocuments = []; paths.forEach(path => { // TMP: For now, exclude paths to other collections - if (_.first(path.path).id !== document.collection.id) return; + if (first(path.path).id !== document.collection.id) return; indexeableDocuments.push(path); }); @@ -91,7 +89,7 @@ class DocumentMove extends React.Component { results = results.filter( result => !result.path.map(doc => doc.id).includes(document.id) && - _.last(result.path.map(doc => doc.id)) !== document.parentDocumentId + last(result.path.map(doc => doc.id)) !== document.parentDocumentId ); return results; @@ -108,12 +106,8 @@ class DocumentMove extends React.Component { } }; - handleClose = () => { - this.props.history.push(this.props.document.url); - }; - - handleFilter = (e: SyntheticInputEvent<*>) => { - this.searchTerm = e.target.value; + handleFilter = (ev: SyntheticInputEvent<*>) => { + this.searchTerm = ev.target.value; }; setFirstDocumentRef = ref => { @@ -123,16 +117,17 @@ class DocumentMove extends React.Component { renderPathToCurrentDocument() { const { collections, document } = this.props; const result = collections.getPathForDocument(document.id); + if (result) { return ; } } render() { - const { document, collections } = this.props; + const { document, collections, onRequestClose } = this.props; return ( - + {document && collections.isLoaded && ( @@ -166,7 +161,7 @@ class DocumentMove extends React.Component { ref={ref => index === 0 && this.setFirstDocumentRef(ref) } - onSuccess={this.handleClose} + onSuccess={onRequestClose} /> ))} @@ -189,4 +184,4 @@ const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)` flex: 1; `; -export default withRouter(inject('documents', 'collections')(DocumentMove)); +export default inject('documents', 'collections')(DocumentMove); diff --git a/app/scenes/DocumentDelete.js b/app/scenes/DocumentDelete.js index 6aab778ba..e69be4681 100644 --- a/app/scenes/DocumentDelete.js +++ b/app/scenes/DocumentDelete.js @@ -8,11 +8,13 @@ import Flex from 'shared/components/Flex'; import HelpText from 'components/HelpText'; import Document from 'models/Document'; import DocumentsStore from 'stores/DocumentsStore'; +import UiStore from 'stores/UiStore'; type Props = { history: Object, document: Document, documents: DocumentsStore, + ui: UiStore, onSubmit: () => void, }; @@ -24,14 +26,16 @@ class DocumentDelete extends React.Component { ev.preventDefault(); this.isDeleting = true; const { collection } = this.props.document; - const success = await this.props.document.delete(); - if (success) { + try { + await this.props.document.delete(); this.props.history.push(collection.url); this.props.onSubmit(); + } catch (err) { + this.props.ui.showToast(err.message); + } finally { + this.isDeleting = false; } - - this.isDeleting = false; }; render() { @@ -53,4 +57,4 @@ class DocumentDelete extends React.Component { } } -export default inject('documents')(withRouter(DocumentDelete)); +export default inject('documents', 'ui')(withRouter(DocumentDelete)); diff --git a/app/scenes/Search/Search.js b/app/scenes/Search/Search.js index 63c753399..a9abad3a7 100644 --- a/app/scenes/Search/Search.js +++ b/app/scenes/Search/Search.js @@ -5,25 +5,23 @@ import keydown from 'react-keydown'; import Waypoint from 'react-waypoint'; import { observable, action } from 'mobx'; import { observer, inject } from 'mobx-react'; -import type { SearchResult } from 'types'; -import _ from 'lodash'; -import DocumentsStore, { - DEFAULT_PAGINATION_LIMIT, -} from 'stores/DocumentsStore'; - +import { debounce } from 'lodash'; import { withRouter } from 'react-router-dom'; -import { searchUrl } from 'utils/routeHelpers'; import styled from 'styled-components'; import ArrowKeyNavigation from 'boundless-arrow-key-navigation'; -import Empty from 'components/Empty'; +import type { SearchResult } from 'types'; +import { DEFAULT_PAGINATION_LIMIT } from 'stores/BaseStore'; +import DocumentsStore from 'stores/DocumentsStore'; +import { searchUrl } from 'utils/routeHelpers'; + import Flex from 'shared/components/Flex'; +import Empty from 'components/Empty'; import CenteredContent from 'components/CenteredContent'; import LoadingIndicator from 'components/LoadingIndicator'; -import SearchField from './components/SearchField'; - import DocumentPreview from 'components/DocumentPreview'; import PageTitle from 'components/PageTitle'; +import SearchField from './components/SearchField'; type Props = { history: Object, @@ -115,11 +113,6 @@ class Search extends React.Component { this.fetchResultsDebounced(); }; - fetchResultsDebounced = _.debounce(this.fetchResults, 350, { - leading: false, - trailing: true, - }); - @action loadMoreResults = async () => { // Don't paginate if there aren't more results or we’re in the middle of fetching @@ -158,6 +151,11 @@ class Search extends React.Component { this.isFetching = false; }; + fetchResultsDebounced = debounce(this.fetchResults, 350, { + leading: false, + trailing: true, + }); + updateLocation = query => { this.props.history.replace(searchUrl(query)); }; @@ -201,7 +199,7 @@ class Search extends React.Component { defaultActiveChildIndex={0} > {this.results.map((result, index) => { - const document = documents.getById(result.document.id); + const document = documents.data.get(result.document.id); if (!document) return null; return ( diff --git a/app/scenes/Settings/Details.js b/app/scenes/Settings/Details.js index 2c33a54ad..6a706374a 100644 --- a/app/scenes/Settings/Details.js +++ b/app/scenes/Settings/Details.js @@ -121,7 +121,7 @@ class Details extends React.Component { name="subdomain" value={this.subdomain || ''} onChange={this.handleSubdomainChange} - autocomplete="off" + autoComplete="off" minLength={4} maxLength={32} short diff --git a/app/scenes/Settings/Export.js b/app/scenes/Settings/Export.js index 402a811f1..3d1787051 100644 --- a/app/scenes/Settings/Export.js +++ b/app/scenes/Settings/Export.js @@ -26,18 +26,18 @@ class Export extends React.Component { ev.preventDefault(); this.isLoading = true; - const success = await this.props.collections.export(); - - if (success) { + try { + await this.props.collections.export(); this.isExporting = true; this.props.ui.showToast('Export in progress…', 'success'); + } finally { + this.isLoading = false; } - this.isLoading = false; }; render() { const { auth } = this.props; - if (!auth.user) return; + if (!auth.user) return null; return ( diff --git a/app/scenes/Settings/Notifications.js b/app/scenes/Settings/Notifications.js new file mode 100644 index 000000000..daf2e16c2 --- /dev/null +++ b/app/scenes/Settings/Notifications.js @@ -0,0 +1,93 @@ +// @flow +import * as React from 'react'; +import { debounce } from 'lodash'; +import { observer, inject } from 'mobx-react'; +import CenteredContent from 'components/CenteredContent'; +import PageTitle from 'components/PageTitle'; +import HelpText from 'components/HelpText'; +import NotificationListItem from './components/NotificationListItem'; + +import UiStore from 'stores/UiStore'; +import NotificationSettingsStore from 'stores/NotificationSettingsStore'; + +type Props = { + ui: UiStore, + notificationSettings: NotificationSettingsStore, +}; + +const options = [ + { + event: 'documents.publish', + title: 'Document published', + description: 'Receive a notification whenever a new document is published', + }, + { + event: 'documents.update', + title: 'Document updated', + description: 'Receive a notification when a document you created is edited', + }, + { + event: 'collections.create', + title: 'Collection created', + description: 'Receive a notification whenever a new collection is created', + }, +]; + +@observer +class Notifications extends React.Component { + componentDidMount() { + this.props.notificationSettings.fetchPage(); + } + + handleChange = async (ev: SyntheticInputEvent<*>) => { + const { notificationSettings } = this.props; + const setting = notificationSettings.getByEvent(ev.target.name); + + if (ev.target.checked) { + await notificationSettings.save({ + event: ev.target.name, + }); + } else if (setting) { + await notificationSettings.delete(setting); + } + + this.showSuccessMessage(); + }; + + showSuccessMessage = debounce(() => { + this.props.ui.showToast('Notifications updated'); + }, 500); + + render() { + const { notificationSettings } = this.props; + + return ( + + +

    Notifications

    + + + Manage when you receive email notifications from Outline. + + + {options.map(option => { + const setting = notificationSettings.getByEvent(option.event); + + return ( + + ); + })} +
    + ); + } +} + +export default inject('notificationSettings', 'ui')(Notifications); diff --git a/app/scenes/Settings/People.js b/app/scenes/Settings/People.js index fd34adae8..a631efeeb 100644 --- a/app/scenes/Settings/People.js +++ b/app/scenes/Settings/People.js @@ -33,7 +33,7 @@ class People extends React.Component { let users = this.props.users.active; if (filter === 'all') { - users = this.props.users.data; + users = this.props.users.orderedData; } else if (filter === 'admins') { users = this.props.users.admins; } diff --git a/app/scenes/Settings/Profile.js b/app/scenes/Settings/Profile.js index 1d01a2a8e..49567c100 100644 --- a/app/scenes/Settings/Profile.js +++ b/app/scenes/Settings/Profile.js @@ -126,6 +126,7 @@ class Profile extends React.Component { } const DangerZone = styled.div` + background: #fff; position: absolute; bottom: 16px; `; diff --git a/app/scenes/Settings/Slack.js b/app/scenes/Settings/Slack.js index c122cbd5f..1d49f149d 100644 --- a/app/scenes/Settings/Slack.js +++ b/app/scenes/Settings/Slack.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react'; import { inject, observer } from 'mobx-react'; -import _ from 'lodash'; +import { find } from 'lodash'; import styled from 'styled-components'; import Button from 'components/Button'; @@ -29,7 +29,7 @@ class Slack extends React.Component { } get commandIntegration() { - return _.find(this.props.integrations.slackIntegrations, { + return find(this.props.integrations.slackIntegrations, { type: 'command', }); } @@ -73,7 +73,7 @@ class Slack extends React.Component { {collections.orderedData.map(collection => { - const integration = _.find(integrations.slackIntegrations, { + const integration = find(integrations.slackIntegrations, { collectionId: collection.id, }); diff --git a/app/scenes/Settings/Tokens.js b/app/scenes/Settings/Tokens.js index c7fb26684..29da04ee4 100644 --- a/app/scenes/Settings/Tokens.js +++ b/app/scenes/Settings/Tokens.js @@ -30,13 +30,13 @@ class Tokens extends React.Component { handleSubmit = async (ev: SyntheticEvent<*>) => { ev.preventDefault(); - await this.props.apiKeys.createApiKey(this.name); + await this.props.apiKeys.create({ name: this.name }); this.name = ''; }; render() { const { apiKeys } = this.props; - const hasApiKeys = apiKeys.data.length > 0; + const hasApiKeys = apiKeys.orderedData.length > 0; return ( @@ -51,11 +51,11 @@ class Tokens extends React.Component { {hasApiKeys && ( - {apiKeys.data.map(token => ( + {apiKeys.orderedData.map(token => ( ))} diff --git a/app/scenes/Settings/components/NotificationListItem.js b/app/scenes/Settings/components/NotificationListItem.js new file mode 100644 index 000000000..e2eb87346 --- /dev/null +++ b/app/scenes/Settings/components/NotificationListItem.js @@ -0,0 +1,36 @@ +// @flow +import * as React from 'react'; +import Checkbox from 'components/Checkbox'; +import NotificationSetting from 'models/NotificationSetting'; + +type Props = { + setting?: NotificationSetting, + title: string, + event: string, + description: string, + disabled: boolean, + onChange: *, +}; + +const NotificationListItem = ({ + setting, + title, + event, + enabled, + onChange, + disabled, + description, +}: Props) => { + return ( + + ); +}; + +export default NotificationListItem; diff --git a/app/scenes/Settings/components/ShareListItem.js b/app/scenes/Settings/components/ShareListItem.js index c05104e9b..d0b7e2713 100644 --- a/app/scenes/Settings/components/ShareListItem.js +++ b/app/scenes/Settings/components/ShareListItem.js @@ -3,7 +3,7 @@ import * as React from 'react'; import ShareMenu from 'menus/ShareMenu'; import ListItem from 'components/List/Item'; import Time from 'shared/components/Time'; -import type { Share } from '../../../types'; +import Share from 'models/Share'; type Props = { share: Share, diff --git a/app/scenes/Settings/components/TokenListItem.js b/app/scenes/Settings/components/TokenListItem.js index d4ad90d70..cc96377c2 100644 --- a/app/scenes/Settings/components/TokenListItem.js +++ b/app/scenes/Settings/components/TokenListItem.js @@ -2,7 +2,7 @@ import * as React from 'react'; import Button from 'components/Button'; import ListItem from 'components/List/Item'; -import type { ApiKey } from '../../../types'; +import ApiKey from 'models/ApiKey'; type Props = { token: ApiKey, diff --git a/app/scenes/Settings/components/UserListItem.js b/app/scenes/Settings/components/UserListItem.js index 3525dc32b..7741f44d3 100644 --- a/app/scenes/Settings/components/UserListItem.js +++ b/app/scenes/Settings/components/UserListItem.js @@ -6,7 +6,7 @@ import UserMenu from 'menus/UserMenu'; import Avatar from 'components/Avatar'; import ListItem from 'components/List/Item'; import Time from 'shared/components/Time'; -import type { User } from '../../../types'; +import User from 'models/User'; type Props = { user: User, diff --git a/app/scenes/UserDelete.js b/app/scenes/UserDelete.js index 00f332441..036ffe107 100644 --- a/app/scenes/UserDelete.js +++ b/app/scenes/UserDelete.js @@ -22,11 +22,8 @@ class UserDelete extends React.Component { this.isDeleting = true; try { - const success = await this.props.auth.deleteUser(); - - if (success) { - this.props.auth.logout(); - } + await this.props.auth.deleteUser(); + this.props.auth.logout(); } finally { this.isDeleting = false; } diff --git a/app/stores/ApiKeysStore.js b/app/stores/ApiKeysStore.js index 40c1b7113..865eeb134 100644 --- a/app/stores/ApiKeysStore.js +++ b/app/stores/ApiKeysStore.js @@ -1,60 +1,12 @@ // @flow -import { observable, action, runInAction } from 'mobx'; -import invariant from 'invariant'; -import { client } from 'utils/ApiClient'; -import type { ApiKey, PaginationParams } from 'types'; +import BaseStore from './BaseStore'; +import RootStore from './RootStore'; +import ApiKey from 'models/ApiKey'; -class ApiKeysStore { - @observable data: ApiKey[] = []; - @observable isFetching: boolean = false; - @observable isSaving: boolean = false; +export default class ApiKeysStore extends BaseStore { + actions = ['list', 'create', 'delete']; - @action - fetchPage = async (options: ?PaginationParams): Promise<*> => { - this.isFetching = true; - - try { - const res = await client.post('/apiKeys.list', options); - invariant(res && res.data, 'Data should be available'); - const { data } = res; - - runInAction('fetchApiKeys', () => { - this.data = data; - }); - } catch (e) { - console.error('Something went wrong'); - } - this.isFetching = false; - }; - - @action - createApiKey = async (name: string) => { - this.isSaving = true; - - try { - const res = await client.post('/apiKeys.create', { name }); - invariant(res && res.data, 'Data should be available'); - const { data } = res; - runInAction('createApiKey', () => { - this.data.push(data); - }); - } catch (e) { - console.error('Something went wrong'); - } - this.isSaving = false; - }; - - @action - deleteApiKey = async (id: string) => { - try { - await client.post('/apiKeys.delete', { id }); - runInAction('deleteApiKey', () => { - this.fetchPage(); - }); - } catch (e) { - console.error('Something went wrong'); - } - }; + constructor(rootStore: RootStore) { + super(rootStore, ApiKey); + } } - -export default ApiKeysStore; diff --git a/app/stores/AuthStore.js b/app/stores/AuthStore.js index 12526bfaa..dffb85ad7 100644 --- a/app/stores/AuthStore.js +++ b/app/stores/AuthStore.js @@ -4,20 +4,45 @@ import invariant from 'invariant'; import Cookie from 'js-cookie'; import { client } from 'utils/ApiClient'; import { stripSubdomain } from 'shared/utils/domains'; -import type { User, Team } from 'types'; +import RootStore from 'stores/RootStore'; +import User from 'models/User'; +import Team from 'models/Team'; const AUTH_STORE = 'AUTH_STORE'; -class AuthStore { +export default class AuthStore { @observable user: ?User; @observable team: ?Team; @observable token: ?string; @observable isSaving: boolean = false; - @observable isLoading: boolean = false; @observable isSuspended: boolean = false; @observable suspendedContactEmail: ?string; + rootStore: RootStore; - /* Computed */ + constructor(rootStore: RootStore) { + // Rehydrate + let data = {}; + try { + data = JSON.parse(localStorage.getItem(AUTH_STORE) || '{}'); + } catch (_) { + // no-op Safari private mode + } + + this.rootStore = rootStore; + this.user = data.user; + this.team = data.team; + this.token = Cookie.get('accessToken'); + + if (this.token) setImmediate(() => this.fetch()); + + autorun(() => { + try { + localStorage.setItem(AUTH_STORE, this.asJson); + } catch (_) { + // no-op Safari private mode + } + }); + } @computed get authenticated(): boolean { @@ -39,8 +64,18 @@ class AuthStore { invariant(res && res.data, 'Auth not available'); runInAction('AuthStore#fetch', () => { - this.user = res.data.user; - this.team = res.data.team; + const { user, team } = res.data; + this.user = user; + this.team = team; + + if (window.Bugsnag) { + Bugsnag.user = { + id: user.id, + name: user.name, + teamId: team.id, + team: team.name, + }; + } }); } catch (err) { if (err.error.error === 'user_suspended') { @@ -52,7 +87,7 @@ class AuthStore { @action deleteUser = async () => { - await client.post(`/user.delete`, { confirmation: true }); + await client.post(`/users.delete`, { confirmation: true }); runInAction('AuthStore#updateUser', () => { this.user = null; @@ -66,7 +101,7 @@ class AuthStore { this.isSaving = true; try { - const res = await client.post(`/user.update`, params); + const res = await client.post(`/users.update`, params); invariant(res && res.data, 'User response not available'); runInAction('AuthStore#updateUser', () => { @@ -120,29 +155,4 @@ class AuthStore { // add a timestamp to force reload from server window.location.href = `${BASE_URL}?done=${new Date().getTime()}`; }; - - constructor() { - // Rehydrate - let data = {}; - try { - data = JSON.parse(localStorage.getItem(AUTH_STORE) || '{}'); - } catch (_) { - // no-op Safari private mode - } - this.user = data.user; - this.team = data.team; - this.token = Cookie.get('accessToken'); - - if (this.token) setImmediate(() => this.fetch()); - - autorun(() => { - try { - localStorage.setItem(AUTH_STORE, this.asJson); - } catch (_) { - // no-op Safari private mode - } - }); - } } - -export default AuthStore; diff --git a/app/stores/BaseStore.js b/app/stores/BaseStore.js index 803576919..950448428 100644 --- a/app/stores/BaseStore.js +++ b/app/stores/BaseStore.js @@ -1,19 +1,162 @@ // @flow -import { EventEmitter } from 'fbemitter'; -import _ from 'lodash'; +import invariant from 'invariant'; +import { observable, set, action, computed, runInAction } from 'mobx'; +import { orderBy } from 'lodash'; +import { client } from 'utils/ApiClient'; +import RootStore from 'stores/RootStore'; +import BaseModel from '../models/BaseModel'; +import type { PaginationParams } from 'types'; -const emitter = new EventEmitter(); -window.__emitter = emitter; +type Action = 'list' | 'info' | 'create' | 'update' | 'delete'; -class BaseStore { - emitter: EventEmitter; - on: (eventName: string, callback: Function) => void; - emit: (eventName: string, data: any) => void; - - constructor() { - _.extend(this, emitter); - this.on = emitter.addListener; - } +function modelNameFromClassName(string) { + return string.charAt(0).toLowerCase() + string.slice(1); } -export default BaseStore; +export const DEFAULT_PAGINATION_LIMIT = 25; + +export default class BaseStore { + @observable data: Map = new Map(); + @observable isFetching: boolean = false; + @observable isSaving: boolean = false; + @observable isLoaded: boolean = false; + + model: Class; + modelName: string; + rootStore: RootStore; + actions: Action[] = ['list', 'info', 'create', 'update', 'delete']; + + constructor(rootStore: RootStore, model: Class) { + this.rootStore = rootStore; + this.model = model; + this.modelName = modelNameFromClassName(model.name); + } + + @action + clear() { + this.data.clear(); + } + + @action + add = (item: Object): T => { + const Model = this.model; + + if (!(item instanceof Model)) { + const existing: ?T = this.data.get(item.id); + if (existing) { + set(existing, item); + return existing; + } else { + item = new Model(item, this); + } + } + + this.data.set(item.id, item); + return item; + }; + + @action + remove(id: string): void { + this.data.delete(id); + } + + save(params: Object) { + if (params.id) return this.update(params); + return this.create(params); + } + + @action + async create(params: Object) { + if (!this.actions.includes('create')) { + throw new Error(`Cannot create ${this.modelName}`); + } + this.isSaving = true; + + try { + const res = await client.post(`/${this.modelName}s.create`, params); + + invariant(res && res.data, 'Data should be available'); + return this.add(res.data); + } finally { + this.isSaving = false; + } + } + + @action + async update(params: Object): * { + if (!this.actions.includes('update')) { + throw new Error(`Cannot update ${this.modelName}`); + } + this.isSaving = true; + + try { + const res = await client.post(`/${this.modelName}s.update`, params); + + invariant(res && res.data, 'Data should be available'); + return this.add(res.data); + } finally { + this.isSaving = false; + } + } + + @action + async delete(item: T) { + if (!this.actions.includes('delete')) { + throw new Error(`Cannot delete ${this.modelName}`); + } + this.isSaving = true; + + try { + await client.post(`/${this.modelName}s.delete`, { id: item.id }); + return this.remove(item.id); + } finally { + this.isSaving = false; + } + } + + @action + async fetch(id: string, options?: Object = {}): Promise<*> { + if (!this.actions.includes('info')) { + throw new Error(`Cannot fetch ${this.modelName}`); + } + + let item = this.data.get(id); + if (item && !options.force) return item; + + this.isFetching = true; + + try { + const res = await client.post(`/${this.modelName}s.info`, { id }); + invariant(res && res.data, 'Data should be available'); + return this.add(res.data); + } finally { + this.isFetching = false; + } + } + + @action + async fetchPage(params: ?PaginationParams): Promise<*> { + if (!this.actions.includes('list')) { + throw new Error(`Cannot list ${this.modelName}`); + } + this.isFetching = true; + + try { + const res = await client.post(`/${this.modelName}s.list`, params); + + invariant(res && res.data, 'Data not available'); + runInAction(`list#${this.modelName}`, () => { + res.data.forEach(this.add); + this.isLoaded = true; + }); + } finally { + this.isFetching = false; + } + } + + @computed + get orderedData(): T[] { + // $FlowIssue + return orderBy(Array.from(this.data.values()), 'createdAt', 'desc'); + } +} diff --git a/app/stores/CollectionsStore.js b/app/stores/CollectionsStore.js index 59db057c0..f0ffb99d6 100644 --- a/app/stores/CollectionsStore.js +++ b/app/stores/CollectionsStore.js @@ -1,20 +1,14 @@ // @flow -import { observable, computed, action, runInAction, ObservableMap } from 'mobx'; +import { computed, runInAction } from 'mobx'; +import { concat, last } from 'lodash'; import { client } from 'utils/ApiClient'; -import _ from 'lodash'; -import invariant from 'invariant'; import BaseStore from './BaseStore'; -import UiStore from './UiStore'; -import Collection from 'models/Collection'; +import RootStore from './RootStore'; +import Collection from '../models/Collection'; import naturalSort from 'shared/utils/naturalSort'; -import type { PaginationParams } from 'types'; -type Options = { - ui: UiStore, -}; - -type DocumentPathItem = { +export type DocumentPathItem = { id: string, title: string, url: string, @@ -25,17 +19,15 @@ export type DocumentPath = DocumentPathItem & { path: DocumentPathItem[], }; -class CollectionsStore extends BaseStore { - @observable data: Map = new ObservableMap([]); - @observable isLoaded: boolean = false; - @observable isFetching: boolean = false; - - ui: UiStore; +export default class CollectionsStore extends BaseStore { + constructor(rootStore: RootStore) { + super(rootStore, Collection); + } @computed get active(): ?Collection { - return this.ui.activeCollectionId - ? this.getById(this.ui.activeCollectionId) + return this.rootStore.ui.activeCollectionId + ? this.data.get(this.rootStore.ui.activeCollectionId) : undefined; } @@ -48,14 +40,14 @@ class CollectionsStore extends BaseStore { * List of paths to each of the documents, where paths are composed of id and title/name pairs */ @computed - get pathsToDocuments(): Array { + get pathsToDocuments(): DocumentPath[] { let results = []; const travelDocuments = (documentList, path) => documentList.forEach(document => { const { id, title, url } = document; const node = { id, title, url, type: 'document' }; - results.push(_.concat(path, node)); - travelDocuments(document.children, _.concat(path, [node])); + results.push(concat(path, node)); + travelDocuments(document.children, concat(path, [node])); }); if (this.isLoaded) { @@ -68,7 +60,7 @@ class CollectionsStore extends BaseStore { } return results.map(result => { - const tail = _.last(result); + const tail = last(result); return { ...tail, path: result, @@ -85,90 +77,16 @@ class CollectionsStore extends BaseStore { if (path) return path.title; } - /* Actions */ + delete(collection: Collection) { + super.delete(collection); - @action - fetchPage = async (options: ?PaginationParams): Promise<*> => { - this.isFetching = true; - - try { - const res = await client.post('/collections.list', options); - invariant(res && res.data, 'Collection list not available'); - const { data } = res; - runInAction('CollectionsStore#fetchPage', () => { - data.forEach(collection => { - this.data.set(collection.id, new Collection(collection)); - }); - this.isLoaded = true; - }); - return res; - } catch (e) { - this.ui.showToast('Failed to load collections'); - } finally { - this.isFetching = false; - } - }; - - @action - fetch = async (id: string): Promise => { - let collection = this.getById(id); - if (collection) return collection; - - this.isFetching = true; - - try { - const res = await client.post('/collections.info', { - id, - }); - invariant(res && res.data, 'Collection not available'); - const { data } = res; - const collection = new Collection(data); - - runInAction('CollectionsStore#fetch', () => { - this.data.set(data.id, collection); - this.isLoaded = true; - }); - - return collection; - } catch (e) { - this.ui.showToast('Something went wrong'); - } finally { - this.isFetching = false; - } - }; - - @action - export = async () => { - try { - await client.post('/collections.exportAll'); - return true; - } catch (err) { - throw err; - } - }; - - @action - add = (collection: Collection): void => { - this.data.set(collection.id, collection); - }; - - @action - remove = (id: string): void => { - this.data.delete(id); - }; - - getById = (id: string): ?Collection => { - return this.data.get(id); - }; - - constructor(options: Options) { - super(); - this.ui = options.ui; - - this.on('collections.delete', (data: { id: string }) => { - this.remove(data.id); + runInAction(() => { + this.rootStore.documents.fetchRecentlyUpdated(); + this.rootStore.documents.fetchRecentlyViewed(); }); } -} -export default CollectionsStore; + export = () => { + return client.post('/collections.exportAll'); + }; +} diff --git a/app/stores/DocumentsStore.js b/app/stores/DocumentsStore.js index 168eeed25..ddf53e8ff 100644 --- a/app/stores/DocumentsStore.js +++ b/app/stores/DocumentsStore.js @@ -1,55 +1,48 @@ // @flow -import { observable, action, computed, ObservableMap, runInAction } from 'mobx'; +import { observable, action, computed, runInAction } from 'mobx'; +import { without, map, find, orderBy, filter, compact, uniq } from 'lodash'; import { client } from 'utils/ApiClient'; -import { map, find, orderBy, filter, compact, uniq, sortBy } from 'lodash'; +import naturalSort from 'shared/utils/naturalSort'; import invariant from 'invariant'; import BaseStore from 'stores/BaseStore'; -import Document from 'models/Document'; -import UiStore from 'stores/UiStore'; -import type { PaginationParams, SearchResult } from 'types'; +import RootStore from 'stores/RootStore'; +import Document from '../models/Document'; +import Revision from '../models/Revision'; +import type { FetchOptions, PaginationParams, SearchResult } from 'types'; -export const DEFAULT_PAGINATION_LIMIT = 25; - -type Options = { - ui: UiStore, -}; - -type FetchOptions = { - prefetch?: boolean, - shareId?: string, -}; - -class DocumentsStore extends BaseStore { +export default class DocumentsStore extends BaseStore { @observable recentlyViewedIds: string[] = []; @observable recentlyUpdatedIds: string[] = []; - @observable data: Map = new ObservableMap([]); - @observable isLoaded: boolean = false; - @observable isFetching: boolean = false; - ui: UiStore; + constructor(rootStore: RootStore) { + super(rootStore, Document); + } @computed - get recentlyViewed(): Document[] { + get recentlyViewed(): * { return orderBy( - compact(this.recentlyViewedIds.map(id => this.getById(id))), + compact(this.recentlyViewedIds.map(id => this.data.get(id))), 'updatedAt', 'desc' ); } @computed - get recentlyUpdated(): Document[] { + get recentlyUpdated(): * { return orderBy( - compact(this.recentlyUpdatedIds.map(id => this.getById(id))), + compact(this.recentlyUpdatedIds.map(id => this.data.get(id))), 'updatedAt', 'desc' ); } - createdByUser(userId: string): Document[] { + createdByUser(userId: string): * { return orderBy( - filter(this.data.values(), document => document.createdBy.id === userId), + filter( + Array.from(this.data.values()), + document => document.createdBy.id === userId + ), 'updatedAt', 'desc' ); @@ -65,7 +58,7 @@ class DocumentsStore extends BaseStore { recentlyUpdatedInCollection(collectionId: string): Document[] { return orderBy( filter( - this.data.values(), + Array.from(this.data.values()), document => document.collectionId === collectionId && !!document.publishedAt ), @@ -76,33 +69,31 @@ class DocumentsStore extends BaseStore { @computed get starred(): Document[] { - return filter(this.data.values(), 'starred'); + return filter(this.orderedData, d => d.starred); } @computed get starredAlphabetical(): Document[] { - return sortBy(this.starred, doc => doc.title.toLowerCase()); + return naturalSort(this.starred, 'title'); } @computed get drafts(): Document[] { return filter( - orderBy(this.data.values(), 'updatedAt', 'desc'), + orderBy(Array.from(this.data.values()), 'updatedAt', 'desc'), doc => !doc.publishedAt ); } @computed get active(): ?Document { - return this.ui.activeDocumentId - ? this.getById(this.ui.activeDocumentId) + return this.rootStore.ui.activeDocumentId + ? this.data.get(this.rootStore.ui.activeDocumentId) : undefined; } - /* Actions */ - @action - fetchPage = async ( + fetchNamedPage = async ( request: string = 'list', options: ?PaginationParams ): Promise => { @@ -112,15 +103,11 @@ class DocumentsStore extends BaseStore { const res = await client.post(`/documents.${request}`, options); invariant(res && res.data, 'Document list not available'); const { data } = res; - runInAction('DocumentsStore#fetchPage', () => { - data.forEach(document => { - this.data.set(document.id, new Document(document)); - }); + runInAction('DocumentsStore#fetchNamedPage', () => { + data.forEach(this.add); this.isLoaded = true; }); return data; - } catch (e) { - this.ui.showToast('Failed to load documents'); } finally { this.isFetching = false; } @@ -128,7 +115,7 @@ class DocumentsStore extends BaseStore { @action fetchRecentlyUpdated = async (options: ?PaginationParams): Promise<*> => { - const data = await this.fetchPage('list', options); + const data = await this.fetchNamedPage('list', options); runInAction('DocumentsStore#fetchRecentlyUpdated', () => { // $FlowFixMe @@ -141,7 +128,7 @@ class DocumentsStore extends BaseStore { @action fetchRecentlyViewed = async (options: ?PaginationParams): Promise<*> => { - const data = await this.fetchPage('viewed', options); + const data = await this.fetchNamedPage('viewed', options); runInAction('DocumentsStore#fetchRecentlyViewed', () => { // $FlowFixMe @@ -154,22 +141,22 @@ class DocumentsStore extends BaseStore { @action fetchStarred = (options: ?PaginationParams): Promise<*> => { - return this.fetchPage('starred', options); + return this.fetchNamedPage('starred', options); }; @action fetchDrafts = (options: ?PaginationParams): Promise<*> => { - return this.fetchPage('drafts', options); + return this.fetchNamedPage('drafts', options); }; @action fetchPinned = (options: ?PaginationParams): Promise<*> => { - return this.fetchPage('pinned', options); + return this.fetchNamedPage('pinned', options); }; @action fetchOwned = (options: ?PaginationParams): Promise<*> => { - return this.fetchPage('list', options); + return this.fetchNamedPage('list', options); }; @action @@ -183,23 +170,26 @@ class DocumentsStore extends BaseStore { }); invariant(res && res.data, 'Search API response should be available'); const { data } = res; - data.forEach(result => this.add(new Document(result.document))); + data.forEach(result => this.add(result.document)); return data; }; @action - prefetchDocument = async (id: string) => { - if (!this.getById(id)) { - this.fetch(id, { prefetch: true }); + prefetchDocument = (id: string) => { + if (!this.data.get(id)) { + return this.fetch(id, { prefetch: true }); } }; @action - fetch = async (id: string, options?: FetchOptions = {}): Promise<*> => { + fetch = async ( + id: string, + options?: FetchOptions = {} + ): Promise => { if (!options.prefetch) this.isFetching = true; try { - const doc = this.getById(id) || this.getByUrl(id); + const doc: ?Document = this.data.get(id) || this.getByUrl(id); if (doc) return doc; const res = await client.post('/documents.info', { @@ -207,24 +197,32 @@ class DocumentsStore extends BaseStore { shareId: options.shareId, }); invariant(res && res.data, 'Document not available'); - const { data } = res; - const document = new Document(data); + this.add(res.data); runInAction('DocumentsStore#fetch', () => { - this.data.set(data.id, document); this.isLoaded = true; }); - return document; - } catch (_err) { - if (!options.prefetch && navigator.onLine) { - this.ui.showToast('Failed to load document'); - } + return this.data.get(res.data.id); } finally { this.isFetching = false; } }; + @action + move = async (document: Document, parentDocumentId: ?string) => { + const res = await client.post('/documents.move', { + id: document.id, + parentDocument: parentDocumentId, + }); + invariant(res && res.data, 'Data not available'); + + const collection = this.getCollectionForDocument(document); + if (collection) collection.refresh(); + + return this.add(res.data); + }; + @action duplicate = async (document: Document): * => { const res = await client.post('/documents.create', { @@ -234,61 +232,69 @@ class DocumentsStore extends BaseStore { title: `${document.title} (duplicate)`, text: document.text, }); + invariant(res && res.data, 'Data should be available'); - if (res && res.data) { - const duped = res.data; - this.emit('documents.create', new Document(duped)); - this.emit('documents.publish', { - id: duped.id, - collectionId: duped.collection.id, - }); - return duped; - } + const collection = this.getCollectionForDocument(document); + if (collection) collection.refresh(); + + return this.add(res.data); }; + async update(params: *) { + const document = await super.update(params); + + // Because the collection object contains the url and title + // we need to ensure they are updated there as well. + const collection = this.getCollectionForDocument(document); + if (collection) collection.updateDocument(document); + return document; + } + + async delete(document: Document) { + await super.delete(document); + + runInAction(() => { + this.recentlyViewedIds = without(this.recentlyViewedIds, document.id); + this.recentlyUpdatedIds = without(this.recentlyUpdatedIds, document.id); + }); + + const collection = this.getCollectionForDocument(document); + if (collection) collection.refresh(); + } + @action - add = (document: Document): void => { - this.data.set(document.id, document); + restore = async (document: Document, revision: Revision) => { + const res = await client.post('/documents.restore', { + id: document.id, + revisionId: revision.id, + }); + runInAction('Document#restore', () => { + invariant(res && res.data, 'Data should be available'); + document.updateFromJson(res.data); + }); }; - @action - remove = (id: string): void => { - this.data.delete(id); + pin = (document: Document) => { + return client.post('/documents.pin', { id: document.id }); }; - getById = (id: string): ?Document => { - return this.data.get(id); + unpin = (document: Document) => { + return client.post('/documents.unpin', { id: document.id }); + }; + + star = (document: Document) => { + return client.post('/documents.star', { id: document.id }); + }; + + unstar = (document: Document) => { + return client.post('/documents.unstar', { id: document.id }); }; - /** - * Match documents by the url ID as the title slug can change - */ getByUrl = (url: string): ?Document => { - return find(this.data.values(), doc => url.endsWith(doc.urlId)); + return find(Array.from(this.data.values()), doc => url.endsWith(doc.urlId)); }; - constructor(options: Options) { - super(); - - this.ui = options.ui; - - this.on('documents.delete', (data: { id: string }) => { - this.remove(data.id); - }); - this.on('documents.create', (data: Document) => { - this.add(data); - }); - - // Re-fetch dashboard content so that we don't show deleted documents - this.on('collections.delete', () => { - this.fetchRecentlyUpdated(); - this.fetchRecentlyViewed(); - }); - this.on('documents.delete', () => { - this.fetchRecentlyUpdated(); - this.fetchRecentlyViewed(); - }); + getCollectionForDocument(document: Document) { + return this.rootStore.collections.data.get(document.collectionId); } } - -export default DocumentsStore; diff --git a/app/stores/IntegrationsStore.js b/app/stores/IntegrationsStore.js index 39e065cf3..5a2d4994b 100644 --- a/app/stores/IntegrationsStore.js +++ b/app/stores/IntegrationsStore.js @@ -1,74 +1,25 @@ // @flow -import { observable, computed, action, runInAction, ObservableMap } from 'mobx'; -import { client } from 'utils/ApiClient'; -import _ from 'lodash'; -import invariant from 'invariant'; -import UiStore from './UiStore'; -import BaseStore from './BaseStore'; +import { computed } from 'mobx'; +import { filter } from 'lodash'; +import naturalSort from 'shared/utils/naturalSort'; +import BaseStore from 'stores/BaseStore'; +import RootStore from 'stores/RootStore'; import Integration from 'models/Integration'; -import type { PaginationParams } from 'types'; -class IntegrationsStore extends BaseStore { - @observable data: Map = new ObservableMap([]); - @observable isLoaded: boolean = false; - @observable isFetching: boolean = false; - - ui: UiStore; +class IntegrationsStore extends BaseStore { + constructor(rootStore: RootStore) { + super(rootStore, Integration); + } @computed get orderedData(): Integration[] { - return _.sortBy(this.data.values(), 'name'); + return naturalSort(Array.from(this.data.values()), 'name'); } @computed get slackIntegrations(): Integration[] { - return _.filter(this.orderedData, { service: 'slack' }); - } - - @action - fetchPage = async (options: ?PaginationParams): Promise<*> => { - this.isFetching = true; - - try { - const res = await client.post('/integrations.list', options); - invariant(res && res.data, 'Integrations list not available'); - const { data } = res; - runInAction('IntegrationsStore#fetchPage', () => { - data.forEach(integration => { - this.data.set(integration.id, new Integration(integration)); - }); - this.isLoaded = true; - }); - return res; - } catch (e) { - this.ui.showToast('Failed to load integrations'); - } finally { - this.isFetching = false; - } - }; - - @action - add = (data: Integration): void => { - this.data.set(data.id, data); - }; - - @action - remove = (id: string): void => { - this.data.delete(id); - }; - - getById = (id: string): ?Integration => { - return this.data.get(id); - }; - - constructor(options: { ui: UiStore }) { - super(); - this.ui = options.ui; - - this.on('integrations.delete', (data: { id: string }) => { - this.remove(data.id); - }); + return filter(this.orderedData, { service: 'slack' }); } } diff --git a/app/stores/NotificationSettingsStore.js b/app/stores/NotificationSettingsStore.js new file mode 100644 index 000000000..cf5fb35d6 --- /dev/null +++ b/app/stores/NotificationSettingsStore.js @@ -0,0 +1,19 @@ +// @flow +import { find } from 'lodash'; +import NotificationSetting from 'models/NotificationSetting'; +import BaseStore from './BaseStore'; +import RootStore from './RootStore'; + +export default class NotificationSettingsStore extends BaseStore< + NotificationSetting +> { + actions = ['list', 'create', 'delete']; + + constructor(rootStore: RootStore) { + super(rootStore, NotificationSetting); + } + + getByEvent = (event: string) => { + return find(this.orderedData, { event }); + }; +} diff --git a/app/stores/RevisionsStore.js b/app/stores/RevisionsStore.js index 09955ff1a..44f82eb68 100644 --- a/app/stores/RevisionsStore.js +++ b/app/stores/RevisionsStore.js @@ -1,22 +1,18 @@ // @flow -import { observable, computed, action, runInAction, ObservableMap } from 'mobx'; -import { client } from 'utils/ApiClient'; -import { orderBy, filter } from 'lodash'; +import { action, runInAction } from 'mobx'; +import { filter } from 'lodash'; import invariant from 'invariant'; -import BaseStore from './BaseStore'; -import UiStore from './UiStore'; -import type { Revision, PaginationParams } from 'types'; +import { client } from 'utils/ApiClient'; +import BaseStore from 'stores/BaseStore'; +import RootStore from 'stores/RootStore'; +import Revision from 'models/Revision'; +import type { FetchOptions, PaginationParams } from 'types'; -class RevisionsStore extends BaseStore { - @observable data: Map = new ObservableMap([]); - @observable isLoaded: boolean = false; - @observable isFetching: boolean = false; +export default class RevisionsStore extends BaseStore { + actions = ['list']; - ui: UiStore; - - @computed - get orderedData(): Revision[] { - return orderBy(this.data.values(), 'createdAt', 'desc'); + constructor(rootStore: RootStore) { + super(rootStore, Revision); } getDocumentRevisions(documentId: string): Revision[] { @@ -24,11 +20,16 @@ class RevisionsStore extends BaseStore { } @action - fetch = async (documentId: string, id: string): Promise<*> => { + fetch = async ( + documentId: string, + options?: FetchOptions + ): Promise => { this.isFetching = true; + const id = options && options.revisionId; + if (!id) throw new Error('revisionId is required'); try { - const rev = this.getById(id); + const rev = this.data.get(id); if (rev) return rev; const res = await client.post('/documents.revision', { @@ -44,8 +45,6 @@ class RevisionsStore extends BaseStore { }); return data; - } catch (e) { - this.ui.showToast('Failed to load document revision'); } finally { this.isFetching = false; } @@ -58,39 +57,14 @@ class RevisionsStore extends BaseStore { try { const res = await client.post('/documents.revisions', options); invariant(res && res.data, 'Document revisions not available'); - const { data } = res; runInAction('RevisionsStore#fetchPage', () => { - data.forEach(revision => { + res.data.forEach(revision => { this.data.set(revision.id, revision); }); this.isLoaded = true; }); - return data; - } catch (e) { - this.ui.showToast('Failed to load document revisions'); } finally { this.isFetching = false; } }; - - @action - add = (data: Revision): void => { - this.data.set(data.id, data); - }; - - @action - remove = (id: string): void => { - this.data.delete(id); - }; - - getById = (id: string): ?Revision => { - return this.data.get(id); - }; - - constructor(options: { ui: UiStore }) { - super(); - this.ui = options.ui; - } } - -export default RevisionsStore; diff --git a/app/stores/RootStore.js b/app/stores/RootStore.js new file mode 100644 index 000000000..73c12d2ce --- /dev/null +++ b/app/stores/RootStore.js @@ -0,0 +1,48 @@ +// @flow +import ApiKeysStore from './ApiKeysStore'; +import AuthStore from './AuthStore'; +import CollectionsStore from './CollectionsStore'; +import DocumentsStore from './DocumentsStore'; +import IntegrationsStore from './IntegrationsStore'; +import NotificationSettingsStore from './NotificationSettingsStore'; +import RevisionsStore from './RevisionsStore'; +import SharesStore from './SharesStore'; +import UiStore from './UiStore'; +import UsersStore from './UsersStore'; + +export default class RootStore { + apiKeys: ApiKeysStore; + auth: AuthStore; + collections: CollectionsStore; + documents: DocumentsStore; + integrations: IntegrationsStore; + notificationSettings: NotificationSettingsStore; + revisions: RevisionsStore; + shares: SharesStore; + ui: UiStore; + users: UsersStore; + + constructor() { + this.apiKeys = new ApiKeysStore(this); + this.auth = new AuthStore(this); + this.collections = new CollectionsStore(this); + this.documents = new DocumentsStore(this); + this.integrations = new IntegrationsStore(this); + this.notificationSettings = new NotificationSettingsStore(this); + this.revisions = new RevisionsStore(this); + this.shares = new SharesStore(this); + this.ui = new UiStore(); + this.users = new UsersStore(this); + } + + logout() { + this.apiKeys.clear(); + this.collections.clear(); + this.documents.clear(); + this.integrations.clear(); + this.notificationSettings.clear(); + this.revisions.clear(); + this.shares.clear(); + this.users.clear(); + } +} diff --git a/app/stores/SharesStore.js b/app/stores/SharesStore.js index 64f35caeb..d1b040560 100644 --- a/app/stores/SharesStore.js +++ b/app/stores/SharesStore.js @@ -1,51 +1,26 @@ // @flow -import _ from 'lodash'; -import { observable, action, runInAction, ObservableMap, computed } from 'mobx'; -import invariant from 'invariant'; +import { sortBy } from 'lodash'; +import { action, computed } from 'mobx'; import { client } from 'utils/ApiClient'; -import type { Share, PaginationParams } from 'types'; +import BaseStore from './BaseStore'; +import RootStore from './RootStore'; +import Share from 'models/Share'; -class SharesStore { - @observable data: Map = new ObservableMap([]); - @observable isFetching: boolean = false; - @observable isSaving: boolean = false; +export default class SharesStore extends BaseStore { + actions = ['list', 'create']; + + constructor(rootStore: RootStore) { + super(rootStore, Share); + } @computed get orderedData(): Share[] { - return _.sortBy(this.data.values(), 'createdAt').reverse(); + return sortBy(Array.from(this.data.values()), 'createdAt').reverse(); } - @action - fetchPage = async (options: ?PaginationParams): Promise<*> => { - this.isFetching = true; - - try { - const res = await client.post('/shares.list', options); - invariant(res && res.data, 'Data should be available'); - const { data } = res; - - runInAction('fetchShares', () => { - data.forEach(share => { - this.data.set(share.id, share); - }); - }); - } catch (e) { - console.error('Something went wrong'); - } - this.isFetching = false; - }; - @action revoke = async (share: Share) => { - try { - await client.post('/shares.revoke', { id: share.id }); - runInAction('revoke', () => { - this.data.delete(share.id); - }); - } catch (e) { - console.error('Something went wrong'); - } + await client.post('/shares.revoke', { id: share.id }); + this.remove(share.id); }; } - -export default SharesStore; diff --git a/app/stores/UiStore.js b/app/stores/UiStore.js index c5477a11b..e3c34c475 100644 --- a/app/stores/UiStore.js +++ b/app/stores/UiStore.js @@ -12,9 +12,8 @@ class UiStore { @observable progressBarVisible: boolean = false; @observable editMode: boolean = false; @observable mobileSidebarVisible: boolean = false; - @observable toasts: Toast[] = observable.array([]); + @observable toasts: Toast[] = []; - /* Actions */ @action setActiveModal = (name: string, props: ?Object): void => { this.activeModalName = name; @@ -85,7 +84,7 @@ class UiStore { @action showToast = ( message: string, - type?: 'warning' | 'error' | 'info' | 'success' = 'warning' + type?: 'warning' | 'error' | 'info' | 'success' = 'success' ): void => { this.toasts.push({ message, type }); }; diff --git a/app/stores/UsersStore.js b/app/stores/UsersStore.js index a6b96bcb5..9ea5ffebc 100644 --- a/app/stores/UsersStore.js +++ b/app/stores/UsersStore.js @@ -1,73 +1,56 @@ // @flow -import { observable, computed, action, runInAction } from 'mobx'; +import { filter } from 'lodash'; +import { computed, action, runInAction } from 'mobx'; import invariant from 'invariant'; import { client } from 'utils/ApiClient'; -import type { User, PaginationParams } from 'types'; +import BaseStore from './BaseStore'; +import RootStore from './RootStore'; +import User from 'models/User'; -class UsersStore { - @observable data: User[] = []; - @observable isSaving: boolean = false; +export default class UsersStore extends BaseStore { + constructor(rootStore: RootStore) { + super(rootStore, User); + } @computed get active(): User[] { - return this.data.filter(user => !user.isSuspended); + return filter(this.orderedData, user => !user.isSuspended); } @computed get admins(): User[] { - return this.data.filter(user => user.isAdmin); + return filter(this.orderedData, user => user.isAdmin); } @action - fetchPage = async (options: ?PaginationParams): Promise<*> => { - try { - const res = await client.post('/team.users', options); - invariant(res && res.data, 'Data should be available'); - const { data } = res; - - runInAction('fetchUsers', () => { - this.data = data.reverse(); - }); - } catch (e) { - console.error('Something went wrong'); - } - }; - - @action - promote = async (user: User) => { + promote = (user: User) => { return this.actionOnUser('promote', user); }; @action - demote = async (user: User) => { + demote = (user: User) => { return this.actionOnUser('demote', user); }; @action - suspend = async (user: User) => { + suspend = (user: User) => { return this.actionOnUser('suspend', user); }; @action - activate = async (user: User) => { + activate = (user: User) => { return this.actionOnUser('activate', user); }; actionOnUser = async (action: string, user: User) => { - try { - const res = await client.post(`/user.${action}`, { - id: user.id, - }); - invariant(res && res.data, 'Data should be available'); - const { data } = res; + const res = await client.post(`/users.${action}`, { + id: user.id, + }); + invariant(res && res.data, 'Data should be available'); + const { data } = res; - runInAction(`UsersStore#${action}`, () => { - this.data = this.data.map(user => (user.id === data.id ? data : user)); - }); - } catch (e) { - console.error('Something went wrong'); - } + runInAction(`UsersStore#${action}`, () => { + this.add(data); + }); }; } - -export default UsersStore; diff --git a/app/stores/index.js b/app/stores/index.js index 634c4b101..052b30acc 100644 --- a/app/stores/index.js +++ b/app/stores/index.js @@ -1,18 +1,6 @@ // @flow -import AuthStore from './AuthStore'; -import UiStore from './UiStore'; -import DocumentsStore from './DocumentsStore'; -import RevisionsStore from './RevisionsStore'; -import SharesStore from './SharesStore'; +import RootStore from 'stores/RootStore'; -const ui = new UiStore(); -const stores = { - user: null, // Including for Layout - auth: new AuthStore(), - ui, - documents: new DocumentsStore({ ui }), - revisions: new RevisionsStore({ ui }), - shares: new SharesStore(), -}; +const stores = new RootStore(); export default stores; diff --git a/app/types/index.js b/app/types/index.js index 9daae0b83..0eb8a207b 100644 --- a/app/types/index.js +++ b/app/types/index.js @@ -1,53 +1,16 @@ // @flow - -export type User = { - avatarUrl: string, - id: string, - name: string, - email: string, - username: string, - isAdmin?: boolean, - isSuspended?: boolean, - createdAt: string, -}; - -export type Revision = { - id: string, - documentId: string, - title: string, - text: string, - createdAt: string, - createdBy: User, - diff: { - added: number, - removed: number, - }, -}; +import Document from 'models/Document'; export type Toast = { message: string, type: 'warning' | 'error' | 'info' | 'success', }; -export type Share = { - id: string, - url: string, - documentTitle: string, - documentUrl: string, - createdBy: User, - createdAt: string, - updatedAt: string, -}; - -export type Team = { - id: string, - name: string, - avatarUrl: string, - slackConnected: boolean, - googleConnected: boolean, - sharing: boolean, - subdomain?: string, - url: string, +export type FetchOptions = { + prefetch?: boolean, + revisionId?: string, + shareId?: string, + force?: boolean, }; export type NavigationNode = { @@ -57,24 +20,6 @@ export type NavigationNode = { children: NavigationNode[], }; -export type Document = { - collaborators: User[], - collection: Object, - createdAt: string, - createdBy: User, - html: string, - id: string, - starred: boolean, - views: number, - team: string, - text: string, - title: string, - updatedAt: string, - updatedBy: User, - url: string, - views: number, -}; - // Pagination response in an API call export type Pagination = { limit: number, @@ -90,12 +35,6 @@ export type PaginationParams = { direction?: 'ASC' | 'DESC', }; -export type ApiKey = { - id: string, - name: string, - secret: string, -}; - export type SearchResult = { ranking: number, context: string, diff --git a/app/utils/ApiClient.js b/app/utils/ApiClient.js index b57724020..9b290a80e 100644 --- a/app/utils/ApiClient.js +++ b/app/utils/ApiClient.js @@ -47,15 +47,23 @@ class ApiClient { headers.set('Authorization', `Bearer ${stores.auth.token}`); } - // $FlowFixMe don't care much about this right now - const response = await fetch(this.baseUrl + (modifiedPath || path), { - method, - body, - headers, - redirect: 'follow', - credentials: 'omit', - cache: 'no-cache', - }); + let response; + try { + response = await fetch(this.baseUrl + (modifiedPath || path), { + method, + body, + headers, + redirect: 'follow', + credentials: 'omit', + cache: 'no-cache', + }); + } catch (err) { + if (window.navigator.onLine) { + throw new Error('A network error occurred, try again?'); + } else { + throw new Error('No internet connection available'); + } + } if (response.status >= 200 && response.status < 300) { return response.json(); @@ -92,9 +100,10 @@ class ApiClient { // Helpers constructQueryString = (data: Object) => { - return map(data, (v, k) => { - return `${encodeURIComponent(k)}=${encodeURIComponent(v)}`; - }).join('&'); + return map( + data, + (v, k) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}` + ).join('&'); }; } diff --git a/app/utils/importFile.js b/app/utils/importFile.js index 17d2d8046..08b7a40e3 100644 --- a/app/utils/importFile.js +++ b/app/utils/importFile.js @@ -15,7 +15,7 @@ const importFile = async ({ documentId, collectionId, }: Options): Promise => { - return new Promise(resolve => { + return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = async ev => { @@ -28,10 +28,13 @@ const importFile = async ({ if (documentId) data.parentDocument = documentId; - let document = new Document(data); - document = await document.save({ publish: true }); - documents.add(document); - resolve(document); + const document = new Document(data, documents); + try { + await document.save({ publish: true }); + resolve(document); + } catch (err) { + reject(err); + } }; reader.readAsText(file); }); diff --git a/app/utils/routeHelpers.js b/app/utils/routeHelpers.js index 0cf02e636..d271a96b2 100644 --- a/app/utils/routeHelpers.js +++ b/app/utils/routeHelpers.js @@ -23,7 +23,7 @@ export function documentUrl(doc: Document): string { } export function documentNewUrl(doc: Document): string { - const newUrl = `${doc.collection.url}/new`; + const newUrl = `${doc.collection.url || ''}/new`; if (doc.parentDocumentId) { return `${newUrl}?parentDocument=${doc.parentDocumentId}`; } @@ -59,7 +59,7 @@ export function updateDocumentUrl(oldUrl: string, newUrl: string): string { } export function newDocumentUrl(collection: Collection): string { - return `${collection.url}/new`; + return `${collection.url || ''}/new`; } export function searchUrl(query?: string): string { diff --git a/app/utils/uploadFile.js b/app/utils/uploadFile.js index 569941b41..81b738ef4 100644 --- a/app/utils/uploadFile.js +++ b/app/utils/uploadFile.js @@ -11,7 +11,7 @@ export const uploadFile = async ( option?: Options = { name: '' } ) => { const filename = file instanceof File ? file.name : option.name; - const response = await client.post('/user.s3Upload', { + const response = await client.post('/users.s3Upload', { kind: file.type, size: file.size, filename, diff --git a/flow-typed/npm/@tommoor/remove-markdown_vx.x.x.js b/flow-typed/npm/@tommoor/remove-markdown_vx.x.x.js index 4f58f9176..10045252b 100644 --- a/flow-typed/npm/@tommoor/remove-markdown_vx.x.x.js +++ b/flow-typed/npm/@tommoor/remove-markdown_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 34ca0e3549dbccf06b1bfc979378b478 -// flow-typed version: <>/@tommoor/remove-markdown_v0.3.1/flow_v0.71.0 +// flow-typed signature: 21d5fe028258d1ffba50a6b0736291d5 +// flow-typed version: <>/@tommoor/remove-markdown_v0.3.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/@tommoor/slate-drop-or-paste-images_vx.x.x.js b/flow-typed/npm/@tommoor/slate-drop-or-paste-images_vx.x.x.js index 1a1679b0d..2858bda9f 100644 --- a/flow-typed/npm/@tommoor/slate-drop-or-paste-images_vx.x.x.js +++ b/flow-typed/npm/@tommoor/slate-drop-or-paste-images_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 1716f73356cbdf5450e8d7ab82dd2e1a -// flow-typed version: <>/@tommoor/slate-drop-or-paste-images_v^0.8.1/flow_v0.71.0 +// flow-typed signature: 331ec8f8b563f7cbdee203ec29d377df +// flow-typed version: <>/@tommoor/slate-drop-or-paste-images_v^0.8.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/autotrack_vx.x.x.js b/flow-typed/npm/autotrack_vx.x.x.js index 22d9b9dcb..943608227 100644 --- a/flow-typed/npm/autotrack_vx.x.x.js +++ b/flow-typed/npm/autotrack_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 0cb55f2f1730c36c81fb4d6e5baedccf -// flow-typed version: <>/autotrack_v^2.4.1/flow_v0.71.0 +// flow-typed signature: 7cf9fa5af6f3abd4a1bff3cd0f6bfa67 +// flow-typed version: <>/autotrack_v^2.4.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/aws-sdk_vx.x.x.js b/flow-typed/npm/aws-sdk_vx.x.x.js index 5c15c43d8..29a7726d9 100644 --- a/flow-typed/npm/aws-sdk_vx.x.x.js +++ b/flow-typed/npm/aws-sdk_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 89d44f4cb92605ee21e7925d36b4d4f1 -// flow-typed version: <>/aws-sdk_v^2.135.0/flow_v0.71.0 +// flow-typed signature: b94812bc40dc3ffa6ade48e2b6be6b75 +// flow-typed version: <>/aws-sdk_v^2.135.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-core_vx.x.x.js b/flow-typed/npm/babel-core_vx.x.x.js index 3ea2633a8..ee5c18274 100644 --- a/flow-typed/npm/babel-core_vx.x.x.js +++ b/flow-typed/npm/babel-core_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: e24af6bf202d8e5fab4e87cde4d2bfa2 -// flow-typed version: <>/babel-core_v^6.24.1/flow_v0.71.0 +// flow-typed signature: fcaf7a7816f60a19ea3a1f56e8d0aece +// flow-typed version: <>/babel-core_v^6.24.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-eslint_vx.x.x.js b/flow-typed/npm/babel-eslint_vx.x.x.js index 2e30cb836..adf9ec999 100644 --- a/flow-typed/npm/babel-eslint_vx.x.x.js +++ b/flow-typed/npm/babel-eslint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 533f9ec506a216e4d7a0f986dfe83e8b -// flow-typed version: <>/babel-eslint_v^8.1.2/flow_v0.71.0 +// flow-typed signature: 2a52dca523c85f00349a5f796ac57f98 +// flow-typed version: <>/babel-eslint_v^8.1.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-jest_vx.x.x.js b/flow-typed/npm/babel-jest_vx.x.x.js index 2290b43c0..1639aea4e 100644 --- a/flow-typed/npm/babel-jest_vx.x.x.js +++ b/flow-typed/npm/babel-jest_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 16a74ec1b3f30574b1d17af2e1aae17e -// flow-typed version: <>/babel-jest_v22/flow_v0.71.0 +// flow-typed signature: 79b7d190650deeee4eae5fe6e58effab +// flow-typed version: <>/babel-jest_v22/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-loader_vx.x.x.js b/flow-typed/npm/babel-loader_vx.x.x.js index 2555f1abf..a21e1417c 100644 --- a/flow-typed/npm/babel-loader_vx.x.x.js +++ b/flow-typed/npm/babel-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a62195ffbfff5c6790934103be75a8ff -// flow-typed version: <>/babel-loader_v^7.1.2/flow_v0.71.0 +// flow-typed signature: 86c74e4861e92b022be2751b4e34c07f +// flow-typed version: <>/babel-loader_v^7.1.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-lodash_vx.x.x.js b/flow-typed/npm/babel-plugin-lodash_vx.x.x.js index 20883d6cb..05b415574 100644 --- a/flow-typed/npm/babel-plugin-lodash_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-lodash_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 47c9088583f2a3b1c3bb03b4055baece -// flow-typed version: <>/babel-plugin-lodash_v^3.2.11/flow_v0.71.0 +// flow-typed signature: caeb055267c108526a45dbe39a1cfcde +// flow-typed version: <>/babel-plugin-lodash_v^3.2.11/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-styled-components_vx.x.x.js b/flow-typed/npm/babel-plugin-styled-components_vx.x.x.js index 58f414e5d..bc4f19178 100644 --- a/flow-typed/npm/babel-plugin-styled-components_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-styled-components_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9da92a15fa5848fd1ac62d37d77a6850 -// flow-typed version: <>/babel-plugin-styled-components_v^1.1.7/flow_v0.71.0 +// flow-typed signature: c1c7db4e6f9c1bbafd00e07275660942 +// flow-typed version: <>/babel-plugin-styled-components_v^1.1.7/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-syntax-dynamic-import_vx.x.x.js b/flow-typed/npm/babel-plugin-syntax-dynamic-import_vx.x.x.js index e46f05145..04ab3672d 100644 --- a/flow-typed/npm/babel-plugin-syntax-dynamic-import_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-syntax-dynamic-import_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 652608d2e8431a8c5274bad836ee0926 -// flow-typed version: <>/babel-plugin-syntax-dynamic-import_v^6.18.0/flow_v0.71.0 +// flow-typed signature: 3f861346197ad057d0c793c36f953381 +// flow-typed version: <>/babel-plugin-syntax-dynamic-import_v^6.18.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-class-properties_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-class-properties_vx.x.x.js index 9167c3dd2..4164808f8 100644 --- a/flow-typed/npm/babel-plugin-transform-class-properties_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-class-properties_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b26839abd705c305219fee62938d492b -// flow-typed version: <>/babel-plugin-transform-class-properties_v^6.24.1/flow_v0.71.0 +// flow-typed signature: b69261b9b5752022ba1f9f5bd7d71ad4 +// flow-typed version: <>/babel-plugin-transform-class-properties_v^6.24.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-decorators-legacy_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-decorators-legacy_vx.x.x.js index 275712c23..feee60e01 100644 --- a/flow-typed/npm/babel-plugin-transform-decorators-legacy_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-decorators-legacy_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: c7f83286bf05aa71691f77b01cfe5ca8 -// flow-typed version: <>/babel-plugin-transform-decorators-legacy_v1.3.4/flow_v0.71.0 +// flow-typed signature: f8bc955b803a9e7e65e580d016899f98 +// flow-typed version: <>/babel-plugin-transform-decorators-legacy_v1.3.4/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-es2015-destructuring_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-es2015-destructuring_vx.x.x.js index e9c5351d7..6d436f9d3 100644 --- a/flow-typed/npm/babel-plugin-transform-es2015-destructuring_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-es2015-destructuring_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9afa5c60629c28d30730ce2df5dcfcd7 -// flow-typed version: <>/babel-plugin-transform-es2015-destructuring_v^6.23.0/flow_v0.71.0 +// flow-typed signature: 62fe854d6357f8fd870790d2a53beac3 +// flow-typed version: <>/babel-plugin-transform-es2015-destructuring_v^6.23.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js index 21dc7fab4..8b57caeb5 100644 --- a/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: c96a996d8250423b12b2ed9faec13a86 -// flow-typed version: <>/babel-plugin-transform-es2015-modules-commonjs_v^6.24.1/flow_v0.71.0 +// flow-typed signature: 7ae27a2d87e57295d80ed091aa9f747d +// flow-typed version: <>/babel-plugin-transform-es2015-modules-commonjs_v^6.24.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js index 63dea3296..f08ff7bc8 100644 --- a/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 51b77fa264d9add521224f44df19df82 -// flow-typed version: <>/babel-plugin-transform-object-rest-spread_v^6.23.0/flow_v0.71.0 +// flow-typed signature: 690f072b1098aac2a8f78eb97deb0a34 +// flow-typed version: <>/babel-plugin-transform-object-rest-spread_v^6.23.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-regenerator_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-regenerator_vx.x.x.js index 7f0cfa259..957858156 100644 --- a/flow-typed/npm/babel-plugin-transform-regenerator_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-regenerator_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 246c54ac874037a4d9cffcb766e3bc91 -// flow-typed version: <>/babel-plugin-transform-regenerator_v^6.24.1/flow_v0.71.0 +// flow-typed signature: b483d64df647a2fadd23197c9430df40 +// flow-typed version: <>/babel-plugin-transform-regenerator_v^6.24.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-preset-env_vx.x.x.js b/flow-typed/npm/babel-preset-env_vx.x.x.js index ab85392e1..4dc1aa4f1 100644 --- a/flow-typed/npm/babel-preset-env_vx.x.x.js +++ b/flow-typed/npm/babel-preset-env_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b27490fff2d5c4468766643659a3eb2b -// flow-typed version: <>/babel-preset-env_v^1.4.0/flow_v0.71.0 +// flow-typed signature: bde8620567de0b1d912548f88f0a26da +// flow-typed version: <>/babel-preset-env_v^1.4.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-preset-react-hmre_vx.x.x.js b/flow-typed/npm/babel-preset-react-hmre_vx.x.x.js index fa647cb9f..14654c22e 100644 --- a/flow-typed/npm/babel-preset-react-hmre_vx.x.x.js +++ b/flow-typed/npm/babel-preset-react-hmre_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: c846d354ff1571b99d50a1c57f08f6e6 -// flow-typed version: <>/babel-preset-react-hmre_v1.1.1/flow_v0.71.0 +// flow-typed signature: 3abd654a395868c66a1c68d8267ff856 +// flow-typed version: <>/babel-preset-react-hmre_v1.1.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-preset-react_vx.x.x.js b/flow-typed/npm/babel-preset-react_vx.x.x.js index 059e71282..58cca82ee 100644 --- a/flow-typed/npm/babel-preset-react_vx.x.x.js +++ b/flow-typed/npm/babel-preset-react_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: ff1392fee98b43939eca4077f29d32a4 -// flow-typed version: <>/babel-preset-react_v6.11.1/flow_v0.71.0 +// flow-typed signature: e6cb7a9810afe2bffa684e40d8fb20bd +// flow-typed version: <>/babel-preset-react_v6.11.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-regenerator-runtime_vx.x.x.js b/flow-typed/npm/babel-regenerator-runtime_vx.x.x.js index 34446a1dd..ffbadb4c6 100644 --- a/flow-typed/npm/babel-regenerator-runtime_vx.x.x.js +++ b/flow-typed/npm/babel-regenerator-runtime_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9feb616713e12a7d3ffecfc3f6a59af1 -// flow-typed version: <>/babel-regenerator-runtime_v6.5.0/flow_v0.71.0 +// flow-typed signature: 42bc50941758f28027f0c9bc0069019b +// flow-typed version: <>/babel-regenerator-runtime_v6.5.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/boundless-arrow-key-navigation_vx.x.x.js b/flow-typed/npm/boundless-arrow-key-navigation_vx.x.x.js index 9c900f418..b735d3030 100644 --- a/flow-typed/npm/boundless-arrow-key-navigation_vx.x.x.js +++ b/flow-typed/npm/boundless-arrow-key-navigation_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 0b32c7cc183482efcdb782eeb3211877 -// flow-typed version: <>/boundless-arrow-key-navigation_v^1.0.4/flow_v0.71.0 +// flow-typed signature: 59397ad69c376794e3d306a1bd552752 +// flow-typed version: <>/boundless-arrow-key-navigation_v^1.0.4/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/boundless-popover_vx.x.x.js b/flow-typed/npm/boundless-popover_vx.x.x.js index cdb866be1..6c5d0f56b 100644 --- a/flow-typed/npm/boundless-popover_vx.x.x.js +++ b/flow-typed/npm/boundless-popover_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 67e1ab65a05ed9fc881e4c87f68ed770 -// flow-typed version: <>/boundless-popover_v^1.0.4/flow_v0.71.0 +// flow-typed signature: 89f743d454f76a3c86e4c6950cc455d4 +// flow-typed version: <>/boundless-popover_v^1.0.4/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/bugsnag_vx.x.x.js b/flow-typed/npm/bugsnag_vx.x.x.js index 0bfde5775..f5d2af7a3 100644 --- a/flow-typed/npm/bugsnag_vx.x.x.js +++ b/flow-typed/npm/bugsnag_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: ea1970876efdaf55e4b2b4d8d011e47a -// flow-typed version: <>/bugsnag_v^1.7.0/flow_v0.71.0 +// flow-typed signature: fa2210251969d5a91003802cd9026d2d +// flow-typed version: <>/bugsnag_v^1.7.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/bull_vx.x.x.js b/flow-typed/npm/bull_vx.x.x.js index 239c8762c..f74fd4cf0 100644 --- a/flow-typed/npm/bull_vx.x.x.js +++ b/flow-typed/npm/bull_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 3a17bfa26cb5d7355c449859669d7139 -// flow-typed version: <>/bull_v^3.3.7/flow_v0.71.0 +// flow-typed signature: e34fc9065fe78e43e54c3456364357b7 +// flow-typed version: <>/bull_v^3.5.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: @@ -22,10 +22,6 @@ declare module 'bull' { * require those files directly. Feel free to delete any files that aren't * needed. */ -declare module 'bull/cron-parse-bug' { - declare module.exports: any; -} - declare module 'bull/examples/cluster-queue' { declare module.exports: any; } @@ -38,6 +34,10 @@ declare module 'bull/examples/state' { declare module.exports: any; } +declare module 'bull/lib/backoffs' { + declare module.exports: any; +} + declare module 'bull/lib/commands/index' { declare module.exports: any; } @@ -94,6 +94,10 @@ declare module 'bull/test/cluster_worker' { declare module.exports: any; } +declare module 'bull/test/fixtures/fixture_processor_bar' { + declare module.exports: any; +} + declare module 'bull/test/fixtures/fixture_processor_callback_fail' { declare module.exports: any; } @@ -102,6 +106,10 @@ declare module 'bull/test/fixtures/fixture_processor_callback' { declare module.exports: any; } +declare module 'bull/test/fixtures/fixture_processor_crash' { + declare module.exports: any; +} + declare module 'bull/test/fixtures/fixture_processor_exit' { declare module.exports: any; } @@ -110,6 +118,10 @@ declare module 'bull/test/fixtures/fixture_processor_fail' { declare module.exports: any; } +declare module 'bull/test/fixtures/fixture_processor_foo' { + declare module.exports: any; +} + declare module 'bull/test/fixtures/fixture_processor_progress' { declare module.exports: any; } @@ -122,6 +134,10 @@ declare module 'bull/test/fixtures/fixture_processor' { declare module.exports: any; } +declare module 'bull/test/test_child-pool' { + declare module.exports: any; +} + declare module 'bull/test/test_cluster' { declare module.exports: any; } @@ -134,6 +150,10 @@ declare module 'bull/test/test_events' { declare module.exports: any; } +declare module 'bull/test/test_getters' { + declare module.exports: any; +} + declare module 'bull/test/test_job' { declare module.exports: any; } @@ -166,14 +186,7 @@ declare module 'bull/test/utils' { declare module.exports: any; } -declare module 'bull/Untitled-1' { - declare module.exports: any; -} - // Filename aliases -declare module 'bull/cron-parse-bug.js' { - declare module.exports: $Exports<'bull/cron-parse-bug'>; -} declare module 'bull/examples/cluster-queue.js' { declare module.exports: $Exports<'bull/examples/cluster-queue'>; } @@ -189,6 +202,9 @@ declare module 'bull/index' { declare module 'bull/index.js' { declare module.exports: $Exports<'bull'>; } +declare module 'bull/lib/backoffs.js' { + declare module.exports: $Exports<'bull/lib/backoffs'>; +} declare module 'bull/lib/commands/index.js' { declare module.exports: $Exports<'bull/lib/commands/index'>; } @@ -231,18 +247,27 @@ declare module 'bull/lib/worker.js' { declare module 'bull/test/cluster_worker.js' { declare module.exports: $Exports<'bull/test/cluster_worker'>; } +declare module 'bull/test/fixtures/fixture_processor_bar.js' { + declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_bar'>; +} declare module 'bull/test/fixtures/fixture_processor_callback_fail.js' { declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_callback_fail'>; } declare module 'bull/test/fixtures/fixture_processor_callback.js' { declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_callback'>; } +declare module 'bull/test/fixtures/fixture_processor_crash.js' { + declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_crash'>; +} declare module 'bull/test/fixtures/fixture_processor_exit.js' { declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_exit'>; } declare module 'bull/test/fixtures/fixture_processor_fail.js' { declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_fail'>; } +declare module 'bull/test/fixtures/fixture_processor_foo.js' { + declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_foo'>; +} declare module 'bull/test/fixtures/fixture_processor_progress.js' { declare module.exports: $Exports<'bull/test/fixtures/fixture_processor_progress'>; } @@ -252,6 +277,9 @@ declare module 'bull/test/fixtures/fixture_processor_slow.js' { declare module 'bull/test/fixtures/fixture_processor.js' { declare module.exports: $Exports<'bull/test/fixtures/fixture_processor'>; } +declare module 'bull/test/test_child-pool.js' { + declare module.exports: $Exports<'bull/test/test_child-pool'>; +} declare module 'bull/test/test_cluster.js' { declare module.exports: $Exports<'bull/test/test_cluster'>; } @@ -261,6 +289,9 @@ declare module 'bull/test/test_connection.js' { declare module 'bull/test/test_events.js' { declare module.exports: $Exports<'bull/test/test_events'>; } +declare module 'bull/test/test_getters.js' { + declare module.exports: $Exports<'bull/test/test_getters'>; +} declare module 'bull/test/test_job.js' { declare module.exports: $Exports<'bull/test/test_job'>; } @@ -285,6 +316,3 @@ declare module 'bull/test/test_worker.js' { declare module 'bull/test/utils.js' { declare module.exports: $Exports<'bull/test/utils'>; } -declare module 'bull/Untitled-1.js' { - declare module.exports: $Exports<'bull/Untitled-1'>; -} diff --git a/flow-typed/npm/cancan_vx.x.x.js b/flow-typed/npm/cancan_vx.x.x.js index 48849d326..53f8d96e1 100644 --- a/flow-typed/npm/cancan_vx.x.x.js +++ b/flow-typed/npm/cancan_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 4f34c4d959be6be217a2ca760d4d8ee2 -// flow-typed version: <>/cancan_v3.1.0/flow_v0.71.0 +// flow-typed signature: 9a572b6bb9612e8d029312779affa26b +// flow-typed version: <>/cancan_v3.1.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/css-loader_vx.x.x.js b/flow-typed/npm/css-loader_vx.x.x.js index 6ab5e99f6..4f234162b 100644 --- a/flow-typed/npm/css-loader_vx.x.x.js +++ b/flow-typed/npm/css-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 0d1fa34e43c771f5ed706bcded38f6fa -// flow-typed version: <>/css-loader_v^0.28.7/flow_v0.71.0 +// flow-typed signature: a2009a68b1b7bd5dceaf1f252af90495 +// flow-typed version: <>/css-loader_v^0.28.7/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/emoji-regex_vx.x.x.js b/flow-typed/npm/emoji-regex_vx.x.x.js index 095ab921f..c498713c2 100644 --- a/flow-typed/npm/emoji-regex_vx.x.x.js +++ b/flow-typed/npm/emoji-regex_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 18e8f3ed605679047cefaa557e6f0508 -// flow-typed version: <>/emoji-regex_v^6.5.1/flow_v0.71.0 +// flow-typed signature: 76e1fb1735e4f12d3649be18dfb65aac +// flow-typed version: <>/emoji-regex_v^6.5.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/enzyme-to-json_vx.x.x.js b/flow-typed/npm/enzyme-to-json_vx.x.x.js index 3e8b28b93..98dc50c86 100644 --- a/flow-typed/npm/enzyme-to-json_vx.x.x.js +++ b/flow-typed/npm/enzyme-to-json_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 668f4808f17d13a62c2026f0c16133c6 -// flow-typed version: <>/enzyme-to-json_v^1.5.1/flow_v0.71.0 +// flow-typed signature: b3402ec9029514be297b23c7a794f1ea +// flow-typed version: <>/enzyme-to-json_v^1.5.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-config-react-app_vx.x.x.js b/flow-typed/npm/eslint-config-react-app_vx.x.x.js index fb05cf47c..4114b9439 100644 --- a/flow-typed/npm/eslint-config-react-app_vx.x.x.js +++ b/flow-typed/npm/eslint-config-react-app_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 42e3a437aef9dbf7bfe21daa33b3fede -// flow-typed version: <>/eslint-config-react-app_v^2.0.1/flow_v0.71.0 +// flow-typed signature: 42a3b11e4f4348a20d4c3246ef18daad +// flow-typed version: <>/eslint-config-react-app_v^2.0.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js b/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js index faf125d2f..b643f723e 100644 --- a/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a4e6eb2652034382c5c81483cb1e008e -// flow-typed version: <>/eslint-plugin-flowtype_v^2.40.1/flow_v0.71.0 +// flow-typed signature: 7e8c2fd36265484e5caf36f1406fb5a6 +// flow-typed version: <>/eslint-plugin-flowtype_v^2.40.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-plugin-import_vx.x.x.js b/flow-typed/npm/eslint-plugin-import_vx.x.x.js index dd07fdae9..603769a9d 100644 --- a/flow-typed/npm/eslint-plugin-import_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-import_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 57a0f8afb19807ebd5db77c02a7596a2 -// flow-typed version: <>/eslint-plugin-import_v^2.8.0/flow_v0.71.0 +// flow-typed signature: c5eaccf0eca7916aeba30441a9bd2039 +// flow-typed version: <>/eslint-plugin-import_v^2.8.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-plugin-jsx-a11y_vx.x.x.js b/flow-typed/npm/eslint-plugin-jsx-a11y_vx.x.x.js index d412f403c..25115e5ac 100644 --- a/flow-typed/npm/eslint-plugin-jsx-a11y_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-jsx-a11y_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: c110f56873e176d5c2504b15fb96c358 -// flow-typed version: <>/eslint-plugin-jsx-a11y_v5.1.1/flow_v0.71.0 +// flow-typed signature: 78b203fd889b1d0056f59672ff63c67e +// flow-typed version: <>/eslint-plugin-jsx-a11y_v5.1.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js b/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js index 1fb377f1d..aecafe826 100644 --- a/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 3aa6f9707bfdf4229ff6adef1567e676 -// flow-typed version: <>/eslint-plugin-prettier_v^2.4.0/flow_v0.71.0 +// flow-typed signature: f0768c6b7e3b0b2c69665ded6d326f12 +// flow-typed version: <>/eslint-plugin-prettier_v^2.4.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-plugin-react_vx.x.x.js b/flow-typed/npm/eslint-plugin-react_vx.x.x.js index c09008812..185e50871 100644 --- a/flow-typed/npm/eslint-plugin-react_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-react_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 273acfd1d99bae8320d5bf128f08cadb -// flow-typed version: <>/eslint-plugin-react_v^7.5.1/flow_v0.71.0 +// flow-typed signature: 3c1ee0edbbc419f7f7ae5fd8a92c35b2 +// flow-typed version: <>/eslint-plugin-react_v^7.5.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint_vx.x.x.js b/flow-typed/npm/eslint_vx.x.x.js index e4efac67d..688caa29d 100644 --- a/flow-typed/npm/eslint_vx.x.x.js +++ b/flow-typed/npm/eslint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 22b9d866ff5972ab23317d268ccb3809 -// flow-typed version: <>/eslint_v^4.14.0/flow_v0.71.0 +// flow-typed signature: 5a9bcad504fffc4a9ee7f22568025a1e +// flow-typed version: <>/eslint_v^4.14.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/exports-loader_vx.x.x.js b/flow-typed/npm/exports-loader_vx.x.x.js index d79c166ad..f5fd83c99 100644 --- a/flow-typed/npm/exports-loader_vx.x.x.js +++ b/flow-typed/npm/exports-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 98f43e76dc0586e0479c5fc344008539 -// flow-typed version: <>/exports-loader_v^0.6.4/flow_v0.71.0 +// flow-typed signature: 30f5ffce00fbce4728d47f2c8b766afc +// flow-typed version: <>/exports-loader_v^0.6.4/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/extract-text-webpack-plugin_vx.x.x.js b/flow-typed/npm/extract-text-webpack-plugin_vx.x.x.js index a3ab41e1d..a2e5bf41e 100644 --- a/flow-typed/npm/extract-text-webpack-plugin_vx.x.x.js +++ b/flow-typed/npm/extract-text-webpack-plugin_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 13580009774392276ae66bdb72190d37 -// flow-typed version: <>/extract-text-webpack-plugin_v^3.0.2/flow_v0.71.0 +// flow-typed signature: f0eb83cfad1c4097bee63b0b5c61a251 +// flow-typed version: <>/extract-text-webpack-plugin_v^3.0.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/fbemitter_vx.x.x.js b/flow-typed/npm/fbemitter_vx.x.x.js index 1f3a61399..4fcd6255a 100644 --- a/flow-typed/npm/fbemitter_vx.x.x.js +++ b/flow-typed/npm/fbemitter_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 2a564681f2da79d9b211e58ae8d6d366 -// flow-typed version: <>/fbemitter_v^2.1.1/flow_v0.71.0 +// flow-typed signature: 81f85c5fc3eb62a3d795e2e4240e438b +// flow-typed version: <>/fbemitter_v^2.1.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/fetch-test-server_vx.x.x.js b/flow-typed/npm/fetch-test-server_vx.x.x.js index 1038b9bf0..cfd2cdca3 100644 --- a/flow-typed/npm/fetch-test-server_vx.x.x.js +++ b/flow-typed/npm/fetch-test-server_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 81603ff9aa21930a190f0d9adb5e5236 -// flow-typed version: <>/fetch-test-server_v^1.1.0/flow_v0.71.0 +// flow-typed signature: 937f635b2a0eb9f800b2469b28b64293 +// flow-typed version: <>/fetch-test-server_v^1.1.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/file-loader_vx.x.x.js b/flow-typed/npm/file-loader_vx.x.x.js index 0e147bcab..41693c492 100644 --- a/flow-typed/npm/file-loader_vx.x.x.js +++ b/flow-typed/npm/file-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: e8f66c6ccde681e2762b1b90d91f79e0 -// flow-typed version: <>/file-loader_v^1.1.6/flow_v0.71.0 +// flow-typed signature: 5f6c8448123145f4163d45b3f317ff70 +// flow-typed version: <>/file-loader_v^1.1.6/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/flow-typed_vx.x.x.js b/flow-typed/npm/flow-typed_vx.x.x.js index f31f7f4c4..eb7da7c41 100644 --- a/flow-typed/npm/flow-typed_vx.x.x.js +++ b/flow-typed/npm/flow-typed_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 8df8e9fddb022eef212e7a5440fa38d4 -// flow-typed version: <>/flow-typed_v^2.4.0/flow_v0.71.0 +// flow-typed signature: 8c4537b24b62f598abede5041178f93d +// flow-typed version: <>/flow-typed_v^2.4.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/fs-extra_vx.x.x.js b/flow-typed/npm/fs-extra_vx.x.x.js index 080b84b82..2420dbf8f 100644 --- a/flow-typed/npm/fs-extra_vx.x.x.js +++ b/flow-typed/npm/fs-extra_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 7d7ff1f6fcc7c443fb095baeca646021 -// flow-typed version: <>/fs-extra_v^4.0.2/flow_v0.71.0 +// flow-typed signature: 223727f6e0c6b223cf1a888802302f6b +// flow-typed version: <>/fs-extra_v^4.0.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/google-auth-library_vx.x.x.js b/flow-typed/npm/google-auth-library_vx.x.x.js index 94c789b7b..a42bb71be 100644 --- a/flow-typed/npm/google-auth-library_vx.x.x.js +++ b/flow-typed/npm/google-auth-library_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 178bc1353a5bc8f7a0b81e60cc7e569c -// flow-typed version: <>/google-auth-library_v^1.5.0/flow_v0.71.0 +// flow-typed signature: 94e9a9f9f9b3e84c58a9c4b55ed616c4 +// flow-typed version: <>/google-auth-library_v^1.5.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/history_vx.x.x.js b/flow-typed/npm/history_vx.x.x.js index 5a35dddbe..a28c5e7fd 100644 --- a/flow-typed/npm/history_vx.x.x.js +++ b/flow-typed/npm/history_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: f958869c92e9a209eaeff48fa8be11db -// flow-typed version: <>/history_v3.0.0/flow_v0.71.0 +// flow-typed signature: 6d6386ebbd509cfc7d51131368611342 +// flow-typed version: <>/history_v3.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/html-webpack-plugin_vx.x.x.js b/flow-typed/npm/html-webpack-plugin_vx.x.x.js index 076d9ed6c..490c77b92 100644 --- a/flow-typed/npm/html-webpack-plugin_vx.x.x.js +++ b/flow-typed/npm/html-webpack-plugin_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a02b4a5c49f09bfee3eb3db5cf9062d7 -// flow-typed version: <>/html-webpack-plugin_v2.17.0/flow_v0.71.0 +// flow-typed signature: 2d5889838efad90a1105daea66313af7 +// flow-typed version: <>/html-webpack-plugin_v2.17.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/identity-obj-proxy_vx.x.x.js b/flow-typed/npm/identity-obj-proxy_vx.x.x.js index b8d5999bb..79bec0eff 100644 --- a/flow-typed/npm/identity-obj-proxy_vx.x.x.js +++ b/flow-typed/npm/identity-obj-proxy_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: e10a8847ab761c9238273cb3bd54facf -// flow-typed version: <>/identity-obj-proxy_v^3.0.0/flow_v0.71.0 +// flow-typed signature: 27c00cb1bc9a39b5242058a544265b1b +// flow-typed version: <>/identity-obj-proxy_v^3.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/imports-loader_vx.x.x.js b/flow-typed/npm/imports-loader_vx.x.x.js index dece1623f..d9e72f7e8 100644 --- a/flow-typed/npm/imports-loader_vx.x.x.js +++ b/flow-typed/npm/imports-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 66d39c39842aa41ca9ffca0951cc7a81 -// flow-typed version: <>/imports-loader_v0.6.5/flow_v0.71.0 +// flow-typed signature: 5b3714908f9c76c83b2cd0184ebc9ad7 +// flow-typed version: <>/imports-loader_v0.6.5/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/isomorphic-fetch_v2.x.x.js b/flow-typed/npm/isomorphic-fetch_v2.x.x.js index 4692d54e1..fc54a065e 100644 --- a/flow-typed/npm/isomorphic-fetch_v2.x.x.js +++ b/flow-typed/npm/isomorphic-fetch_v2.x.x.js @@ -1,7 +1,9 @@ -// flow-typed signature: 47370d221401bec823c43c3598266e26 -// flow-typed version: ec28077c25/isomorphic-fetch_v2.x.x/flow_>=v0.25.x +// flow-typed signature: 33bbf70064fc58400833489a101165a7 +// flow-typed version: 45acb9a3f7/isomorphic-fetch_v2.x.x/flow_>=v0.25.x - -declare module 'isomorphic-fetch' { - declare module.exports: (input: string | Request | URL, init?: RequestOptions) => Promise; +declare module "isomorphic-fetch" { + declare module.exports: ( + input: string | Request | URL, + init?: RequestOptions + ) => Promise; } diff --git a/flow-typed/npm/jest-cli_vx.x.x.js b/flow-typed/npm/jest-cli_vx.x.x.js index c032e5501..2ca509ab4 100644 --- a/flow-typed/npm/jest-cli_vx.x.x.js +++ b/flow-typed/npm/jest-cli_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 919b5375355510e8278463cfb50f4bff -// flow-typed version: <>/jest-cli_v22/flow_v0.71.0 +// flow-typed signature: f779571d521232ddbbf7cc505053d0de +// flow-typed version: <>/jest-cli_v22/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/js-cookie_v2.x.x.js b/flow-typed/npm/js-cookie_v2.x.x.js index 2453f240f..e69de29bb 100644 --- a/flow-typed/npm/js-cookie_v2.x.x.js +++ b/flow-typed/npm/js-cookie_v2.x.x.js @@ -1,28 +0,0 @@ -// flow-typed signature: 09c1fe82fbc9e980fc0fd1c8dee0628b -// flow-typed version: 91c31c78d9/js-cookie_v2.x.x/flow_>=v0.38.x - -declare module 'js-cookie' { - declare type CookieOptions = { - expires?: number | Date, - path?: string, - domain?: string, - secure?: boolean - } - declare type ConverterFunc = (value: string, name: string) => string; - declare type ConverterObj = { - read: ConverterFunc, - write: ConverterFunc - }; - declare class Cookie { - defaults: CookieOptions; - set(name: string, value: mixed, options?: CookieOptions): void; - get(...args: Array): { [key: string]: string }; - get(name: string, ...args: Array): string | void; - remove(name: string, options?: CookieOptions): void; - getJSON(name: string): Object; - withConverter(converter: ConverterFunc | ConverterObj): this; - noConflict(): this; - } - - declare module.exports: Cookie; -} diff --git a/flow-typed/npm/js-search_vx.x.x.js b/flow-typed/npm/js-search_vx.x.x.js index f183fd6f0..f8d6bdaae 100644 --- a/flow-typed/npm/js-search_vx.x.x.js +++ b/flow-typed/npm/js-search_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 6a6a191b599141bb11b3d93899e43f1b -// flow-typed version: <>/js-search_v^1.4.2/flow_v0.71.0 +// flow-typed signature: 012a8150801e96fc6ce3193fa10fbcd8 +// flow-typed version: <>/js-search_v^1.4.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/json-loader_vx.x.x.js b/flow-typed/npm/json-loader_vx.x.x.js index 3fa5582e1..1f58d9abc 100644 --- a/flow-typed/npm/json-loader_vx.x.x.js +++ b/flow-typed/npm/json-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 33d10e95fdc39017d61e9c2ba7e461bf -// flow-typed version: <>/json-loader_v0.5.4/flow_v0.71.0 +// flow-typed signature: 12b9ed0d1179e6c2e22ef2595a324989 +// flow-typed version: <>/json-loader_v0.5.4/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/jsonwebtoken_vx.x.x.js b/flow-typed/npm/jsonwebtoken_vx.x.x.js index 54e7dda92..6245f22c5 100644 --- a/flow-typed/npm/jsonwebtoken_vx.x.x.js +++ b/flow-typed/npm/jsonwebtoken_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 69c860f2741ede15238fac888eeda802 -// flow-typed version: <>/jsonwebtoken_v7.0.1/flow_v0.71.0 +// flow-typed signature: e1164f76d8c95c943b8c98bcf9491a06 +// flow-typed version: <>/jsonwebtoken_v7.0.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/jszip_vx.x.x.js b/flow-typed/npm/jszip_vx.x.x.js index 6ba471fa8..738845542 100644 --- a/flow-typed/npm/jszip_vx.x.x.js +++ b/flow-typed/npm/jszip_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 8e32d5e372e2df42a22f7d8915f1c450 -// flow-typed version: <>/jszip_v3.1.5/flow_v0.71.0 +// flow-typed signature: 04ed31d511fd7a09f98eae990a5a09be +// flow-typed version: <>/jszip_v3.1.5/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-compress_vx.x.x.js b/flow-typed/npm/koa-compress_vx.x.x.js index 2d79d2a4d..accd96e25 100644 --- a/flow-typed/npm/koa-compress_vx.x.x.js +++ b/flow-typed/npm/koa-compress_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: ba5bba9e04a906866671688b51057c5b -// flow-typed version: <>/koa-compress_v2.0.0/flow_v0.71.0 +// flow-typed signature: 04e2d1be8bd50cd33eed9d79de249797 +// flow-typed version: <>/koa-compress_v2.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-connect_vx.x.x.js b/flow-typed/npm/koa-connect_vx.x.x.js index 6ed581cf0..5927c7333 100644 --- a/flow-typed/npm/koa-connect_vx.x.x.js +++ b/flow-typed/npm/koa-connect_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b685e62eface5c97d8b4fda5a054440d -// flow-typed version: <>/koa-connect_v1.0.0/flow_v0.71.0 +// flow-typed signature: 9505cb4568f30f07959d9aacbd66f36a +// flow-typed version: <>/koa-connect_v1.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-convert_vx.x.x.js b/flow-typed/npm/koa-convert_vx.x.x.js index f639a6f6d..87edf5f0e 100644 --- a/flow-typed/npm/koa-convert_vx.x.x.js +++ b/flow-typed/npm/koa-convert_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9db483741148ac6ce073abb078d95b26 -// flow-typed version: <>/koa-convert_v1.2.0/flow_v0.71.0 +// flow-typed signature: afa68193f6b54a5a41b24208dfbcf508 +// flow-typed version: <>/koa-convert_v1.2.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-helmet_vx.x.x.js b/flow-typed/npm/koa-helmet_vx.x.x.js index 77ed45a2a..42d69a8f4 100644 --- a/flow-typed/npm/koa-helmet_vx.x.x.js +++ b/flow-typed/npm/koa-helmet_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 01719aca9d37d24e76d77529e594870c -// flow-typed version: <>/koa-helmet_v3.2.0/flow_v0.71.0 +// flow-typed signature: 57282b3b38e9794fd4ac287ad9f035a9 +// flow-typed version: <>/koa-helmet_v3.2.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-jwt_vx.x.x.js b/flow-typed/npm/koa-jwt_vx.x.x.js index 76d79e533..410038521 100644 --- a/flow-typed/npm/koa-jwt_vx.x.x.js +++ b/flow-typed/npm/koa-jwt_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: f76652e8648b2a381dc68b1c3e9b6ccd -// flow-typed version: <>/koa-jwt_v^3.2.1/flow_v0.71.0 +// flow-typed signature: cf719b195e8f38c2182c052d863b2f5c +// flow-typed version: <>/koa-jwt_v^3.2.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-logger_vx.x.x.js b/flow-typed/npm/koa-logger_vx.x.x.js index 224253e92..587653e17 100644 --- a/flow-typed/npm/koa-logger_vx.x.x.js +++ b/flow-typed/npm/koa-logger_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9b79c5924a74185b98f768d70b00fb77 -// flow-typed version: <>/koa-logger_v^2.0.1/flow_v0.71.0 +// flow-typed signature: 24a698b7d41b17b77a5d40f931aae753 +// flow-typed version: <>/koa-logger_v^2.0.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-mount_vx.x.x.js b/flow-typed/npm/koa-mount_vx.x.x.js index 37da555e4..9e5c7c1e8 100644 --- a/flow-typed/npm/koa-mount_vx.x.x.js +++ b/flow-typed/npm/koa-mount_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: da8cd9f23db359c6c3612e0239ce431d -// flow-typed version: <>/koa-mount_v^3.0.0/flow_v0.71.0 +// flow-typed signature: fd2291ae4b53447a1abe42be7c0db0be +// flow-typed version: <>/koa-mount_v^3.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-onerror_vx.x.x.js b/flow-typed/npm/koa-onerror_vx.x.x.js index d172ac887..10bb29bc2 100644 --- a/flow-typed/npm/koa-onerror_vx.x.x.js +++ b/flow-typed/npm/koa-onerror_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 5de337753354c6d6467aae61dec405a5 -// flow-typed version: <>/koa-onerror_v^4.0.0/flow_v0.71.0 +// flow-typed signature: b29b16a37eb672f7e8c66507d1fb781f +// flow-typed version: <>/koa-onerror_v^4.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-router_vx.x.x.js b/flow-typed/npm/koa-router_vx.x.x.js index f0745b2b9..572648580 100644 --- a/flow-typed/npm/koa-router_vx.x.x.js +++ b/flow-typed/npm/koa-router_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 0839b244c6ed3252cfce863ca47616e2 -// flow-typed version: <>/koa-router_v7.0.1/flow_v0.71.0 +// flow-typed signature: 9d87c7231941632541c2dbe33cb14117 +// flow-typed version: <>/koa-router_v7.0.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-sendfile_vx.x.x.js b/flow-typed/npm/koa-sendfile_vx.x.x.js index a064279f4..bccab8956 100644 --- a/flow-typed/npm/koa-sendfile_vx.x.x.js +++ b/flow-typed/npm/koa-sendfile_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 40d2c7de6932fe128d7accc903707306 -// flow-typed version: <>/koa-sendfile_v2.0.0/flow_v0.71.0 +// flow-typed signature: c8ac7fdec0c40461e9194ad83b340aa7 +// flow-typed version: <>/koa-sendfile_v2.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-sslify_vx.x.x.js b/flow-typed/npm/koa-sslify_vx.x.x.js index e20ad8a60..8007fe008 100644 --- a/flow-typed/npm/koa-sslify_vx.x.x.js +++ b/flow-typed/npm/koa-sslify_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: ff33d86d918ff9102533115616ed317d -// flow-typed version: <>/koa-sslify_v2.1.2/flow_v0.71.0 +// flow-typed signature: 68e5267ec98da651fad8413a61f05d53 +// flow-typed version: <>/koa-sslify_v2.1.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-webpack-dev-middleware_vx.x.x.js b/flow-typed/npm/koa-webpack-dev-middleware_vx.x.x.js index c7feb0220..65dad5cd0 100644 --- a/flow-typed/npm/koa-webpack-dev-middleware_vx.x.x.js +++ b/flow-typed/npm/koa-webpack-dev-middleware_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 8471947c40dbe31c872217f40f93ae80 -// flow-typed version: <>/koa-webpack-dev-middleware_v1.4.5/flow_v0.71.0 +// flow-typed signature: a676d803401bc91e63bc2d4f2c414ff0 +// flow-typed version: <>/koa-webpack-dev-middleware_v1.4.5/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa-webpack-hot-middleware_vx.x.x.js b/flow-typed/npm/koa-webpack-hot-middleware_vx.x.x.js index a40a600b5..5ef976bb3 100644 --- a/flow-typed/npm/koa-webpack-hot-middleware_vx.x.x.js +++ b/flow-typed/npm/koa-webpack-hot-middleware_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b1eb8c4a8294f42cec34bcc98e171dce -// flow-typed version: <>/koa-webpack-hot-middleware_v1.0.3/flow_v0.71.0 +// flow-typed signature: 58252b3a88edb4e2caa49ef8f37e0704 +// flow-typed version: <>/koa-webpack-hot-middleware_v1.0.3/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/koa_v2.x.x.js b/flow-typed/npm/koa_v2.x.x.js index 787c1fc94..30ef20994 100644 --- a/flow-typed/npm/koa_v2.x.x.js +++ b/flow-typed/npm/koa_v2.x.x.js @@ -201,7 +201,7 @@ declare module 'koa' { }; // https://github.com/pillarjs/cookies declare type CookiesSetOptions = { - domain: string, // domain of the cookie (no default). + domain?: string, // domain of the cookie (no default). maxAge?: number, // milliseconds from Date.now() for expiry expires?: Date, //cookie's expiration date (expires at the end of session by default). path?: string, // the path of the cookie (/ by default). diff --git a/flow-typed/npm/lint-staged_vx.x.x.js b/flow-typed/npm/lint-staged_vx.x.x.js index 630d65658..b56be6cee 100644 --- a/flow-typed/npm/lint-staged_vx.x.x.js +++ b/flow-typed/npm/lint-staged_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: af040f265d532c0b83974221b6d209db -// flow-typed version: <>/lint-staged_v^3.4.0/flow_v0.71.0 +// flow-typed signature: 8ca9af500578f9f609c7590a5c521cf1 +// flow-typed version: <>/lint-staged_v^3.4.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/marked_v0.3.x.js b/flow-typed/npm/marked_v0.3.x.js deleted file mode 100644 index d98c9197e..000000000 --- a/flow-typed/npm/marked_v0.3.x.js +++ /dev/null @@ -1,158 +0,0 @@ -// flow-typed signature: c2bec7cd5128630b7dfee2f1a66a4782 -// flow-typed version: f111325994/marked_v0.3.x/flow_>=v0.28.x - -type marked$AlignFlag = 'left' | 'right' | 'center'; - -type marked$NodeCallback = (e: ?Error, d: ?T) => void; - -class marked$Renderer { - options: marked$MarkedOptions; - code: (c: string, l: string) => string; - blockquote: (q: string) => string; - html: (h: string) => string; - heading: (t: string, l: number) => string; - hr: () => string; - list: (b: string, o: boolean) => string; - listitem: (t: string) => string; - paragraph: (t: string) => string; - table: (h: string, b: string) => string; - tablerow: (c: string) => string; - tablecell: (c: string, f: ?marked$AlignFlag) => string; - heading: (t: string, l: number) => string; - strong: (t: string) => string; - em: (t: string) => string; - codespan: (c: string) => string; - br: () => string; - del: (t: string) => string; - link: (h: string, ti: string, te: string) => string; - image: (h: string, ti: string, te: string) => string; - text: (t: string) => string; -} - -type marked$HighlightFunction = - | ((c: string, l: string, cb: marked$NodeCallback) => void) - | ((c: string, cb: marked$NodeCallback) => void) - | ((c: string, l?: string) => string); - -type marked$MarkedOptions = { - highlight?: marked$HighlightFunction, - renderer?: marked$Renderer, - gfm?: boolean, - tables?: boolean, - breaks?: boolean, - pedantic?: boolean, - sanitize?: boolean, - smartLists?: boolean, - smartypants?: boolean, -}; - -/* - * marked$Tokens - */ - -type marked$Space = { type: 'space' }; -type marked$Code = { type: 'code', text: string, lang?: string }; -type marked$Heading = { type: 'heading', depth: number, text: string }; -type marked$Table = { - type: 'table', - header: string, - align: Array, - cells: Array>, -}; -type marked$Hr = { type: 'hr' }; -type marked$BlockquoteStart = { type: 'blockquote_start' }; -type marked$BlockquoteEnd = { type: 'blockquote_end' }; -type marked$ListStart = { type: 'list_start' }; -type marked$ListEnd = { type: 'list_end' }; -type marked$Paragraph = { type: 'paragraph', pre: boolean, text: string }; -type marked$Html = { type: 'paragraph', pre: boolean, text: string }; -type marked$Text = { type: 'text', text: string }; - -type marked$Token = - | marked$Space - | marked$Code - | marked$Heading - | marked$Table - | marked$Hr - | marked$BlockquoteStart - | marked$BlockquoteEnd - | marked$ListStart - | marked$ListEnd - | marked$Paragraph - | marked$Html - | marked$Text; - -type marked$Link = { - title: ?string, - href: string, -}; - -type marked$Tokens = { links: Array } & Array; - -type marked$NoopRule = { - (i: mixed): void, - exec: (i: mixed) => void, -}; - -type marked$Rule = RegExp | marked$NoopRule; - -type marked$lex = (t: string) => marked$Tokens; - -class marked$Lexer { - static lexer: (t: string, o?: marked$MarkedOptions) => marked$Tokens; - static rules: { [key: string]: marked$Rule }; - rules: { [key: string]: marked$Rule }; - lex: marked$lex; - tokens: marked$Tokens; - options: marked$MarkedOptions; -} - -class marked$Parser { - static parse: (t: marked$Tokens, o?: marked$MarkedOptions) => string; - parse: (t: marked$Tokens) => string; - next: () => marked$Token; - peek: () => marked$Token; - parsemarked$Text: () => string; - tok: () => string; - tokens: marked$Tokens; - token: ?marked$Token; - options: marked$MarkedOptions; - renderer: marked$Renderer; -} - -class marked$InlineLexer { - static rules: Array; - static output: ( - s: string, - l: Array, - o?: marked$MarkedOptions - ) => string; - output: (s: string) => string; - outputmarked$Link: (c: Array, l: marked$Link) => string; - smartypants: (t: string) => string; - mangle: (t: string) => string; - options: marked$MarkedOptions; - links: Array; - rules: Array; - renderer: marked$Renderer; -} - -type marked$Marked = { - (md: string, o: marked$MarkedOptions, cb: marked$NodeCallback): void, - (md: string, cb: marked$NodeCallback): void, - (md: string, o?: marked$MarkedOptions): string, - setOptions: (o: marked$MarkedOptions) => void, - defaults: marked$MarkedOptions, - Parser: typeof marked$Parser, - parser: typeof marked$Parser.parse, - Lexer: typeof marked$Lexer, - lexer: typeof marked$Lexer.lexer, - InlineLexer: typeof marked$InlineLexer, - inlinelexer: marked$InlineLexer.output, - Renderer: typeof marked$Renderer, - parse: marked$Marked, -}; - -declare module marked { - declare export default marked$Marked -} diff --git a/flow-typed/npm/mobx-react-devtools_vx.x.x.js b/flow-typed/npm/mobx-react-devtools_vx.x.x.js index 302a26d48..0ed584098 100644 --- a/flow-typed/npm/mobx-react-devtools_vx.x.x.js +++ b/flow-typed/npm/mobx-react-devtools_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9dfd68f79417dcbe8773b9c44fd893b0 -// flow-typed version: <>/mobx-react-devtools_v^4.2.11/flow_v0.71.0 +// flow-typed signature: c459b6fa9feb4f8ad65122a56e3648f0 +// flow-typed version: <>/mobx-react-devtools_v^4.2.11/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/mobx-react_vx.x.x.js b/flow-typed/npm/mobx-react_vx.x.x.js index db54d7c63..d4468f848 100644 --- a/flow-typed/npm/mobx-react_vx.x.x.js +++ b/flow-typed/npm/mobx-react_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 56b12b99239cfd17e23ba8e956d6812c -// flow-typed version: <>/mobx-react_v^4.1.8/flow_v0.71.0 +// flow-typed signature: 72f4dda370c62477740e019492f5b339 +// flow-typed version: <>/mobx-react_v^4.1.8/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/natural-sort_vx.x.x.js b/flow-typed/npm/natural-sort_vx.x.x.js index f96b1abe1..889108d70 100644 --- a/flow-typed/npm/natural-sort_vx.x.x.js +++ b/flow-typed/npm/natural-sort_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b5342d4d1d47437f9918a7fc93259e22 -// flow-typed version: <>/natural-sort_v^1.0.0/flow_v0.71.0 +// flow-typed signature: 89b05c482deef90c424f9328d76844ed +// flow-typed version: <>/natural-sort_v^1.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/node-dev_vx.x.x.js b/flow-typed/npm/node-dev_vx.x.x.js index ac6021cc9..3b93060bd 100644 --- a/flow-typed/npm/node-dev_vx.x.x.js +++ b/flow-typed/npm/node-dev_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 52f978864c264a5b8db13bdb0b6425da -// flow-typed version: <>/node-dev_v3.1.0/flow_v0.71.0 +// flow-typed signature: bb542302bc35e2d9212a92ff9be8869c +// flow-typed version: <>/node-dev_v3.1.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/nodemailer_vx.x.x.js b/flow-typed/npm/nodemailer_vx.x.x.js index c3782a193..850b98727 100644 --- a/flow-typed/npm/nodemailer_vx.x.x.js +++ b/flow-typed/npm/nodemailer_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 20960ec90133f5f2b621128db5577293 -// flow-typed version: <>/nodemailer_v^4.4.0/flow_v0.71.0 +// flow-typed signature: 64ddca47610bf9f3b3493cd68aa6f946 +// flow-typed version: <>/nodemailer_v^4.4.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/nodemon_vx.x.x.js b/flow-typed/npm/nodemon_vx.x.x.js index 89bdecd66..ec1bca780 100644 --- a/flow-typed/npm/nodemon_vx.x.x.js +++ b/flow-typed/npm/nodemon_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: f90e3a9dd492d09941f65b3029903562 -// flow-typed version: <>/nodemon_v1.11.0/flow_v0.71.0 +// flow-typed signature: ca46f5f2009e781580e393d8d9456f7b +// flow-typed version: <>/nodemon_v1.11.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/outline-icons_vx.x.x.js b/flow-typed/npm/outline-icons_vx.x.x.js index e6911d44c..87f967572 100644 --- a/flow-typed/npm/outline-icons_vx.x.x.js +++ b/flow-typed/npm/outline-icons_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 2cdcd7d87c53bb68b860d9c0fcc5ecd5 -// flow-typed version: <>/outline-icons_v^1.3.2/flow_v0.71.0 +// flow-typed signature: 07992e210a908fea3ac2250ae5c966e7 +// flow-typed version: <>/outline-icons_v^1.5.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: @@ -70,6 +70,10 @@ declare module 'outline-icons/lib/components/EditIcon' { declare module.exports: any; } +declare module 'outline-icons/lib/components/EmailIcon' { + declare module.exports: any; +} + declare module 'outline-icons/lib/components/ExpandedIcon' { declare module.exports: any; } @@ -78,6 +82,10 @@ declare module 'outline-icons/lib/components/GoToIcon' { declare module.exports: any; } +declare module 'outline-icons/lib/components/HashtagIcon' { + declare module.exports: any; +} + declare module 'outline-icons/lib/components/Heading1Icon' { declare module.exports: any; } @@ -231,12 +239,18 @@ declare module 'outline-icons/lib/components/DocumentIcon.js' { declare module 'outline-icons/lib/components/EditIcon.js' { declare module.exports: $Exports<'outline-icons/lib/components/EditIcon'>; } +declare module 'outline-icons/lib/components/EmailIcon.js' { + declare module.exports: $Exports<'outline-icons/lib/components/EmailIcon'>; +} declare module 'outline-icons/lib/components/ExpandedIcon.js' { declare module.exports: $Exports<'outline-icons/lib/components/ExpandedIcon'>; } declare module 'outline-icons/lib/components/GoToIcon.js' { declare module.exports: $Exports<'outline-icons/lib/components/GoToIcon'>; } +declare module 'outline-icons/lib/components/HashtagIcon.js' { + declare module.exports: $Exports<'outline-icons/lib/components/HashtagIcon'>; +} declare module 'outline-icons/lib/components/Heading1Icon.js' { declare module.exports: $Exports<'outline-icons/lib/components/Heading1Icon'>; } diff --git a/flow-typed/npm/oy-vey_vx.x.x.js b/flow-typed/npm/oy-vey_vx.x.x.js index 9184a0f88..30b9257ef 100644 --- a/flow-typed/npm/oy-vey_vx.x.x.js +++ b/flow-typed/npm/oy-vey_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: ad69ab2250a355e029acddf8caf4f155 -// flow-typed version: <>/oy-vey_v^0.10.0/flow_v0.71.0 +// flow-typed signature: f4c5cdcb9bb78a6558d6d86f40128474 +// flow-typed version: <>/oy-vey_v^0.10.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/parse-domain_vx.x.x.js b/flow-typed/npm/parse-domain_vx.x.x.js new file mode 100644 index 000000000..9dbf810ea --- /dev/null +++ b/flow-typed/npm/parse-domain_vx.x.x.js @@ -0,0 +1,151 @@ +// flow-typed signature: 21d87fdf0efc02246bbb85ba4e27543e +// flow-typed version: <>/parse-domain_v2.1.6/flow_v0.86.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'parse-domain' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'parse-domain' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'parse-domain/lib/normalize' { + declare module.exports: any; +} + +declare module 'parse-domain/lib/parseDomain' { + declare module.exports: any; +} + +declare module 'parse-domain/lib/tries/lookUp' { + declare module.exports: any; +} + +declare module 'parse-domain/lib/tries/parsePubSuffixList' { + declare module.exports: any; +} + +declare module 'parse-domain/lib/tries/parseTrie' { + declare module.exports: any; +} + +declare module 'parse-domain/lib/tries/separators' { + declare module.exports: any; +} + +declare module 'parse-domain/lib/tries/serializeTrie' { + declare module.exports: any; +} + +declare module 'parse-domain/lists/icann.complete' { + declare module.exports: any; +} + +declare module 'parse-domain/lists/icann.light' { + declare module.exports: any; +} + +declare module 'parse-domain/lists/private.complete' { + declare module.exports: any; +} + +declare module 'parse-domain/scripts/build-tries' { + declare module.exports: any; +} + +declare module 'parse-domain/scripts/link-src' { + declare module.exports: any; +} + +declare module 'parse-domain/test/parseDomain.test' { + declare module.exports: any; +} + +declare module 'parse-domain/test/snapshots.test' { + declare module.exports: any; +} + +declare module 'parse-domain/test/tries/lookUp.test' { + declare module.exports: any; +} + +declare module 'parse-domain/test/tries/parsePubSuffixList.test' { + declare module.exports: any; +} + +declare module 'parse-domain/test/tries/parseTrie.test' { + declare module.exports: any; +} + +declare module 'parse-domain/test/tries/serializeTrie.test' { + declare module.exports: any; +} + +// Filename aliases +declare module 'parse-domain/lib/normalize.js' { + declare module.exports: $Exports<'parse-domain/lib/normalize'>; +} +declare module 'parse-domain/lib/parseDomain.js' { + declare module.exports: $Exports<'parse-domain/lib/parseDomain'>; +} +declare module 'parse-domain/lib/tries/lookUp.js' { + declare module.exports: $Exports<'parse-domain/lib/tries/lookUp'>; +} +declare module 'parse-domain/lib/tries/parsePubSuffixList.js' { + declare module.exports: $Exports<'parse-domain/lib/tries/parsePubSuffixList'>; +} +declare module 'parse-domain/lib/tries/parseTrie.js' { + declare module.exports: $Exports<'parse-domain/lib/tries/parseTrie'>; +} +declare module 'parse-domain/lib/tries/separators.js' { + declare module.exports: $Exports<'parse-domain/lib/tries/separators'>; +} +declare module 'parse-domain/lib/tries/serializeTrie.js' { + declare module.exports: $Exports<'parse-domain/lib/tries/serializeTrie'>; +} +declare module 'parse-domain/lists/icann.complete.js' { + declare module.exports: $Exports<'parse-domain/lists/icann.complete'>; +} +declare module 'parse-domain/lists/icann.light.js' { + declare module.exports: $Exports<'parse-domain/lists/icann.light'>; +} +declare module 'parse-domain/lists/private.complete.js' { + declare module.exports: $Exports<'parse-domain/lists/private.complete'>; +} +declare module 'parse-domain/scripts/build-tries.js' { + declare module.exports: $Exports<'parse-domain/scripts/build-tries'>; +} +declare module 'parse-domain/scripts/link-src.js' { + declare module.exports: $Exports<'parse-domain/scripts/link-src'>; +} +declare module 'parse-domain/test/parseDomain.test.js' { + declare module.exports: $Exports<'parse-domain/test/parseDomain.test'>; +} +declare module 'parse-domain/test/snapshots.test.js' { + declare module.exports: $Exports<'parse-domain/test/snapshots.test'>; +} +declare module 'parse-domain/test/tries/lookUp.test.js' { + declare module.exports: $Exports<'parse-domain/test/tries/lookUp.test'>; +} +declare module 'parse-domain/test/tries/parsePubSuffixList.test.js' { + declare module.exports: $Exports<'parse-domain/test/tries/parsePubSuffixList.test'>; +} +declare module 'parse-domain/test/tries/parseTrie.test.js' { + declare module.exports: $Exports<'parse-domain/test/tries/parseTrie.test'>; +} +declare module 'parse-domain/test/tries/serializeTrie.test.js' { + declare module.exports: $Exports<'parse-domain/test/tries/serializeTrie.test'>; +} diff --git a/flow-typed/npm/pg-hstore_vx.x.x.js b/flow-typed/npm/pg-hstore_vx.x.x.js index 17f05e4ef..9427e881e 100644 --- a/flow-typed/npm/pg-hstore_vx.x.x.js +++ b/flow-typed/npm/pg-hstore_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 225f5c7515e21a2d6914fec01df4f2ac -// flow-typed version: <>/pg-hstore_v2.3.2/flow_v0.71.0 +// flow-typed signature: 42ad823ab37dde13ce79450fdc022ab4 +// flow-typed version: <>/pg-hstore_v2.3.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/pui-react-tooltip_vx.x.x.js b/flow-typed/npm/pui-react-tooltip_vx.x.x.js index f1c707bd9..896924fce 100644 --- a/flow-typed/npm/pui-react-tooltip_vx.x.x.js +++ b/flow-typed/npm/pui-react-tooltip_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 7da3d28962c82e31b8d50d9fe76c65af -// flow-typed version: <>/pui-react-tooltip_v^8.3.3/flow_v0.71.0 +// flow-typed signature: c863df4d6a6503443aae949b78b7d534 +// flow-typed version: <>/pui-react-tooltip_v^8.3.3/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/query-string_vx.x.x.js b/flow-typed/npm/query-string_vx.x.x.js index c4b4a6751..c858ec044 100644 --- a/flow-typed/npm/query-string_vx.x.x.js +++ b/flow-typed/npm/query-string_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: ed4bb1502d9bf1bed52a3aca47420c03 -// flow-typed version: <>/query-string_v^4.3.4/flow_v0.71.0 +// flow-typed signature: e5e829bb17d027c423b454487b7c996f +// flow-typed version: <>/query-string_v^4.3.4/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/raf_vx.x.x.js b/flow-typed/npm/raf_vx.x.x.js index 0cbed730d..59b9aee69 100644 --- a/flow-typed/npm/raf_vx.x.x.js +++ b/flow-typed/npm/raf_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 0678b907aea37ebdff11d65316b18ca4 -// flow-typed version: <>/raf_v^3.4.0/flow_v0.71.0 +// flow-typed signature: b8e3c9dfaf26481ebfc01f25e0f5211b +// flow-typed version: <>/raf_v^3.4.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/raw-loader_vx.x.x.js b/flow-typed/npm/raw-loader_vx.x.x.js index 725c6ea20..ffb8e02e9 100644 --- a/flow-typed/npm/raw-loader_vx.x.x.js +++ b/flow-typed/npm/raw-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 8c21082592421b1ec812a12171d9ae5c -// flow-typed version: <>/raw-loader_v^0.5.1/flow_v0.71.0 +// flow-typed signature: c1321b8e8317d3c6c66c86aa58f83200 +// flow-typed version: <>/raw-loader_v^0.5.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/react-avatar-editor_vx.x.x.js b/flow-typed/npm/react-avatar-editor_vx.x.x.js index ec1de1ffd..440f99b99 100644 --- a/flow-typed/npm/react-avatar-editor_vx.x.x.js +++ b/flow-typed/npm/react-avatar-editor_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: c7abb539c7cf9804e3d7e9c56e405ce2 -// flow-typed version: <>/react-avatar-editor_v^10.3.0/flow_v0.71.0 +// flow-typed signature: acbf038bc2f5bf8369654264e27c2082 +// flow-typed version: <>/react-avatar-editor_v^10.3.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/react-keydown_vx.x.x.js b/flow-typed/npm/react-keydown_vx.x.x.js index d1a08c876..cb7649751 100644 --- a/flow-typed/npm/react-keydown_vx.x.x.js +++ b/flow-typed/npm/react-keydown_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 6fca626c25643bea727225c90df1f42c -// flow-typed version: <>/react-keydown_v^1.7.3/flow_v0.71.0 +// flow-typed signature: 0fd2804a80a80606ad7fd30b7e6c1027 +// flow-typed version: <>/react-keydown_v^1.7.3/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/react-markdown_vx.x.x.js b/flow-typed/npm/react-markdown_vx.x.x.js index 0ed5fea7e..ff04f5fe7 100644 --- a/flow-typed/npm/react-markdown_vx.x.x.js +++ b/flow-typed/npm/react-markdown_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 288146b7c4a1ae6cfec86fa1bed560b4 -// flow-typed version: <>/react-markdown_v^3.0.2/flow_v0.71.0 +// flow-typed signature: 64fdc56cd4b5784b1431aa538cb228c1 +// flow-typed version: <>/react-markdown_v^3.0.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/react-medium-image-zoom_vx.x.x.js b/flow-typed/npm/react-medium-image-zoom_vx.x.x.js index 189a726b7..6fce02b98 100644 --- a/flow-typed/npm/react-medium-image-zoom_vx.x.x.js +++ b/flow-typed/npm/react-medium-image-zoom_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: bf2e56ab9890e98d605225fc3f665cb8 -// flow-typed version: <>/react-medium-image-zoom_v^3.0.10/flow_v0.71.0 +// flow-typed signature: b2e57725dbefc3fbf78a0e3c00570031 +// flow-typed version: <>/react-medium-image-zoom_v^3.0.10/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/react-portal_vx.x.x.js b/flow-typed/npm/react-portal_vx.x.x.js index 2c6c79cc9..906aec579 100644 --- a/flow-typed/npm/react-portal_vx.x.x.js +++ b/flow-typed/npm/react-portal_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a718fc339b7287756fe1d6da5095dead -// flow-typed version: <>/react-portal_v^4.0.0/flow_v0.71.0 +// flow-typed signature: 453840e2b934f656295e9c6be5cf7ae8 +// flow-typed version: <>/react-portal_v^4.0.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/react-router-dom_v4.x.x.js b/flow-typed/npm/react-router-dom_v4.x.x.js index 3b9819bc0..03262573c 100644 --- a/flow-typed/npm/react-router-dom_v4.x.x.js +++ b/flow-typed/npm/react-router-dom_v4.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 53be1849af6037db65e90a7abc558afe -// flow-typed version: f4e99ca1ed/react-router-dom_v4.x.x/flow_>=v0.63.x +// flow-typed signature: e536f4b8192631923c57c19f39ba2b84 +// flow-typed version: 631e6dae97/react-router-dom_v4.x.x/flow_>=v0.63.x declare module "react-router-dom" { import type { ComponentType, ElementConfig, Node, Component } from 'react'; @@ -145,7 +145,7 @@ declare module "react-router-dom" { component?: ComponentType<*>, render?: (router: ContextRouter) => Node, children?: ComponentType | Node, - path?: string, + path?: string | Array, exact?: boolean, strict?: boolean, location?: LocationShape, diff --git a/flow-typed/npm/react-waypoint_vx.x.x.js b/flow-typed/npm/react-waypoint_vx.x.x.js index bb94b35e5..3c5cd9568 100644 --- a/flow-typed/npm/react-waypoint_vx.x.x.js +++ b/flow-typed/npm/react-waypoint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 1adeedf3c744400ee4ebf005944b65ae -// flow-typed version: <>/react-waypoint_v^7.3.1/flow_v0.71.0 +// flow-typed signature: 39f909e74e64eaf0770bcca744569611 +// flow-typed version: <>/react-waypoint_v^7.3.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/redis-lock_vx.x.x.js b/flow-typed/npm/redis-lock_vx.x.x.js index 00865293b..263fbf62d 100644 --- a/flow-typed/npm/redis-lock_vx.x.x.js +++ b/flow-typed/npm/redis-lock_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 189e4b922f086cb6e0f8f7ff74e13e05 -// flow-typed version: <>/redis-lock_v^0.1.0/flow_v0.71.0 +// flow-typed signature: 2bca3687eec48bccb25d35348d28be07 +// flow-typed version: <>/redis-lock_v^0.1.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/rich-markdown-editor_vx.x.x.js b/flow-typed/npm/rich-markdown-editor_vx.x.x.js index 47f2746d6..3665f5dad 100644 --- a/flow-typed/npm/rich-markdown-editor_vx.x.x.js +++ b/flow-typed/npm/rich-markdown-editor_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b9dcab5c03d1b9a4a8a4697c6ea86dac -// flow-typed version: <>/rich-markdown-editor_v6.0.1/flow_v0.71.0 +// flow-typed signature: 0a20db42510ecb339c149ca69c52342b +// flow-typed version: <>/rich-markdown-editor_v^6.1.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/safestart_vx.x.x.js b/flow-typed/npm/safestart_vx.x.x.js index 9214a3786..7e4a36340 100644 --- a/flow-typed/npm/safestart_vx.x.x.js +++ b/flow-typed/npm/safestart_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 113e63a01aeee0162a23f106e6a1ca05 -// flow-typed version: <>/safestart_v1.1.0/flow_v0.71.0 +// flow-typed signature: 9b305842cfea0dc6aeb2ff078f6071a1 +// flow-typed version: <>/safestart_v1.1.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/sequelize-cli_vx.x.x.js b/flow-typed/npm/sequelize-cli_vx.x.x.js index 51fea0806..ee573d97c 100644 --- a/flow-typed/npm/sequelize-cli_vx.x.x.js +++ b/flow-typed/npm/sequelize-cli_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: cbd86b8cacc4b9ff2994005fa4a1a3e5 -// flow-typed version: <>/sequelize-cli_v^2.7.0/flow_v0.71.0 +// flow-typed signature: 5572c51f74566df46bf9aa91d0bda5df +// flow-typed version: <>/sequelize-cli_v^2.7.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/sequelize-encrypted_vx.x.x.js b/flow-typed/npm/sequelize-encrypted_vx.x.x.js index d07d72306..a3de43843 100644 --- a/flow-typed/npm/sequelize-encrypted_vx.x.x.js +++ b/flow-typed/npm/sequelize-encrypted_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 1513c99e6d890a10654bbf5775d77239 -// flow-typed version: <>/sequelize-encrypted_v0.1.0/flow_v0.71.0 +// flow-typed signature: d5886d0503c3cf91cf0f956d5954cc8f +// flow-typed version: <>/sequelize-encrypted_v0.1.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/string-hash_vx.x.x.js b/flow-typed/npm/string-hash_vx.x.x.js index ec6b82daa..928ee44d9 100644 --- a/flow-typed/npm/string-hash_vx.x.x.js +++ b/flow-typed/npm/string-hash_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9aa47dd7456fdb2a00b695f4b5c9dc9d -// flow-typed version: <>/string-hash_v^1.1.0/flow_v0.71.0 +// flow-typed signature: 8c9ecb5481ebe97f283678da6aa050db +// flow-typed version: <>/string-hash_v^1.1.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/string-replace-to-array_vx.x.x.js b/flow-typed/npm/string-replace-to-array_vx.x.x.js index 73a50bdd2..aeaa21893 100644 --- a/flow-typed/npm/string-replace-to-array_vx.x.x.js +++ b/flow-typed/npm/string-replace-to-array_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 7fac54342239dcdbbd3dfd6b2ec22e0e -// flow-typed version: <>/string-replace-to-array_v^1.0.3/flow_v0.71.0 +// flow-typed signature: e35e89af8873cd5bae364d5238ccc4d9 +// flow-typed version: <>/string-replace-to-array_v^1.0.3/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/style-loader_vx.x.x.js b/flow-typed/npm/style-loader_vx.x.x.js index cac35dcbc..eddd543b0 100644 --- a/flow-typed/npm/style-loader_vx.x.x.js +++ b/flow-typed/npm/style-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a5372d37af08e7a44036c033c0e88534 -// flow-typed version: <>/style-loader_v^0.18.2/flow_v0.71.0 +// flow-typed signature: 04c6b5a073e3f6e139d773f3fa779b3f +// flow-typed version: <>/style-loader_v^0.18.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/styled-components-breakpoint_vx.x.x.js b/flow-typed/npm/styled-components-breakpoint_vx.x.x.js index b393fc761..77c9b7f80 100644 --- a/flow-typed/npm/styled-components-breakpoint_vx.x.x.js +++ b/flow-typed/npm/styled-components-breakpoint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 8717a51487fa9b130bec2099080c49df -// flow-typed version: <>/styled-components-breakpoint_v^1.0.1/flow_v0.71.0 +// flow-typed signature: 61e1a5dcd05cd849f4cc5f44bacd86bc +// flow-typed version: <>/styled-components-breakpoint_v^1.0.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/styled-components_v3.x.x.js b/flow-typed/npm/styled-components_v3.x.x.js deleted file mode 100644 index 072914a3c..000000000 --- a/flow-typed/npm/styled-components_v3.x.x.js +++ /dev/null @@ -1,393 +0,0 @@ -// flow-typed signature: ce09cfddc81b167b0a751ef4cd182f77 -// flow-typed version: 76d655b490/styled-components_v3.x.x/flow_>=v0.57.x - -// @flow - -type $npm$styledComponents$Interpolation = ((executionContext: C) => string) | string | number; -type $npm$styledComponents$NameGenerator = (hash: number) => string; - -type $npm$styledComponents$TaggedTemplateLiteral = {| (Array, $npm$styledComponents$Interpolation): R |}; - -// ---- FUNCTIONAL COMPONENT DEFINITIONS ---- -type $npm$styledComponents$ReactComponentFunctional = - & { defaultProps: DefaultProps } - & $npm$styledComponents$ReactComponentFunctionalUndefinedDefaultProps - -type $npm$styledComponents$ReactComponentFunctionalUndefinedDefaultProps = - React$StatelessFunctionalComponent - -// ---- CLASS COMPONENT DEFINITIONS ---- -class $npm$styledComponents$ReactComponent extends React$Component { - static defaultProps: DefaultProps -} -type $npm$styledComponents$ReactComponentClass = Class<$npm$styledComponents$ReactComponent> -type $npm$styledComponents$ReactComponentClassUndefinedDefaultProps = Class> - -// ---- COMPONENT FUNCTIONS INPUT (UNION) & OUTPUT (INTERSECTION) ---- -type $npm$styledComponents$ReactComponentUnion = - $npm$styledComponents$ReactComponentUnionWithDefaultProps - -type $npm$styledComponents$ReactComponentUnionWithDefaultProps = - | $npm$styledComponents$ReactComponentFunctional - | $npm$styledComponents$ReactComponentFunctionalUndefinedDefaultProps - | $npm$styledComponents$ReactComponentClass - | $npm$styledComponents$ReactComponentClassUndefinedDefaultProps - -type $npm$styledComponents$ReactComponentIntersection = - & $npm$styledComponents$ReactComponentFunctional - & $npm$styledComponents$ReactComponentClass; - -// ---- WITHCOMPONENT ---- -type $npm$styledComponents$ReactComponentStyledWithComponent = < - Props, DefaultProps, - Input: - | ComponentList - | $npm$styledComponents$ReactComponentStyled - | $npm$styledComponents$ReactComponentUnionWithDefaultProps ->(Input) => $npm$styledComponents$ReactComponentStyled - -// ---- STATIC PROPERTIES ---- -type $npm$styledComponents$ReactComponentStyledStaticProps = {| - attrs: (AdditionalProps) => $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteral, - extend: $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteral, -|} - -type $npm$styledComponents$ReactComponentStyledStaticPropsWithComponent = {| - withComponent: $npm$styledComponents$ReactComponentStyledWithComponent, - attrs: (AdditionalProps) => $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent, - extend: $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent, -|} - -// ---- STYLED FUNCTION ---- -// Error: styled(CustomComponent).withComponent('a') -// Ok: styled('div').withComponent('a') -type $npm$styledComponents$Call = - & (ComponentListKeys => $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent<{}, ComponentListKeys>) - & (($npm$styledComponents$ReactComponentUnion) => $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteral) - -// ---- STYLED COMPONENT ---- -type $npm$styledComponents$ReactComponentStyled = - & $npm$styledComponents$ReactComponentStyledStaticPropsWithComponent - & $npm$styledComponents$ReactComponentIntersection - -// ---- TAGGED TEMPLATE LITERAL ---- -type $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteral = - & $npm$styledComponents$ReactComponentStyledStaticProps - & $npm$styledComponents$TaggedTemplateLiteral<$npm$styledComponents$ReactComponentStyled> - -type $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent = - & $npm$styledComponents$ReactComponentStyledStaticPropsWithComponent - & $npm$styledComponents$TaggedTemplateLiteral<$npm$styledComponents$ReactComponentStyled> - -// ---- WITHTHEME ---- -type $npm$styledComponents$WithThemeReactComponentClass = < - InputProps: { theme: $npm$styledComponents$Theme }, - InputDefaultProps: {}, - OutputProps: $Diff, - OutputDefaultProps: InputDefaultProps & { theme: $npm$styledComponents$Theme }, ->($npm$styledComponents$ReactComponentClass) => $npm$styledComponents$ReactComponentClass - -type $npm$styledComponents$WithThemeReactComponentClassUndefinedDefaultProps = < - InputProps: { theme: $npm$styledComponents$Theme }, - OutputProps: $Diff, ->($npm$styledComponents$ReactComponentClassUndefinedDefaultProps) => $npm$styledComponents$ReactComponentClass - -type $npm$styledComponents$WithThemeReactComponentFunctional = < - InputProps: { theme: $npm$styledComponents$Theme }, - InputDefaultProps: {}, - OutputProps: $Diff, - OutputDefaultProps: InputDefaultProps & { theme: $npm$styledComponents$Theme }, ->($npm$styledComponents$ReactComponentFunctional) => $npm$styledComponents$ReactComponentFunctional - -type $npm$styledComponents$WithThemeReactComponentFunctionalUndefinedDefaultProps = < - InputProps: { theme: $npm$styledComponents$Theme }, - OutputProps: $Diff ->($npm$styledComponents$ReactComponentFunctionalUndefinedDefaultProps) => $npm$styledComponents$ReactComponentFunctional - -type $npm$styledComponents$WithTheme = - & $npm$styledComponents$WithThemeReactComponentClass - & $npm$styledComponents$WithThemeReactComponentClassUndefinedDefaultProps - & $npm$styledComponents$WithThemeReactComponentFunctional - & $npm$styledComponents$WithThemeReactComponentFunctionalUndefinedDefaultProps - -// ---- MISC ---- -type $npm$styledComponents$Theme = {[key: string]: mixed}; -type $npm$styledComponents$ThemeProviderProps = { - theme: $npm$styledComponents$Theme | ((outerTheme: $npm$styledComponents$Theme) => void) -}; - -class Npm$StyledComponents$ThemeProvider extends React$Component<$npm$styledComponents$ThemeProviderProps> {} - -class Npm$StyledComponents$StyleSheetManager extends React$Component<{ sheet: mixed }> {} - -class Npm$StyledComponents$ServerStyleSheet { - instance: StyleSheet - collectStyles: (children: any) => React$Node - getStyleTags: () => string - getStyleElement: () => React$Node - interleaveWithNodeStream: (readableStream: stream$Readable) => stream$Readable -} - -type $npm$styledComponents$StyledComponentsComponentListKeys = - $Subtype<$Keys<$npm$styledComponents$StyledComponentsComponentList>> - -type $npm$styledComponents$StyledComponentsComponentListValue = - $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent<{}, $npm$styledComponents$StyledComponentsComponentListKeys> - -// ---- COMPONENT LIST ---- -type $npm$styledComponents$StyledComponentsComponentList = {| - a: $npm$styledComponents$StyledComponentsComponentListValue, - abbr: $npm$styledComponents$StyledComponentsComponentListValue, - address: $npm$styledComponents$StyledComponentsComponentListValue, - area: $npm$styledComponents$StyledComponentsComponentListValue, - article: $npm$styledComponents$StyledComponentsComponentListValue, - aside: $npm$styledComponents$StyledComponentsComponentListValue, - audio: $npm$styledComponents$StyledComponentsComponentListValue, - b: $npm$styledComponents$StyledComponentsComponentListValue, - base: $npm$styledComponents$StyledComponentsComponentListValue, - bdi: $npm$styledComponents$StyledComponentsComponentListValue, - bdo: $npm$styledComponents$StyledComponentsComponentListValue, - big: $npm$styledComponents$StyledComponentsComponentListValue, - blockquote: $npm$styledComponents$StyledComponentsComponentListValue, - body: $npm$styledComponents$StyledComponentsComponentListValue, - br: $npm$styledComponents$StyledComponentsComponentListValue, - button: $npm$styledComponents$StyledComponentsComponentListValue, - canvas: $npm$styledComponents$StyledComponentsComponentListValue, - caption: $npm$styledComponents$StyledComponentsComponentListValue, - cite: $npm$styledComponents$StyledComponentsComponentListValue, - code: $npm$styledComponents$StyledComponentsComponentListValue, - col: $npm$styledComponents$StyledComponentsComponentListValue, - colgroup: $npm$styledComponents$StyledComponentsComponentListValue, - data: $npm$styledComponents$StyledComponentsComponentListValue, - datalist: $npm$styledComponents$StyledComponentsComponentListValue, - dd: $npm$styledComponents$StyledComponentsComponentListValue, - del: $npm$styledComponents$StyledComponentsComponentListValue, - details: $npm$styledComponents$StyledComponentsComponentListValue, - dfn: $npm$styledComponents$StyledComponentsComponentListValue, - dialog: $npm$styledComponents$StyledComponentsComponentListValue, - div: $npm$styledComponents$StyledComponentsComponentListValue, - dl: $npm$styledComponents$StyledComponentsComponentListValue, - dt: $npm$styledComponents$StyledComponentsComponentListValue, - em: $npm$styledComponents$StyledComponentsComponentListValue, - embed: $npm$styledComponents$StyledComponentsComponentListValue, - fieldset: $npm$styledComponents$StyledComponentsComponentListValue, - figcaption: $npm$styledComponents$StyledComponentsComponentListValue, - figure: $npm$styledComponents$StyledComponentsComponentListValue, - footer: $npm$styledComponents$StyledComponentsComponentListValue, - form: $npm$styledComponents$StyledComponentsComponentListValue, - h1: $npm$styledComponents$StyledComponentsComponentListValue, - h2: $npm$styledComponents$StyledComponentsComponentListValue, - h3: $npm$styledComponents$StyledComponentsComponentListValue, - h4: $npm$styledComponents$StyledComponentsComponentListValue, - h5: $npm$styledComponents$StyledComponentsComponentListValue, - h6: $npm$styledComponents$StyledComponentsComponentListValue, - head: $npm$styledComponents$StyledComponentsComponentListValue, - header: $npm$styledComponents$StyledComponentsComponentListValue, - hgroup: $npm$styledComponents$StyledComponentsComponentListValue, - hr: $npm$styledComponents$StyledComponentsComponentListValue, - html: $npm$styledComponents$StyledComponentsComponentListValue, - i: $npm$styledComponents$StyledComponentsComponentListValue, - iframe: $npm$styledComponents$StyledComponentsComponentListValue, - img: $npm$styledComponents$StyledComponentsComponentListValue, - input: $npm$styledComponents$StyledComponentsComponentListValue, - ins: $npm$styledComponents$StyledComponentsComponentListValue, - kbd: $npm$styledComponents$StyledComponentsComponentListValue, - keygen: $npm$styledComponents$StyledComponentsComponentListValue, - label: $npm$styledComponents$StyledComponentsComponentListValue, - legend: $npm$styledComponents$StyledComponentsComponentListValue, - li: $npm$styledComponents$StyledComponentsComponentListValue, - link: $npm$styledComponents$StyledComponentsComponentListValue, - main: $npm$styledComponents$StyledComponentsComponentListValue, - map: $npm$styledComponents$StyledComponentsComponentListValue, - mark: $npm$styledComponents$StyledComponentsComponentListValue, - menu: $npm$styledComponents$StyledComponentsComponentListValue, - menuitem: $npm$styledComponents$StyledComponentsComponentListValue, - meta: $npm$styledComponents$StyledComponentsComponentListValue, - meter: $npm$styledComponents$StyledComponentsComponentListValue, - nav: $npm$styledComponents$StyledComponentsComponentListValue, - noscript: $npm$styledComponents$StyledComponentsComponentListValue, - object: $npm$styledComponents$StyledComponentsComponentListValue, - ol: $npm$styledComponents$StyledComponentsComponentListValue, - optgroup: $npm$styledComponents$StyledComponentsComponentListValue, - option: $npm$styledComponents$StyledComponentsComponentListValue, - output: $npm$styledComponents$StyledComponentsComponentListValue, - p: $npm$styledComponents$StyledComponentsComponentListValue, - param: $npm$styledComponents$StyledComponentsComponentListValue, - picture: $npm$styledComponents$StyledComponentsComponentListValue, - pre: $npm$styledComponents$StyledComponentsComponentListValue, - progress: $npm$styledComponents$StyledComponentsComponentListValue, - q: $npm$styledComponents$StyledComponentsComponentListValue, - rp: $npm$styledComponents$StyledComponentsComponentListValue, - rt: $npm$styledComponents$StyledComponentsComponentListValue, - ruby: $npm$styledComponents$StyledComponentsComponentListValue, - s: $npm$styledComponents$StyledComponentsComponentListValue, - samp: $npm$styledComponents$StyledComponentsComponentListValue, - script: $npm$styledComponents$StyledComponentsComponentListValue, - section: $npm$styledComponents$StyledComponentsComponentListValue, - select: $npm$styledComponents$StyledComponentsComponentListValue, - small: $npm$styledComponents$StyledComponentsComponentListValue, - source: $npm$styledComponents$StyledComponentsComponentListValue, - span: $npm$styledComponents$StyledComponentsComponentListValue, - strong: $npm$styledComponents$StyledComponentsComponentListValue, - style: $npm$styledComponents$StyledComponentsComponentListValue, - sub: $npm$styledComponents$StyledComponentsComponentListValue, - summary: $npm$styledComponents$StyledComponentsComponentListValue, - sup: $npm$styledComponents$StyledComponentsComponentListValue, - table: $npm$styledComponents$StyledComponentsComponentListValue, - tbody: $npm$styledComponents$StyledComponentsComponentListValue, - td: $npm$styledComponents$StyledComponentsComponentListValue, - textarea: $npm$styledComponents$StyledComponentsComponentListValue, - tfoot: $npm$styledComponents$StyledComponentsComponentListValue, - th: $npm$styledComponents$StyledComponentsComponentListValue, - thead: $npm$styledComponents$StyledComponentsComponentListValue, - time: $npm$styledComponents$StyledComponentsComponentListValue, - title: $npm$styledComponents$StyledComponentsComponentListValue, - tr: $npm$styledComponents$StyledComponentsComponentListValue, - track: $npm$styledComponents$StyledComponentsComponentListValue, - u: $npm$styledComponents$StyledComponentsComponentListValue, - ul: $npm$styledComponents$StyledComponentsComponentListValue, - var: $npm$styledComponents$StyledComponentsComponentListValue, - video: $npm$styledComponents$StyledComponentsComponentListValue, - wbr: $npm$styledComponents$StyledComponentsComponentListValue, - - // SVG - circle: $npm$styledComponents$StyledComponentsComponentListValue, - clipPath: $npm$styledComponents$StyledComponentsComponentListValue, - defs: $npm$styledComponents$StyledComponentsComponentListValue, - ellipse: $npm$styledComponents$StyledComponentsComponentListValue, - g: $npm$styledComponents$StyledComponentsComponentListValue, - image: $npm$styledComponents$StyledComponentsComponentListValue, - line: $npm$styledComponents$StyledComponentsComponentListValue, - linearGradient: $npm$styledComponents$StyledComponentsComponentListValue, - mask: $npm$styledComponents$StyledComponentsComponentListValue, - path: $npm$styledComponents$StyledComponentsComponentListValue, - pattern: $npm$styledComponents$StyledComponentsComponentListValue, - polygon: $npm$styledComponents$StyledComponentsComponentListValue, - polyline: $npm$styledComponents$StyledComponentsComponentListValue, - radialGradient: $npm$styledComponents$StyledComponentsComponentListValue, - rect: $npm$styledComponents$StyledComponentsComponentListValue, - stop: $npm$styledComponents$StyledComponentsComponentListValue, - svg: $npm$styledComponents$StyledComponentsComponentListValue, - text: $npm$styledComponents$StyledComponentsComponentListValue, - tspan: $npm$styledComponents$StyledComponentsComponentListValue, -|} - -type $npm$styledComponents$StyledComponentsNativeComponentListKeys = - $Subtype<$Keys<$npm$styledComponents$StyledComponentsNativeComponentList>> - -type $npm$styledComponents$StyledComponentsNativeComponentListValue = - $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent<{}, $npm$styledComponents$StyledComponentsNativeComponentListKeys> - -type $npm$styledComponents$StyledComponentsNativeComponentList = {| - ActivityIndicator: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ActivityIndicatorIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ART: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Button: $npm$styledComponents$StyledComponentsNativeComponentListValue, - DatePickerIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - DrawerLayoutAndroid: $npm$styledComponents$StyledComponentsNativeComponentListValue, - FlatList: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Image: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ImageEditor: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ImageStore: $npm$styledComponents$StyledComponentsNativeComponentListValue, - KeyboardAvoidingView: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ListView: $npm$styledComponents$StyledComponentsNativeComponentListValue, - MapView: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Modal: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Navigator: $npm$styledComponents$StyledComponentsNativeComponentListValue, - NavigatorIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Picker: $npm$styledComponents$StyledComponentsNativeComponentListValue, - PickerIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ProgressBarAndroid: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ProgressViewIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - RecyclerViewBackedScrollView: $npm$styledComponents$StyledComponentsNativeComponentListValue, - RefreshControl: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ScrollView: $npm$styledComponents$StyledComponentsNativeComponentListValue, - SectionList: $npm$styledComponents$StyledComponentsNativeComponentListValue, - SegmentedControlIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Slider: $npm$styledComponents$StyledComponentsNativeComponentListValue, - SliderIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - SnapshotViewIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - StatusBar: $npm$styledComponents$StyledComponentsNativeComponentListValue, - SwipeableListView: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Switch: $npm$styledComponents$StyledComponentsNativeComponentListValue, - SwitchAndroid: $npm$styledComponents$StyledComponentsNativeComponentListValue, - SwitchIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - TabBarIOS: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Text: $npm$styledComponents$StyledComponentsNativeComponentListValue, - TextInput: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ToastAndroid: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ToolbarAndroid: $npm$styledComponents$StyledComponentsNativeComponentListValue, - Touchable: $npm$styledComponents$StyledComponentsNativeComponentListValue, - TouchableHighlight: $npm$styledComponents$StyledComponentsNativeComponentListValue, - TouchableNativeFeedback: $npm$styledComponents$StyledComponentsNativeComponentListValue, - TouchableOpacity: $npm$styledComponents$StyledComponentsNativeComponentListValue, - TouchableWithoutFeedback: $npm$styledComponents$StyledComponentsNativeComponentListValue, - View: $npm$styledComponents$StyledComponentsNativeComponentListValue, - ViewPagerAndroid: $npm$styledComponents$StyledComponentsNativeComponentListValue, - VirtualizedList: $npm$styledComponents$StyledComponentsNativeComponentListValue, - WebView: $npm$styledComponents$StyledComponentsNativeComponentListValue, -|} - -declare module 'styled-components' { - declare type Interpolation = $npm$styledComponents$Interpolation; - declare type NameGenerator = $npm$styledComponents$NameGenerator; - declare type Theme = $npm$styledComponents$Theme; - declare type ThemeProviderProps = $npm$styledComponents$ThemeProviderProps; - declare type TaggedTemplateLiteral = $npm$styledComponents$TaggedTemplateLiteral; - declare type ComponentListKeys = $npm$styledComponents$StyledComponentsComponentListKeys; - - declare type ReactComponentFunctional = $npm$styledComponents$ReactComponentFunctional; - declare type ReactComponentFunctionalUndefinedDefaultProps = $npm$styledComponents$ReactComponentFunctionalUndefinedDefaultProps; - declare type ReactComponentClass = $npm$styledComponents$ReactComponentClass; - declare type ReactComponentClassUndefinedDefaultProps = $npm$styledComponents$ReactComponentClassUndefinedDefaultProps; - declare type ReactComponentUnion = $npm$styledComponents$ReactComponentUnion; - declare type ReactComponentIntersection = $npm$styledComponents$ReactComponentIntersection; - declare type ReactComponentStyledStaticProps = $npm$styledComponents$ReactComponentStyledStaticPropsWithComponent; - declare type ReactComponentStyled = $npm$styledComponents$ReactComponentStyled; - declare type ReactComponentStyledTaggedTemplateLiteral = $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent; - - declare module.exports: { - $call: $npm$styledComponents$Call, - - injectGlobal: TaggedTemplateLiteral, - css: TaggedTemplateLiteral>, - keyframes: TaggedTemplateLiteral, - withTheme: $npm$styledComponents$WithTheme, - ServerStyleSheet: typeof Npm$StyledComponents$ServerStyleSheet, - StyleSheetManager: typeof Npm$StyledComponents$StyleSheetManager, - ThemeProvider: typeof Npm$StyledComponents$ThemeProvider, - - ...$npm$styledComponents$StyledComponentsComponentList, - ...$npm$styledComponents$StyledComponentsNativeComponentList, - }; -} - -declare module 'styled-components/native' { - declare type Interpolation = $npm$styledComponents$Interpolation; - declare type NameGenerator = $npm$styledComponents$NameGenerator; - declare type Theme = $npm$styledComponents$Theme; - declare type ThemeProviderProps = $npm$styledComponents$ThemeProviderProps; - declare type TaggedTemplateLiteral = $npm$styledComponents$TaggedTemplateLiteral; - declare type NativeComponentListKeys = $npm$styledComponents$StyledComponentsNativeComponentListKeys; - - declare type ReactComponentFunctional = $npm$styledComponents$ReactComponentFunctional; - declare type ReactComponentFunctionalUndefinedDefaultProps = $npm$styledComponents$ReactComponentFunctionalUndefinedDefaultProps; - declare type ReactComponentClass = $npm$styledComponents$ReactComponentClass; - declare type ReactComponentClassUndefinedDefaultProps = $npm$styledComponents$ReactComponentClassUndefinedDefaultProps; - declare type ReactComponentUnion = $npm$styledComponents$ReactComponentUnion; - declare type ReactComponentIntersection = $npm$styledComponents$ReactComponentIntersection; - declare type ReactComponentStyledStaticProps = $npm$styledComponents$ReactComponentStyledStaticPropsWithComponent; - declare type ReactComponentStyled = $npm$styledComponents$ReactComponentStyled; - declare type ReactComponentStyledTaggedTemplateLiteral = $npm$styledComponents$ReactComponentStyledTaggedTemplateLiteralWithComponent; - - declare module.exports: { - $call: $npm$styledComponents$Call, - - css: TaggedTemplateLiteral>, - keyframes: TaggedTemplateLiteral, - withTheme: $npm$styledComponents$WithTheme, - ThemeProvider: typeof Npm$StyledComponents$ThemeProvider, - - ...$npm$styledComponents$StyledComponentsNativeComponentList, - }; -} diff --git a/flow-typed/npm/styled-components_vx.x.x.js b/flow-typed/npm/styled-components_vx.x.x.js index 8e2a2c3f6..1b383c687 100644 --- a/flow-typed/npm/styled-components_vx.x.x.js +++ b/flow-typed/npm/styled-components_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: cf12e9fab4c41b9ccf9fa8e75c00e74c -// flow-typed version: <>/styled-components_v4.0.3/flow_v0.71.0 +// flow-typed signature: 8072a5267b376c8aaecd28d2087d012e +// flow-typed version: <>/styled-components_v4.0.3/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/styled-normalize_vx.x.x.js b/flow-typed/npm/styled-normalize_vx.x.x.js index 7193cb0c3..1bf692a9f 100644 --- a/flow-typed/npm/styled-normalize_vx.x.x.js +++ b/flow-typed/npm/styled-normalize_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 363df9bda6a4fdad1abf4cef8300e285 -// flow-typed version: <>/styled-normalize_v^2.2.1/flow_v0.71.0 +// flow-typed signature: 95ae6b3cca0216de345b07c8df8e59c2 +// flow-typed version: <>/styled-normalize_v^2.2.1/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/tmp_vx.x.x.js b/flow-typed/npm/tmp_vx.x.x.js index b0e4b9ab4..09d2775b8 100644 --- a/flow-typed/npm/tmp_vx.x.x.js +++ b/flow-typed/npm/tmp_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 904869861e7edd1f3dde79042150393f -// flow-typed version: <>/tmp_v0.0.33/flow_v0.71.0 +// flow-typed signature: 866e6f32aa7676ff1ce3cb9ca30c5e52 +// flow-typed version: <>/tmp_v0.0.33/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/uglifyjs-webpack-plugin_vx.x.x.js b/flow-typed/npm/uglifyjs-webpack-plugin_vx.x.x.js index b37e8e998..71e7bac95 100644 --- a/flow-typed/npm/uglifyjs-webpack-plugin_vx.x.x.js +++ b/flow-typed/npm/uglifyjs-webpack-plugin_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 77f5d8d713dbb3d128c5c8092d25f395 -// flow-typed version: <>/uglifyjs-webpack-plugin_v1.2.5/flow_v0.71.0 +// flow-typed signature: 5101c3ddb7ff7f9f905f2f2fe5379b7d +// flow-typed version: <>/uglifyjs-webpack-plugin_v1.2.5/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/url-loader_vx.x.x.js b/flow-typed/npm/url-loader_vx.x.x.js index 3bf6af479..eb6a67a00 100644 --- a/flow-typed/npm/url-loader_vx.x.x.js +++ b/flow-typed/npm/url-loader_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 6c39fe45cf77f5d447f45b5abf75d8ca -// flow-typed version: <>/url-loader_v^0.6.2/flow_v0.71.0 +// flow-typed signature: 3c9a9053104bba94d2a34306fff0449f +// flow-typed version: <>/url-loader_v^0.6.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/validator_vx.x.x.js b/flow-typed/npm/validator_vx.x.x.js index 7e6a0e805..e2e4af38c 100644 --- a/flow-typed/npm/validator_vx.x.x.js +++ b/flow-typed/npm/validator_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: af8d19cdb2cf53118095cb557500d4b6 -// flow-typed version: <>/validator_v5.2.0/flow_v0.71.0 +// flow-typed signature: 1ca1023909ef1b1c5af608288a84e921 +// flow-typed version: <>/validator_v5.2.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/webpack-manifest-plugin_vx.x.x.js b/flow-typed/npm/webpack-manifest-plugin_vx.x.x.js index 75989b7da..3a7aeea2b 100644 --- a/flow-typed/npm/webpack-manifest-plugin_vx.x.x.js +++ b/flow-typed/npm/webpack-manifest-plugin_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 4763d2093535bf04c5192eb2b996e2fb -// flow-typed version: <>/webpack-manifest-plugin_v^1.3.2/flow_v0.71.0 +// flow-typed signature: 0b3baf5d28fe95d466ff4d0bb5b8501f +// flow-typed version: <>/webpack-manifest-plugin_v^1.3.2/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/webpack_vx.x.x.js b/flow-typed/npm/webpack_vx.x.x.js index 44b514c1d..7b4b4555e 100644 --- a/flow-typed/npm/webpack_vx.x.x.js +++ b/flow-typed/npm/webpack_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 3a06dd2c6a4f0b4230a9c83604a621d7 -// flow-typed version: <>/webpack_v3.10.0/flow_v0.71.0 +// flow-typed signature: f3b8d71ed1a086951b01438d53a7c010 +// flow-typed version: <>/webpack_v3.10.0/flow_v0.86.0 /** * This is an autogenerated libdef stub for: diff --git a/package.json b/package.json index 1d8a6ad6f..798ec503a 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,6 @@ "emoji-regex": "^6.5.1", "exports-loader": "^0.6.4", "extract-text-webpack-plugin": "^3.0.2", - "fbemitter": "^2.1.1", "file-loader": "^1.1.6", "flow-typed": "^2.4.0", "fs-extra": "^4.0.2", @@ -129,14 +128,14 @@ "koa-sslify": "2.1.2", "koa-static": "^4.0.1", "lodash": "^4.17.4", - "mobx": "^3.1.9", - "mobx-react": "^4.1.8", + "mobx": "4.6.0", + "mobx-react": "^5.4.2", "natural-sort": "^1.0.0", "node-dev": "3.1.0", "nodemailer": "^4.4.0", "normalize.css": "^7.0.0", "normalizr": "2.0.1", - "outline-icons": "^1.3.2", + "outline-icons": "^1.5.0", "oy-vey": "^0.10.0", "parse-domain": "2.1.6", "pg": "^6.1.5", @@ -194,13 +193,13 @@ "eslint-plugin-prettier": "^2.4.0", "eslint-plugin-react": "^7.5.1", "fetch-test-server": "^1.1.0", - "flow-bin": "^0.71.0", + "flow-bin": "0.86.0", "identity-obj-proxy": "^3.0.0", "jest-cli": "22", "koa-webpack-dev-middleware": "1.4.5", "koa-webpack-hot-middleware": "1.0.3", "lint-staged": "^3.4.0", - "mobx-react-devtools": "^4.2.11", + "mobx-react-devtools": "^6.0.3", "nodemon": "1.11.0", "prettier": "1.8.2", "raf": "^3.4.0", diff --git a/server/api/__snapshots__/team.test.js.snap b/server/api/__snapshots__/team.test.js.snap deleted file mode 100644 index a92a594e2..000000000 --- a/server/api/__snapshots__/team.test.js.snap +++ /dev/null @@ -1,63 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`#team.users should require admin for detailed info 1`] = ` -Object { - "data": Array [ - Object { - "avatarUrl": "http://example.com/avatar.png", - "createdAt": "2018-01-01T00:00:00.000Z", - "id": "fa952cff-fa64-4d42-a6ea-6955c9689046", - "name": "Admin User", - "username": "admin", - }, - Object { - "avatarUrl": "http://example.com/avatar.png", - "createdAt": "2018-01-01T00:00:00.000Z", - "id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61", - "name": "User 1", - "username": "user1", - }, - ], - "ok": true, - "pagination": Object { - "limit": 15, - "nextPath": "/api/team.users?limit=15&offset=15", - "offset": 0, - }, - "status": 200, -} -`; - -exports[`#team.users should return teams paginated user list 1`] = ` -Object { - "data": Array [ - Object { - "avatarUrl": "http://example.com/avatar.png", - "createdAt": "2018-01-01T00:00:00.000Z", - "email": "user1@example.com", - "id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61", - "isAdmin": false, - "isSuspended": false, - "name": "User 1", - "username": "user1", - }, - Object { - "avatarUrl": "http://example.com/avatar.png", - "createdAt": "2018-01-01T00:00:00.000Z", - "email": "admin@example.com", - "id": "fa952cff-fa64-4d42-a6ea-6955c9689046", - "isAdmin": true, - "isSuspended": false, - "name": "Admin User", - "username": "admin", - }, - ], - "ok": true, - "pagination": Object { - "limit": 15, - "nextPath": "/api/team.users?limit=15&offset=15", - "offset": 0, - }, - "status": 200, -} -`; diff --git a/server/api/__snapshots__/user.test.js.snap b/server/api/__snapshots__/users.test.js.snap similarity index 54% rename from server/api/__snapshots__/user.test.js.snap rename to server/api/__snapshots__/users.test.js.snap index ea9827a22..a45a788ec 100644 --- a/server/api/__snapshots__/user.test.js.snap +++ b/server/api/__snapshots__/users.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`#user.activate should activate a suspended user 1`] = ` +exports[`#users.activate should activate a suspended user 1`] = ` Object { "data": Object { "avatarUrl": "http://example.com/avatar.png", @@ -17,7 +17,7 @@ Object { } `; -exports[`#user.activate should require admin 1`] = ` +exports[`#users.activate should require admin 1`] = ` Object { "error": "admin_required", "message": "An admin role is required to access this resource", @@ -26,7 +26,7 @@ Object { } `; -exports[`#user.delete should require authentication 1`] = ` +exports[`#users.delete should require authentication 1`] = ` Object { "error": "authentication_required", "message": "Authentication required", @@ -35,7 +35,7 @@ Object { } `; -exports[`#user.demote should demote an admin 1`] = ` +exports[`#users.demote should demote an admin 1`] = ` Object { "data": Object { "avatarUrl": "http://example.com/avatar.png", @@ -52,7 +52,7 @@ Object { } `; -exports[`#user.demote should require admin 1`] = ` +exports[`#users.demote should require admin 1`] = ` Object { "error": "admin_required", "message": "An admin role is required to access this resource", @@ -61,7 +61,7 @@ Object { } `; -exports[`#user.demote shouldn't demote admins if only one available 1`] = ` +exports[`#users.demote shouldn't demote admins if only one available 1`] = ` Object { "error": "validation_error", "message": "At least one admin is required", @@ -70,7 +70,69 @@ Object { } `; -exports[`#user.promote should promote a new admin 1`] = ` +exports[`#users.list should require admin for detailed info 1`] = ` +Object { + "data": Array [ + Object { + "avatarUrl": "http://example.com/avatar.png", + "createdAt": "2018-01-01T00:00:00.000Z", + "id": "fa952cff-fa64-4d42-a6ea-6955c9689046", + "name": "Admin User", + "username": "admin", + }, + Object { + "avatarUrl": "http://example.com/avatar.png", + "createdAt": "2018-01-01T00:00:00.000Z", + "id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61", + "name": "User 1", + "username": "user1", + }, + ], + "ok": true, + "pagination": Object { + "limit": 15, + "nextPath": "/api/users.list?limit=15&offset=15", + "offset": 0, + }, + "status": 200, +} +`; + +exports[`#users.list should return teams paginated user list 1`] = ` +Object { + "data": Array [ + Object { + "avatarUrl": "http://example.com/avatar.png", + "createdAt": "2018-01-01T00:00:00.000Z", + "email": "user1@example.com", + "id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61", + "isAdmin": false, + "isSuspended": false, + "name": "User 1", + "username": "user1", + }, + Object { + "avatarUrl": "http://example.com/avatar.png", + "createdAt": "2018-01-01T00:00:00.000Z", + "email": "admin@example.com", + "id": "fa952cff-fa64-4d42-a6ea-6955c9689046", + "isAdmin": true, + "isSuspended": false, + "name": "Admin User", + "username": "admin", + }, + ], + "ok": true, + "pagination": Object { + "limit": 15, + "nextPath": "/api/users.list?limit=15&offset=15", + "offset": 0, + }, + "status": 200, +} +`; + +exports[`#users.promote should promote a new admin 1`] = ` Object { "data": Object { "avatarUrl": "http://example.com/avatar.png", @@ -87,7 +149,7 @@ Object { } `; -exports[`#user.promote should require admin 1`] = ` +exports[`#users.promote should require admin 1`] = ` Object { "error": "admin_required", "message": "An admin role is required to access this resource", @@ -96,7 +158,7 @@ Object { } `; -exports[`#user.suspend should require admin 1`] = ` +exports[`#users.suspend should require admin 1`] = ` Object { "error": "admin_required", "message": "An admin role is required to access this resource", @@ -105,7 +167,7 @@ Object { } `; -exports[`#user.suspend should suspend an user 1`] = ` +exports[`#users.suspend should suspend an user 1`] = ` Object { "data": Object { "avatarUrl": "http://example.com/avatar.png", @@ -122,7 +184,7 @@ Object { } `; -exports[`#user.suspend shouldn't allow suspending the user themselves 1`] = ` +exports[`#users.suspend shouldn't allow suspending the user themselves 1`] = ` Object { "error": "validation_error", "message": "Unable to suspend the current user", @@ -131,7 +193,7 @@ Object { } `; -exports[`#user.update should require authentication 1`] = ` +exports[`#users.update should require authentication 1`] = ` Object { "error": "authentication_required", "message": "Authentication required", @@ -140,7 +202,7 @@ Object { } `; -exports[`#user.update should update user profile information 1`] = ` +exports[`#users.update should update user profile information 1`] = ` Object { "data": Object { "avatarUrl": "http://example.com/avatar.png", diff --git a/server/api/index.js b/server/api/index.js index 8088c3137..784e503d2 100644 --- a/server/api/index.js +++ b/server/api/index.js @@ -4,7 +4,7 @@ import Koa from 'koa'; import Router from 'koa-router'; import auth from './auth'; -import user from './user'; +import users from './users'; import collections from './collections'; import documents from './documents'; import views from './views'; @@ -13,6 +13,7 @@ import apiKeys from './apiKeys'; import shares from './shares'; import team from './team'; import integrations from './integrations'; +import notificationSettings from './notificationSettings'; import errorHandling from './middlewares/errorHandling'; import validation from '../middlewares/validation'; @@ -33,7 +34,7 @@ api.use(apiWrapper()); // routes router.use('/', auth.routes()); -router.use('/', user.routes()); +router.use('/', users.routes()); router.use('/', collections.routes()); router.use('/', documents.routes()); router.use('/', views.routes()); @@ -42,6 +43,7 @@ router.use('/', apiKeys.routes()); router.use('/', shares.routes()); router.use('/', team.routes()); router.use('/', integrations.routes()); +router.use('/', notificationSettings.routes()); // Router is embedded in a Koa application wrapper, because koa-router does not // allow middleware to catch any routes which were not explicitly defined. diff --git a/server/api/notificationSettings.js b/server/api/notificationSettings.js new file mode 100644 index 000000000..7ef09c845 --- /dev/null +++ b/server/api/notificationSettings.js @@ -0,0 +1,60 @@ +// @flow +import Router from 'koa-router'; + +import auth from '../middlewares/authentication'; +import { NotificationSetting } from '../models'; +import { presentNotificationSetting } from '../presenters'; +import policy from '../policies'; + +const { authorize } = policy; +const router = new Router(); + +router.post('notificationSettings.create', auth(), async ctx => { + const { event } = ctx.body; + ctx.assertPresent(event, 'event is required'); + + const user = ctx.state.user; + authorize(user, 'create', NotificationSetting); + + const [setting] = await NotificationSetting.findOrCreate({ + where: { + userId: user.id, + teamId: user.teamId, + event, + }, + }); + + ctx.body = { + data: presentNotificationSetting(ctx, setting), + }; +}); + +router.post('notificationSettings.list', auth(), async ctx => { + const user = ctx.state.user; + const settings = await NotificationSetting.findAll({ + where: { + userId: user.id, + }, + }); + + ctx.body = { + data: settings.map(setting => presentNotificationSetting(ctx, setting)), + }; +}); + +router.post('notificationSettings.delete', auth(), async ctx => { + const { id } = ctx.body; + ctx.assertPresent(id, 'id is required'); + + const user = ctx.state.user; + const setting = await NotificationSetting.findById(id); + authorize(user, 'delete', setting); + + await setting.destroy(); + + ctx.body = { + success: true, + }; +}); + +export default router; diff --git a/server/api/team.js b/server/api/team.js index ab62d7059..4b8e4a6a9 100644 --- a/server/api/team.js +++ b/server/api/team.js @@ -1,11 +1,10 @@ // @flow import Router from 'koa-router'; -import { User, Team } from '../models'; +import { Team } from '../models'; import { publicS3Endpoint } from '../utils/s3'; import auth from '../middlewares/authentication'; -import pagination from './middlewares/pagination'; -import { presentUser, presentTeam } from '../presenters'; +import { presentTeam } from '../presenters'; import policy from '../policies'; const { authorize } = policy; @@ -33,24 +32,4 @@ router.post('team.update', auth(), async ctx => { ctx.body = { data: await presentTeam(ctx, team) }; }); -router.post('team.users', auth(), pagination(), async ctx => { - const user = ctx.state.user; - - const users = await User.findAll({ - where: { - teamId: user.teamId, - }, - order: [['createdAt', 'DESC']], - offset: ctx.state.pagination.offset, - limit: ctx.state.pagination.limit, - }); - - ctx.body = { - pagination: ctx.state.pagination, - data: users.map(listUser => - presentUser(ctx, listUser, { includeDetails: user.isAdmin }) - ), - }; -}); - export default router; diff --git a/server/api/team.test.js b/server/api/team.test.js index 3a266cc34..a9d2f1d8f 100644 --- a/server/api/team.test.js +++ b/server/api/team.test.js @@ -1,6 +1,5 @@ /* eslint-disable flowtype/require-valid-file-annotation */ import TestServer from 'fetch-test-server'; - import app from '..'; import { flushdb, seed } from '../test/support'; @@ -10,31 +9,6 @@ const server = new TestServer(app.callback()); beforeEach(flushdb); afterAll(server.close); -describe('#team.users', async () => { - it('should return teams paginated user list', async () => { - const { admin } = await seed(); - - const res = await server.post('/api/team.users', { - body: { token: admin.getJwtToken() }, - }); - const body = await res.json(); - - expect(res.status).toEqual(200); - expect(body).toMatchSnapshot(); - }); - - it('should require admin for detailed info', async () => { - const { user } = await seed(); - const res = await server.post('/api/team.users', { - body: { token: user.getJwtToken() }, - }); - const body = await res.json(); - - expect(res.status).toEqual(200); - expect(body).toMatchSnapshot(); - }); -}); - describe('#team.update', async () => { it('should update team details', async () => { const { admin } = await seed(); diff --git a/server/api/user.js b/server/api/users.js similarity index 81% rename from server/api/user.js rename to server/api/users.js index ad1fcb1a9..e11c138cd 100644 --- a/server/api/user.js +++ b/server/api/users.js @@ -5,17 +5,38 @@ import { makePolicy, signPolicy, publicS3Endpoint } from '../utils/s3'; import { ValidationError } from '../errors'; import { Event, User, Team } from '../models'; import auth from '../middlewares/authentication'; +import pagination from './middlewares/pagination'; import { presentUser } from '../presenters'; import policy from '../policies'; const { authorize } = policy; const router = new Router(); -router.post('user.info', auth(), async ctx => { +router.post('users.list', auth(), pagination(), async ctx => { + const user = ctx.state.user; + + const users = await User.findAll({ + where: { + teamId: user.teamId, + }, + order: [['createdAt', 'DESC']], + offset: ctx.state.pagination.offset, + limit: ctx.state.pagination.limit, + }); + + ctx.body = { + pagination: ctx.state.pagination, + data: users.map(listUser => + presentUser(ctx, listUser, { includeDetails: user.isAdmin }) + ), + }; +}); + +router.post('users.info', auth(), async ctx => { ctx.body = { data: await presentUser(ctx, ctx.state.user) }; }); -router.post('user.update', auth(), async ctx => { +router.post('users.update', auth(), async ctx => { const { user } = ctx.state; const { name, avatarUrl } = ctx.body; const endpoint = publicS3Endpoint(); @@ -30,7 +51,7 @@ router.post('user.update', auth(), async ctx => { ctx.body = { data: await presentUser(ctx, user, { includeDetails: true }) }; }); -router.post('user.s3Upload', auth(), async ctx => { +router.post('users.s3Upload', auth(), async ctx => { const { filename, kind, size } = ctx.body; ctx.assertPresent(filename, 'filename is required'); ctx.assertPresent(kind, 'kind is required'); @@ -79,7 +100,7 @@ router.post('user.s3Upload', auth(), async ctx => { // Admin specific -router.post('user.promote', auth(), async ctx => { +router.post('users.promote', auth(), async ctx => { const userId = ctx.body.id; const teamId = ctx.state.user.teamId; ctx.assertPresent(userId, 'id is required'); @@ -95,7 +116,7 @@ router.post('user.promote', auth(), async ctx => { }; }); -router.post('user.demote', auth(), async ctx => { +router.post('users.demote', auth(), async ctx => { const userId = ctx.body.id; const teamId = ctx.state.user.teamId; ctx.assertPresent(userId, 'id is required'); @@ -120,7 +141,7 @@ router.post('user.demote', auth(), async ctx => { * * Admin can suspend users to reduce the number of accounts on their billing plan */ -router.post('user.suspend', auth(), async ctx => { +router.post('users.suspend', auth(), async ctx => { const admin = ctx.state.user; const userId = ctx.body.id; const teamId = ctx.state.user.teamId; @@ -147,7 +168,7 @@ router.post('user.suspend', auth(), async ctx => { * Admin can activate users to let them access resources. These users will also * account towards the billing plan limits. */ -router.post('user.activate', auth(), async ctx => { +router.post('users.activate', auth(), async ctx => { const admin = ctx.state.user; const userId = ctx.body.id; const teamId = ctx.state.user.teamId; @@ -164,7 +185,7 @@ router.post('user.activate', auth(), async ctx => { }; }); -router.post('user.delete', auth(), async ctx => { +router.post('users.delete', auth(), async ctx => { const { confirmation } = ctx.body; ctx.assertPresent(confirmation, 'confirmation is required'); diff --git a/server/api/user.test.js b/server/api/users.test.js similarity index 72% rename from server/api/user.test.js rename to server/api/users.test.js index f2f7a5a08..5e4e6c0f5 100644 --- a/server/api/user.test.js +++ b/server/api/users.test.js @@ -10,10 +10,35 @@ const server = new TestServer(app.callback()); beforeEach(flushdb); afterAll(server.close); -describe('#user.info', async () => { +describe('#users.list', async () => { + it('should return teams paginated user list', async () => { + const { admin } = await seed(); + + const res = await server.post('/api/users.list', { + body: { token: admin.getJwtToken() }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body).toMatchSnapshot(); + }); + + it('should require admin for detailed info', async () => { + const { user } = await seed(); + const res = await server.post('/api/users.list', { + body: { token: user.getJwtToken() }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body).toMatchSnapshot(); + }); +}); + +describe('#users.info', async () => { it('should return known user', async () => { const user = await buildUser(); - const res = await server.post('/api/user.info', { + const res = await server.post('/api/users.info', { body: { token: user.getJwtToken() }, }); const body = await res.json(); @@ -24,15 +49,15 @@ describe('#user.info', async () => { }); it('should require authentication', async () => { - const res = await server.post('/api/user.info'); + const res = await server.post('/api/users.info'); expect(res.status).toEqual(401); }); }); -describe('#user.delete', async () => { +describe('#users.delete', async () => { it('should not allow deleting without confirmation', async () => { const user = await buildUser(); - const res = await server.post('/api/user.delete', { + const res = await server.post('/api/users.delete', { body: { token: user.getJwtToken() }, }); expect(res.status).toEqual(400); @@ -40,7 +65,7 @@ describe('#user.delete', async () => { it('should allow deleting last admin if only user', async () => { const user = await buildUser({ isAdmin: true }); - const res = await server.post('/api/user.delete', { + const res = await server.post('/api/users.delete', { body: { token: user.getJwtToken(), confirmation: true }, }); expect(res.status).toEqual(200); @@ -50,7 +75,7 @@ describe('#user.delete', async () => { const user = await buildUser({ isAdmin: true }); await buildUser({ teamId: user.teamId, isAdmin: false }); - const res = await server.post('/api/user.delete', { + const res = await server.post('/api/users.delete', { body: { token: user.getJwtToken(), confirmation: true }, }); expect(res.status).toEqual(400); @@ -58,14 +83,14 @@ describe('#user.delete', async () => { it('should allow deleting user account with confirmation', async () => { const user = await buildUser(); - const res = await server.post('/api/user.delete', { + const res = await server.post('/api/users.delete', { body: { token: user.getJwtToken(), confirmation: true }, }); expect(res.status).toEqual(200); }); it('should require authentication', async () => { - const res = await server.post('/api/user.delete'); + const res = await server.post('/api/users.delete'); const body = await res.json(); expect(res.status).toEqual(401); @@ -73,10 +98,10 @@ describe('#user.delete', async () => { }); }); -describe('#user.update', async () => { +describe('#users.update', async () => { it('should update user profile information', async () => { const { user } = await seed(); - const res = await server.post('/api/user.update', { + const res = await server.post('/api/users.update', { body: { token: user.getJwtToken(), name: 'New name' }, }); const body = await res.json(); @@ -86,7 +111,7 @@ describe('#user.update', async () => { }); it('should require authentication', async () => { - const res = await server.post('/api/user.update'); + const res = await server.post('/api/users.update'); const body = await res.json(); expect(res.status).toEqual(401); @@ -94,11 +119,11 @@ describe('#user.update', async () => { }); }); -describe('#user.promote', async () => { +describe('#users.promote', async () => { it('should promote a new admin', async () => { const { admin, user } = await seed(); - const res = await server.post('/api/user.promote', { + const res = await server.post('/api/users.promote', { body: { token: admin.getJwtToken(), id: user.id }, }); const body = await res.json(); @@ -109,7 +134,7 @@ describe('#user.promote', async () => { it('should require admin', async () => { const user = await buildUser(); - const res = await server.post('/api/user.promote', { + const res = await server.post('/api/users.promote', { body: { token: user.getJwtToken(), id: user.id }, }); const body = await res.json(); @@ -119,12 +144,12 @@ describe('#user.promote', async () => { }); }); -describe('#user.demote', async () => { +describe('#users.demote', async () => { it('should demote an admin', async () => { const { admin, user } = await seed(); await user.update({ isAdmin: true }); // Make another admin - const res = await server.post('/api/user.demote', { + const res = await server.post('/api/users.demote', { body: { token: admin.getJwtToken(), id: user.id, @@ -139,7 +164,7 @@ describe('#user.demote', async () => { it("shouldn't demote admins if only one available ", async () => { const admin = await buildUser({ isAdmin: true }); - const res = await server.post('/api/user.demote', { + const res = await server.post('/api/users.demote', { body: { token: admin.getJwtToken(), id: admin.id, @@ -153,7 +178,7 @@ describe('#user.demote', async () => { it('should require admin', async () => { const user = await buildUser(); - const res = await server.post('/api/user.promote', { + const res = await server.post('/api/users.promote', { body: { token: user.getJwtToken(), id: user.id }, }); const body = await res.json(); @@ -163,11 +188,11 @@ describe('#user.demote', async () => { }); }); -describe('#user.suspend', async () => { +describe('#users.suspend', async () => { it('should suspend an user', async () => { const { admin, user } = await seed(); - const res = await server.post('/api/user.suspend', { + const res = await server.post('/api/users.suspend', { body: { token: admin.getJwtToken(), id: user.id, @@ -181,7 +206,7 @@ describe('#user.suspend', async () => { it("shouldn't allow suspending the user themselves", async () => { const admin = await buildUser({ isAdmin: true }); - const res = await server.post('/api/user.suspend', { + const res = await server.post('/api/users.suspend', { body: { token: admin.getJwtToken(), id: admin.id, @@ -195,7 +220,7 @@ describe('#user.suspend', async () => { it('should require admin', async () => { const user = await buildUser(); - const res = await server.post('/api/user.suspend', { + const res = await server.post('/api/users.suspend', { body: { token: user.getJwtToken(), id: user.id }, }); const body = await res.json(); @@ -205,7 +230,7 @@ describe('#user.suspend', async () => { }); }); -describe('#user.activate', async () => { +describe('#users.activate', async () => { it('should activate a suspended user', async () => { const { admin, user } = await seed(); await user.update({ @@ -214,7 +239,7 @@ describe('#user.activate', async () => { }); expect(user.isSuspended).toBe(true); - const res = await server.post('/api/user.activate', { + const res = await server.post('/api/users.activate', { body: { token: admin.getJwtToken(), id: user.id, @@ -228,7 +253,7 @@ describe('#user.activate', async () => { it('should require admin', async () => { const user = await buildUser(); - const res = await server.post('/api/user.activate', { + const res = await server.post('/api/users.activate', { body: { token: user.getJwtToken(), id: user.id }, }); const body = await res.json(); diff --git a/server/emails/CollectionNotificationEmail.js b/server/emails/CollectionNotificationEmail.js new file mode 100644 index 000000000..f7fde6124 --- /dev/null +++ b/server/emails/CollectionNotificationEmail.js @@ -0,0 +1,57 @@ +// @flow +import * as React from 'react'; +import { User, Collection } from '../models'; +import EmailTemplate from './components/EmailLayout'; +import Body from './components/Body'; +import Button from './components/Button'; +import Heading from './components/Heading'; +import Header from './components/Header'; +import Footer from './components/Footer'; +import EmptySpace from './components/EmptySpace'; + +export type Props = { + actor: User, + collection: Collection, + eventName: string, +}; + +export const collectionNotificationEmailText = ({ + actor, + collection, + eventName = 'created', +}: Props) => ` +"${document.title}" ${eventName} + +${actor.name} ${eventName} the collection "${collection.name}" + +Open Collection: ${process.env.URL}${collection.url} +`; + +export const CollectionNotificationEmail = ({ + actor, + collection, + eventName = 'created', +}: Props) => { + return ( + +
    + + + + "{collection.name}" {eventName} + +

    + {actor.name} {eventName} the collection "{collection.title}". +

    + +

    + +

    + + +