From 464096297d34bb824ed665aa50dfb58f268c1d12 Mon Sep 17 00:00:00 2001
From: madelinegr <mgriswold99@gmail.com>
Date: Tue, 4 Jun 2019 20:01:59 -0400
Subject: BUttons in Presentation CSSed and grouping up insertion done, removal
 almost done

---
 src/client/views/MainView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a093ffdec..c98e6e8fb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -14,7 +14,7 @@ import { Docs } from '../documents/Documents';
 import { SetupDrag, DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
-import { PresentationView } from './PresentationView';
+import { PresentationView } from './presentationview/PresentationView';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { ContextMenu } from './ContextMenu';
 import { DocumentDecorations } from './DocumentDecorations';
-- 
cgit v1.2.3-70-g09d2


From 9a93306ba382018ebf3c686117bd4301beda61ef Mon Sep 17 00:00:00 2001
From: madelinegr <mgriswold99@gmail.com>
Date: Wed, 12 Jun 2019 15:33:51 -0400
Subject: BUtton status and pres status saved on refresh, only groups left

---
 src/client/views/MainView.tsx                      |  7 ++-
 .../views/presentationview/PresentationElement.tsx | 42 ++++++++++------
 .../views/presentationview/PresentationView.tsx    | 57 ++++++++++++++++------
 3 files changed, 73 insertions(+), 33 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c98e6e8fb..3d772916e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -200,12 +200,15 @@ export class MainView extends React.Component {
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
                 ContainingCollectionView={undefined} />;
-        const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
+        let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
+        console.log("GETTING mainContent()");
+        console.log(castRes instanceof Promise);
+        console.log(castRes);
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
                 <div ref={measureRef} id="mainContent-div">
                     {content}
-                    {pres ? <PresentationView Document={pres} key="presentation" /> : null}
+                    {castRes ? <PresentationView Document={castRes} key="presentation" /> : null}
                 </div>
             }
         </Measure>;
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 7928722f2..9fe69c2e3 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -1,9 +1,9 @@
 import { observer } from "mobx-react";
 import React = require("react");
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
 import { NumCast, BoolCast, StrCast, Cast } from "../../../new_fields/Types";
 import { Id } from "../../../new_fields/FieldSymbols";
-import { observable, action, computed } from "mobx";
+import { observable, action, computed, runInAction } from "mobx";
 import "./PresentationView.scss";
 import { Utils } from "../../../Utils";
 import { library } from '@fortawesome/fontawesome-svg-core';
@@ -64,21 +64,33 @@ export default class PresentationElement extends React.Component<PresentationEle
     get selected() {
         return this.selectedButtons;
     }
-    @action
-    componentDidMount() {
+
+    async componentDidMount() {
         // let castedList = Cast(this.props.presButtonBackUp.selectedButtons, listSpec(Doc), null) as any as List<List<boolean>>;
-        let castedList = DocListCast(this.props.presButtonBackUp.selectedButtonDocs);
-        if (castedList.length !== 0) {
-            // this.selectedButtons = castedList;
-            let selectedButtonOfDoc = Cast(castedList[this.props.index].selectedButtons, listSpec("boolean"), null);
+        let castedList = Cast(this.props.presButtonBackUp.selectedButtonDocs, listSpec(Doc));
+        if (!castedList) {
+            this.props.presButtonBackUp.selectedButtonDocs = castedList = new List<Doc>();
+        }
+        // let castedList = DocListCast(this.props.presButtonBackUp.selectedButtonDocs);
+
+        console.log("Mounted!!");
+        console.log("CastedList Len: ", castedList.length);
+        console.log("Index of doc: ", this.props.index);
+
+        if (castedList.length <= this.props.index) {
+            console.log("Entered here by index : ", this.props.index);
+            castedList.push(new Doc());
+        } else {
+            let curDoc: Doc = await castedList[this.props.index];
+            let selectedButtonOfDoc = Cast(curDoc.selectedButtons, listSpec("boolean"), null);
+            console.log("Entered First Place");
             if (selectedButtonOfDoc !== undefined) {
-                this.selectedButtons = selectedButtonOfDoc;
+                runInAction(() => this.selectedButtons = selectedButtonOfDoc);
+                console.log("Entered Second Place");
             }
-            console.log("Entered!!");
-
         }
-        console.log("Mounted!!");
-        //this.props.presButtonBackUp.elIndex = this.props.index;
+
+
     }
 
     /**
@@ -195,9 +207,9 @@ export default class PresentationElement extends React.Component<PresentationEle
     }
 
     @action
-    autoSaveButtonChange = (index: buttonIndex) => {
+    autoSaveButtonChange = async (index: buttonIndex) => {
         // let castedList = Cast(this.props.presButtonBackUp.selectedButtons, listSpec(Doc), null) as any as List<List<boolean>>;
-        let castedList = DocListCast(this.props.presButtonBackUp.selectedButtonDocs);
+        let castedList = (await DocListCastAsync(this.props.presButtonBackUp.selectedButtonDocs))!;
         castedList[this.props.index].selectedButtons = new List(this.selectedButtons);
 
         //this.props.mainDocument.presButtonBackUp = this.props.presButtonBackUp;
diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx
index 8b0b919fd..b872181ae 100644
--- a/src/client/views/presentationview/PresentationView.tsx
+++ b/src/client/views/presentationview/PresentationView.tsx
@@ -78,7 +78,7 @@ class PresentationViewList extends React.Component<PresListProps> {
         const children = DocListCast(this.props.Document.data);
         this.initializeGroupIds(children);
         this.props.setChildrenDocs(children);
-        this.initializeSelectedButtonDocs(children);
+        //this.initializeSelectedButtonDocs(children);
         return (
 
             <div className="presentationView-listCont">
@@ -105,23 +105,47 @@ export class PresentationView extends React.Component<PresViewProps>  {
     @observable presGroupBackUp: Doc = new Doc();
     @observable presButtonBackUp: Doc = new Doc();
 
-    @action
+
+
     componentDidMount() {
-        let castedGroupBackUp: Doc = Cast(this.props.Document.presGroupBackUp, Doc, new Doc());
-        let castedButtonBackUp: Doc = Cast(this.props.Document.presButtonBackUp, Doc, new Doc());
-        // if (castedButtonBackUp === undefined) {
-        //     this.props.Document.presButtonBackUp = new Doc();
-        // } else {
-        this.presButtonBackUp = castedButtonBackUp;
-        // }
-        // if (castedGroupBackUp === undefined) {
-        //     this.props.Document.presGroupBackUp = new Doc();
-        // } else {
-        this.presGroupBackUp = castedGroupBackUp;
-        // 
-
-        // }
+        // let newDoc = new Doc();
+        // let newDoc2 = new Doc();
+        let castedGroupBackUp = Cast(this.props.Document.presGroupBackUp, Doc);
+        let castedButtonBackUp = Cast(this.props.Document.presButtonBackUp, Doc);
+        if (castedGroupBackUp instanceof Promise) {
+            castedGroupBackUp.then(doc => {
+                let toAssign = doc ? doc : new Doc();
+                this.props.Document.presGroupBackUp = toAssign;
+                runInAction(() => this.presGroupBackUp = toAssign);
+            });
+        } else {
+            runInAction(() => {
+                let toAssign = new Doc();
+                this.presGroupBackUp = toAssign;
+                this.props.Document.presGroupBackUp = toAssign;
+
+            });
+
+        }
+
+        if (castedButtonBackUp instanceof Promise) {
+            castedButtonBackUp.then(doc => {
+                let toAssign = doc ? doc : new Doc();
+                this.props.Document.presButtonBackUp = toAssign;
+                runInAction(() => this.presButtonBackUp = toAssign);
+            });
+
+        } else {
+            runInAction(() => {
+                let toAssign = new Doc();
+                this.presButtonBackUp = toAssign;
+                this.props.Document.presButtonBackUp = toAssign;
+            });
+
+        }
 
+        let presStatusBackUp = BoolCast(this.props.Document.presStatus, null);
+        runInAction(() => this.presStatus = presStatusBackUp);
     }
 
     //observable means render is re-called every time variable is changed
@@ -362,6 +386,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
             this.startPresentation(0);
             this.gotoDocument(0);
         }
+        this.props.Document.presStatus = this.presStatus;
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From 218c8b6476621ef0ffe151014f77bb1d506705a3 Mon Sep 17 00:00:00 2001
From: madelinegr <mgriswold99@gmail.com>
Date: Fri, 14 Jun 2019 18:24:01 -0400
Subject: Zooming added to presentation, and done

---
 src/client/util/DocumentManager.ts                 | 31 +++++++-
 src/client/util/TooltipTextMenu.tsx                |  2 +-
 src/client/views/MainOverlayTextBox.tsx            |  2 +-
 src/client/views/MainView.tsx                      |  5 +-
 src/client/views/SearchItem.tsx                    |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  6 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +-
 .../views/collections/CollectionStackingView.tsx   |  2 +
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 31 +++++---
 src/client/views/nodes/DocumentView.tsx            | 10 ++-
 src/client/views/nodes/FieldView.tsx               |  5 +-
 .../views/presentationview/PresentationElement.tsx | 31 +++++++-
 .../views/presentationview/PresentationView.tsx    | 93 +++++++++++++++++++---
 14 files changed, 184 insertions(+), 42 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 65c4b9e4b..a613625cb 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,4 +1,4 @@
-import { computed, observable } from 'mobx';
+import { computed, observable, action } from 'mobx';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
 import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types';
@@ -115,7 +115,7 @@ export class DocumentManager {
     }
 
     @undoBatch
-    public jumpToDocument = async (docDelegate: Doc, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number): Promise<void> => {
+    public jumpToDocument = async (docDelegate: Doc, willZoom: boolean, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number): Promise<void> => {
         let doc = Doc.GetProto(docDelegate);
         const contextDoc = await Cast(doc.annotationOn, Doc);
         if (contextDoc) {
@@ -128,7 +128,7 @@ export class DocumentManager {
         if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) {
             docView.props.Document.libraryBrush = true;
             if (linkPage !== undefined) docView.props.Document.curPage = linkPage;
-            docView.props.focus(docView.props.Document);
+            docView.props.focus(docView.props.Document, willZoom);
         } else {
             if (!contextDoc) {
                 const actualDoc = Doc.MakeAlias(docDelegate);
@@ -140,11 +140,34 @@ export class DocumentManager {
                 docDelegate.libraryBrush = true;
                 if (!forceDockFunc && (contextView = DocumentManager.Instance.getDocumentView(contextDoc))) {
                     contextDoc.panTransformType = "Ease";
-                    contextView.props.focus(contextDoc);
+                    contextView.props.focus(contextDoc, willZoom);
                 } else {
                     (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc);
                 }
             }
         }
     }
+
+    @action
+    zoomIntoScale = (docDelegate: Doc, scale: number) => {
+        let doc = Doc.GetProto(docDelegate);
+
+        let docView: DocumentView | null;
+        docView = DocumentManager.Instance.getDocumentView(doc);
+        if (docView) {
+            docView.props.zoomToScale(scale);
+        }
+    }
+
+    getScaleOfDocView = (docDelegate: Doc) => {
+        let doc = Doc.GetProto(docDelegate);
+
+        let docView: DocumentView | null;
+        docView = DocumentManager.Instance.getDocumentView(doc);
+        if (docView) {
+            return docView.props.getScale();
+        } else {
+            return 1;
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index f517f757a..34785446b 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -192,7 +192,7 @@ export class TooltipTextMenu {
                         DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
                             if (f instanceof Doc) {
                                 if (DocumentManager.Instance.getDocumentView(f)) {
-                                    DocumentManager.Instance.getDocumentView(f)!.props.focus(f);
+                                    DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false);
                                 }
                                 else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f);
                             }
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 24327b995..d1224febe 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -102,6 +102,6 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
                 </div>
             </ div>;
         }
-        else return (null); Z
+        else return (null);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3d772916e..f78879efe 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -199,7 +199,10 @@ export class MainView extends React.Component {
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
-                ContainingCollectionView={undefined} />;
+                ContainingCollectionView={undefined}
+                zoomToScale={emptyFunction}
+                getScale={returnOne}
+            />;
         let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
         console.log("GETTING mainContent()");
         console.log(castRes instanceof Promise);
diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx
index 01c7316d6..101d893de 100644
--- a/src/client/views/SearchItem.tsx
+++ b/src/client/views/SearchItem.tsx
@@ -24,7 +24,7 @@ library.add(faFilm);
 export class SearchItem extends React.Component<SearchProps> {
 
     onClick = () => {
-        DocumentManager.Instance.jumpToDocument(this.props.doc);
+        DocumentManager.Instance.jumpToDocument(this.props.doc, false);
     }
 
     //needs help
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index dcc1bd95d..81f574a6c 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -9,7 +9,7 @@ import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc";
 import { FieldId } from "../../../new_fields/RefField";
 import { listSpec } from "../../../new_fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnTrue, Utils } from "../../../Utils";
+import { emptyFunction, returnTrue, Utils, returnOne } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { DragLinksAsDocuments, DragManager } from "../../util/DragManager";
 import { Transform } from '../../util/Transform';
@@ -483,7 +483,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     whenActiveChanged={emptyFunction}
                     focus={emptyFunction}
                     addDocTab={this.addDocTab}
-                    ContainingCollectionView={undefined} />
+                    ContainingCollectionView={undefined}
+                    zoomToScale={emptyFunction}
+                    getScale={returnOne} />
             </div >);
     }
 
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 11d71d023..715faafd0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -7,7 +7,7 @@ import { observer } from "mobx-react";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
 import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss';
 import "react-table/react-table.css";
-import { emptyFunction, returnFalse, returnZero } from "../../../Utils";
+import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils";
 import { SetupDrag } from "../../util/DragManager";
 import { CompileScript } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
@@ -451,6 +451,8 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         bringToFront={emptyFunction}
                         addDocTab={this.props.addDocTab}
                         collapseToPoint={this.collapseToPoint}
+                        zoomToScale={emptyFunction}
+                        getScale={returnOne}
                     />
                 </div>)}
             {input}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index da7ea50c6..e1453c658 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -146,6 +146,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     bringToFront={emptyFunction}
                     whenActiveChanged={this.props.whenActiveChanged}
                     collapseToPoint={this.collapseToPoint}
+                    zoomToScale={emptyFunction}
+                    getScale={returnOne}
                 />
             </div>);
         })
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 2814c0502..c80bd8fce 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -147,7 +147,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" });
                 ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" });
                 if (DocumentManager.Instance.getDocumentViews(this.props.document).length) {
-                    ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) });
+                    ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document, false)) });
                 }
                 ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
             } else {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d26cb0c2..419d95b5f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -244,7 +244,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         doc.zIndex = docs.length + 1;
     }
 
-    focusDocument = (doc: Doc) => {
+    focusDocument = (doc: Doc, willZoom: boolean) => {
         const panX = this.Document.panX;
         const panY = this.Document.panY;
         const id = this.Document[Id];
@@ -271,9 +271,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         newState.initializers[id] = { panX: newPanX, panY: newPanY };
         HistoryUtil.pushState(newState);
         this.setPan(newPanX, newPanY);
+
         this.props.Document.panTransformType = "Ease";
         this.props.focus(this.props.Document);
-        //this.setScaleToZoom(doc);
+        if (willZoom) {
+            this.setScaleToZoom(doc);
+        }
 
     }
 
@@ -281,21 +284,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         let p = this.props;
         let PanelHeight = p.PanelHeight();
         let panelWidth = p.PanelWidth();
-        // let heightDif: number = PanelHeight - NumCast(doc.height);
-        // let widthDif: number = panelWidth - NumCast(doc.width);
+
         let docHeight = NumCast(doc.height);
         let docWidth = NumCast(doc.width);
-        let targetHeight = 0.8 * PanelHeight;
-        let targetWidth = 0.8 * panelWidth;
+        let targetHeight = 0.5 * PanelHeight;
+        let targetWidth = 0.5 * panelWidth;
 
         let maxScaleX: number = targetWidth / docWidth;
         let maxScaleY: number = targetHeight / docHeight;
-        // let maxScaleX: number = NumCast(doc.width) / (panelWidth - 10);
-        // let maxScaleY: number = NumCast(doc.height) / (PanelHeight - 10);
         let maxApplicableScale = Math.min(maxScaleX, maxScaleY);
         this.Document.scale = maxApplicableScale;
     }
 
+    zoomToScale = (scale: number) => {
+        this.Document.scale = scale;
+    }
+
+    getScale = () => {
+        if (this.Document.scale) {
+            return this.Document.scale;
+        }
+        return 1;
+    }
+
 
     getDocumentViewProps(document: Doc): DocumentViewProps {
         return {
@@ -315,6 +326,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             whenActiveChanged: this.props.whenActiveChanged,
             bringToFront: this.bringToFront,
             addDocTab: this.props.addDocTab,
+            zoomToScale: this.zoomToScale,
+            getScale: this.getScale
         };
     }
 
@@ -346,7 +359,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private childViews = () => [
         <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
         ...this.views
-    ];
+    ]
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         const easing = () => this.props.Document.panTransformType === "Ease";
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 449fa76a7..fb7657b68 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -69,13 +69,15 @@ export interface DocumentViewProps {
     ContentScaling: () => number;
     PanelWidth: () => number;
     PanelHeight: () => number;
-    focus: (doc: Doc) => void;
+    focus: (doc: Doc, willZoom: boolean) => void;
     selectOnLoad: boolean;
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     bringToFront: (doc: Doc) => void;
     addDocTab: (doc: Doc, where: string) => void;
     collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
+    zoomToScale: (scale: number) => void;
+    getScale: () => number;
 }
 
 const schema = createSchema({
@@ -302,8 +304,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this._lastTap = Date.now();
     }
 
-    deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }
-    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") };
+    deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); };
+    fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); };
     makeBtnClicked = (): void => {
         let doc = Doc.GetProto(this.props.Document);
         doc.isButton = !BoolCast(doc.isButton, false);
@@ -422,7 +424,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight");
             }, icon: "search"
         });
-        cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" });
+        cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
         cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" });
         cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
         cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 7b642b299..3047d55a3 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -103,7 +103,10 @@ export class FieldView extends React.Component<FieldViewProps> {
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
                     whenActiveChanged={this.props.whenActiveChanged}
-                    bringToFront={emptyFunction} />
+                    bringToFront={emptyFunction}
+                    zoomToScale={emptyFunction}
+                    getScale={returnOne}
+                />
             );
         }
         else if (field instanceof List) {
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index c58570798..00dc07921 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -12,6 +12,7 @@ import { faFile as fileSolid, faLocationArrow, faArrowUp, faSearch } from '@fort
 import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons';
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
+import { DocumentManager } from "../../util/DocumentManager";
 
 
 
@@ -27,7 +28,7 @@ interface PresentationElementProps {
     document: Doc;
     index: number;
     deleteDocument(index: number): void;
-    gotoDocument(index: number): void;
+    gotoDocument(index: number, fromDoc: number): Promise<void>;
     allListElements: Doc[];
     groupMappings: Map<String, Doc[]>;
     presStatus: boolean;
@@ -300,6 +301,9 @@ export default class PresentationElement extends React.Component<PresentationEle
             this.selectedButtons[buttonIndex.Navigate] = false;
 
         } else {
+            if (this.selectedButtons[buttonIndex.Show]) {
+                this.selectedButtons[buttonIndex.Show] = false;
+            }
             this.selectedButtons[buttonIndex.Navigate] = true;
         }
 
@@ -307,6 +311,27 @@ export default class PresentationElement extends React.Component<PresentationEle
 
     }
 
+    /**
+    * The function that is called on click to turn zoom option of docs on/off.
+    */
+    @action
+    onZoomDocumentClick = (e: React.MouseEvent) => {
+        e.stopPropagation();
+        if (this.selectedButtons[buttonIndex.Show]) {
+            this.selectedButtons[buttonIndex.Show] = false;
+            this.props.document.viewScale = 1;
+
+        } else {
+            if (this.selectedButtons[buttonIndex.Navigate]) {
+                this.selectedButtons[buttonIndex.Navigate] = false;
+            }
+            this.selectedButtons[buttonIndex.Show] = true;
+        }
+
+        this.autoSaveButtonChange(buttonIndex.Show);
+
+    }
+
 
     render() {
         let p = this.props;
@@ -330,13 +355,13 @@ export default class PresentationElement extends React.Component<PresentationEle
                     outlineStyle: "dashed",
                     outlineWidth: BoolCast(p.document.libraryBrush, false) || BoolCast(p.document.protoBrush, false) ? `1px` : "0px",
                 }}
-                onClick={e => { p.gotoDocument(p.index); e.stopPropagation(); }}>
+                onClick={e => { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}>
                 <strong className="presentationView-name">
                     {`${p.index + 1}. ${title}`}
                 </strong>
                 <button className="presentation-icon" onClick={e => { this.props.deleteDocument(p.index); e.stopPropagation(); }}>X</button>
                 <br></br>
-                <button title="Zoom" className={this.selectedButtons[buttonIndex.Show] ? "presentation-interaction-selected" : "presentation-interaction"}><FontAwesomeIcon icon={"search"} /></button>
+                <button title="Zoom" className={this.selectedButtons[buttonIndex.Show] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onZoomDocumentClick}><FontAwesomeIcon icon={"search"} /></button>
                 <button title="Navigate" className={this.selectedButtons[buttonIndex.Navigate] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onNavigateDocumentClick}><FontAwesomeIcon icon={"location-arrow"} /></button>
                 <button title="Hide Document Till Presented" className={this.selectedButtons[buttonIndex.HideTillPressed] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onHideDocumentUntilPressClick}><FontAwesomeIcon icon={fileSolid} /></button>
                 <button title="Fade Document After Presented" className={this.selectedButtons[buttonIndex.FadeAfter] ? "presentation-interaction-selected" : "presentation-interaction"} onClick={this.onFadeDocumentAfterPresentedClick}><FontAwesomeIcon icon={fileRegular} color={"gray"} /></button>
diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx
index d3365725b..6cf5fad7e 100644
--- a/src/client/views/presentationview/PresentationView.tsx
+++ b/src/client/views/presentationview/PresentationView.tsx
@@ -26,7 +26,7 @@ export interface PresViewProps {
 
 interface PresListProps extends PresViewProps {
     deleteDocument(index: number): void;
-    gotoDocument(index: number): void;
+    gotoDocument(index: number, fromDoc: number): Promise<void>;
     groupMappings: Map<String, Doc[]>;
     presElementsMappings: Map<Doc, PresentationElement>;
     setChildrenDocs: (docList: Doc[]) => void;
@@ -71,9 +71,24 @@ class PresentationViewList extends React.Component<PresListProps> {
         });
     }
 
+    /**
+     * Initially every document starts with a viewScale 1, which means
+     * that they will be displayed in a canvas with scale 1.
+     */
+    @action
+    initializeScaleViews = (docList: Doc[]) => {
+        docList.forEach((doc: Doc) => {
+            let curScale = NumCast(doc.viewScale, null);
+            if (curScale === undefined) {
+                doc.viewScale = 1;
+            }
+        });
+    }
+
     render() {
         const children = DocListCast(this.props.Document.data);
         this.initializeGroupIds(children);
+        this.initializeScaleViews(children);
         this.props.setChildrenDocs(children);
         return (
 
@@ -102,7 +117,6 @@ export class PresentationView extends React.Component<PresViewProps>  {
     @observable presButtonBackUp: Doc = new Doc();
 
 
-
     componentDidMount() {
         //getting both backUp documents
         let castedGroupBackUp = Cast(this.props.Document.presGroupBackUp, Doc);
@@ -195,7 +209,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
             if (nextSelected === current) nextSelected = current + 1;
         }
 
-        this.gotoDocument(nextSelected);
+        this.gotoDocument(nextSelected, current);
 
     }
     back = async () => {
@@ -209,6 +223,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
         //asking for its presentation id.
         let curPresId = StrCast(docAtCurrent.presentId);
         let prevSelected = current - 1;
+        let zoomOut: boolean = false;
 
         //checking if this presentation id is mapped to a group, if so chosing the first element in group
         if (this.groupMappings.has(curPresId)) {
@@ -217,11 +232,29 @@ export class PresentationView extends React.Component<PresViewProps>  {
             //end of grup so go beyond
             if (prevSelected === current) prevSelected = current - 1;
 
+            //checking if any of the group members had used zooming in
+            currentsArray.forEach((doc: Doc) => {
+                if (this.presElementsMappings.get(doc)!.selected[buttonIndex.Show]) {
+                    zoomOut = true;
+                    return;
+                }
+            });
 
         }
 
+        // if a group set that flag to zero or a single element
+        //If so making sure to zoom out, which goes back to state before zooming action
+        if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.selected[buttonIndex.Show]) {
+            let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null);
+            let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]);
+            if (prevScale !== undefined) {
+                if (prevScale !== curScale) {
+                    DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale);
+                }
+            }
+        }
+        this.gotoDocument(prevSelected, current);
 
-        this.gotoDocument(prevSelected);
     }
 
     /**
@@ -285,9 +318,10 @@ export class PresentationView extends React.Component<PresViewProps>  {
      * has the option open and last in the group. If not in the group, and it has
      * te option open, navigates to that element.
      */
-    navigateToElement = (curDoc: Doc) => {
+    navigateToElement = async (curDoc: Doc, fromDoc: number) => {
         let docToJump: Doc = curDoc;
         let curDocPresId = StrCast(curDoc.presentId, null);
+        let willZoom: boolean = false;
 
         //checking if in group
         if (curDocPresId !== undefined) {
@@ -297,6 +331,11 @@ export class PresentationView extends React.Component<PresViewProps>  {
                     let selectedButtons: boolean[] = this.presElementsMappings.get(doc)!.selected;
                     if (selectedButtons[buttonIndex.Navigate]) {
                         docToJump = doc;
+                        willZoom = false;
+                    }
+                    if (selectedButtons[buttonIndex.Show]) {
+                        docToJump = doc;
+                        willZoom = true;
                     }
                 });
             }
@@ -305,13 +344,35 @@ export class PresentationView extends React.Component<PresViewProps>  {
         //docToJump stayed same meaning, it was not in the group or was the last element in the group
         if (docToJump === curDoc) {
             //checking if curDoc has navigation open
-            if (this.presElementsMappings.get(curDoc)!.selected[buttonIndex.Navigate]) {
-                DocumentManager.Instance.jumpToDocument(curDoc);
-            } else {
-                return;
+            let curDocButtons = this.presElementsMappings.get(curDoc)!.selected;
+            if (curDocButtons[buttonIndex.Navigate]) {
+                DocumentManager.Instance.jumpToDocument(curDoc, false);
+            } else if (curDocButtons[buttonIndex.Show]) {
+                let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
+                //awaiting jump so that new scale can be found, since jumping is async
+                await DocumentManager.Instance.jumpToDocument(curDoc, true);
+                let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
+                curDoc.viewScale = newScale;
+
+                //saving the scale user was on before zooming in
+                if (curScale !== 1) {
+                    this.childrenDocs[fromDoc].viewScale = curScale;
+                }
+
             }
+            return;
+        }
+        let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
+
+        //awaiting jump so that new scale can be found, since jumping is async
+        await DocumentManager.Instance.jumpToDocument(docToJump, willZoom);
+        let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
+        curDoc.viewScale = newScale;
+        //saving the scale that user was on
+        if (curScale !== 1) {
+            this.childrenDocs[fromDoc].viewScale = curScale;
         }
-        DocumentManager.Instance.jumpToDocument(docToJump);
+
     }
 
     /**
@@ -340,7 +401,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
         }
     }
     @action
-    public gotoDocument = async (index: number) => {
+    public gotoDocument = async (index: number, fromDoc: number) => {
         const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc)));
         if (!list) {
             return;
@@ -357,7 +418,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
 
         const doc = await list[index];
         if (this.presStatus) {
-            this.navigateToElement(doc);
+            this.navigateToElement(doc, fromDoc);
             this.hideIfNotPresented(index);
             this.showAfterPresented(index);
         }
@@ -407,7 +468,8 @@ export class PresentationView extends React.Component<PresViewProps>  {
         } else {
             this.presStatus = true;
             this.startPresentation(0);
-            this.gotoDocument(0);
+            const current = NumCast(this.props.Document.selectedDoc);
+            this.gotoDocument(0, current);
         }
         this.props.Document.presStatus = this.presStatus;
     }
@@ -423,6 +485,11 @@ export class PresentationView extends React.Component<PresViewProps>  {
             doc.opacity = 1;
         });
         this.props.Document.selectedDoc = 0;
+        if (this.childrenDocs.length === 0) {
+            return;
+        }
+        DocumentManager.Instance.zoomIntoScale(this.childrenDocs[0], 1);
+        this.childrenDocs[0].viewScale = 1;
 
     }
 
-- 
cgit v1.2.3-70-g09d2


From ae86fd1237873e7aa3144e7a0c6890ae60507d66 Mon Sep 17 00:00:00 2001
From: madelinegr <mgriswold99@gmail.com>
Date: Mon, 17 Jun 2019 20:03:03 -0400
Subject: Presentation Selection UI a bit, with bugs

---
 src/client/views/MainView.tsx                      |   7 +-
 .../views/presentationview/PresentationView.tsx    | 117 ++++++++++++++-------
 2 files changed, 85 insertions(+), 39 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f78879efe..7d2aa3199 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -32,6 +32,7 @@ import { listSpec } from '../../new_fields/Schema';
 import { Id } from '../../new_fields/FieldSymbols';
 import { HistoryUtil } from '../util/History';
 import { CollectionBaseView } from './collections/CollectionBaseView';
+import { List } from '../../new_fields/List';
 
 
 @observer
@@ -46,7 +47,7 @@ export class MainView extends React.Component {
     private set mainContainer(doc: Opt<Doc>) {
         if (doc) {
             if (!("presentationView" in doc)) {
-                doc.presentationView = Docs.TreeDocument([], { title: "Presentation" });
+                doc.presentationView = new List<Doc>([Docs.TreeDocument([], { title: "Presentation" })]);
             }
             CurrentUserUtils.UserDocument.activeWorkspace = doc;
         }
@@ -203,7 +204,7 @@ export class MainView extends React.Component {
                 zoomToScale={emptyFunction}
                 getScale={returnOne}
             />;
-        let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined;
+        let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, listSpec(Doc))) : undefined;
         console.log("GETTING mainContent()");
         console.log(castRes instanceof Promise);
         console.log(castRes);
@@ -211,7 +212,7 @@ export class MainView extends React.Component {
             {({ measureRef }) =>
                 <div ref={measureRef} id="mainContent-div">
                     {content}
-                    {castRes ? <PresentationView Document={castRes} key="presentation" /> : null}
+                    {castRes ? <PresentationView Documents={castRes} key="presentation" /> : null}
                 </div>
             }
         </Measure>;
diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx
index 6cf5fad7e..ff5c6df9c 100644
--- a/src/client/views/presentationview/PresentationView.tsx
+++ b/src/client/views/presentationview/PresentationView.tsx
@@ -13,18 +13,22 @@ import { CurrentUserUtils } from "../../../server/authentication/models/current_
 import PresentationElement, { buttonIndex } from "./PresentationElement";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faArrowRight, faArrowLeft, faPlay, faStop } from '@fortawesome/free-solid-svg-icons';
+import { faArrowRight, faArrowLeft, faPlay, faStop, faPlus, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { Docs } from "../../documents/Documents";
 
 library.add(faArrowLeft);
 library.add(faArrowRight);
 library.add(faPlay);
 library.add(faStop);
+library.add(faPlus);
+library.add(faTimes);
 
 export interface PresViewProps {
-    Document: Doc;
+    Documents: List<Doc>;
 }
 
-interface PresListProps extends PresViewProps {
+interface PresListProps {
+    mainDocument: Doc;
     deleteDocument(index: number): void;
     gotoDocument(index: number, fromDoc: number): Promise<void>;
     groupMappings: Map<String, Doc[]>;
@@ -85,15 +89,17 @@ class PresentationViewList extends React.Component<PresListProps> {
         });
     }
 
+
+
     render() {
-        const children = DocListCast(this.props.Document.data);
+        const children = DocListCast(this.props.mainDocument.data);
         this.initializeGroupIds(children);
         this.initializeScaleViews(children);
         this.props.setChildrenDocs(children);
         return (
 
             <div className="presentationView-listCont">
-                {children.map((doc: Doc, index: number) => <PresentationElement ref={(e) => { if (e) { this.props.presElementsMappings.set(doc, e); } }} key={index} mainDocument={this.props.Document} document={doc} index={index} deleteDocument={this.props.deleteDocument} gotoDocument={this.props.gotoDocument} groupMappings={this.props.groupMappings} allListElements={children} presStatus={this.props.presStatus} presButtonBackUp={this.props.presButtonBackUp} presGroupBackUp={this.props.presGroupBackUp} />)}
+                {children.map((doc: Doc, index: number) => <PresentationElement ref={(e) => { if (e) { this.props.presElementsMappings.set(doc, e); } }} key={index} mainDocument={this.props.mainDocument} document={doc} index={index} deleteDocument={this.props.deleteDocument} gotoDocument={this.props.gotoDocument} groupMappings={this.props.groupMappings} allListElements={children} presStatus={this.props.presStatus} presButtonBackUp={this.props.presButtonBackUp} presGroupBackUp={this.props.presGroupBackUp} />)}
             </div>
         );
     }
@@ -115,17 +121,32 @@ export class PresentationView extends React.Component<PresViewProps>  {
     //back-up so that presentation stays the way it's when refreshed
     @observable presGroupBackUp: Doc = new Doc();
     @observable presButtonBackUp: Doc = new Doc();
+    @observable curPresentation: Doc = new Doc();
+    @observable presentationsMapping: Map<String, Doc> = new Map();
+    @observable selectedPresentation: HTMLSelectElement = new HTMLSelectElement();
+
+
+    //initilize class variables
+    constructor(props: PresViewProps) {
+        super(props);
+        PresentationView.Instance = this;
+    }
 
 
+    async componentWillMount() {
+        let docAtZero = await this.props.Documents[0];
+        runInAction(() => this.curPresentation = docAtZero);
+    }
+
     componentDidMount() {
         //getting both backUp documents
-        let castedGroupBackUp = Cast(this.props.Document.presGroupBackUp, Doc);
-        let castedButtonBackUp = Cast(this.props.Document.presButtonBackUp, Doc);
+        let castedGroupBackUp = Cast(this.curPresentation.presGroupBackUp, Doc);
+        let castedButtonBackUp = Cast(this.curPresentation.presButtonBackUp, Doc);
         //if instantiated before 
         if (castedGroupBackUp instanceof Promise) {
             castedGroupBackUp.then(doc => {
                 let toAssign = doc ? doc : new Doc();
-                this.props.Document.presGroupBackUp = toAssign;
+                this.curPresentation.presGroupBackUp = toAssign;
                 runInAction(() => this.presGroupBackUp = toAssign);
                 if (doc) {
                     if (toAssign[Id] === doc[Id]) {
@@ -138,7 +159,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
             runInAction(() => {
                 let toAssign = new Doc();
                 this.presGroupBackUp = toAssign;
-                this.props.Document.presGroupBackUp = toAssign;
+                this.curPresentation.presGroupBackUp = toAssign;
 
             });
 
@@ -148,7 +169,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
         if (castedButtonBackUp instanceof Promise) {
             castedButtonBackUp.then(doc => {
                 let toAssign = doc ? doc : new Doc();
-                this.props.Document.presButtonBackUp = toAssign;
+                this.curPresentation.presButtonBackUp = toAssign;
                 runInAction(() => this.presButtonBackUp = toAssign);
             });
 
@@ -157,14 +178,14 @@ export class PresentationView extends React.Component<PresViewProps>  {
             runInAction(() => {
                 let toAssign = new Doc();
                 this.presButtonBackUp = toAssign;
-                this.props.Document.presButtonBackUp = toAssign;
+                this.curPresentation.presButtonBackUp = toAssign;
             });
 
         }
 
 
         //storing the presentation status,ie. whether it was stopped or playing
-        let presStatusBackUp = BoolCast(this.props.Document.presStatus, null);
+        let presStatusBackUp = BoolCast(this.curPresentation.presStatus, null);
         runInAction(() => this.presStatus = presStatusBackUp);
     }
 
@@ -188,9 +209,9 @@ export class PresentationView extends React.Component<PresViewProps>  {
     //observable means render is re-called every time variable is changed
     @observable
     collapsed: boolean = false;
-    closePresentation = action(() => this.props.Document.width = 0);
+    closePresentation = action(() => this.curPresentation.width = 0);
     next = async () => {
-        const current = NumCast(this.props.Document.selectedDoc);
+        const current = NumCast(this.curPresentation.selectedDoc);
         //asking to get document at current index
         let docAtCurrentNext = await this.getDocAtIndex(current + 1);
         if (docAtCurrentNext === undefined) {
@@ -213,7 +234,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
 
     }
     back = async () => {
-        const current = NumCast(this.props.Document.selectedDoc);
+        const current = NumCast(this.curPresentation.selectedDoc);
         //requesting for the doc at current index
         let docAtCurrent = await this.getDocAtIndex(current);
         if (docAtCurrent === undefined) {
@@ -379,7 +400,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
      * Async function that supposedly return the doc that is located at given index.
      */
     getDocAtIndex = async (index: number) => {
-        const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc)));
+        const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
         if (!list) {
             return undefined;
         }
@@ -387,7 +408,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
             return undefined;
         }
 
