From 74e4909a77ac143ecdb1d038ad182aae9c710129 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Wed, 3 Jul 2019 22:15:25 -0400
Subject: implemented directory import routine

---
 src/client/views/MainView.tsx | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'src/client/views/MainView.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ab9fe0118..e38fd3c8b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,6 +38,7 @@ import PDFMenu from './pdf/PDFMenu';
 import { InkTool } from '../../new_fields/InkField';
 import _ from "lodash";
 import KeyManager from './GlobalKeyHandler';
+import BulkImporter from '../util/Import & Export/ImageImporter';
 
 @observer
 export class MainView extends React.Component {
@@ -351,11 +352,13 @@ export class MainView extends React.Component {
         let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
         let addTreeNode = action(() => CurrentUserUtils.UserDocument);
         let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
+        let addImportCollectionNode = action(() => Docs.DirectoryImportDocument({ title: "Directory Import", width: 150, height: 150 }));
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
             [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
             [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
             [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode],
+            [React.createRef<HTMLDivElement>(), "arrow-up", "Import Directory", addImportCollectionNode],
         ];
 
         return < div id="add-nodes-menu" >
-- 
cgit v1.2.3-70-g09d2


From aedd283fb9f9eff4145e27658bc6647982256032 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Fri, 5 Jul 2019 00:34:19 -0400
Subject: beginning implementation of key value tags for all imported documents

---
 src/client/documents/Documents.ts                  |  12 +-
 .../util/Import & Export/DirectoryImportBox.scss   |   0
 .../util/Import & Export/DirectoryImportBox.tsx    | 181 +++++++++++++++++++++
 src/client/util/Import & Export/ImageImporter.tsx  |  67 --------
 src/client/util/Import & Export/ImportBox.tsx      | 134 ---------------
 src/client/util/request-image-size.js              |   6 +-
 src/client/views/MainView.tsx                      |   1 -
 src/client/views/collections/CollectionSubView.tsx |   4 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   4 +-
 src/client/views/nodes/ImageBox.tsx                |   4 +-
 10 files changed, 198 insertions(+), 215 deletions(-)
 create mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss
 create mode 100644 src/client/util/Import & Export/DirectoryImportBox.tsx
 delete mode 100644 src/client/util/Import & Export/ImageImporter.tsx
 delete mode 100644 src/client/util/Import & Export/ImportBox.tsx

(limited to 'src/client/views/MainView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5d637dd3a..26b5498a2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -36,7 +36,7 @@ import { UndoManager } from "../util/UndoManager";
 import { RouteStore } from "../../server/RouteStore";
 import { LinkManager } from "../util/LinkManager";
 import { DocumentManager } from "../util/DocumentManager";
-import ImportBox from "../util/Import & Export/ImportBox";
+import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
 var requestImageSize = require('../util/request-image-size');
 var path = require('path');
 
@@ -180,7 +180,7 @@ export namespace Docs {
     }
 
     function CreateImportPrototype(): Doc {
-        let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
+        let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", DirectoryImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
         return importProto;
     }
 
@@ -347,7 +347,7 @@ export namespace Docs {
         return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
     }
 
-    export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise<Opt<Doc>> {
+    export async function getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> {
         let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
         if (type.indexOf("image") !== -1) {
             ctor = Docs.ImageDocument;
@@ -370,17 +370,17 @@ export namespace Docs {
             if (path.includes(window.location.hostname)) {
                 let s = path.split('/');
                 let id = s[s.length - 1];
-                DocServer.GetRefField(id).then(field => {
+                return DocServer.GetRefField(id).then(field => {
                     if (field instanceof Doc) {
                         let alias = Doc.MakeAlias(field);
                         alias.x = options.x || 0;
                         alias.y = options.y || 0;
                         alias.width = options.width || 300;
                         alias.height = options.height || options.width || 300;
-                        addDocument && addDocument(alias, false);
+                        return alias;
                     }
+                    return undefined;
                 });
-                return undefined;
             }
             ctor = Docs.WebDocument;
             options = { height: options.width, ...options, title: path, nativeWidth: undefined };
diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
new file mode 100644
index 000000000..2d77f6ae6
--- /dev/null
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -0,0 +1,181 @@
+import "fs";
+import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
+import { DocServer } from "../../DocServer";
+import { RouteStore } from "../../../server/RouteStore";
+import { action, observable, runInAction } from "mobx";
+import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
+import Measure, { ContentRect } from "react-measure";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons';
+import { Docs, DocumentOptions } from "../../documents/Documents";
+import { EditableView } from "../../views/EditableView";
+
+export default class DirectoryImportBox extends React.Component<FieldViewProps> {
+    private selector = React.createRef<HTMLInputElement>();
+    @observable private top = 0;
+    @observable private left = 0;
+    private dimensions = 50;
+
+    @observable private key = "Key";
+    @observable private value = "Value";
+
+    public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); }
+
+    constructor(props: FieldViewProps) {
+        super(props);
+        library.add(faArrowUp, faTag);
+    }
+
+    updateKey = (newKey: string) => {
+        runInAction(() => this.key = newKey);
+        console.log("KEY ", this.key);
+        return true;
+    }
+
+    updateValue = (newValue: string) => {
+        runInAction(() => this.value = newValue);
+        console.log("VALUE ", this.value);
+        return true;
+    }
+
+    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
+        let promises: Promise<void>[] = [];
+        let docs: Doc[] = [];
+
+        let files = e.target.files;
+        if (!files || files.length === 0) return;
+
+        let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
+
+        for (let i = 0; i < files.length; i++) {
+            let uploaded_file = files.item(i);
+
+            if (!uploaded_file) {
+                continue;
+            }
+
+            let formData = new FormData();
+            formData.append('file', uploaded_file);
+            let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
+            let type = uploaded_file.type;
+
+            let prom = fetch(DocServer.prepend(RouteStore.upload), {
+                method: 'POST',
+                body: formData
+            }).then(async (res: Response) => {
+                (await res.json()).map(action((file: any) => {
+                    let path = DocServer.prepend(file);
+                    console.log(path);
+                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
+                    docPromise.then(doc => doc && docs.push(doc));
+                }));
+            });
+            promises.push(prom);
+        }
+
+        await Promise.all(promises);
+
+        let doc = this.props.Document;
+        let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
+        let parent = this.props.ContainingCollectionView;
+        if (parent) {
+            let importContainer = Docs.StackingDocument(docs, options);
+            Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
+            this.props.removeDocument && this.props.removeDocument(doc);
+        }
+    }
+
+    componentDidMount() {
+        this.selector.current!.setAttribute("directory", "");
+        this.selector.current!.setAttribute("webkitdirectory", "");
+    }
+
+    @action
+    preserveCentering = (rect: ContentRect) => {
+        let bounds = rect.offset!;
+        if (bounds.width === 0 || bounds.height === 0) {
+            return;
+        }
+        let offset = this.dimensions / 2;
+        this.left = bounds.width / 2 - offset;
+        this.top = bounds.height / 2 - offset;
+    }
+
+    render() {
+        let dimensions = 50;
+        let keyValueStyle = { paddingLeft: 5, width: "50%" };
+        return (
+            <Measure offset onResize={this.preserveCentering}>
+                {({ measureRef }) =>
+                    <div ref={measureRef} style={{ width: "100%", height: "100%", pointerEvents: "all" }} >
+                        <input
+                            id={"selector"}
+                            ref={this.selector}
+                            name={"selector"}
+                            onChange={this.handleSelection}
+                            type="file"
+                            style={{
+                                position: "absolute",
+                                display: "none"
+                            }} />
+                        <label htmlFor={"selector"}>
+                            <div style={{
+                                width: dimensions,
+                                height: dimensions,
+                                borderRadius: "50%",
+                                background: "black",
+                                position: "absolute",
+                                left: this.left,
+                                top: this.top
+                            }} />
+                            <div style={{
+                                position: "absolute",
+                                left: this.left + 12.5,
+                                top: this.top + 11
+                            }}>
+                                <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} />
+                            </div>
+                        </label>
+                        <div style={{
+                            position: "absolute",
+                            top: 5,
+                            right: 5,
+                            borderRadius: "50%",
+                            width: 25,
+                            height: 25,
+                            background: "black"
+                        }} />
+                        <div style={{
+                            position: "absolute",
+                            right: 9.5,
+                            top: 11
+                        }}>
+                            <FontAwesomeIcon icon={faTag} color="#FFFFFF" size={"1x"} />
+                        </div>
+                        <div style={{ display: "flex", flexDirection: "row", borderBottom: "1px solid black", paddingBottom: 5 }} >
+                            <div className={"key_container"} style={keyValueStyle}>
+                                <EditableView
+                                    contents={this.key}
+                                    SetValue={this.updateKey}
+                                    GetValue={() => this.key}
+                                    oneLine={true}
+                                />
+                            </div>
+                            <div className={"value_container"} style={keyValueStyle}>
+                                <EditableView
+                                    contents={this.value}
+                                    SetValue={this.updateValue}
+                                    GetValue={() => this.value}
+                                    oneLine={true}
+                                />
+                            </div>
+                        </div>
+                    </div>
+                }
+            </Measure>
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx
deleted file mode 100644
index d664f6487..000000000
--- a/src/client/util/Import & Export/ImageImporter.tsx	
+++ /dev/null
@@ -1,67 +0,0 @@
-import "fs";
-import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
-import { DocServer } from "../../DocServer";
-import { RouteStore } from "../../../server/RouteStore";
-import { action } from "mobx";
-import { Docs } from "../../documents/Documents";
-import { FieldViewProps } from "../../views/nodes/FieldView";
-
-interface ImageImporterProps {
-    addSchema: (imageDocs: Doc[]) => void;
-}
-
-export default class BulkImporter extends React.Component<FieldViewProps> {
-    private selector = React.createRef<HTMLInputElement>();
-
-    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
-        let promises: Promise<void>[] = [];
-        let docs: Doc[] = [];
-
-        let files = e.target.files;
-        if (!files) return;
-
-        for (let i = 0; i < files.length; i++) {
-            let target = files.item(i);
-
-            if (target === null) {
-                continue;
-            }
-
-            let type = target.type;
-            let formData = new FormData();
-            formData.append('file', target);
-            let dropFileName = target ? target.name : "-empty-";
-
-            let prom = fetch(DocServer.prepend(RouteStore.upload), {
-                method: 'POST',
-                body: formData
-            }).then(async (res: Response) => {
-                (await res.json()).map(action((file: any) => {
-                    let path = window.location.origin + file;
-                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
-                    docPromise.then(doc => doc && docs.push(doc));
-                }));
-            });
-            promises.push(prom);
-        }
-
-        await Promise.all(promises);
-
-        let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" });
-    }
-
-    componentDidMount() {
-        this.selector.current!.setAttribute("directory", "true");
-        this.selector.current!.setAttribute("webkitdirectory", "true");
-    }
-
-    render() {
-        return (
-            <div>
-                <input ref={this.selector} name={"selector"} onChange={this.handleSelection} type="file" style={{ position: "absolute" }} />
-            </div>
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx
deleted file mode 100644
index 630911710..000000000
--- a/src/client/util/Import & Export/ImportBox.tsx	
+++ /dev/null
@@ -1,134 +0,0 @@
-import "fs";
-import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
-import { DocServer } from "../../DocServer";
-import { RouteStore } from "../../../server/RouteStore";
-import { action, observable } from "mobx";
-import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
-import Measure, { ContentRect } from "react-measure";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faArrowUp } from '@fortawesome/free-solid-svg-icons';
-import { Docs, DocumentOptions } from "../../documents/Documents";
-
-interface ImageImporterProps {
-    addSchema: (imageDocs: Doc[]) => void;
-}
-
-export default class ImportBox extends React.Component<FieldViewProps> {
-    @observable private top = 0;
-    @observable private left = 0;
-    private dimensions = 50;
-
-    constructor(props: FieldViewProps) {
-        super(props);
-        library.add(faArrowUp);
-    }
-
-    public static LayoutString() { return FieldView.LayoutString(ImportBox); }
-
-    private selector = React.createRef<HTMLInputElement>();
-
-    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
-        let promises: Promise<void>[] = [];
-        let docs: Doc[] = [];
-
-        let files = e.target.files;
-        if (!files || files.length === 0) return;
-
-        let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
-
-        for (let i = 0; i < files.length; i++) {
-            let uploaded_file = files.item(i);
-
-            if (!uploaded_file) {
-                continue;
-            }
-
-            let formData = new FormData();
-            formData.append('file', uploaded_file);
-            let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
-            let type = uploaded_file.type;
-
-            let prom = fetch(DocServer.prepend(RouteStore.upload), {
-                method: 'POST',
-                body: formData
-            }).then(async (res: Response) => {
-                (await res.json()).map(action((file: any) => {
-                    let path = DocServer.prepend(file);
-                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
-                    docPromise.then(doc => doc && docs.push(doc));
-                }));
-            });
-            promises.push(prom);
-        }
-
-        await Promise.all(promises);
-
-        let doc = this.props.Document;
-        let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
-        let parent = this.props.ContainingCollectionView;
-        if (parent) {
-            let importContainer = Docs.StackingDocument(docs, options);
-            Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
-            this.props.removeDocument && this.props.removeDocument(doc);
-        }
-    }
-
-    componentDidMount() {
-        this.selector.current!.setAttribute("directory", "true");
-        this.selector.current!.setAttribute("webkitdirectory", "true");
-    }
-
-    @action
-    preserveCentering = (rect: ContentRect) => {
-        let bounds = rect.offset!;
-        if (bounds.width === 0 || bounds.height === 0) {
-            return;
-        }
-        let offset = this.dimensions / 2;
-        this.left = bounds.width / 2 - offset;
-        this.top = bounds.height / 2 - offset;
-    }
-
-    render() {
-        let dimensions = 50;
-        return (
-            <Measure offset onResize={this.preserveCentering}>
-                {({ measureRef }) =>
-                    <div ref={measureRef} style={{ width: "100%", height: "100%", pointerEvents: "all" }} >
-                        <input
-                            id={"selector"}
-                            ref={this.selector}
-                            name={"selector"}
-                            onChange={this.handleSelection}
-                            type="file"
-                            style={{
-                                position: "absolute",
-                                display: "none"
-                            }} />
-                        <label htmlFor={"selector"}>
-                            <div style={{
-                                width: dimensions,
-                                height: dimensions,
-                                borderRadius: "50%",
-                                background: "black",
-                                position: "absolute",
-                                left: this.left,
-                                top: this.top
-                            }} />
-                            <div style={{
-                                position: "absolute",
-                                left: this.left + 12.5,
-                                top: this.top + 11
-                            }}>
-                                <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} />
-                            </div>
-                        </label>
-                    </div>
-                }
-            </Measure>
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js
index 0f9328872..27605d167 100644
--- a/src/client/util/request-image-size.js
+++ b/src/client/util/request-image-size.js
@@ -21,7 +21,9 @@ module.exports = function requestImageSize(options) {
     if (options && typeof options === 'object') {
         opts = Object.assign(options, opts);
     } else if (options && typeof options === 'string') {
-        opts = Object.assign({ uri: options }, opts);
+        opts = Object.assign({
+            uri: options
+        }, opts);
     } else {
         return Promise.reject(new Error('You should provide an URI string or a "request" options object.'));
     }
@@ -70,4 +72,4 @@ module.exports = function requestImageSize(options) {
 
         req.on('error', err => reject(err));
     });
-};
+};
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e38fd3c8b..3d059b2f3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,7 +38,6 @@ import PDFMenu from './pdf/PDFMenu';
 import { InkTool } from '../../new_fields/InkField';
 import _ from "lodash";
 import KeyManager from './GlobalKeyHandler';
-import BulkImporter from '../util/Import & Export/ImageImporter';
 
 @observer
 export class MainView extends React.Component {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e886ad84a..8cb2e1beb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -182,7 +182,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         .then(result => {
                             let type = result["content-type"];
                             if (type) {
-                                Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }, this.props.addDocument)
+                                Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
                                     .then(doc => doc && this.props.addDocument(doc, false));
                             }
                         });
@@ -204,7 +204,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     }).then(async (res: Response) => {
                         (await res.json()).map(action((file: any) => {
                             let path = window.location.origin + file;
-                            let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName }, this.props.addDocument);
+                            let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
                             docPromise.then(doc => doc && this.props.addDocument(doc));
                         }));
                     });
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e9fec1588..0bfb2954f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,7 +24,7 @@ import { Without, OmitKeys } from "../../../Utils";
 import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { Doc } from "../../../new_fields/Doc";
-import ImportBox from "../../util/Import & Export/ImportBox";
+import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -107,7 +107,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
-            components={{ FormattedTextBox, ImageBox, IconBox, ImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document)}
             jsx={this.finalLayout}
             showWarnings={true}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 06bf65f73..88c122b61 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -213,7 +213,9 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                     }), 0);
                 }
             })
