diff --git a/app/components/DropToImport/DropToImport.js b/app/components/DropToImport/DropToImport.js
index d948b8d9..a72a4bac 100644
--- a/app/components/DropToImport/DropToImport.js
+++ b/app/components/DropToImport/DropToImport.js
@@ -4,10 +4,10 @@ import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import { injectGlobal } from 'styled-components';
import { color } from 'shared/styles/constants';
+import importFile from 'utils/importFile';
import invariant from 'invariant';
import _ from 'lodash';
import Dropzone from 'react-dropzone';
-import Document from 'models/Document';
import DocumentsStore from 'stores/DocumentsStore';
import LoadingIndicator from 'components/LoadingIndicator';
@@ -19,7 +19,6 @@ type Props = {
rejectClassName?: string,
documents: DocumentsStore,
disabled: boolean,
- dropzoneRef: Function,
history: Object,
};
@@ -40,30 +39,6 @@ class DropToImport extends Component {
@observable isImporting: boolean = false;
props: Props;
- importFile = async ({ file, documentId, collectionId, redirect }) => {
- const reader = new FileReader();
-
- reader.onload = async ev => {
- const text = ev.target.result;
- let data = {
- parentDocument: undefined,
- collection: { id: collectionId },
- text,
- };
-
- if (documentId) data.parentDocument = documentId;
-
- let document = new Document(data);
- document = await document.save();
- this.props.documents.add(document);
-
- if (redirect && this.props.history) {
- this.props.history.push(document.url);
- }
- };
- reader.readAsText(file);
- };
-
onDropAccepted = async (files = []) => {
this.isImporting = true;
@@ -79,7 +54,16 @@ class DropToImport extends Component {
}
for (const file of files) {
- await this.importFile({ file, documentId, collectionId, redirect });
+ const doc = await importFile({
+ documents: this.props.documents,
+ file,
+ documentId,
+ collectionId,
+ });
+
+ if (redirect) {
+ this.props.history.push(doc.url);
+ }
}
} catch (err) {
// TODO: show error alert.
@@ -96,7 +80,6 @@ class DropToImport extends Component {
'collectionId',
'documents',
'disabled',
- 'dropzoneRef',
'menuOpen'
);
@@ -110,7 +93,6 @@ class DropToImport extends Component {
disableClick
disablePreview
multiple
- ref={this.props.dropzoneRef}
{...props}
>
{this.isImporting && }
diff --git a/app/components/Sidebar/components/Collections.js b/app/components/Sidebar/components/Collections.js
index a4fdbcd0..4dfd25c7 100644
--- a/app/components/Sidebar/components/Collections.js
+++ b/app/components/Sidebar/components/Collections.js
@@ -76,14 +76,9 @@ type CollectionLinkProps = {
@observer
class CollectionLink extends Component {
props: CollectionLinkProps;
- dropzoneRef;
@observable menuOpen = false;
- handleImport = () => {
- this.dropzoneRef.open();
- };
-
renderDocuments() {
const {
history,
@@ -119,7 +114,6 @@ class CollectionLink extends Component {
collectionId={collection.id}
activeClassName="activeDropZone"
menuOpen={this.menuOpen}
- dropzoneRef={ref => (this.dropzoneRef = ref)}
>
{collection.name}
-
-
- (this.menuOpen = true)}
- onClose={() => (this.menuOpen = false)}
- onImport={this.handleImport}
- open={this.menuOpen}
- />
-
+
+ (this.menuOpen = true)}
+ onClose={() => (this.menuOpen = false)}
+ />
+
);
}
@@ -233,6 +224,7 @@ const CollectionName = styled(Flex)`
const CollectionAction = styled.span`
position: absolute;
right: 0;
+ top: 0;
color: ${color.slate};
svg {
opacity: 0.75;
@@ -246,6 +238,8 @@ const CollectionAction = styled.span`
`;
const StyledDropToImport = styled(DropToImport)`
+ position: relative;
+
${CollectionAction} {
display: ${props => (props.menuOpen ? 'inline' : 'none')};
}
@@ -260,6 +254,7 @@ const StyledDropToImport = styled(DropToImport)`
const CollectionChildren = styled(Flex)`
margin-top: -4px;
margin-left: 36px;
+ padding-bottom: 4px;
`;
const DocumentChildren = styled(Flex)`
diff --git a/app/menus/CollectionMenu.js b/app/menus/CollectionMenu.js
index fb1d7e99..fb7d3434 100644
--- a/app/menus/CollectionMenu.js
+++ b/app/menus/CollectionMenu.js
@@ -1,24 +1,31 @@
// @flow
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
+import styled from 'styled-components';
+import getDataTransferFiles from 'utils/getDataTransferFiles';
+import importFile from 'utils/importFile';
import Collection from 'models/Collection';
import UiStore from 'stores/UiStore';
+import DocumentsStore from 'stores/DocumentsStore';
import MoreIcon from 'components/Icon/MoreIcon';
import Flex from 'shared/components/Flex';
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
+type Props = {
+ label?: React$Element<*>,
+ onOpen?: () => void,
+ onClose?: () => void,
+ history: Object,
+ ui: UiStore,
+ documents: DocumentsStore,
+ collection: Collection,
+};
+
@observer
class CollectionMenu extends Component {
- props: {
- label?: React$Element<*>,
- onOpen?: () => void,
- onClose?: () => void,
- onImport?: () => void,
- history: Object,
- ui: UiStore,
- collection: Collection,
- };
+ props: Props;
+ file: HTMLInputElement;
onNewDocument = (ev: SyntheticEvent) => {
ev.preventDefault();
@@ -26,6 +33,24 @@ class CollectionMenu extends Component {
history.push(`${collection.url}/new`);
};
+ onImportDocument = (ev: SyntheticEvent) => {
+ ev.preventDefault();
+
+ // simulate a click on the file upload input element
+ this.file.click();
+ };
+
+ 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);
+ };
+
onEdit = (ev: SyntheticEvent) => {
ev.preventDefault();
const { collection } = this.props;
@@ -39,32 +64,47 @@ class CollectionMenu extends Component {
};
render() {
- const { collection, label, onOpen, onClose, onImport } = this.props;
+ const { collection, label, onOpen, onClose } = this.props;
const { allowDelete } = collection;
return (
- }
- onOpen={onOpen}
- onClose={onClose}
- >
- {collection && (
-
-
- New document
-
-
- Import document
-
- Edit…
-
- )}
- {allowDelete && (
- Delete…
- )}
-
+
+ (this.file = ref)}
+ onChange={this.onFilePicked}
+ accept="text/markdown, text/plain"
+ />
+ }
+ onOpen={onOpen}
+ onClose={onClose}
+ >
+ {collection && (
+
+
+ New document
+
+
+ Import document
+
+ Edit…
+
+ )}
+ {allowDelete && (
+ Delete…
+ )}
+
+
);
}
}
-export default inject('ui')(CollectionMenu);
+const HiddenInput = styled.input`
+ position: absolute;
+ top: -100px;
+ left: -100px;
+ visibility: hidden;
+`;
+
+export default inject('ui', 'documents')(CollectionMenu);
diff --git a/app/scenes/Collection/Collection.js b/app/scenes/Collection/Collection.js
index 74bc1b4a..9736bcf1 100644
--- a/app/scenes/Collection/Collection.js
+++ b/app/scenes/Collection/Collection.js
@@ -133,7 +133,10 @@ class CollectionScene extends Component {
/>
-
+
diff --git a/app/utils/importFile.js b/app/utils/importFile.js
new file mode 100644
index 00000000..05b2118b
--- /dev/null
+++ b/app/utils/importFile.js
@@ -0,0 +1,40 @@
+// @flow
+import Document from '../models/Document';
+import DocumentsStore from '../stores/DocumentsStore';
+
+type Options = {
+ file: File,
+ documents: DocumentsStore,
+ collectionId: string,
+ documentId?: string,
+};
+
+const importFile = async ({
+ documents,
+ file,
+ documentId,
+ collectionId,
+}: Options): Promise => {
+ return new Promise(resolve => {
+ const reader = new FileReader();
+
+ reader.onload = async ev => {
+ const text = ev.target.result;
+ let data = {
+ parentDocument: undefined,
+ collection: { id: collectionId },
+ text,
+ };
+
+ if (documentId) data.parentDocument = documentId;
+
+ let document = new Document(data);
+ document = await document.save();
+ documents.add(document);
+ resolve(document);
+ };
+ reader.readAsText(file);
+ });
+};
+
+export default importFile;