-        this.props.Document.selectedDoc = index;
+        this.curPresentation.selectedDoc = index;
         //awaiting async call to finish to get Doc instance
         const doc = await list[index];
         return doc;
@@ -395,21 +416,21 @@ export class PresentationView extends React.Component<PresViewProps>  {
 
     @action
     public RemoveDoc = (index: number) => {
-        const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc)));
+        const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
         if (value) {
             value.splice(index, 1);
         }
     }
     @action
     public gotoDocument = async (index: number, fromDoc: number) => {
-        const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc)));
+        const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc)));
         if (!list) {
             return;
         }
         if (index < 0 || index >= list.length) {
             return;
         }
-        this.props.Document.selectedDoc = index;
+        this.curPresentation.selectedDoc = index;
 
         if (!this.presStatus) {
             this.presStatus = true;
@@ -425,11 +446,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
 
     }
 
-    //initilize class variables
-    constructor(props: PresViewProps) {
-        super(props);
-        PresentationView.Instance = this;
-    }
+
 
     /**
      * Adds a document to the presentation view
@@ -437,14 +454,14 @@ export class PresentationView extends React.Component<PresViewProps>  {
     @action
     public PinDoc(doc: Doc) {
         //add this new doc to props.Document
-        const data = Cast(this.props.Document.data, listSpec(Doc));
+        const data = Cast(this.curPresentation.data, listSpec(Doc));
         if (data) {
             data.push(doc);
         } else {
-            this.props.Document.data = new List([doc]);
+            this.curPresentation.data = new List([doc]);
         }
 
-        this.props.Document.width = 300;
+        this.curPresentation.width = 300;
     }
 
     @action
@@ -468,10 +485,10 @@ export class PresentationView extends React.Component<PresViewProps>  {
         } else {
             this.presStatus = true;
             this.startPresentation(0);
-            const current = NumCast(this.props.Document.selectedDoc);
+            const current = NumCast(this.curPresentation.selectedDoc);
             this.gotoDocument(0, current);
         }
-        this.props.Document.presStatus = this.presStatus;
+        this.curPresentation.presStatus = this.presStatus;
     }
 
     @action
@@ -484,7 +501,7 @@ export class PresentationView extends React.Component<PresViewProps>  {
             //doc.presentId = Utils.GenerateGuid();
             doc.opacity = 1;
         });
-        this.props.Document.selectedDoc = 0;
+        this.curPresentation.selectedDoc = 0;
         if (this.childrenDocs.length === 0) {
             return;
         }
@@ -518,23 +535,51 @@ export class PresentationView extends React.Component<PresViewProps>  {
 
     }
 
+    @action
+    addNewPresentation = () => {
+        let title = "Presentation " + (this.props.Documents.length + 1);
+        let newPresentationDoc = Docs.TreeDocument([], { title: title });
+        this.props.Documents.push(newPresentationDoc);
+        this.curPresentation = newPresentationDoc;
+    }
+
+    @action
+    getSelectedPresentation = (e: React.ChangeEvent<HTMLSelectElement>) => {
+        this.curPresentation = this.presentationsMapping.get(e.target.value)!;
+    }
+
+
     render() {
-        let titleStr = StrCast(this.props.Document.title);
-        let width = NumCast(this.props.Document.width);
+        let titleStr = StrCast(this.curPresentation.title);
+        let width = NumCast(this.curPresentation.width);
+        let presentationList = DocListCast(this.props.Documents);
+
+
+        console.log("width: ", width);
+        console.log("title : ", titleStr);
 
         //TODO: next and back should be icons
         return (
             <div className="presentationView-cont" style={{ width: width, overflow: "hidden" }}>
                 <div className="presentationView-heading">
-                    <div className="presentationView-title">{titleStr}</div>
-                    <button className='presentation-icon' onClick={this.closePresentation}>X</button>
+                    {/* <div className="presentationView-title">{titleStr}</div> */}
+                    <select className="presentationView-title" onChange={this.getSelectedPresentation} ref={(e) => this.selectedPresentation = e!}>
+                        {presentationList.map((doc: Doc, index: number) => {
+                            let newGuid = Utils.GenerateGuid();
+                            this.presentationsMapping.set(newGuid, doc);
+                            return <option key={index} value={newGuid}>{StrCast(doc.title)}</option>;
+                        })}
+                    </select>
+                    <button title="Close Presentation" className='presentation-icon' onClick={this.closePresentation}><FontAwesomeIcon icon={"times"} /></button>
+                    <button title="Add Presentation" className="presentation-icon" style={{ marginRight: 10 }} onClick={this.addNewPresentation}><FontAwesomeIcon icon={"plus"} /></button>
+
                 </div>
                 <div className="presentation-buttons">
                     <button title="Back" className="presentation-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
                     {this.renderPlayPauseButton()}
                     <button title="Next" className="presentation-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
                 </div>