-            .catch((err: any) => console.log(err));
+            .catch((err: any) => {
+                console.log(err);
+            });
     }
 
     render() {
-- 
cgit v1.2.3-70-g09d2


From 12788cbe0586d0349f70b73d1b6f6a481b4bf2cf Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Fri, 5 Jul 2019 17:31:20 -0400
Subject: first pass at implementation of directory import

---
 deploy/assets/loading.gif                          | Bin 0 -> 114208 bytes
 .../util/Import & Export/DirectoryImportBox.scss   |   0
 .../util/Import & Export/DirectoryImportBox.tsx    | 194 +++++++++++++++------
 src/client/util/Import & Export/KeyValue.tsx       |  79 +++++++++
 src/client/views/MainView.tsx                      |   2 +-
 5 files changed, 217 insertions(+), 58 deletions(-)
 create mode 100644 deploy/assets/loading.gif
 delete mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss
 create mode 100644 src/client/util/Import & Export/KeyValue.tsx

(limited to 'src/client/views/MainView.tsx')

diff --git a/deploy/assets/loading.gif b/deploy/assets/loading.gif
new file mode 100644
index 000000000..0652bf021
Binary files /dev/null and b/deploy/assets/loading.gif differ
diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 2d77f6ae6..cbc8c6fdc 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -3,44 +3,46 @@ import React = require("react");
 import { Doc } from "../../../new_fields/Doc";
 import { DocServer } from "../../DocServer";
 import { RouteStore } from "../../../server/RouteStore";
-import { action, observable, runInAction } from "mobx";
+import { action, observable, autorun, runInAction } from "mobx";
 import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
 import Measure, { ContentRect } from "react-measure";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons';
+import { faArrowUp, faTag, faPlus } from '@fortawesome/free-solid-svg-icons';
 import { Docs, DocumentOptions } from "../../documents/Documents";
-import { EditableView } from "../../views/EditableView";
+import { observer } from "mobx-react";
+import KeyValue from "./KeyValue";
+import { Utils } from "../../../Utils";
+import { doesNotReject } from "assert";
+import { remove } from "typescript-collections/dist/lib/arrays";
 
+@observer
 export default class DirectoryImportBox extends React.Component<FieldViewProps> {
     private selector = React.createRef<HTMLInputElement>();
     @observable private top = 0;
     @observable private left = 0;
     private dimensions = 50;
 
-    @observable private key = "Key";
-    @observable private value = "Value";
+    @observable private editingMetadata = false;
+    @observable private metadata_guids: string[] = [];
+    @observable private entries: KeyValue[] = [];
+
+    @observable private quota = 1;
+    @observable private remaining = 1;
+
+    @observable private uploadBegun = false;
 
     public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); }
 
     constructor(props: FieldViewProps) {
         super(props);
-        library.add(faArrowUp, faTag);
-    }
-
-    updateKey = (newKey: string) => {
-        runInAction(() => this.key = newKey);
-        console.log("KEY ", this.key);
-        return true;
-    }
-
-    updateValue = (newValue: string) => {
-        runInAction(() => this.value = newValue);
-        console.log("VALUE ", this.value);
-        return true;
+        library.add(faArrowUp, faTag, faPlus);
     }
 
