diff --git a/frontend/components/DropdownMenu/DropdownMenu.js b/frontend/components/DropdownMenu/DropdownMenu.js index 89fa150c..d36da02c 100644 --- a/frontend/components/DropdownMenu/DropdownMenu.js +++ b/frontend/components/DropdownMenu/DropdownMenu.js @@ -2,22 +2,11 @@ import React from 'react'; import { observable } from 'mobx'; import { observer } from 'mobx-react'; +import keydown from 'react-keydown'; import styled from 'styled-components'; import Flex from 'components/Flex'; import { color } from 'styles/constants'; - -type MenuItemProps = { - onClick?: Function, - children?: React.Element, -}; - -const DropdownMenuItem = ({ onClick, children }: MenuItemProps) => { - return ( - - {children} - - ); -}; +import { fadeAndScaleIn } from 'styles/animations'; type DropdownMenuProps = { label: React.Element, @@ -27,22 +16,30 @@ type DropdownMenuProps = { @observer class DropdownMenu extends React.Component { props: DropdownMenuProps; - @observable menuOpen: boolean = false; + @observable open: boolean = false; handleClick = () => { - this.menuOpen = !this.menuOpen; + this.open = !this.open; + }; + + @keydown('esc') + handleEscape() { + this.open = false; + } + + handleClickOutside = (ev: SyntheticEvent) => { + ev.stopPropagation(); + this.open = false; }; render() { return ( - {this.menuOpen && } - + {this.open && } - - {this.menuOpen && + {this.open && {this.props.children} } @@ -73,40 +70,18 @@ const MenuContainer = styled.div` `; const Menu = styled.div` + animation: ${fadeAndScaleIn} 250ms ease; + transform-origin: 75% 0; + position: absolute; right: 0; z-index: 1000; - border: 1px solid #eee; - background-color: #fff; + border: ${color.slateLight}; + background: ${color.white}; + border-radius: 2px; min-width: 160px; + overflow: hidden; + box-shadow: 0 0 0 1px rgba(0,0,0,.05), 0 4px 8px rgba(0,0,0,.08), 0 2px 4px rgba(0,0,0,.08); `; -const MenuItem = styled.div` - margin: 0; - padding: 5px 10px; - height: 32px; - - display: flex; - justify-content: space-between; - align-items: center; - cursor: pointer; - border-left: 2px solid transparent; - - color: ${color.text}; - - span { - margin-top: 2px; - } - - a { - text-decoration: none; - color: ${color.text}; - width: 100%; - } - - &:hover { - border-left: 2px solid ${color.primary}; - } -`; - -export { DropdownMenu, DropdownMenuItem }; +export default DropdownMenu; diff --git a/frontend/components/DropdownMenu/DropdownMenuItem.js b/frontend/components/DropdownMenu/DropdownMenuItem.js new file mode 100644 index 00000000..113dc091 --- /dev/null +++ b/frontend/components/DropdownMenu/DropdownMenuItem.js @@ -0,0 +1,43 @@ +// @flow +import React from 'react'; +import styled from 'styled-components'; +import { color } from 'styles/constants'; + +const DropdownMenuItem = ({ + onClick, + children, +}: { + onClick?: Function, + children?: React.Element, +}) => { + return ( + + {children} + + ); +}; + +const MenuItem = styled.div` + margin: 0; + padding: 5px 10px; + height: 32px; + + color: ${color.slateDark}; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + font-size: 15px; + + a { + text-decoration: none; + width: 100%; + } + + &:hover { + color: ${color.white}; + background: ${color.primary}; + } +`; + +export default DropdownMenuItem; diff --git a/frontend/components/DropdownMenu/index.js b/frontend/components/DropdownMenu/index.js index 11e84f9c..f6b44681 100644 --- a/frontend/components/DropdownMenu/index.js +++ b/frontend/components/DropdownMenu/index.js @@ -1,2 +1,3 @@ // @flow -export { DropdownMenu, DropdownMenuItem } from './DropdownMenu'; +export { default as DropdownMenu } from './DropdownMenu'; +export { default as DropdownMenuItem } from './DropdownMenuItem'; diff --git a/frontend/components/Layout/Layout.js b/frontend/components/Layout/Layout.js index 48592645..af08b23a 100644 --- a/frontend/components/Layout/Layout.js +++ b/frontend/components/Layout/Layout.js @@ -1,31 +1,26 @@ // @flow import React from 'react'; -import { Link, withRouter } from 'react-router-dom'; +import { withRouter } from 'react-router-dom'; import Helmet from 'react-helmet'; import styled from 'styled-components'; import { observer, inject } from 'mobx-react'; -import { observable } from 'mobx'; -import _ from 'lodash'; import keydown from 'react-keydown'; import Flex from 'components/Flex'; import { color, layout } from 'styles/constants'; import { documentEditUrl, homeUrl, searchUrl } from 'utils/routeHelpers'; -import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; import Avatar from 'components/Avatar'; import { LoadingIndicatorBar } from 'components/LoadingIndicator'; import Scrollable from 'components/Scrollable'; -import Modal from 'components/Modal'; import Icon from 'components/Icon'; -import CollectionNew from 'scenes/CollectionNew'; -import CollectionEdit from 'scenes/CollectionEdit'; -import KeyboardShortcuts from 'scenes/KeyboardShortcuts'; -import Settings from 'scenes/Settings'; +import CollectionMenu from 'menus/CollectionMenu'; +import AccountMenu from 'menus/AccountMenu'; import SidebarCollection from './components/SidebarCollection'; import SidebarCollectionList from './components/SidebarCollectionList'; import SidebarLink from './components/SidebarLink'; import HeaderBlock from './components/HeaderBlock'; +import Modals from './components/Modals'; import AuthStore from 'stores/AuthStore'; import UiStore from 'stores/UiStore'; @@ -52,8 +47,6 @@ type Props = { search: true, }; - @observable modal = null; - @keydown(['/', 't']) goToSearch(ev) { ev.preventDefault(); @@ -76,37 +69,21 @@ type Props = { this.props.history.push(documentEditUrl(activeDocument)); } - handleLogout = () => { - this.props.auth.logout(() => this.props.history.push('/')); - }; - @keydown('shift+/') goToOpenKeyboardShortcuts() { - this.modal = 'keyboard-shortcuts'; + this.props.ui.setActiveModal('keyboard-shortcuts'); } - handleOpenKeyboardShortcuts = () => { - this.goToOpenKeyboardShortcuts(); - }; - - handleOpenSettings = () => { - this.modal = 'settings'; - }; - handleCreateCollection = () => { - this.modal = 'create-collection'; + this.props.ui.setActiveModal('collection-new'); }; handleEditCollection = () => { - this.modal = 'edit-collection'; - }; - - handleCloseModal = () => { - this.modal = null; + this.props.ui.setActiveModal('collection-edit'); }; render() { - const { auth, documents, collections, history, ui } = this.props; + const { auth, documents, collections, ui } = this.props; const { user, team } = auth; return ( @@ -130,29 +107,15 @@ type Props = { user && team && - } - > - - Settings - - - Keyboard shortcuts - - - API - - - Logout - - + /> - + @@ -167,8 +130,8 @@ type Props = { {collections.active - ? - + ? + : @@ -189,44 +152,7 @@ type Props = { {this.props.children} - - - - - {collections.active && - } - - - - - - - + ); } @@ -255,10 +181,6 @@ const Content = styled(Flex)` transition: margin-left 200ms ease-in-out; `; -const MenuLink = styled(Link)` - color: ${color.text}; -`; - const Sidebar = styled(Flex)` position: fixed; top: 0; diff --git a/frontend/components/Layout/components/Modals.js b/frontend/components/Layout/components/Modals.js new file mode 100644 index 00000000..37193b8b --- /dev/null +++ b/frontend/components/Layout/components/Modals.js @@ -0,0 +1,62 @@ +// @flow +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; +import BaseModal from 'components/Modal'; +import UiStore from 'stores/UiStore'; +import CollectionNew from 'scenes/CollectionNew'; +import CollectionEdit from 'scenes/CollectionEdit'; +import CollectionDelete from 'scenes/CollectionDelete'; +import DocumentDelete from 'scenes/DocumentDelete'; +import KeyboardShortcuts from 'scenes/KeyboardShortcuts'; +import Settings from 'scenes/Settings'; + +@observer class Modals extends Component { + props: { + ui: UiStore, + }; + + handleClose = () => { + this.props.ui.clearActiveModal(); + }; + + render() { + const { activeModalName, activeModalProps } = this.props.ui; + + const Modal = ({ name, children, ...rest }) => { + return ( + + {React.cloneElement(children, activeModalProps)} + + ); + }; + + return ( + + + + + + + + + + + + + + + + + + + + + ); + } +} + +export default Modals; diff --git a/frontend/components/Scrollable/Scrollable.js b/frontend/components/Scrollable/Scrollable.js index 050af487..526cc29e 100644 --- a/frontend/components/Scrollable/Scrollable.js +++ b/frontend/components/Scrollable/Scrollable.js @@ -6,7 +6,6 @@ const Scroll = styled.div` height: 100%; overflow-y: auto; overflow-x: hidden; - transform: translateZ(0); -webkit-overflow-scrolling: touch; `; diff --git a/frontend/menus/AccountMenu.js b/frontend/menus/AccountMenu.js new file mode 100644 index 00000000..77ca6cb2 --- /dev/null +++ b/frontend/menus/AccountMenu.js @@ -0,0 +1,53 @@ +// @flow +import React, { Component } from 'react'; +import { Link, withRouter } from 'react-router-dom'; +import { inject, observer } from 'mobx-react'; +import UiStore from 'stores/UiStore'; +import AuthStore from 'stores/AuthStore'; +import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; + +@observer class AccountMenu extends Component { + props: { + label?: React$Element, + history: Object, + ui: UiStore, + auth: AuthStore, + }; + + handleOpenKeyboardShortcuts = () => { + this.props.ui.setActiveModal('keyboard-shortcuts'); + }; + + handleOpenSettings = () => { + this.props.ui.setActiveModal('settings'); + }; + + handleLogout = () => { + this.props.auth.logout(); + this.props.history.push('/'); + }; + + render() { + return ( + + + Settings + + + Keyboard shortcuts + + + API documentation + + + Logout + + + ); + } +} + +export default withRouter(inject('ui', 'auth')(AccountMenu)); diff --git a/frontend/menus/CollectionMenu.js b/frontend/menus/CollectionMenu.js new file mode 100644 index 00000000..9fdff8ac --- /dev/null +++ b/frontend/menus/CollectionMenu.js @@ -0,0 +1,43 @@ +// @flow +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; +import { inject, observer } from 'mobx-react'; +import Collection from 'models/Collection'; +import UiStore from 'stores/UiStore'; +import Icon from 'components/Icon'; +import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; + +@observer class CollectionMenu extends Component { + props: { + label?: React$Element, + history: Object, + ui: UiStore, + collection: Collection, + }; + + onEdit = () => { + const { collection } = this.props; + this.props.ui.setActiveModal('collection-edit', { collection }); + }; + + onDelete = () => { + const { collection } = this.props; + this.props.ui.setActiveModal('collection-delete', { collection }); + }; + + render() { + const { collection, label } = this.props; + const { allowDelete } = collection; + + return ( + }> + {collection && + Edit} + {allowDelete && + Delete} + + ); + } +} + +export default withRouter(inject('ui')(CollectionMenu)); diff --git a/frontend/menus/DocumentMenu.js b/frontend/menus/DocumentMenu.js new file mode 100644 index 00000000..e87ebc00 --- /dev/null +++ b/frontend/menus/DocumentMenu.js @@ -0,0 +1,53 @@ +// @flow +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; +import { inject, observer } from 'mobx-react'; +import Document from 'models/Document'; +import UiStore from 'stores/UiStore'; +import Icon from 'components/Icon'; +import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; + +@observer class DocumentMenu extends Component { + props: { + ui: UiStore, + label?: React$Element, + history: Object, + document: Document, + }; + + onCreateDocument = () => { + this.props.history.push(`${this.props.document.collection.url}/new`); + }; + + onCreateChild = () => { + this.props.history.push(`${this.props.document.url}/new`); + }; + + onDelete = () => { + const { document } = this.props; + this.props.ui.setActiveModal('document-delete', { document }); + }; + + onExport = () => { + this.props.document.download(); + }; + + render() { + const { document, label } = this.props; + const { collection, allowDelete } = document; + + return ( + }> + {collection && + + New document + } + Export + {allowDelete && + Delete} + + ); + } +} + +export default withRouter(inject('ui')(DocumentMenu)); diff --git a/frontend/models/Collection.js b/frontend/models/Collection.js index 55248f2d..93923e9c 100644 --- a/frontend/models/Collection.js +++ b/frontend/models/Collection.js @@ -32,6 +32,10 @@ class Collection extends BaseModel { : this.url; } + @computed get allowDelete(): boolean { + return true; + } + /* Actions */ @action fetch = async () => { @@ -84,13 +88,15 @@ class Collection extends BaseModel { @action delete = async () => { try { - const res = await client.post('/collections.delete', { id: this.id }); - invariant(res && res.data, 'Data should be available'); - const { data } = res; - return data.success; + await client.post('/collections.delete', { id: this.id }); + this.emit('collections.delete', { + id: this.id, + }); + return true; } catch (e) { this.errors.add('Collection failed to delete'); } + return false; }; @action updateData(data: Object = {}) { diff --git a/frontend/models/Document.js b/frontend/models/Document.js index cafcdefb..95504f48 100644 --- a/frontend/models/Document.js +++ b/frontend/models/Document.js @@ -85,6 +85,16 @@ class Document extends BaseModel { return !this.isEmpty && !this.isSaving; } + @computed get allowDelete(): boolean { + const collection = this.collection; + return ( + collection && + collection.type === 'atlas' && + collection.documents && + collection.documents.length > 1 + ); + } + /* Actions */ @action star = async () => { @@ -205,12 +215,21 @@ class Document extends BaseModel { id: this.id, collectionId: this.collection.id, }); + return true; } catch (e) { this.errors.add('Error while deleting the document'); } - return; + return false; }; + download() { + const a = window.document.createElement('a'); + a.textContent = 'download'; + a.download = `${this.title}.md`; + a.href = `data:text/markdown;charset=UTF-8,${encodeURIComponent(this.text)}`; + a.click(); + } + updateData(data: Object = {}, dirty: boolean = false) { if (data.text) { const { title, emoji } = parseTitle(data.text); diff --git a/frontend/scenes/CollectionDelete/CollectionDelete.js b/frontend/scenes/CollectionDelete/CollectionDelete.js new file mode 100644 index 00000000..31573e90 --- /dev/null +++ b/frontend/scenes/CollectionDelete/CollectionDelete.js @@ -0,0 +1,61 @@ +// @flow +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; +import { observable } from 'mobx'; +import { inject, observer } from 'mobx-react'; +import { homeUrl } from 'utils/routeHelpers'; +import Button from 'components/Button'; +import Flex from 'components/Flex'; +import HelpText from 'components/HelpText'; +import Collection from 'models/Collection'; +import CollectionsStore from 'stores/CollectionsStore'; + +type Props = { + history: Object, + collection: Collection, + collections: CollectionsStore, + onSubmit: () => void, +}; + +@observer class CollectionDelete extends Component { + props: Props; + @observable isDeleting: boolean; + + handleSubmit = async (ev: SyntheticEvent) => { + ev.preventDefault(); + this.isDeleting = true; + const success = await this.props.collection.delete(); + + if (success) { + this.props.collections.remove(this.props.collection.id); + this.props.history.push(homeUrl()); + this.props.onSubmit(); + } + + this.isDeleting = false; + }; + + render() { + const { collection } = this.props; + + return ( + +
+ + Are you sure? Deleting the + {' '} + {collection.name} + {' '} + collection is permanant and will also delete all of the documents within + it, so be careful with that. + + +
+
+ ); + } +} + +export default inject('collections')(withRouter(CollectionDelete)); diff --git a/frontend/scenes/CollectionDelete/index.js b/frontend/scenes/CollectionDelete/index.js new file mode 100644 index 00000000..e86f5889 --- /dev/null +++ b/frontend/scenes/CollectionDelete/index.js @@ -0,0 +1,3 @@ +// @flow +import CollectionDelete from './CollectionDelete'; +export default CollectionDelete; diff --git a/frontend/scenes/CollectionEdit/CollectionEdit.js b/frontend/scenes/CollectionEdit/CollectionEdit.js index e3718808..8cb2401e 100644 --- a/frontend/scenes/CollectionEdit/CollectionEdit.js +++ b/frontend/scenes/CollectionEdit/CollectionEdit.js @@ -1,27 +1,23 @@ // @flow import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; import { observable } from 'mobx'; -import { observer } from 'mobx-react'; -import { homeUrl } from 'utils/routeHelpers'; +import { inject, observer } from 'mobx-react'; import Button from 'components/Button'; import Input from 'components/Input'; import Flex from 'components/Flex'; import HelpText from 'components/HelpText'; import Collection from 'models/Collection'; -import CollectionsStore from 'stores/CollectionsStore'; type Props = { history: Object, collection: Collection, - collections: CollectionsStore, onSubmit: () => void, }; @observer class CollectionEdit extends Component { props: Props; @observable name: string; - @observable isConfirming: boolean; - @observable isDeleting: boolean; @observable isSaving: boolean; componentWillMount() { @@ -46,34 +42,12 @@ type Props = { this.name = ev.target.value; }; - confirmDelete = () => { - this.isConfirming = true; - }; - - cancelDelete = () => { - this.isConfirming = false; - }; - - confirmedDelete = async (ev: SyntheticEvent) => { - ev.preventDefault(); - this.isDeleting = true; - const success = await this.props.collection.delete(); - - if (success) { - this.props.collections.remove(this.props.collection.id); - this.props.history.push(homeUrl()); - this.props.onSubmit(); - } - - this.isDeleting = false; - }; - render() { return (
- You can edit a collection name at any time, but doing so might + You can edit a collection's name at any time, however doing so might confuse your team mates.
-
-
- - Deleting a collection will also delete all of the documents within - it, so be careful with that. - - {!this.isConfirming && - } - {this.isConfirming && - - - - } -
); } } -export default CollectionEdit; +export default inject('collections')(withRouter(CollectionEdit)); diff --git a/frontend/scenes/CollectionNew/CollectionNew.js b/frontend/scenes/CollectionNew/CollectionNew.js index 4b9f7819..e52d28ee 100644 --- a/frontend/scenes/CollectionNew/CollectionNew.js +++ b/frontend/scenes/CollectionNew/CollectionNew.js @@ -1,7 +1,8 @@ // @flow import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; import { observable } from 'mobx'; -import { observer } from 'mobx-react'; +import { inject, observer } from 'mobx-react'; import Button from 'components/Button'; import Input from 'components/Input'; import HelpText from 'components/HelpText'; @@ -69,4 +70,4 @@ type Props = { } } -export default CollectionNew; +export default inject('collections')(withRouter(CollectionNew)); diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js index 234805e1..5ae41caf 100644 --- a/frontend/scenes/Document/Document.js +++ b/frontend/scenes/Document/Document.js @@ -19,7 +19,7 @@ import Document from 'models/Document'; import DocumentMove from './components/DocumentMove'; import UiStore from 'stores/UiStore'; import DocumentsStore from 'stores/DocumentsStore'; -import Menu from './components/Menu'; +import DocumentMenu from 'menus/DocumentMenu'; import SaveAction from './components/SaveAction'; import LoadingPlaceholder from 'components/LoadingPlaceholder'; import Editor from 'components/Editor'; @@ -251,8 +251,7 @@ type Props = { /> - {document && - !isNew && + {!isNew && !isEditing && } @@ -270,10 +269,12 @@ type Props = { Edit } + {isEditing && + + Cancel + } - {isEditing - ? Cancel - : } + {!isEditing && } diff --git a/frontend/scenes/Document/components/Menu.js b/frontend/scenes/Document/components/Menu.js deleted file mode 100644 index 7eca8f58..00000000 --- a/frontend/scenes/Document/components/Menu.js +++ /dev/null @@ -1,87 +0,0 @@ -// @flow -import React, { Component } from 'react'; -import invariant from 'invariant'; -import get from 'lodash/get'; -import { withRouter } from 'react-router-dom'; -import { observer } from 'mobx-react'; -import Document from 'models/Document'; -import Icon from 'components/Icon'; -import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; - -type Props = { - history: Object, - document: Document, -}; - -@observer class Menu extends Component { - props: Props; - - onCreateDocument = () => { - this.props.history.push(`${this.props.document.collection.url}/new`); - }; - - onCreateChild = () => { - invariant(this.props.document, 'Document is not available'); - this.props.history.push(`${this.props.document.url}/new`); - }; - - onDelete = async () => { - let msg; - if (get(this.props, 'document.collection.type') === 'atlas') { - msg = - "Are you sure you want to delete this document and all it's child documents (if any)?"; - } else { - msg = 'Are you sure you want to delete this document?'; - } - - if (confirm(msg)) { - await this.props.document.delete(); - this.props.history.push(this.props.document.collection.url); - } - }; - - onExport = () => { - const doc = this.props.document; - if (doc) { - const a = document.createElement('a'); - a.textContent = 'download'; - a.download = `${doc.title}.md`; - a.href = `data:text/markdown;charset=UTF-8,${encodeURIComponent(doc.text)}`; - a.click(); - } - }; - - onMove = () => { - this.props.history.push(`${this.props.document.url}/move`); - }; - - render() { - const document = get(this.props, 'document'); - if (document) { - const collection = document.collection; - const allowDelete = - collection && - collection.type === 'atlas' && - collection.documents && - collection.documents.length > 1; - - return ( - }> - {collection && -
- - New document - -
} - Move - Export - {allowDelete && - Delete} -
- ); - } - return null; - } -} - -export default withRouter(Menu); diff --git a/frontend/scenes/DocumentDelete/DocumentDelete.js b/frontend/scenes/DocumentDelete/DocumentDelete.js new file mode 100644 index 00000000..0a8799c7 --- /dev/null +++ b/frontend/scenes/DocumentDelete/DocumentDelete.js @@ -0,0 +1,60 @@ +// @flow +import React, { Component } from 'react'; +import { withRouter } from 'react-router-dom'; +import { observable } from 'mobx'; +import { inject, observer } from 'mobx-react'; +import { homeUrl } from 'utils/routeHelpers'; +import Button from 'components/Button'; +import Flex from 'components/Flex'; +import HelpText from 'components/HelpText'; +import Document from 'models/Document'; +import DocumentsStore from 'stores/DocumentsStore'; + +type Props = { + history: Object, + document: Document, + documents: DocumentsStore, + onSubmit: () => void, +}; + +@observer class DocumentDelete extends Component { + props: Props; + @observable isDeleting: boolean; + + handleSubmit = async (ev: SyntheticEvent) => { + ev.preventDefault(); + this.isDeleting = true; + const success = await this.props.document.delete(); + + if (success) { + this.props.documents.remove(this.props.document.id); + this.props.history.push(homeUrl()); + this.props.onSubmit(); + } + + this.isDeleting = false; + }; + + render() { + const { document } = this.props; + + return ( + +
+ + Are you sure? Deleting the + {' '} + {document.title} + {' '} + document is permanant and will also delete all of its history. + + +
+
+ ); + } +} + +export default inject('documents')(withRouter(DocumentDelete)); diff --git a/frontend/scenes/DocumentDelete/index.js b/frontend/scenes/DocumentDelete/index.js new file mode 100644 index 00000000..90e2e111 --- /dev/null +++ b/frontend/scenes/DocumentDelete/index.js @@ -0,0 +1,3 @@ +// @flow +import DocumentDelete from './DocumentDelete'; +export default DocumentDelete; diff --git a/frontend/stores/AuthStore.js b/frontend/stores/AuthStore.js index f5b77ecb..c0c7800e 100644 --- a/frontend/stores/AuthStore.js +++ b/frontend/stores/AuthStore.js @@ -30,10 +30,9 @@ class AuthStore { /* Actions */ - @action logout = (cb: Function) => { + @action logout = () => { this.user = null; this.token = null; - cb(); }; @action getOauthState = () => { diff --git a/frontend/stores/UiStore.js b/frontend/stores/UiStore.js index e8db5c5e..5d249619 100644 --- a/frontend/stores/UiStore.js +++ b/frontend/stores/UiStore.js @@ -3,12 +3,23 @@ import { observable, action } from 'mobx'; import Document from 'models/Document'; class UiStore { + @observable activeModalName: ?string; + @observable activeModalProps: ?Object; @observable activeDocumentId: ?string; @observable activeCollectionId: ?string; @observable progressBarVisible: boolean = false; @observable editMode: boolean = false; /* Actions */ + @action setActiveModal = (name: string, props: ?Object): void => { + this.activeModalName = name; + this.activeModalProps = props; + }; + + @action clearActiveModal = (): void => { + this.activeModalName = undefined; + this.activeModalProps = undefined; + }; @action setActiveDocument = (document: Document): void => { this.activeDocumentId = document.id; diff --git a/frontend/utils/ApiClient.js b/frontend/utils/ApiClient.js index aff280e9..3c7ff2be 100644 --- a/frontend/utils/ApiClient.js +++ b/frontend/utils/ApiClient.js @@ -65,7 +65,9 @@ class ApiClient { // Handle 401, log out user if (response.status === 401) { - return stores.auth.logout(() => (window.location = '/')); + stores.auth.logout(); + window.location = '/'; + return; } // Handle failed responses diff --git a/yarn.lock b/yarn.lock index 3b83cf3b..eaabe224 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2476,11 +2476,7 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" -electron-to-chromium@^1.2.7: - version "1.3.21" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.21.tgz#a967ebdcfe8ed0083fc244d1894022a8e8113ea2" - -electron-to-chromium@^1.3.18: +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.18: version "1.3.20" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.20.tgz#2eedd5ccbae7ddc557f68ad1fce9c172e915e4e5"