-                <PresentationViewList Document={this.props.Document} deleteDocument={this.RemoveDoc} gotoDocument={this.gotoDocument} groupMappings={this.groupMappings} presElementsMappings={this.presElementsMappings} setChildrenDocs={this.setChildrenDocs} presStatus={this.presStatus} presButtonBackUp={this.presButtonBackUp} presGroupBackUp={this.presGroupBackUp} />
+                <PresentationViewList mainDocument={this.curPresentation} deleteDocument={this.RemoveDoc} gotoDocument={this.gotoDocument} groupMappings={this.groupMappings} presElementsMappings={this.presElementsMappings} setChildrenDocs={this.setChildrenDocs} presStatus={this.presStatus} presButtonBackUp={this.presButtonBackUp} presGroupBackUp={this.presGroupBackUp} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 41cf1e8536964764f18ab752140e484e36cbe464 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Tue, 25 Jun 2019 17:09:36 -0400
Subject: links can save

---
 src/client/documents/Documents.ts                  |  58 +---
 src/client/util/DocumentManager.ts                 |  14 +-
 src/client/util/DragManager.ts                     |  24 +-
 src/client/util/LinkManager.ts                     | 239 +++++++++++----
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/MainView.tsx                      |   7 +
 .../views/collections/CollectionDockingView.tsx    |   4 +-
 .../views/collections/CollectionTreeView.tsx       |  18 +-
 .../CollectionFreeFormLinksView.tsx                |   7 +-
 src/client/views/nodes/DocumentView.tsx            |   8 +-
 src/client/views/nodes/LinkButtonBox.scss          |  34 +--
 src/client/views/nodes/LinkButtonBox.tsx           | 126 ++++----
 src/client/views/nodes/LinkEditor.tsx              | 323 +++++++++------------
 src/client/views/nodes/LinkMenu.tsx                |   2 +-
 src/client/views/nodes/LinkMenuGroup.tsx           |   8 +-
 src/client/views/nodes/LinkMenuItem.tsx            |   6 +-
 src/new_fields/Doc.ts                              |   9 +-
 src/new_fields/LinkButtonField.ts                  |  58 ++--
 18 files changed, 503 insertions(+), 444 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 64032e096..fbd96fb66 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -35,8 +35,8 @@ import { DateField } from "../../new_fields/DateField";
 import { UndoManager } from "../util/UndoManager";
 import { RouteStore } from "../../server/RouteStore";
 import { LinkManager } from "../util/LinkManager";