+    @action
     handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.uploadBegun = true;
+
         let promises: Promise<void>[] = [];
         let docs: Doc[] = [];
 
@@ -49,9 +51,15 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
         let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
 
+        let validated: File[] = [];
         for (let i = 0; i < files.length; i++) {
-            let uploaded_file = files.item(i);
+            let file = files.item(i);
+            file && validated.push(file);
+        }
+
+        this.quota = validated.length;
 
+        for (let uploaded_file of validated) {
             if (!uploaded_file) {
                 continue;
             }
@@ -61,15 +69,18 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
             let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
             let type = uploaded_file.type;
 
+            this.remaining++;
+
             let prom = fetch(DocServer.prepend(RouteStore.upload), {
                 method: 'POST',
                 body: formData
             }).then(async (res: Response) => {
                 (await res.json()).map(action((file: any) => {
                     let path = DocServer.prepend(file);
-                    console.log(path);
                     let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
-                    docPromise.then(doc => doc && docs.push(doc));
+                    docPromise.then(doc => {
+                        doc && docs.push(doc) && runInAction(() => this.remaining--);
+                    });
                 }));
             });
             promises.push(prom);
@@ -77,11 +88,14 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
         await Promise.all(promises);
 
+        docs.forEach(doc => this.entries.forEach(entry => doc[entry.key] = entry.value));
+
         let doc = this.props.Document;
         let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
         let parent = this.props.ContainingCollectionView;
         if (parent) {
             let importContainer = Docs.StackingDocument(docs, options);
+            importContainer.singleColumn = false;
             Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
             this.props.removeDocument && this.props.removeDocument(doc);
         }
@@ -103,9 +117,29 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
         this.top = bounds.height / 2 - offset;
     }
 