-import { LinkButtonBox } from "../views/nodes/LinkButtonBox";
-import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField";
+// import { LinkButtonBox } from "../views/nodes/LinkButtonBox";
+// import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField";
 import { DocumentManager } from "../util/DocumentManager";
 import { Id } from "../../new_fields/FieldSymbols";
 var requestImageSize = require('request-image-size');
@@ -100,6 +100,7 @@ export namespace DocUtils {
             let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 });
             let linkDocProto = Doc.GetProto(linkDoc);
 
+            linkDocProto.context = targetContext;
             linkDocProto.title = title; //=== "" ? source.title + " to " + target.title : title;
             linkDocProto.linkDescription = description;
             linkDocProto.linkTags = tags;
@@ -111,36 +112,7 @@ export namespace DocUtils {
             linkDocProto.anchor2Page = target.curPage;
             linkDocProto.anchor2Groups = new List<Doc>([]);
 
-            linkDocProto.context = targetContext;
-
-            let sourceViews = DocumentManager.Instance.getDocumentViews(source);
-            let targetViews = DocumentManager.Instance.getDocumentViews(target);
-            sourceViews.forEach(sv => {
-                targetViews.forEach(tv => {
-
-                    // TODO: do only for when diff contexts
-                    let proxy1 = Docs.LinkButtonDocument(
-                        { sourceViewId: StrCast(sv.props.Document[Id]), targetViewId: StrCast(tv.props.Document[Id]) },
-                        { width: 200, height: 100, borderRounding: 0 });
-                    let proxy1Proto = Doc.GetProto(proxy1);
-                    proxy1Proto.sourceViewId = StrCast(sv.props.Document[Id]);
-                    proxy1Proto.targetViewId = StrCast(tv.props.Document[Id]);
-                    proxy1Proto.isLinkButton = true;
-
-                    let proxy2 = Docs.LinkButtonDocument(
-                        { sourceViewId: StrCast(tv.props.Document[Id]), targetViewId: StrCast(sv.props.Document[Id]) },
-                        { width: 200, height: 100, borderRounding: 0 });
-                    let proxy2Proto = Doc.GetProto(proxy2);
-                    proxy2Proto.sourceViewId = StrCast(tv.props.Document[Id]);
-                    proxy2Proto.targetViewId = StrCast(sv.props.Document[Id]);
-                    proxy2Proto.isLinkButton = true;
-
-                    LinkManager.Instance.linkProxies.push(proxy1);
-                    LinkManager.Instance.linkProxies.push(proxy2);
-                });
-            });
-
-            LinkManager.Instance.allLinks.push(linkDoc);
+            LinkManager.Instance.addLink(linkDoc);
 
             return linkDoc;
         }, "make link");
@@ -160,7 +132,7 @@ export namespace Docs {
     let audioProto: Doc;
     let pdfProto: Doc;
     let iconProto: Doc;
-    let linkProto: Doc;
+    // let linkProto: Doc;
     const textProtoId = "textProto";
     const histoProtoId = "histoProto";
     const pdfProtoId = "pdfProto";
@@ -171,7 +143,7 @@ export namespace Docs {
     const videoProtoId = "videoProto";
     const audioProtoId = "audioProto";
     const iconProtoId = "iconProto";
-    const linkProtoId = "linkProto";
+    // const linkProtoId = "linkProto";
 
     export function initProtos(): Promise<void> {
         return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => {
@@ -185,7 +157,7 @@ export namespace Docs {
             audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype();
             pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype();
             iconProto = fields[iconProtoId] as Doc || CreateIconPrototype();
-            linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype();
+            // linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype();
         });
     }
 
@@ -218,11 +190,11 @@ export namespace Docs {
             { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) });
         return iconProto;
     }
-    function CreateLinkPrototype(): Doc {
-        let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(),
-            { x: 0, y: 0, width: 300 });
-        return linkProto;
-    }
+    // function CreateLinkPrototype(): Doc {
+    //     let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(),
+    //         { x: 0, y: 0, width: 300 });
+    //     return linkProto;
+    // }
     function CreateTextPrototype(): Doc {
         let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(),
             { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb" });
@@ -309,9 +281,9 @@ export namespace Docs {
     export function IconDocument(icon: string, options: DocumentOptions = {}) {
         return CreateInstance(iconProto, new IconField(icon), options);
     }
-    export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) {
-        return CreateInstance(linkProto, new LinkButtonField(data), options);
-    }
+    // export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) {
+    //     return CreateInstance(linkProto, new LinkButtonField(data), options);
+    // }
     export function PdfDocument(url: string, options: DocumentOptions = {}) {
         return CreateInstance(pdfProto, new PdfField(new URL(url)), options);
     }
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 89e6183d6..767abe63f 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -85,12 +85,11 @@ export class DocumentManager {
 
     @computed
     public get LinkedDocumentViews() {
-        console.log("link");
-        return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => {
-            let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document);
+        let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => {
+            let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
             pairs.push(...linksList.reduce((pairs, link) => {
                 if (link) {
-                    let linkToDoc = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document);
+                    let linkToDoc = LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
                     DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
                         pairs.push({ a: dv, b: docView1, l: link });
                     });
@@ -100,6 +99,13 @@ export class DocumentManager {
             // }
             return pairs;
         }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
+
+        // console.log("LINKED DOCUMENT VIEWS");
+        // pairs.forEach(p => {
+        //     console.log(StrCast(p.a.Document.title), p.a.props.Document[Id], StrCast(p.b.Document.title), p.b.props.Document[Id]);
+        // });
+
+        return pairs;
     }
 
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index f4c8adc8e..1aacf2c53 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -46,7 +46,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
 }
 
 export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) {
-    let draggeddoc = LinkManager.Instance.findOppositeAnchor(linkDoc, sourceDoc);
+    let draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc);
 
     let moddrag = await Cast(draggeddoc.annotationOn, Doc);
     let dragData = new DragManager.DocumentDragData(moddrag ? [moddrag] : [draggeddoc]);
@@ -66,10 +66,10 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n
     // TODO: if not in same context then don't drag
 
     if (srcTarg) {
-        let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg);
+        let linkDocs = LinkManager.Instance.getAllRelatedLinks(srcTarg);
         if (linkDocs) {
             draggedDocs = linkDocs.map(link => {
-                return LinkManager.Instance.findOppositeAnchor(link, sourceDoc);
+                return LinkManager.Instance.getOppositeAnchor(link, sourceDoc);
             });
         }
     }
@@ -236,10 +236,16 @@ export namespace DragManager {
                         if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) {
                             return d;
                         } else {
-                            return Doc.MakeAlias(d);
+                            // return d;
+                            let r = Doc.MakeAlias(d);
+                            // DocUtils.MakeLink(sourceDoc, r);
+                            return r;
                         }
                     } else {
-                        return Doc.MakeAlias(d);
+                        // return d;
+                        let r = Doc.MakeAlias(d);
+                        // DocUtils.MakeLink(sourceDoc, r);
+                        return r;
                     }
                     // return (dv && dv.props.ContainingCollectionView !== SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) || !dv ?
                     //     Doc.MakeAlias(d) : d;
@@ -282,10 +288,10 @@ export namespace DragManager {
         StartDrag([ele], dragData, downX, downY, options);
     }
 
-    export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
-        runInAction(() => StartDragFunctions.map(func => func()));
-        StartDrag([ele], dragData, downX, downY, options);
-    }
+    // export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
+    //     runInAction(() => StartDragFunctions.map(func => func()));
+    //     StartDrag([ele], dragData, downX, downY, options);
+    // }
 
     export let AbortDrag: () => void = emptyFunction;
 
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 745255f31..82c3a9acd 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,9 +1,10 @@
 import { observable, action } from "mobx";
-import { StrCast, Cast } from "../../new_fields/Types";
+import { StrCast, Cast, FieldValue } from "../../new_fields/Types";
 import { Doc, DocListCast } from "../../new_fields/Doc";
 import { listSpec } from "../../new_fields/Schema";
 import { List } from "../../new_fields/List";
 import { Id } from "../../new_fields/FieldSymbols";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 
 
 /* 
@@ -23,6 +24,11 @@ import { Id } from "../../new_fields/FieldSymbols";
  * - user defined kvps 
  */
 export class LinkManager {
+    // static Instance: LinkManager;
+    // private constructor() {
+    //     LinkManager.Instance = this;
+    // }
+
     private static _instance: LinkManager;
     public static get Instance(): LinkManager {
         return this._instance || (this._instance = new this());
@@ -30,25 +36,138 @@ export class LinkManager {
     private constructor() {
     }
 
-    @observable public allLinks: Array<Doc> = []; // list of link docs
-    @observable public groupMetadataKeys: Map<string, Array<string>> = new Map();
-    // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it hodls
-    @observable public linkProxies: Array<Doc> = []; // list of linkbutton docs - used to visualize link when an anchors are not in the same context
+    public get LinkManagerDoc(): Doc | undefined {
+        return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc));
+    }
+    // @observable public allLinks: Array<Doc> = []; //List<Doc> = new List<Doc>([]); // list of link docs
+    // @observable public groupMetadataKeys: Map<string, Array<string>> = new Map();
+    // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it holds
+
+    public getAllLinks(): Doc[] {
+        return LinkManager.Instance.LinkManagerDoc ? LinkManager.Instance.LinkManagerDoc.allLinks ? DocListCast(LinkManager.Instance.LinkManagerDoc.allLinks) : [] : [];
+    }
+
+    public addLink(linkDoc: Doc): boolean {
+        let linkList = LinkManager.Instance.getAllLinks();
+        linkList.push(linkDoc);
+        console.log("link man doc", LinkManager.Instance.LinkManagerDoc);
+        if (LinkManager.Instance.LinkManagerDoc) {
+            LinkManager.Instance.LinkManagerDoc.allLinks = new List<Doc>(linkList);
+            return true;
+        }
+        return false;
+    }
+
+    public deleteLink(linkDoc: Doc): boolean {
+        let linkList = LinkManager.Instance.getAllLinks();
+        let index = LinkManager.Instance.getAllLinks().indexOf(linkDoc);
+        if (index > -1) {
+            linkList.splice(index, 1);
+            if (LinkManager.Instance.LinkManagerDoc) {
+                LinkManager.Instance.LinkManagerDoc.allLinks = new List<Doc>(linkList);
+                return true;
+            }
+        }
+        return false;
+    }
 
     // finds all links that contain the given anchor
-    public findAllRelatedLinks(anchor: Doc): Array<Doc> {
-        return LinkManager.Instance.allLinks.filter(link => {
+    public getAllRelatedLinks(anchor: Doc): Doc[] {//List<Doc> {
+        let related = LinkManager.Instance.getAllLinks().filter(link => {
             let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc));
             let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc));
-            // let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]);
-            // let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]);
-            return protomatch1 || protomatch2;// || idmatch1 || idmatch2;
+            return protomatch1 || protomatch2;
         });
+        return related;
+    }
+
+    public addGroupType(groupType: string): boolean {
+        if (LinkManager.Instance.LinkManagerDoc) {
+            LinkManager.Instance.LinkManagerDoc[groupType] = new List<string>([]);
+            let groupTypes = LinkManager.Instance.getAllGroupTypes();
+            groupTypes.push(groupType);
+            LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List<string>(groupTypes);
+            return true;
+        }
+        return false;
+    }
+
+    // removes all group docs from all links with the given group type
+    public deleteGroupType(groupType: string): boolean {
+        if (LinkManager.Instance.LinkManagerDoc) {
+            if (LinkManager.Instance.LinkManagerDoc[groupType]) {
+                LinkManager.Instance.LinkManagerDoc[groupType] = undefined;
+                LinkManager.Instance.getAllLinks().forEach(linkDoc => {
+                    LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType);
+                    LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType);
+                });
+            }
+            return true;
+        } else return false;
+    }
+
+    public getAllGroupTypes(): string[] {
+        if (LinkManager.Instance.LinkManagerDoc) {
+            if (LinkManager.Instance.LinkManagerDoc.allGroupTypes) {
+                return Cast(LinkManager.Instance.LinkManagerDoc.allGroupTypes, listSpec("string"), []);
+            } else {
+                LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List<string>([]);
+                return [];
+            }
+        }
+        return [];
+    }
+
+    // gets the groups associates with an anchor in a link
+    public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array<Doc> {
+        if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
+            return DocListCast(linkDoc.anchor1Groups);
+        } else {
+            return DocListCast(linkDoc.anchor2Groups);
+        }
+    }
+
+    // sets the groups of the given anchor in the given link
+    public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) {
+        if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
+            linkDoc.anchor1Groups = new List<Doc>(groups);
+        } else {
+            linkDoc.anchor2Groups = new List<Doc>(groups);
+        }
+    }
+
+    public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) {
+        let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
+        let index = groups.findIndex(gDoc => {
+            return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase();
+        });
+        if (index > -1 && replace) {
+            groups[index] = groupDoc;
+        }
+        if (index === -1) {
+            groups.push(groupDoc);
+        }
+        LinkManager.Instance.setAnchorGroups(linkDoc, anchor, groups);
+    }
+
+    // removes group doc of given group type only from given anchor on given link
+    public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) {
+        let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
+        let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase());
+        LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups);
     }
 
+    // public doesAnchorHaveGroup(linkDoc: Doc, anchor: Doc, groupDoc: Doc): boolean {
+    //     let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
+    //     let index = groups.findIndex(gDoc => {
+    //         return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase();
+    //     });
+    //     return index > -1;
+    // }
+
     // returns map of group type to anchor's links in that group type
-    public findRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
-        let related = this.findAllRelatedLinks(anchor);
+    public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
+        let related = this.getAllRelatedLinks(anchor);
         let anchorGroups = new Map<string, Array<Doc>>();
         related.forEach(link => {
             let groups = LinkManager.Instance.getAnchorGroups(link, anchor);
@@ -73,10 +192,41 @@ export class LinkManager {
         return anchorGroups;
     }
 
+    // public addMetadataKeyToGroup(groupType: string, key: string): boolean {
+    //     if (LinkManager.Instance.LinkManagerDoc) {
+    //         if (LinkManager.Instance.LinkManagerDoc[groupType]) {
+    //             let keyList = LinkManager.Instance.findMetadataKeysInGroup(groupType);
+    //             keyList.push(key);
+    //             LinkManager.Instance.LinkManagerDoc[groupType] = new List<string>(keyList);
+    //             return true;
+    //         }
+    //         return false;
+    //     }
+    //     return false;
+    // }
+
+    public getMetadataKeysInGroup(groupType: string): string[] {
+        if (LinkManager.Instance.LinkManagerDoc) {
+            return LinkManager.Instance.LinkManagerDoc[groupType] ? Cast(LinkManager.Instance.LinkManagerDoc[groupType], listSpec("string"), []) : [];
+        }
+        return [];
+    }
+
+    public setMetadataKeysForGroup(groupType: string, keys: string[]): boolean {
+        if (LinkManager.Instance.LinkManagerDoc) {
+            // if (LinkManager.Instance.LinkManagerDoc[groupType]) {
+            LinkManager.Instance.LinkManagerDoc[groupType] = new List<string>(keys);
+            return true;
+            // }
+            // return false;
+        }
+        return false;
+    }
+
     // returns a list of all metadata docs associated with the given group type
-    public findAllMetadataDocsInGroup(groupType: string): Array<Doc> {
+    public getAllMetadataDocsInGroup(groupType: string): Array<Doc> {
         let md: Doc[] = [];
-        let allLinks = LinkManager.Instance.allLinks;
+        let allLinks = LinkManager.Instance.getAllLinks();
         allLinks.forEach(linkDoc => {
             let anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc));
             let anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc));
@@ -86,27 +236,9 @@ export class LinkManager {
         return md;
     }
 
-    // removes all group docs from all links with the given group type
-    public deleteGroup(groupType: string): void {
-        let deleted = LinkManager.Instance.groupMetadataKeys.delete(groupType);
-        if (deleted) {
-            LinkManager.Instance.allLinks.forEach(linkDoc => {
-                LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType);
-                LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType);
-            });
-        }
-    }
-
-    // removes group doc of given group type only from given anchor on given link
-    public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) {
-        let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
-        let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase());
-        LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups);
-    }
-
     // checks if a link with the given anchors exists