+    @action
+    addMetadataEntry = () => {
+        this.metadata_guids.push(Utils.GenerateGuid());
+    }
+
+    @action
+    remove = (entry: KeyValue) => {
+        let index = this.entries.indexOf(entry);
+        let key = entry.key;
+        this.entries.splice(index, 1);
+        this.metadata_guids.splice(this.metadata_guids.indexOf(key), 1);
+    }
+
     render() {
         let dimensions = 50;
-        let keyValueStyle = { paddingLeft: 5, width: "50%" };
+        let guids = this.metadata_guids.map(el => el);
+        let isEditing = this.editingMetadata;
+        let remaining = this.remaining;
+        let quota = this.quota;
+        let percent = `${100 - (remaining / quota * 100)}`;
+        let uploadBegun = this.uploadBegun;
+        percent = percent.split(".")[0];
+        percent = percent.startsWith("100") ? "99" : percent;
         return (
             <Measure offset onResize={this.preserveCentering}>
                 {({ measureRef }) =>
@@ -113,14 +147,20 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
                         <input
                             id={"selector"}
                             ref={this.selector}
-                            name={"selector"}
                             onChange={this.handleSelection}
                             type="file"
                             style={{
                                 position: "absolute",
                                 display: "none"
                             }} />
-                        <label htmlFor={"selector"}>
+                        <label
+                            htmlFor={"selector"}
+                            style={{
+                                opacity: isEditing ? 0 : 1,
+                                pointerEvents: isEditing ? "none" : "all",
+                                transition: "0.4s ease opacity"
+                            }}
+                        >
                             <div style={{
                                 width: dimensions,
                                 height: dimensions,
@@ -132,46 +172,86 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
                             }} />
                             <div style={{
                                 position: "absolute",
-                                left: this.left + 12.5,
-                                top: this.top + 11
+                                left: this.left + 12.6,
+                                top: this.top + 11,
+                                opacity: uploadBegun ? 0 : 1,
+                                transition: "0.4s opacity ease"
                             }}>
                                 <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} />
                             </div>
                         </label>
-                        <div style={{
-                            position: "absolute",
-                            top: 5,
-                            right: 5,
-                            borderRadius: "50%",
-                            width: 25,
-                            height: 25,
-                            background: "black"
-                        }} />
-                        <div style={{
-                            position: "absolute",
-                            right: 9.5,
-                            top: 11
-                        }}>
-                            <FontAwesomeIcon icon={faTag} color="#FFFFFF" size={"1x"} />
-                        </div>
-                        <div style={{ display: "flex", flexDirection: "row", borderBottom: "1px solid black", paddingBottom: 5 }} >
-                            <div className={"key_container"} style={keyValueStyle}>
-                                <EditableView
-                                    contents={this.key}
-                                    SetValue={this.updateKey}
-                                    GetValue={() => this.key}
-                                    oneLine={true}
-                                />
-                            </div>
-                            <div className={"value_container"} style={keyValueStyle}>
-                                <EditableView
-                                    contents={this.value}
-                                    SetValue={this.updateValue}
-                                    GetValue={() => this.value}
-                                    oneLine={true}
+                        <div
+                            style={{
+                                transition: "0.4s opacity ease",
+                                opacity: uploadBegun ? 1 : 0,
+                                pointerEvents: "none",
+                                position: "absolute",
+                                left: 10,
+                                top: this.top + 12.3,
+                                fontSize: 18,
+                                color: "white",
+                                marginLeft: this.left - 1.6
+                            }}>{percent}%</div>
+                        <div
+                            style={{
+                                position: "absolute",
+                                top: 10,
+                                right: 10,
+                                borderRadius: "50%",
+                                width: 25,
+                                height: 25,
+                                background: "black"
+                            }}
+                            onClick={action(() => this.editingMetadata = !this.editingMetadata)}
+                        />
+                        <FontAwesomeIcon
+                            style={{
+                                pointerEvents: "none",
+                                position: "absolute",
+                                right: isEditing ? 16.3 : 14.5,
+                                top: isEditing ? 15.4 : 16
+                            }}
+                            icon={isEditing ? faArrowUp : faTag}
+                            color="#FFFFFF"
+                            size={"1x"}
+                        />
+                        <div
+                            style={{
+                                transition: "0.4s ease opacity",
+                                width: "100%",
+                                height: "100%",
+                                pointerEvents: isEditing ? "all" : "none",
+                                opacity: isEditing ? 1 : 0,
+                                overflowY: "scroll"
+                            }}
+                        >
+                            <div
+                                style={{
+                                    borderRadius: "50%",
+                                    width: 25,
+                                    height: 25,
+                                    marginLeft: 10,
+                                    position: "absolute",
+                                    right: 41,
+                                    top: 10
+                                }}
+                                onClick={this.addMetadataEntry}
+                            >
+                                <FontAwesomeIcon
+                                    style={{
+                                        pointerEvents: "none",
+                                        marginLeft: 6.4,
+                                        marginTop: 5.2
+                                    }}
+                                    icon={faPlus}
+                                    size={"1x"}
                                 />
                             </div>
+                            <p style={{ paddingLeft: 10, paddingTop: 8, paddingBottom: 7 }} >Add metadata to your import...</p>
+                            <hr style={{ margin: "6px 10px 12px 10px" }} />
+                            {guids.map(guid => <KeyValue remove={this.remove} key={guid} ref={(el) => { if (el) this.entries.push(el); }} />)}
                         </div>
+                        {/* <img style={{ width: 30, height: 30 }} src={"./loading.gif"}></img> */}
                     </div>
                 }
             </Measure>
diff --git a/src/client/util/Import & Export/KeyValue.tsx b/src/client/util/Import & Export/KeyValue.tsx
new file mode 100644
index 000000000..15ef25f89
--- /dev/null
+++ b/src/client/util/Import & Export/KeyValue.tsx	
@@ -0,0 +1,79 @@
+import React = require("react");
+import { observer } from "mobx-react";
+import { EditableView } from "../../views/EditableView";
+import { observable, action } from "mobx";
+
+interface KeyValueProps {
+    remove: (self: KeyValue) => void;
+}
+
+@observer
+export default class KeyValue extends React.Component<KeyValueProps> {
+    @observable public key = "Key";
+    @observable public value = "Value";
+
+    @action
+    updateKey = (newKey: string) => {
+        this.key = newKey;
+        return true;
+    }
+
+    @action
+    updateValue = (newValue: string) => {
+        this.value = newValue;
+        return true;
+    }
+
+    render() {
+        let keyValueStyle = { paddingLeft: 10, width: "50%" };
+        let keySpecified = (this.key.length > 0 && this.key !== "Key");
+        return (
+            <div
+                style={{
+                    display: "flex",
+                    flexDirection: "row",
+                    paddingBottom: 5,
+                    paddingRight: 5,
+                    justifyContent: "center",
+                    alignItems: "center",
+                    alignContent: "center"
+                }}
+                onClick={() => this.props.remove(this)}
+            >
+                <input type="checkbox" />
+                <div className={"key_container"} style={keyValueStyle}>
+                    <EditableView
+                        contents={this.key}
+                        SetValue={this.updateKey}
+                        GetValue={() => this.key}
+                        oneLine={true}
+                    />
+                </div>
+                <div
+                    className={"value_container"}
+                    style={{
+                        opacity: keySpecified ? 1 : 0.5,
+                        pointerEvents: keySpecified ? "all" : "none",
+                        ...keyValueStyle
+                    }}>
+                    <EditableView
+                        contents={this.value}
+                        SetValue={this.updateValue}
+                        GetValue={() => this.value}
+                        oneLine={true}
+                    />
+                </div>
+                <div style={{
+                    borderRadius: "50%",
+                    width: 10,
+                    height: 10,
+                    background: "red",
+                    marginLeft: 15,
+                    marginRight: 15
+                }} />
+
+            </div>
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3d059b2f3..60decff25 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -351,7 +351,7 @@ export class MainView extends React.Component {
         let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
         let addTreeNode = action(() => CurrentUserUtils.UserDocument);
         let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
-        let addImportCollectionNode = action(() => Docs.DirectoryImportDocument({ title: "Directory Import", width: 150, height: 150 }));
+        let addImportCollectionNode = action(() => Docs.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
             [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
-- 
cgit v1.2.3-70-g09d2


From 15914700e307a372a1e304955a5244184108a6fa Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 9 Jul 2019 19:52:09 -0400
Subject: logging in now directs you to original target url and restored logout
 button functionality

---
 src/client/views/MainView.tsx                      |  2 +-
 .../authentication/controllers/user_controller.ts  | 22 +++++++++++++---------
 src/server/index.ts                                | 10 +++++++---
 3 files changed, 21 insertions(+), 13 deletions(-)

(limited to 'src/client/views/MainView.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b8fc3f47b..b37ba1cb0 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -413,7 +413,7 @@ export class MainView extends React.Component {
         return [
             this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
             <div className="main-buttonDiv" key="logout" style={{ bottom: '0px', right: '1px', position: 'absolute' }} ref={logoutRef}>
-                <button onClick={() => request.get(DocServer.prepend(RouteStore.logout), emptyFunction)}>Log Out</button></div>
+                <button onClick={() => window.location.assign(DocServer.prepend(RouteStore.logout))}>Log Out</button></div>
         ];
 
     }
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index 1dacdf3fa..ca4fc171c 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -42,10 +42,6 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
     const errors = req.validationErrors();
 
     if (errors) {
-        res.render("signup.pug", {
-            title: "Sign Up",
-            user: req.user,
-        });
         return res.redirect(RouteStore.signup);
     }
 
@@ -66,16 +62,23 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
         user.save((err) => {
             if (err) { return next(err); }
             req.logIn(user, (err) => {
-                if (err) {
-                    return next(err);
-                }
-                res.redirect(RouteStore.home);
+                if (err) { return next(err); }
+                tryRedirectToTarget(req, res);
             });
         });
     });
 
 };
 
+let tryRedirectToTarget = (req: Request, res: Response) => {
+    if (req.session && req.session.target) {
+        res.redirect(req.session.target);
+        req.session.target = undefined;
+    } else {
+        res.redirect(RouteStore.home);
+    }
+};
+
 
 /**
  * GET /login
@@ -83,6 +86,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
  */
 export let getLogin = (req: Request, res: Response) => {
     if (req.user) {
+        req.session!.target = undefined;
         return res.redirect(RouteStore.home);
     }
     res.render("login.pug", {
@@ -115,7 +119,7 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
         }
         req.logIn(user, (err) => {
             if (err) { next(err); return; }
-            res.redirect(RouteStore.home);
+            tryRedirectToTarget(req, res);
         });
     })(req, res, next);
 };
diff --git a/src/server/index.ts b/src/server/index.ts
index c9ec11bfa..5ecbfa41a 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -40,6 +40,7 @@ import { Search } from './Search';
 import { debug } from 'util';
 import _ = require('lodash');
 import { Response } from 'express-serve-static-core';
+import { DocServer } from '../client/DocServer';
 const MongoStore = require('connect-mongo')(session);
 const mongoose = require('mongoose');
 const probe = require("probe-image-size");
@@ -69,7 +70,7 @@ app.use(session({
     secret: "64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc",
     resave: true,
     cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
-    saveUninitialized: true,
+    saveUninitialized: false,
     store: new MongoStore({ url: 'mongodb://localhost:27017/Dash' })
 }));
 
@@ -82,6 +83,7 @@ app.use(passport.initialize());
 app.use(passport.session());
 app.use((req, res, next) => {
     res.locals.user = req.user;
+    // res.locals.target = req.session!.target;
     next();
 });
 
@@ -103,14 +105,16 @@ enum Method {
  */
 function addSecureRoute(method: Method,
     handler: (user: DashUserModel, res: express.Response, req: express.Request) => void,
-    onRejection: (res: express.Response) => any = (res) => res.redirect(RouteStore.logout),
+    onRejection: (res: express.Response, req: express.Request) => any = res => res.redirect(RouteStore.login),
     ...subscribers: string[]
 ) {
     let abstracted = (req: express.Request, res: express.Response) => {
         if (req.user) {
             handler(req.user, res, req);
         } else {
-            onRejection(res);
+            let target = `http://localhost:${port}${req.originalUrl}`;
+            req.session!.target = target;
+            onRejection(res, req);
         }
     };
     subscribers.forEach(route => {
-- 
cgit v1.2.3-70-g09d2