-    public doesLinkExist(anchor1: Doc, anchor2: Doc) {
-        let allLinks = LinkManager.Instance.allLinks;
+    public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean {
+        let allLinks = LinkManager.Instance.getAllLinks();
         let index = allLinks.findIndex(linkDoc => {
             return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor2)) ||
                 (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor1));
@@ -115,7 +247,7 @@ export class LinkManager {
     }
 
     // finds the opposite anchor of a given anchor in a link
-    public findOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc {
+    public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc {
         if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
             return Cast(linkDoc.anchor2, Doc, new Doc);
         } else {
@@ -123,34 +255,17 @@ export class LinkManager {
         }
     }
 
-    // gets the groups associates with an anchor in a link
-    public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array<Doc> {
-        if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
-            return DocListCast(linkDoc.anchor1Groups);
-        } else {
-            return DocListCast(linkDoc.anchor2Groups);
-        }
-    }
 
-    // sets the groups of the given anchor in the given link
-    public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) {
-        if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
-            linkDoc.anchor1Groups = new List<Doc>(groups);
-        } else {
-            linkDoc.anchor2Groups = new List<Doc>(groups);
-        }
-    }
-
-    @action
-    public addLinkProxy(proxy: Doc) {
-        LinkManager.Instance.linkProxies.push(proxy);
-    }
+    // @action
+    // public addLinkProxy(proxy: Doc) {
+    //     LinkManager.Instance.linkProxies.push(proxy);
+    // }
 
-    public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined {
-        let index = LinkManager.Instance.linkProxies.findIndex(p => {
-            return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId;
-        });
-        return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined;
-    }
+    // public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined {
+    //     let index = LinkManager.Instance.linkProxies.findIndex(p => {
+    //         return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId;
+    //     });
+    //     return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined;
+    // }
 
 }
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 926273633..3a2752d7e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -573,7 +573,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         let linkButton = null;
         if (SelectionManager.SelectedDocuments().length > 0) {
             let selFirst = SelectionManager.SelectedDocuments()[0];
-            let linkCount = LinkManager.Instance.findAllRelatedLinks(selFirst.props.Document).length;
+            let linkCount = LinkManager.Instance.getAllRelatedLinks(selFirst.props.Document).length;
             linkButton = (<Flyout
                 anchorPoint={anchorPoints.RIGHT_TOP}
                 content={<LinkMenu docView={selFirst}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 51630c29b..d90e9d23e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -35,6 +35,8 @@ import { HistoryUtil } from '../util/History';
 import { CollectionBaseView } from './collections/CollectionBaseView';
 import PDFMenu from './pdf/PDFMenu';
 import { InkTool } from '../../new_fields/InkField';
+import { LinkManager } from '../util/LinkManager';
+import { List } from '../../new_fields/List';
 
 
 @observer
@@ -146,6 +148,11 @@ export class MainView extends React.Component {
             let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` });
             var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
             let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id);
+            if (!CurrentUserUtils.UserDocument.linkManagerDoc) {
+                let linkManagerDoc = new Doc();
+                linkManagerDoc.allLinks = new List<Doc>([]);
+                CurrentUserUtils.UserDocument.linkManagerDoc = linkManagerDoc;
+            }
             list.push(mainDoc);
             // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
             setTimeout(() => {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c82027da5..4140f8029 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -354,9 +354,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                     tab.reactComponents = [upDiv];
                     tab.element.append(upDiv);
                     counter.DashDocId = tab.contentItem.config.props.documentId;
-                    tab.reactionDisposer = reaction(() => [LinkManager.Instance.findAllRelatedLinks(doc), doc.title],
+                    tab.reactionDisposer = reaction(() => [LinkManager.Instance.getAllRelatedLinks(doc), doc.title],
                         () => {
-                            counter.innerHTML = LinkManager.Instance.findAllRelatedLinks(doc).length;
+                            counter.innerHTML = LinkManager.Instance.getAllRelatedLinks(doc).length;
                             tab.titleElement[0].textContent = doc.title;
                         }, { fireImmediately: true });
                     tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 0b922b3c4..7bc3ad124 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -73,7 +73,7 @@ class TreeView extends React.Component<TreeViewProps> {
     @undoBatch delete = () => this.props.deleteDoc(this.props.document);
     @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight");
 
-    onPointerDown = (e: React.PointerEvent) => e.stopPropagation()
+    onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.active() && (this.props.document.libraryBrush = true);
         if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
@@ -115,7 +115,7 @@ class TreeView extends React.Component<TreeViewProps> {
         return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
     }
     @action
-    indent = () => this.props.addDocument(this.props.document) && this.delete();
+    indent = () => this.props.addDocument(this.props.document) && this.delete()
 
     renderBullet() {
         let docList = Cast(this.props.document["data"], listSpec(Doc));
@@ -167,7 +167,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 keyList.push(key);
             }
         });
-        if (LinkManager.Instance.findAllRelatedLinks(this.props.document).length > 0) keyList.push("links");
+        if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links");
         if (keyList.indexOf("data") !== -1) {
             keyList.splice(keyList.indexOf("data"), 1);
         }
@@ -281,9 +281,9 @@ class TreeView extends React.Component<TreeViewProps> {
         let ele: JSX.Element[] = [];
         let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey);
         let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before);
-        let groups = LinkManager.Instance.findRelatedGroupedLinks(this.props.document);
+        let groups = LinkManager.Instance.getRelatedGroupedLinks(this.props.document);
         groups.forEach((groupLinkDocs, groupType) => {
-            let destLinks = groupLinkDocs.map(d => LinkManager.Instance.findOppositeAnchor(d, this.props.document));
+            let destLinks = groupLinkDocs.map(d => LinkManager.Instance.getOppositeAnchor(d, this.props.document));
             ele.push(
                 <div key={"treeviewlink-" + groupType + "subtitle"}>
                     <div className="collectionTreeView-subtitle">{groupType}:</div>
@@ -325,7 +325,7 @@ class TreeView extends React.Component<TreeViewProps> {
                         addDocTab={this.props.addDocTab}
                         setPreviewScript={emptyFunction}>
                     </CollectionSchemaPreview>
-                </div>
+                </div>;
             }
         }
         return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onContextMenu={this.onWorkspaceContextMenu}>
@@ -364,14 +364,14 @@ class TreeView extends React.Component<TreeViewProps> {
                     TreeView.AddDocToList(docList[i - 1], fieldKey, child);
                     remove(child);
                 }
-            }
+            };
             let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
                 return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false);
-            }
+            };
             let rowHeight = () => {
                 let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0);
                 return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym]();
-            }
+            };
             return <TreeView
                 document={child}
                 treeViewId={treeViewId}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 76e089f08..bb8e8a5c2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -98,17 +98,17 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
         //     console.log("CONNECTION", StrCast(d.a.props.Document.title), StrCast(d.b.props.Document.title));
         // });
 
-        console.log("CONNECTIONS");
+        // console.log("CONNECTIONS");
 
         let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
             let srcViews = this.documentAnchors(connection.a);
             let targetViews = this.documentAnchors(connection.b);
 
-            console.log(srcViews.length, targetViews.length);
+            // console.log(srcViews.length, targetViews.length);
             let possiblePairs: { a: Doc, b: Doc, }[] = [];
             srcViews.map(sv => {
                 targetViews.map(tv => {
-                    console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id));
+                    // console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id));
                     possiblePairs.push({ a: sv.props.Document, b: tv.props.Document });
                 });
             });
@@ -142,7 +142,6 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
     }
 
     render() {
-        console.log("\n");
         return (
             <div className="collectionfreeformlinksview-container">
                 <svg className="collectionfreeformlinksview-svgCanvas">
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1fc2cf770..7b185336b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -285,7 +285,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
                 let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
                 let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
-                let linkedDocs = LinkManager.Instance.findAllRelatedLinks(this.props.Document);
+                let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
                 let expandedDocs: Doc[] = [];
                 expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
                 expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
@@ -536,11 +536,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; };
     onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; };
-    onDragOver = (e: React.DragEvent): void => {
-        this.props.Document.libraryBrush = true;
-        console.log("dragOver");
-    };
-    onDragLeave = (e: React.DragEvent): void => { this.props.Document.libraryBrush = false; };
 
     isSelected = () => SelectionManager.IsSelected(this);
     @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
@@ -585,7 +580,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     // display: display ? "block" : "none"
                 }}
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-                onDragOver={this.onDragOver} onDragLeave={this.onDragLeave}
                 onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
             >
                 {this.contents}
diff --git a/src/client/views/nodes/LinkButtonBox.scss b/src/client/views/nodes/LinkButtonBox.scss
index 24bfd2c9f..6be2dcf60 100644
--- a/src/client/views/nodes/LinkButtonBox.scss
+++ b/src/client/views/nodes/LinkButtonBox.scss
@@ -1,18 +1,18 @@
-.linkBox-cont {
-    width: 200px;
-    height: 100px;
-    background-color: black;
-    text-align: center;
-    color: white;
-    padding: 10px;
-    border-radius: 5px;
-    position: relative;
+// .linkBox-cont {
+//     width: 200px;
+//     height: 100px;
+//     background-color: black;
+//     text-align: center;
+//     color: white;
+//     padding: 10px;
+//     border-radius: 5px;
+//     position: relative;
 
-    .linkBox-cont-wrapper {
-        width: calc(100% - 20px);
-        position: absolute;
-        left: 50%;
-        top: 50%;
-        transform: translate(-50%, -50%);
-    }
-}
\ No newline at end of file
+//     .linkBox-cont-wrapper {
+//         width: calc(100% - 20px);
+//         position: absolute;
+//         left: 50%;
+//         top: 50%;
+//         transform: translate(-50%, -50%);
+//     }
+// }
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkButtonBox.tsx b/src/client/views/nodes/LinkButtonBox.tsx
index 8a7c1ed8b..440847ead 100644
--- a/src/client/views/nodes/LinkButtonBox.tsx
+++ b/src/client/views/nodes/LinkButtonBox.tsx
@@ -1,63 +1,63 @@
-import React = require("react");
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { FieldView, FieldViewProps } from './FieldView';
-import "./LinkButtonBox.scss";
-import { DocumentView } from "./DocumentView";
-import { Doc } from "../../../new_fields/Doc";
-import { LinkButtonField } from "../../../new_fields/LinkButtonField";
-import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { DocumentManager } from "../../util/DocumentManager";
-import { Id } from "../../../new_fields/FieldSymbols";
-
-library.add(faCaretUp);
-library.add(faObjectGroup);
-library.add(faStickyNote);
-library.add(faFilePdf);
-library.add(faFilm);
-
-@observer
-export class LinkButtonBox extends React.Component<FieldViewProps> {
-    public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); }
-
-    followLink = (): void => {
-        console.log("follow link???");
-        let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" }));
-        let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId);
-        if (targetView && targetView.props.ContainingCollectionView) {
-            CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document);
-        }
-    }
-
-    render() {
-
-        let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" }));
-        let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId);
-
-        let text = "Could not find link";
-        if (targetView) {
-            let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : "";
-            text = "Link to " + StrCast(targetView.props.Document.title) + context;
-        }
-
-        let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false));
-        let display = activeDvs.reduce((found, dv) => {
-            let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]);
-            let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]);
-            let match = matchSv || matchTv;
-            return match || found;
-        }, false);
-
-        return (
-            <div className="linkBox-cont" style={{ display: display ? "block" : "none" }}>
-                <div className="linkBox-cont-wrapper">
-                    <p>{text}</p>
-                </div>
-            </div >
-        );
-    }
-}
\ No newline at end of file
+// import React = require("react");
+// import { library } from '@fortawesome/fontawesome-svg-core';
+// import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
+// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+// import { computed, observable, runInAction } from "mobx";
+// import { observer } from "mobx-react";
+// import { FieldView, FieldViewProps } from './FieldView';
+// import "./LinkButtonBox.scss";
+// import { DocumentView } from "./DocumentView";
+// import { Doc } from "../../../new_fields/Doc";
+// import { LinkButtonField } from "../../../new_fields/LinkButtonField";
+// import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
+// import { CollectionDockingView } from "../collections/CollectionDockingView";
+// import { DocumentManager } from "../../util/DocumentManager";
+// import { Id } from "../../../new_fields/FieldSymbols";
+
+// library.add(faCaretUp);
+// library.add(faObjectGroup);
+// library.add(faStickyNote);
+// library.add(faFilePdf);
+// library.add(faFilm);
+
+// @observer
+// export class LinkButtonBox extends React.Component<FieldViewProps> {
+//     public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); }
+
+//     followLink = (): void => {
+//         console.log("follow link???");
+//         let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" }));
+//         let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId);
+//         if (targetView && targetView.props.ContainingCollectionView) {
+//             CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document);
+//         }
+//     }
+
+//     render() {
+
+//         let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" }));
+//         let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId);
+
+//         let text = "Could not find link";
+//         if (targetView) {
+//             let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : "";
+//             text = "Link to " + StrCast(targetView.props.Document.title) + context;
+//         }
+
+//         let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false));
+//         let display = activeDvs.reduce((found, dv) => {
+//             let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]);
+//             let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]);
+//             let match = matchSv || matchTv;
+//             return match || found;
+//         }, false);
+
+//         return (
+//             <div className="linkBox-cont" style={{ display: display ? "block" : "none" }}>
+//                 <div className="linkBox-cont-wrapper">
+//                     <p>{text}</p>
+//                 </div>
+//             </div >
+//         );
+//     }
+// }
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
index 95199bae2..5f4f7d4f0 100644
--- a/src/client/views/nodes/LinkEditor.tsx
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -17,9 +17,8 @@ library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, fa
 
 
 interface GroupTypesDropdownProps {
-    groupId: string;
     groupType: string;
-    setGroup: (groupId: string, group: string) => void;
+    setGroupType: (group: string) => void;
 }
 // this dropdown could be generalized
 @observer
@@ -32,20 +31,20 @@ class GroupTypesDropdown extends React.Component<GroupTypesDropdownProps> {
 
     @action
     createGroup = (groupType: string): void => {
-        this.props.setGroup(this.props.groupId, groupType);
-        LinkManager.Instance.groupMetadataKeys.set(groupType, []);
+        this.props.setGroupType(groupType);
+        LinkManager.Instance.addGroupType(groupType);
     }
 
     renderOptions = (): JSX.Element[] | JSX.Element => {
         if (this._searchTerm === "") return <></>;
 
-        let allGroupTypes = Array.from(LinkManager.Instance.groupMetadataKeys.keys());
+        let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
         let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
         let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
 
         let options = groupOptions.map(groupType => {
             return <div key={groupType} className="linkEditor-option"
-                onClick={() => { this.props.setGroup(this.props.groupId, groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}</div>;
+                onClick={() => { this.props.setGroupType(groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}</div>;
         });
 
         // if search term does not already exist as a group type, give option to create new group type
@@ -85,7 +84,7 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
 
     @action
     setMetadataKey = (value: string): void => {
-        let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!);
+        let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
 
         // don't allow user to create existing key
         let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase());
@@ -98,12 +97,15 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
         }
 
         // set new value for key
-        let currIndex = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
+        let currIndex = groupMdKeys.findIndex(key => {
+            console.log("finding index this", key.toUpperCase(), "that", this._key.toUpperCase());
+            return StrCast(key).toUpperCase() === this._key.toUpperCase();
+        });
         if (currIndex === -1) console.error("LinkMetadataEditor: key was not found");
         groupMdKeys[currIndex] = value;
 
         this._key = value;
-        LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys);
+        LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, groupMdKeys);
     }
 
     @action
@@ -116,13 +118,13 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
 
     @action
     removeMetadata = (): void => {
-        let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!);
+        let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
 
         let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
         if (index === -1) console.error("LinkMetadataEditor: key was not found");
         groupMdKeys.splice(index, 1);
 
-        LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys);
+        LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, groupMdKeys);
         this._key = "";
     }
 
@@ -137,215 +139,176 @@ class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
     }
 }
 
-
-interface LinkEditorProps {
+interface LinkGroupEditorProps {
     sourceDoc: Doc;
     linkDoc: Doc;
-    showLinks: () => void;
+    groupDoc: Doc;
 }
 @observer
-export class LinkEditor extends React.Component<LinkEditorProps> {
+export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
 
-    // map of temporary group id to the corresponding group doc
-    @observable private _groups: Map<string, Doc> = new Map();
-
-    constructor(props: LinkEditorProps) {
-        super(props);
+    @action
+    setGroupType = (groupType: string): void => {
+        console.log("SET GROUP TYPE TO", groupType);
+        this.props.groupDoc.type = groupType;
+        console.log("GROUP TYPE HAS BEEN SET TO ", StrCast(this.props.groupDoc.type));
+    }
 
-        let groups = new Map<string, Doc>();
-        let groupList = LinkManager.Instance.getAnchorGroups(props.linkDoc, props.sourceDoc);
-        groupList.forEach(groupDoc => {
-            let id = Utils.GenerateGuid();
-            groups.set(id, groupDoc);
-        });
-        this._groups = groups;
+    removeGroupFromLink = (groupType: string): void => {
+        LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType);
     }
 
-    @action
-    deleteLink = (): void => {
-        let index = LinkManager.Instance.allLinks.indexOf(this.props.linkDoc);
-        LinkManager.Instance.allLinks.splice(index, 1);
-        this.props.showLinks();
+    deleteGroup = (groupType: string): void => {
+        LinkManager.Instance.deleteGroupType(groupType);
     }
 
-    @action
-    addGroup = (): void => {
-        // new group only gets added if there is not already a group with type "new group"
-        let index = Array.from(this._groups.values()).findIndex(groupDoc => {
-            return groupDoc.type === "New Group";
-        });
-        if (index > -1) return;
+    copyGroup = (groupType: string): void => {
+        let sourceGroupDoc = this.props.groupDoc;
+        let sourceMdDoc = Cast(sourceGroupDoc.metadata, Doc, new Doc);
 
-        // create new metadata document for group
-        let mdDoc = Docs.TextDocument();
-        mdDoc.proto!.anchor1 = this.props.sourceDoc.title;
-        mdDoc.proto!.anchor2 = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title;
+        let destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+        // let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc);
+        let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
 
-        // create new group document
-        let groupDoc = Docs.TextDocument();
-        groupDoc.proto!.type = "New Group";
-        groupDoc.proto!.metadata = mdDoc;
+        // create new metadata doc with copied kvp
+        let destMdDoc = new Doc();
+        destMdDoc.anchor1 = StrCast(sourceMdDoc.anchor2);
+        destMdDoc.anchor2 = StrCast(sourceMdDoc.anchor1);
+        keys.forEach(key => {
+            let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
+            destMdDoc[key] = val;
+        });
 
-        this._groups.set(Utils.GenerateGuid(), groupDoc);
+        // create new group doc with new metadata doc
+        let destGroupDoc = new Doc();
+        destGroupDoc.type = groupType;
+        destGroupDoc.metadata = destMdDoc;
 
-        let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
-        LinkManager.Instance.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
+        LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, destDoc, destGroupDoc, true);
     }
 
     @action
-    setGroupType = (groupId: string, groupType: string): void => {
-        let groupDoc = this._groups.get(groupId);
-        if (groupDoc) {
-            groupDoc.proto!.type = groupType;
-            this._groups.set(groupId, groupDoc);
-            LinkManager.Instance.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
-        }
-    }
-
-    removeGroupFromLink = (groupId: string, groupType: string): void => {
-        let groupDoc = this._groups.get(groupId);
-        if (!groupDoc) console.error("LinkEditor: group not found");
-        LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType);
-        this._groups.delete(groupId);
-    }
-
-    deleteGroup = (groupId: string, groupType: string): void => {
-        let groupDoc = this._groups.get(groupId);
-        if (!groupDoc) console.error("LinkEditor: group not found");
-        LinkManager.Instance.deleteGroup(groupType);
-        this._groups.delete(groupId);
+    addMetadata = (groupType: string): void => {
+        let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        // only add "new key" if there is no other key with value "new key"; prevents spamming
+        if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key");
+        LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys);
     }
 
-    copyGroup = (groupId: string, groupType: string): void => {
-        let sourceGroupDoc = this._groups.get(groupId);
-        let sourceMdDoc = Cast(sourceGroupDoc!.metadata, Doc, new Doc);
-        let destDoc = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
-        let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc);
-        let keys = LinkManager.Instance.groupMetadataKeys.get(groupType);
-
-        // create new metadata doc with copied kvp
-        let destMdDoc = Docs.TextDocument();
-        destMdDoc.proto!.anchor1 = StrCast(sourceMdDoc.anchor2);
-        destMdDoc.proto!.anchor2 = StrCast(sourceMdDoc.anchor1);
-        if (keys) {
-            keys.forEach(key => {
-                let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
-                destMdDoc[key] = val;
+    renderMetadata = (): JSX.Element[] => {
+        let metadata: Array<JSX.Element> = [];
+        let groupDoc = this.props.groupDoc;
+        let mdDoc = Cast(groupDoc.metadata, Doc, new Doc);
+        let groupType = StrCast(groupDoc.type);
+        let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        if (groupMdKeys) {
+            groupMdKeys.forEach((key, index) => {
+                metadata.push(
+                    <LinkMetadataEditor key={"mded-" + index} groupType={groupType} mdDoc={mdDoc} mdKey={key} mdValue={(mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key])} />
+                );
             });
         }
-
-        // create new group doc with new metadata doc
-        let destGroupDoc = Docs.TextDocument();
-        destGroupDoc.proto!.type = groupType;
-        destGroupDoc.proto!.metadata = destMdDoc;
-
-        // if group does not already exist on opposite anchor, create group doc
-        let index = destGroupList.findIndex(groupDoc => { StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase(); });
-        if (index > -1) {
-            destGroupList[index] = destGroupDoc;
-        } else {
-            destGroupList.push(destGroupDoc);
-        }
-
-        LinkManager.Instance.setAnchorGroups(this.props.linkDoc, destDoc, destGroupList);
+        return metadata;
     }
 
-    viewGroupAsTable = (groupId: string, groupType: string): JSX.Element => {
-        let keys = LinkManager.Instance.groupMetadataKeys.get(groupType);
-        let groupDoc = this._groups.get(groupId);
-        if (keys && groupDoc) {
-            let docs: Doc[] = LinkManager.Instance.findAllMetadataDocsInGroup(groupType);
-            let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" }));
-            let ref = React.createRef<HTMLDivElement>();
-            return <div ref={ref}><button className="linkEditor-button" onPointerDown={SetupDrag(ref, createTable)}><FontAwesomeIcon icon="table" size="sm" /></button></div>;
-        } else {
-            return <button className="linkEditor-button" disabled><FontAwesomeIcon icon="table" size="sm" /></button>;
-        }
+    viewGroupAsTable = (groupType: string): JSX.Element => {
+        let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
+        let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
+        let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys], docs, { width: 500, height: 300, title: groupType + " table" }));
+        let ref = React.createRef<HTMLDivElement>();
+        return <div ref={ref}><button className="linkEditor-button" onPointerDown={SetupDrag(ref, createTable)}><FontAwesomeIcon icon="table" size="sm" /></button></div>;
     }
 
-    renderGroup = (groupId: string, groupDoc: Doc): JSX.Element => {
-        let type = StrCast(groupDoc.type);
-        if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") {
-            let buttons;
-            if (type === "New Group") {
-                buttons = (
-                    <>
-                        <button className="linkEditor-button" disabled={true} title="Add KVP"><FontAwesomeIcon icon="plus" size="sm" /></button>
-                        <button className="linkEditor-button" disabled title="Copy group to opposite anchor"><FontAwesomeIcon icon="exchange-alt" size="sm" /></button>
-                        <button className="linkEditor-button" onClick={() => this.removeGroupFromLink(groupId, type)} title="Remove group from link"><FontAwesomeIcon icon="times" size="sm" /></button>
-                        <button className="linkEditor-button" disabled title="Delete group"><FontAwesomeIcon icon="trash" size="sm" /></button>
-                        <button className="linkEditor-button" disabled><FontAwesomeIcon icon="table" size="sm" /></button>
-                    </>
-                );
-            } else {
-                buttons = (
-                    <>
-                        <button className="linkEditor-button" onClick={() => this.addMetadata(StrCast(groupDoc.proto!.type))} title="Add KVP"><FontAwesomeIcon icon="plus" size="sm" /></button>
-                        <button className="linkEditor-button" onClick={() => this.copyGroup(groupId, type)} title="Copy group to opposite anchor"><FontAwesomeIcon icon="exchange-alt" size="sm" /></button>
-                        <button className="linkEditor-button" onClick={() => this.removeGroupFromLink(groupId, type)} title="Remove group from link"><FontAwesomeIcon icon="times" size="sm" /></button>
-                        <button className="linkEditor-button" onClick={() => this.deleteGroup(groupId, type)} title="Delete group"><FontAwesomeIcon icon="trash" size="sm" /></button>
-                        {this.viewGroupAsTable(groupId, type)}
-                    </>
-                );
-            }
-
-            return (
-                <div key={groupId} className="linkEditor-group">
-                    <div className="linkEditor-group-row">
-                        <p className="linkEditor-group-row-label">type:</p>
-                        <GroupTypesDropdown groupId={groupId} groupType={StrCast(groupDoc.proto!.type)} setGroup={this.setGroupType} />
-                    </div>
-                    {this.renderMetadata(groupId)}
-                    <div className="linkEditor-group-buttons">
-                        {buttons}
-                    </div>
-                </div>
+    render() {
+        let groupType = StrCast(this.props.groupDoc.type);
+        // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") {
+        let buttons;
+        if (groupType === "") {
+            buttons = (
+                <>
+                    <button className="linkEditor-button" disabled={true} title="Add KVP"><FontAwesomeIcon icon="plus" size="sm" /></button>
+                    <button className="linkEditor-button" disabled title="Copy group to opposite anchor"><FontAwesomeIcon icon="exchange-alt" size="sm" /></button>
+                    <button className="linkEditor-button" onClick={() => this.removeGroupFromLink(groupType)} title="Remove group from link"><FontAwesomeIcon icon="times" size="sm" /></button>
+                    <button className="linkEditor-button" disabled title="Delete group"><FontAwesomeIcon icon="trash" size="sm" /></button>
+                    <button className="linkEditor-button" disabled><FontAwesomeIcon icon="table" size="sm" /></button>
+                </>
             );
         } else {
-            return <></>;
+            buttons = (
+                <>
+                    <button className="linkEditor-button" onClick={() => this.addMetadata(groupType)} title="Add KVP"><FontAwesomeIcon icon="plus" size="sm" /></button>
+                    <button className="linkEditor-button" onClick={() => this.copyGroup(groupType)} title="Copy group to opposite anchor"><FontAwesomeIcon icon="exchange-alt" size="sm" /></button>
+                    <button className="linkEditor-button" onClick={() => this.removeGroupFromLink(groupType)} title="Remove group from link"><FontAwesomeIcon icon="times" size="sm" /></button>
+                    <button className="linkEditor-button" onClick={() => this.deleteGroup(groupType)} title="Delete group"><FontAwesomeIcon icon="trash" size="sm" /></button>
+                    {this.viewGroupAsTable(groupType)}
+                </>
+            );
         }
+
+        return (
+            <div className="linkEditor-group">
+                <div className="linkEditor-group-row">
+                    <p className="linkEditor-group-row-label">type:</p>
+                    <GroupTypesDropdown groupType={groupType} setGroupType={this.setGroupType} />
+                </div>
+                {this.renderMetadata()}
+                <div className="linkEditor-group-buttons">
+                    {buttons}
+                </div>
+            </div>
+        );
     }
+    // else {
+    //     return <></>;
+    // }
+    // }
+}
+
 
+interface LinkEditorProps {
+    sourceDoc: Doc;
+    linkDoc: Doc;
+    showLinks: () => void;
+}
+@observer
+export class LinkEditor extends React.Component<LinkEditorProps> {
 
     @action
-    addMetadata = (groupType: string): void => {
-        console.log("ADD MD");
-        let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType);
-        if (mdKeys) {
-            // only add "new key" if there is no other key with value "new key"; prevents spamming
-            if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key");
-        } else {
-            mdKeys = ["new key"];
-        }
-        LinkManager.Instance.groupMetadataKeys.set(groupType, mdKeys);
+    deleteLink = (): void => {
+        LinkManager.Instance.deleteLink(this.props.linkDoc);
+        this.props.showLinks();
     }
 
-    renderMetadata = (groupId: string): JSX.Element[] => {
-        let metadata: Array<JSX.Element> = [];
-        let groupDoc = this._groups.get(groupId);
-        if (groupDoc) {
-            let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc);
-            let groupType = StrCast(groupDoc.proto!.type);
-            let groupMdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType);
-            if (groupMdKeys) {
-                groupMdKeys.forEach((key, index) => {
-                    metadata.push(
-                        <LinkMetadataEditor key={"mded-" + index} groupType={groupType} mdDoc={mdDoc} mdKey={key} mdValue={(mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key])} />
-                    );
-                });
-            }
-        }
-        return metadata;
+    @action
+    addGroup = (): void => {
+        // create new metadata document for group
+        let mdDoc = new Doc();
+        mdDoc.anchor1 = this.props.sourceDoc.title;
+        mdDoc.anchor2 = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title;
+
+        // create new group document
+        let groupDoc = new Doc();
+        groupDoc.type = "";
+        groupDoc.metadata = mdDoc;
+
+        LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, this.props.sourceDoc, groupDoc);
     }
 
     render() {
-        let destination = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+        let destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
 
-        let groups: Array<JSX.Element> = [];
-        this._groups.forEach((groupDoc, groupId) => {
-            groups.push(this.renderGroup(groupId, groupDoc));
+        let groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
+        console.log("NUM GROUPS", groupList.length);
+        let groups = groupList.map(groupDoc => {
+            return <LinkGroupEditor key={"gred-" + StrCast(groupDoc.type)} linkDoc={this.props.linkDoc} sourceDoc={this.props.sourceDoc} groupDoc={groupDoc} />;
         });
 
+
+        // let groups: Array<JSX.Element> = [];
+        // this._groups.forEach((groupDoc, groupId) => {
+        //     groups.push(this.renderGroup(groupId, groupDoc));
+        // });
+
         return (
             <div className="linkEditor">
                 <button className="linkEditor-back" onPointerDown={() => this.props.showLinks()}><FontAwesomeIcon icon="arrow-left" size="sm" /></button>
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index f96c7d2e4..04ca47db3 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -38,7 +38,7 @@ export class LinkMenu extends React.Component<Props> {
 
     render() {
         let sourceDoc = this.props.docView.props.Document;
-        let groups: Map<string, Doc[]> = LinkManager.Instance.findRelatedGroupedLinks(sourceDoc);
+        let groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
         if (this._editingLink === undefined) {
             return (
                 <div className="linkMenu">
diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx
index 229143d99..71326f703 100644
--- a/src/client/views/nodes/LinkMenuGroup.tsx
+++ b/src/client/views/nodes/LinkMenuGroup.tsx
@@ -42,10 +42,10 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
             document.removeEventListener("pointermove", this.onLinkButtonMoved);
             document.removeEventListener("pointerup", this.onLinkButtonUp);
 
-            let draggedDocs = this.props.group.map(linkDoc => LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc));
+            let draggedDocs = this.props.group.map(linkDoc => LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc));
             let dragData = new DragManager.DocumentDragData(draggedDocs);
 
-            DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, {
+            DragManager.StartLinkedDocumentDrag([this._drag.current], this.props.sourceDoc, dragData, e.x, e.y, {
                 handlers: {
                     dragComplete: action(emptyFunction),
                 },
@@ -57,7 +57,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
 
     render() {
         let groupItems = this.props.group.map(linkDoc => {
-            let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc);
+            let destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
             return <LinkMenuItem key={destination[Id] + this.props.sourceDoc[Id]} groupType={this.props.groupType}
                 linkDoc={linkDoc} sourceDoc={this.props.sourceDoc} destinationDoc={destination} showEditor={this.props.showEditor} />;
         });
@@ -69,6 +69,6 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
                     {groupItems}
                 </div>
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
index 42ef353b7..28694721d 100644
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ b/src/client/views/nodes/LinkMenuItem.tsx
@@ -53,8 +53,8 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         let mdRows: Array<JSX.Element> = [];
         if (groupDoc) {
             let mdDoc = Cast(groupDoc.metadata, Doc, new Doc);
-            let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType);
-            mdRows = keys!.map(key => {
+            let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
+            mdRows = keys.map(key => {
                 return (<div key={key} className="link-metadata-row"><b>{key}</b>: {StrCast(mdDoc[key])}</div>);
             });
         }
@@ -88,7 +88,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
     render() {
 
-        let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType);
+        let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
         let canExpand = keys ? keys.length > 0 : false;
 
         return (
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index fda788f2d..9b104184f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -234,23 +234,20 @@ export namespace Doc {
     export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc {
         const copy = new Doc;
         Object.keys(doc).forEach(key => {
-            console.log(key);
             const field = doc[key];
             if (key === "proto" && copyProto) {
-                console.log(1);
                 if (field instanceof Doc) {
-                    console.log(2);
                     copy[key] = Doc.MakeCopy(field);
                 }
             } else {
                 if (field instanceof RefField) {
-                    console.log(3);
+                    console.log("equals field, ref", key);
                     copy[key] = field;
                 } else if (field instanceof ObjectField) {
-                    console.log(4);
+                    console.log("copy field, object", key);
                     copy[key] = ObjectField.MakeCopy(field);
                 } else {
-                    console.log(5);
+                    console.log("equals field", key);
                     copy[key] = field;
                 }
             }
diff --git a/src/new_fields/LinkButtonField.ts b/src/new_fields/LinkButtonField.ts
index 92e1ed922..e6d1de749 100644
--- a/src/new_fields/LinkButtonField.ts
+++ b/src/new_fields/LinkButtonField.ts
@@ -1,35 +1,35 @@
-import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, primitive, createSimpleSchema, object } from "serializr";
-import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString } from "./FieldSymbols";
-import { Doc } from "./Doc";
-import { DocumentView } from "../client/views/nodes/DocumentView";
+// import { Deserializable } from "../client/util/SerializationHelper";
+// import { serializable, primitive, createSimpleSchema, object } from "serializr";
+// import { ObjectField } from "./ObjectField";
+// import { Copy, ToScriptString } from "./FieldSymbols";
+// import { Doc } from "./Doc";
+// import { DocumentView } from "../client/views/nodes/DocumentView";
 
-export type LinkButtonData = {
-    sourceViewId: string,
-    targetViewId: string
-};
+// export type LinkButtonData = {
+//     sourceViewId: string,
+//     targetViewId: string
+// };
 
-const LinkButtonSchema = createSimpleSchema({
-    sourceViewId: true,
-    targetViewId: true
-});
+// const LinkButtonSchema = createSimpleSchema({
+//     sourceViewId: true,
+//     targetViewId: true
+// });
 
-@Deserializable("linkButton")
-export class LinkButtonField extends ObjectField {
-    @serializable(object(LinkButtonSchema))
-    readonly data: LinkButtonData;
+// @Deserializable("linkButton")
+// export class LinkButtonField extends ObjectField {
+//     @serializable(object(LinkButtonSchema))
+//     readonly data: LinkButtonData;
 
-    constructor(data: LinkButtonData) {
-        super();
-        this.data = data;
-    }
+//     constructor(data: LinkButtonData) {
+//         super();
+//         this.data = data;
+//     }
 
-    [Copy]() {
-        return new LinkButtonField(this.data);
-    }
+//     [Copy]() {
+//         return new LinkButtonField(this.data);
+//     }
 
-    [ToScriptString]() {
-        return "invalid";
-    }
-}
+//     [ToScriptString]() {
+//         return "invalid";
+//     }
+// }
-- 
cgit v1.2.3-70-g09d2


From 8954bac59b50aa3618625379a17dbefe9aceca72 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
Date: Wed, 26 Jun 2019 12:29:07 -0400
Subject: removed console.logs

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

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7d2aa3199..4fcc9faa2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -205,9 +205,6 @@ export class MainView extends React.Component {
                 getScale={returnOne}
             />;
         let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, listSpec(Doc))) : undefined;
-        console.log("GETTING mainContent()");
-        console.log(castRes instanceof Promise);
-        console.log(castRes);
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
                 <div ref={measureRef} id="mainContent-div">
-- 
cgit v1.2.3-70-g09d2

