From 3dab85351e1fe862a54294cec70e7139abea0d7d Mon Sep 17 00:00:00 2001
From: Lionel Han <47760119+IGoByJoe@users.noreply.github.com>
Date: Mon, 15 Jun 2020 00:10:16 -0700
Subject: added ? to a lot of things

---
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1fab54d7e..b0b648b66 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -188,7 +188,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }
             const state = this._editorView.state.apply(tx);
-            this._editorView.updateState(state);
+            this?._editorView?.updateState(state);
             (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks);
 
             const tsel = this._editorView.state.selection.$from;
@@ -1213,7 +1213,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
         const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground;
         if (this.props.isSelected()) {
-            this._editorView && RichTextMenu.Instance.updateFromDash(this._editorView, undefined, this.props);
+            this._editorView && RichTextMenu.Instance?.updateFromDash(this._editorView, undefined, this.props);
         } else if (FormattedTextBoxComment.textBox === this) {
             FormattedTextBoxComment.Hide();
         }
-- 
cgit v1.2.3-70-g09d2


From 4b1dc65c9e0bb917c1f1f780f8a63fc0c91b4ad6 Mon Sep 17 00:00:00 2001
From: geireann <60007097+geireann@users.noreply.github.com>
Date: Tue, 16 Jun 2020 23:53:16 +0800
Subject: mobile menu bug fixes + doc button fix

---
 deploy/mobile/image.html                           |  1 +
 src/client/util/CurrentUserUtils.ts                | 73 ++++++++++++----------
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 src/client/views/nodes/PresBox.scss                |  5 ++
 src/client/views/nodes/RadialMenu.tsx              |  6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 +-
 .../views/presentationview/PresElementBox.scss     |  2 +-
 src/mobile/MobileInterface.tsx                     | 69 +++++++-------------
 src/mobile/MobileMenu.scss                         |  4 +-
 10 files changed, 77 insertions(+), 90 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/deploy/mobile/image.html b/deploy/mobile/image.html
index d30ad6ac2..718550f02 100644
--- a/deploy/mobile/image.html
+++ b/deploy/mobile/image.html
@@ -3,6 +3,7 @@
 <head>
     <title>Dash Mobile</title>
     <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0" />
 </head>
 
 <body>
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 323e398be..9ce162897 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -55,6 +55,15 @@ export class CurrentUserUtils {
             });
         }
 
+        if (doc["template-mobile-button"] === undefined) {
+            const queryTemplate = this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, _backgroundColor: "lightgrey" }, [this.ficon({ ignoreClick: true, icon: "mobile", backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]);
+            doc["template-mobile-button"] = CurrentUserUtils.ficon({
+                onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+                dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
+                removeDropProperties: new List<string>(["dropAction"]), title: "mobile button", icon: "mobile"
+            });
+        }
+
         if (doc["template-button-slides"] === undefined) {
             const slideTemplate = Docs.Create.MultirowDocument(
                 [
@@ -165,16 +174,6 @@ export class CurrentUserUtils {
             });
         }
 
-        // if (doc["mobile-button"] === undefined) {
-        //     const mobileTemplate = this.mobileButton({ title: "mobile button", _backgroundColor: "lightgrey" }, [this.ficon({ ignoreClick: true, icon: "mobile", backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, "text"), this.mobileButtonInfo({}, "This is a default mobile button for use in the mobile menu")])]);
-        //     mobileTemplate.isTemplateDoc = makeTemplate(mobileTemplate);
-        //     doc["mobile-button"] = CurrentUserUtils.ficon({
-        //         onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
-        //         dragFactory: new PrefetchProxy(mobileTemplate) as any as Doc,
-        //         removeDropProperties: new List<string>(["dropAction"]), title: "mobile button view", icon: "mobile"
-        //     });
-        // }
-
         if (doc["template-button-detail"] === undefined) {
             const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create;
 
@@ -229,6 +228,7 @@ export class CurrentUserUtils {
             doc["template-button-slides"] as Doc,
             doc["template-button-description"] as Doc,
             doc["template-button-query"] as Doc,
+            doc["template-mobile-button"] as Doc,
             doc["template-button-detail"] as Doc,
             doc["template-button-link"] as Doc,
             doc["template-button-switch"] as Doc];
@@ -378,9 +378,8 @@ export class CurrentUserUtils {
         if (doc.emptyWebpage === undefined) {
             doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 600, UseCors: true });
         }
-        if (doc.activeMobile === undefined) {
-            console.log("phone setup");
-            this.setupActiveMobile(doc);
+        if (doc.activeMobileMenu === undefined) {
+            this.setupActiveMobileMenu(doc);
         }
         return [
             { title: "Drag a comparison box", label: "Comp", icon: "columns", ignoreClick: true, drag: 'Docs.Create.ComparisonDocument()' },
@@ -395,7 +394,7 @@ export class CurrentUserUtils {
             { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
             { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
             { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
-            { title: "Drag a mobile view", label: "Phone", icon: "mobile", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.activeMobile as Doc },
+            { title: "Drag a mobile view", label: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
             { title: "Drag an instance of the device collection", label: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' },
             // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "blue", ischecked: `sameDocs(this.activeInkPen,  this)`, activeInkPen: doc },
             // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
@@ -449,12 +448,12 @@ export class CurrentUserUtils {
         return doc.myItemCreators as Doc;
     }
 
-    static setupActiveMobile(doc: Doc) {
-        if (doc.activeMobile === undefined) {
+    static setupActiveMobileMenu(doc: Doc) {
+        if (doc.activeMobileMenu === undefined) {
             console.log("undefined");
-            doc.activeMobile = this.setupMobileMenu();
+            doc.activeMobileMenu = this.setupMobileMenu();
         }
-        return doc.activeMobile as Doc;
+        return doc.activeMobileMenu as Doc;
     }
 
     static setupMobileMenu() {
@@ -466,37 +465,45 @@ export class CurrentUserUtils {
 
     static setupMobileButtons(doc?: Doc, buttons?: string[]) {
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
-            { title: "LIBRARY", icon: "bars", click: 'switchToLibrary()', backgroundColor: "#ffd6d6", info: "Navigate and access all of your documents within their respective collections" },
-            { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "#ffbfbf", info: "Use your mobile to record audio and access it on Dash Web." },
-            { title: "UPLOAD", icon: "upload", click: 'uploadImageMobile()', backgroundColor: "#ff9e9e", info: "Upload an image from your mobile device so it can be accessed on Dash Web" },
+            { title: "LIBRARY", icon: "bars", click: 'switchToLibrary()', backgroundColor: "#ffd6d6", info: "Navigate and access all of your documents within their respective collections." },
+            { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "#ffbfbf", info: "Use your phone to record and upload audio onto Dash Web." },
+            { title: "UPLOAD", icon: "upload", click: 'uploadImageMobile()', backgroundColor: "#ff9e9e", info: "Upload images or videos from your mobile device so they can be accessed on Dash Web." },
             { title: "PRESENTATION", icon: "desktop", click: 'openMobilePresentation()', backgroundColor: "#ff8080", info: "Use your phone as a remote for you presentation." },
-            { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "#ff5e5e", info: "Change your password, log out, or manage your account security" }
+            { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "#ff5e5e", info: "Change your password, log out, or manage your account security." }
         ];
-        return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => this.mobileButton({ title: data.title, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, _backgroundColor: data.backgroundColor }, [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]));
+        return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data =>
+            this.mobileButton({
+                title: data.title,
+                lockedPosition: true,
+                onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
+                _backgroundColor: data.backgroundColor
+            },
+                [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])
+        );
     }
 
-    static mobileButton = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.MulticolumnDocument(docs, {
+    static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, {
         ...opts,
         dropAction: undefined, removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15,
-        borderRounding: "5px", boxShadow: "0 0", _chromeStatus: "disabled",
-    })) as any as Doc
+        borderRounding: "5px", boxShadow: "0 0", _chromeStatus: "disabled"
+    }) as any as Doc
 
-    static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.MultirowDocument(docs, {
+    static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, {
         ...opts,
         dropAction: undefined, removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25,
         backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", _chromeStatus: "disabled", ignoreClick: true
-    })) as any as Doc
+    }) as any as Doc
 
 
-    static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => new PrefetchProxy(Docs.Create.TextDocument(buttonTitle, {
+    static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, {
         ...opts,
         dropAction: undefined, title: buttonTitle, _fontSize: 37, _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)"
-    })) as any as Doc
+    }) as any as Doc
 
-    static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => new PrefetchProxy(Docs.Create.TextDocument(buttonInfo, {
+    static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, {
         ...opts,
         dropAction: undefined, title: "info", _fontSize: 25, _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2,
-    })) as any as Doc
+    }) as any as Doc
 
 
     static setupThumbButtons(doc: Doc) {
@@ -795,7 +802,7 @@ export class CurrentUserUtils {
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupRightSidebar(doc);  // sets up the right sidebar collection for mobile upload documents and sharing
-        this.setupActiveMobile(doc);
+        this.setupActiveMobileMenu(doc);
         this.setupOverlays(doc);  // documents in overlay layer
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupDefaultPresentation(doc); // presentation that's initially triggered
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0a1b03522..bd0b69da8 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -476,7 +476,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                         transformOrigin: "top left",
                     }}
                     onScroll={action(e => {
-                        if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
+                        if (!this.props.isSelected() && window.innerWidth > 1000) e.currentTarget.scrollTop = this._scroll;
                         else this._scroll = e.currentTarget.scrollTop;
                     })}
                     onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1a935d9b0..2e0be1c6a 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -228,7 +228,7 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
             {!this.path ?
                 <div className="audiobox-buttons">
                     <div className="audiobox-dictation" onClick={this.onFile}>
-                        <FontAwesomeIcon style={{ width: "30px", background: this.layoutDoc.playOnSelect ? "yellow" : "dimGray" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+                        <FontAwesomeIcon style={{ width: "30px", background: this.layoutDoc.playOnSelect ? "yellow" : "rgba(0,0,0,0)" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
                     </div>
                     <button className={`audiobox-record${interactive}`} style={{ backgroundColor: this.audioState === "recording" ? "red" : "black" }}>
                         {this.audioState === "recording" ? "STOP" : "RECORD"}
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index 2bb249ee4..ccce6e627 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -126,4 +126,9 @@
         position: absolute;
         min-width: 50px;
     }
+
+    .input,
+    .select {
+        font-size: 100%;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index ddfdb67b4..2bbb80b2a 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -1,9 +1,9 @@
 import React = require("react");
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import MobileInterface from "../../../mobile/MobileInterface";
 import "./RadialMenu.scss";
 import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem";
+import { MobileInterface } from "../../../mobile/MobileInterface";
 
 @observer
 export class RadialMenu extends React.Component {
@@ -38,7 +38,6 @@ export class RadialMenu extends React.Component {
         this._mouseY = e.clientY;
         this.used = false;
         document.addEventListener("pointermove", this.onPointerMove);
-
     }
 
     @observable
@@ -176,7 +175,6 @@ export class RadialMenu extends React.Component {
 
     @action
     openMenu = (x: number, y: number) => {
-
         this._pageX = x;
         this._pageY = y;
         this._shouldDisplay;
@@ -216,7 +214,7 @@ export class RadialMenu extends React.Component {
 
 
     render() {
-        if (!this._display || MobileInterface.Instance) {
+        if (!this._display) {
             return null;
         }
         const style = this._yRelativeToTop ? { left: this._pageX - 130, top: this._pageY - 130 } :
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1db50a5cc..912bafc46 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -262,7 +262,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     public unhighlightSearchTerms = () => {
-        if (this._editorView && (this._editorView as any).docView) {
+        if (window.innerWidth < 1000) null;
+        else if (this._editorView && (this._editorView as any).docView) {
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
             const end = this._editorView.state.doc.nodeSize - 2;
diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss
index ec82b0c51..bf268eb8d 100644
--- a/src/client/views/presentationview/PresElementBox.scss
+++ b/src/client/views/presentationview/PresElementBox.scss
@@ -79,7 +79,7 @@
 }
 
 .presElementBox-name {
-    font-size: 12pxππ;
+    font-size: 12px;
     position: absolute;
     display: inline-block;
     width: calc(100% - 45px);
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 25dc9fd55..7af11a9c4 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -35,6 +35,7 @@ import { nullAudio } from '../fields/URLField';
 import GestureOverlay from "../client/views/GestureOverlay";
 import { ScriptField } from "../fields/ScriptField";
 import InkOptionsMenu from "../client/views/collections/collectionFreeForm/InkOptionsMenu";
+import { RadialMenu } from "../client/views/nodes/RadialMenu";
 
 library.add(faTasks, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
     faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
@@ -48,7 +49,7 @@ export class MobileInterface extends React.Component {
     @observable static Instance: MobileInterface;
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
-    @observable private mainDoc: any = CurrentUserUtils.setupActiveMobile(this.userDoc);
+    @observable private mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(this.userDoc);
     @observable private renderView?: () => JSX.Element;
     @observable private sidebarActive: boolean = false;
     @observable private imageUploadActive: boolean = false;
@@ -94,31 +95,6 @@ export class MobileInterface extends React.Component {
         this.renderView = renderView;
     }
 
-    onSwitchUpload = async () => {
-        let width = 300;
-        let height = 300;
-        const res = await rp.get(Utils.prepend("/getUserDocumentId"));
-
-        // get width and height of the collection doc
-        if (this.mainContainer) {
-            const data = Cast(this.mainContainer.data, listSpec(Doc));
-            if (data) {
-                const collectionDoc = await data[1]; // this should be the collection doc since the positions should be locked
-                const docView = DocumentManager.Instance.getDocumentView(collectionDoc);
-                if (docView) {
-                    width = docView.nativeWidth ? docView.nativeWidth : 300;
-                    height = docView.nativeHeight ? docView.nativeHeight : 300;
-                }
-            }
-        }
-        DocServer.Mobile.dispatchOverlayTrigger({
-            enableOverlay: true,
-            width: width,
-            height: height,
-            text: "Documents uploaded from mobile will show here",
-        });
-    }
-
     // For toggling the hamburger menu
     @action
     toggleSidebar = () => this.sidebarActive = !this.sidebarActive
@@ -138,8 +114,8 @@ export class MobileInterface extends React.Component {
      * Back method for navigating
      */
     back = () => {
-        let header = document.getElementById("header") as HTMLElement;
-        let doc = Cast(this._parents.pop(), Doc) as Doc;
+        const header = document.getElementById("header") as HTMLElement;
+        const doc = Cast(this._parents.pop(), Doc) as Doc;
 
         if (doc === Cast(this._library, Doc) as Doc) {
             this._child = null;
@@ -198,7 +174,7 @@ export class MobileInterface extends React.Component {
         if (this.mainContainer) {
             const backgroundColor = () => "white";
             return (
-                <div style={{ position: "relative", top: '200px', height: `calc(100% - 200px)`, width: "100%", overflow: "hidden", left: "0%" }}>
+                <div style={{ position: "relative", top: '200px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
                     <DocumentView
                         Document={this.mainContainer}
                         DataDoc={undefined}
@@ -238,16 +214,15 @@ export class MobileInterface extends React.Component {
      * @param doc: doc for which the method is called
      */
     handleClick = async (doc: Doc) => {
-        let children = DocListCast(doc.data);
+        const children = DocListCast(doc.data);
         if (doc.type !== "collection") {
             this._parents.push(this._activeDoc);
             this._activeDoc = doc;
             this.switchCurrentView((userDoc: Doc) => doc);
             this._homeMenu = false;
             this.toggleSidebar();
-        } else if (doc.type === "collection" && children.length === 0) {
-            console.log("This collection has no children");
-        } else {
+        } else if (doc.type === "collection" && children.length === 0) console.log("This collection has no children");
+        else {
             this._parents.push(this._activeDoc);
             this._activeDoc = doc;
             this.switchCurrentView((userDoc: Doc) => doc);
@@ -260,7 +235,7 @@ export class MobileInterface extends React.Component {
      * Handles creation of array which is then rendered in renderPathbar()
      */
     createPathname = () => {
-        let docArray = [];
+        const docArray = [];
         this._parents.map((doc: Doc, index: any) => {
             docArray.push(doc);
         });
@@ -270,8 +245,8 @@ export class MobileInterface extends React.Component {
 
     // Renders the graphical pathbar
     renderPathbar = () => {
-        let docArray = this.createPathname();
-        let items = docArray.map((doc: Doc, index: any) => {
+        const docArray = this.createPathname();
+        const items = docArray.map((doc: Doc, index: any) => {
             if (index === 0) {
                 return (
                     <>
@@ -341,9 +316,8 @@ export class MobileInterface extends React.Component {
             this._child = null;
             this.switchCurrentView((userDoc: Doc) => doc);
             this._parents.length = index;
-        } else if (doc === this._homeDoc) {
-            this.returnHome();
-        } else {
+        } else if (doc === this._homeDoc) this.returnHome();
+        else {
             this._activeDoc = doc;
             this._child = doc;
             this.switchCurrentView((userDoc: Doc) => doc);
@@ -384,7 +358,7 @@ export class MobileInterface extends React.Component {
             workspaces = this._child;
         }
 
-        let buttons = DocListCast(workspaces.data).map((doc: Doc, index: any) => {
+        const buttons = DocListCast(workspaces.data).map((doc: Doc, index: any) => {
             if (doc.type !== "ink") {
                 return (
                     <div
@@ -473,7 +447,7 @@ export class MobileInterface extends React.Component {
                 console.log("here");
                 return <div className="colorSelector">
                     <InkOptionsMenu />
-                </div>
+                </div>;
 
             }
         }
@@ -677,17 +651,18 @@ export class MobileInterface extends React.Component {
                     {this.uploadImage()}
                 </div>
                 {this.switchMenuView()}
-                <div className="docButtonContainer">
-                    {this.pinToPresentation()}
-                    {this.downloadDocument()}
-                    {this.drawInk()}
-                    {this.uploadAudioButton()}
-                </div>
                 {this.inkMenu()}
                 <GestureOverlay>
+                    <div className="docButtonContainer">
+                        {this.pinToPresentation()}
+                        {this.downloadDocument()}
+                        {this.drawInk()}
+                        {this.uploadAudioButton()}
+                    </div>
                     {this.displayWorkspaces()}
                     {this.renderDefaultContent()}
                 </GestureOverlay>
+                <RadialMenu />
             </div>
         );
     }
diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss
index 8d996f779..3126a7d0e 100644
--- a/src/mobile/MobileMenu.scss
+++ b/src/mobile/MobileMenu.scss
@@ -256,7 +256,7 @@ body {
 }
 
 .docButtonContainer {
-    top: 90%;
+    top: 80%;
     position: absolute;
     display: flex;
     transform: translate(-50%, 0);
@@ -306,7 +306,7 @@ body {
     width: 200px;
     left: 0px;
     top: 0px;
-    background-image: linear-gradient(to right, #1a1a1a, rgba(255, 0, 0, 0));
+    background-image: linear-gradient(to right, #1a1a1a, rgba(0, 0, 0, 0));
     text-align: center;
     user-select: none;
     z-index: 99;
-- 
cgit v1.2.3-70-g09d2


From 57d4c562b6f0ef39dcd68f2985a56c4a683fcf49 Mon Sep 17 00:00:00 2001
From: geireann <60007097+geireann@users.noreply.github.com>
Date: Thu, 18 Jun 2020 04:38:08 +0800
Subject: pdf changes + radial menu + create new workspaces

(bug with ink)
---
 src/client/views/GestureOverlay.tsx                |  8 ---
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 .../collectionFreeForm/InkOptionsMenu.tsx          |  4 +-
 src/client/views/nodes/DocumentView.tsx            | 12 ++--
 src/client/views/nodes/PDFBox.scss                 |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  7 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  4 +-
 src/mobile/ImageUpload.tsx                         |  6 +-
 src/mobile/MobileInterface.tsx                     | 80 +++++++++++++++++++---
 src/server/ApiManagers/PDFManager.ts               |  3 +-
 11 files changed, 94 insertions(+), 36 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index e51e2d4e1..1c305ebeb 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -575,14 +575,6 @@ export default class GestureOverlay extends Touchable {
             const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
             //push first points to so interactionUtil knows pointer is up
             this._points.push({ X: this._points[0].X, Y: this._points[0].Y });
-            if (MobileInterface.Instance && MobileInterface.Instance.drawingInk) {
-                DocServer.Mobile.dispatchGesturePoints({
-                    points: this._points,
-                    bounds: B,
-                    color: ActiveInkColor(),
-                    width: ActiveInkWidth()
-                });
-            }
 
             const initialPoint = this._points[0.];
             const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + (this.height);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 797aabf18..9f1b5d63c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -476,7 +476,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                         transformOrigin: "top left",
                     }}
                     onScroll={action(e => {
-                        if (!this.props.isSelected() && this.props.renderDepth && window.innerWidth > 1000) e.currentTarget.scrollTop = this._scroll;
+                        if (!this.props.isSelected() && this.props.renderDepth && window.screen.width > 600) e.currentTarget.scrollTop = this._scroll;
                         else this._scroll = e.currentTarget.scrollTop;
                     })}
                     onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
index 647847b68..676dc10f4 100644
--- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx
@@ -130,13 +130,13 @@ export default class InkOptionsMenu extends AntimodeMenu {
         ];
 
         const mobileButtons = [
-            this.shapeButtons,
+            ...this.shapeButtons,
             this.bezierButton,
             this.widthPicker,
             this.colorPicker,
         ];
 
-        return (window.innerWidth < 1000 ? this.getElement(mobileButtons) : this.getElement(buttons));
+        return (window.screen.width < 600 ? this.getElement(mobileButtons) : this.getElement(buttons));
     }
 }
 Scripting.addGlobal(function activatePen(penBtn: any) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 75627607b..e027b6a0f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -31,7 +31,7 @@ import { SelectionManager } from "../../util/SelectionManager";
 import SharingManager from '../../util/SharingManager';
 import { Transform } from "../../util/Transform";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionDockingView, DockedFrameRenderer } from "../collections/CollectionDockingView";
 import { CollectionView, CollectionViewType } from '../collections/CollectionView';
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -43,6 +43,7 @@ import "./DocumentView.scss";
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
+import { MobileInterface } from '../../../mobile/MobileInterface';
 
 library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
     fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -181,10 +182,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
         RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15);
 
-        RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Delete this document", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "layer-group", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "folder", selected: -1 });
+        // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 });
+        RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
+        // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 });
+        RadialMenu.Instance.addItem({ description: "Pin", event: () => DockedFrameRenderer.PinDoc(this.props.Document), icon: "map-pin", selected: -1 });
+        RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 });
 
         SelectionManager.DeselectAll();
     }
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 474587159..1a31d2916 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -224,7 +224,7 @@
     }
 }
 
-@media only screen and (max-width: 1000px) {
+@media only screen and (max-width: 600px) {
 
     .pdfBox .pdfBox-ui .pdfBox-settingsCont .pdfBox-settingsButton,
     .pdfBox-interactive .pdfBox-ui .pdfBox-settingsCont .pdfBox-settingsButton {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 78b7faeee..4b4348d3c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -215,13 +215,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
 
     @computed get contentScaling() { return this.props.ContentScaling(); }
     @computed get renderTitleBox() {
-        console.log("fitWidth ?: " + !(this.props.Document._fitWidth) && (window.innerWidth > 1000));
+        console.log("fitWidth ?: " + !(this.props.Document._fitWidth) && (window.screen.width > 600));
         console.log("_nativeHeight: " + this.Document._nativeHeight);
         console.log("%: " + `${100 / this.contentScaling}%`);
         const classname = "pdfBox" + (this.active() ? "-interactive" : "");
         return <div className={classname} style={{
             width: !this.props.Document._fitWidth ? this.Document._nativeWidth || 0 : `${100 / this.contentScaling}%`,
-            height: !this.props.Document._fitWidth && (window.innerWidth > 1000) ? this.Document._nativeHeight || 0 : `${100 / this.contentScaling}%`, //Adjusted for mobile (!window.innerWidth < 1000)
+            //height adjusted for mobile (window.screen.width > 600)
+            height: !this.props.Document._fitWidth && (window.screen.width > 600) ? this.Document._nativeHeight || 0 : `${100 / this.contentScaling}%`,
             transform: `scale(${this.contentScaling})`
         }}  >
             <div className="pdfBox-title-outer">
@@ -233,7 +234,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
     isChildActive = (outsideReaction?: boolean) => this._isChildActive;
     @computed get renderPdfView() {
         const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
-        return <div className={"pdfBox"} onContextMenu={this.specificContextMenu} style={{ height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.innerWidth > 1000) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
+        return <div className={"pdfBox"} onContextMenu={this.specificContextMenu} style={{ height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
             <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
                 setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
                 renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f3d0d135c..ae508cd67 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -261,7 +261,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     public unhighlightSearchTerms = () => {
-        if (window.innerWidth < 1000) null;
+        if (window.screen.width < 600) null;
         else if (this._editorView && (this._editorView as any).docView) {
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9cdbea2da..57b7bceca 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -709,8 +709,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
             onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
             style={{
                 overflowX: this._zoomed !== 1 ? "scroll" : undefined,
-                width: !this.props.Document._fitWidth && (window.innerWidth > 1000) ? NumCast(this.props.Document._nativeWidth) : `${100 / this.contentScaling}%`,
-                height: !this.props.Document._fitWidth && (window.innerWidth > 1000) ? NumCast(this.props.Document._nativeHeight) : `${100 / this.contentScaling}%`,
+                width: !this.props.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.props.Document._nativeWidth) : `${100 / this.contentScaling}%`,
+                height: !this.props.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.props.Document._nativeHeight) : `${100 / this.contentScaling}%`,
                 transform: `scale(${this.props.ContentScaling()})`
             }}  >
             {this.pdfViewerDiv}
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index d8b1913ee..b66f0461d 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -65,8 +65,11 @@ export class Uploader extends React.Component {
                             }
                             const path = Utils.prepend(result.accessPaths.agnostic.client);
                             let doc = null;
+                            console.log("type: " + file.type);
                             if (file.type === "video/mp4") {
                                 doc = Docs.Create.VideoDocument(path, { _nativeWidth: 200, _width: 200, title: name });
+                            } else if (file.type === "application/pdf") {
+                                doc = Docs.Create.PdfDocument(path, { _width: 200, title: name });
                             } else {
                                 doc = Docs.Create.ImageDocument(path, { _nativeWidth: 200, _width: 200, title: name });
                             }
@@ -175,7 +178,7 @@ export class Uploader extends React.Component {
     private get uploadInterface() {
         return (
             <div className="imgupload_cont">
-                <input type="file" accept="video/*,image/*" className="inputFile" id="input_image_file" ref={inputRef} onChange={this.inputLabel} multiple></input>
+                <input type="file" accept="application/pdf, video/*,image/*" className="inputFile" id="input_image_file" ref={inputRef} onChange={this.inputLabel} multiple></input>
                 <label className="file" id="label" htmlFor="input_image_file">{this.nm}</label>
                 <div className="upload_label" onClick={this.onClick}>
                     <FontAwesomeIcon icon="upload" size="sm" />
@@ -217,4 +220,3 @@ export class Uploader extends React.Component {
 }
 
 
-// DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload");
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 5acdc1dea..644535179 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -1,7 +1,7 @@
 import * as React from "react";
 import { library } from '@fortawesome/fontawesome-svg-core';
 import {
-    faTasks, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+    faTasks, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
     faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
     faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
     faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
@@ -17,7 +17,7 @@ import { FieldValue, Cast } from '../fields/Types';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { emptyPath, emptyFunction, returnFalse, returnOne, returnTrue, returnZero, Utils } from '../Utils';
 import { DocServer } from '../client/DocServer';
-import { Docs } from '../client/documents/Documents';
+import { Docs, DocumentOptions } from '../client/documents/Documents';
 import { Scripting } from '../client/util/Scripting';
 import { DocumentView } from '../client/views/nodes/DocumentView';
 import { Transform } from '../client/util/Transform';
@@ -36,8 +36,11 @@ import GestureOverlay from "../client/views/GestureOverlay";
 import { ScriptField } from "../fields/ScriptField";
 import InkOptionsMenu from "../client/views/collections/collectionFreeForm/InkOptionsMenu";
 import { RadialMenu } from "../client/views/nodes/RadialMenu";
+import { UndoManager } from "../client/util/UndoManager";
+import { MainView } from "../client/views/MainView";
+import { List } from "../fields/List";
 
-library.add(faTasks, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+library.add(faTasks, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
     faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
     faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
     faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
@@ -149,7 +152,7 @@ export class MobileInterface extends React.Component {
      * Return 'Home", which implies returning to 'Home' buttons
      */
     returnHome = () => {
-        if (this._homeMenu === false || this.sidebarActive === true) {
+        if (this._homeMenu || this.sidebarActive) {
             this._homeMenu = true;
             this._parents = [];
             this._activeDoc = this._homeDoc;
@@ -220,7 +223,7 @@ export class MobileInterface extends React.Component {
      */
     handleClick = async (doc: Doc) => {
         const children = DocListCast(doc.data);
-        if (doc.type !== "collection") {
+        if (doc.type !== "collection" && this.sidebarActive) {
             this._parents.push(this._activeDoc);
             this._activeDoc = doc;
             this.switchCurrentView((userDoc: Doc) => doc);
@@ -333,7 +336,7 @@ export class MobileInterface extends React.Component {
 
     // Renders the contents of the menu and sidebar
     renderDefaultContent = () => {
-        if (this._homeMenu === true) {
+        if (this._homeMenu) {
             return (
                 <div>
                     <div className="navbar">
@@ -351,7 +354,7 @@ export class MobileInterface extends React.Component {
                         <div className="sidebarButtons">
                             <div
                                 className="item"
-                                onClick={() => ScriptField.MakeScript("createNewWorkspace()")}>Create New Workspace
+                                onClick={() => MainView.Instance?.createNewWorkspace()}>Create New Workspace
                             </div>
                         </div>
                     </div>
@@ -403,7 +406,10 @@ export class MobileInterface extends React.Component {
                                 {buttons}
                                 <div
                                     className="item"
-                                    onClick={() => ScriptField.MakeScript("createNewWorkspace()")}>Create New Workspace
+                                    style={{ opacity: 0.7 }}
+                                    onClick={() => this.createNewWorkspace()}>
+                                    <FontAwesomeIcon className="right" icon="plus" size="lg" />
+                                    <div className="item-type">Create New Workspace</div>
                                 </div>
                             </>
                         }
@@ -413,6 +419,32 @@ export class MobileInterface extends React.Component {
         );
     }
 
+    /**
+     * Handles the Create New Workspace button in the menu
+     */
+    @action
+    createNewWorkspace = async (id?: string) => {
+        const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
+        const workspaceCount = DocListCast(workspaces.data).length + 1;
+        const freeformOptions: DocumentOptions = {
+            x: 0,
+            y: 400,
+            title: "Collection " + workspaceCount,
+            _LODdisable: true
+        };
+        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+
+        const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
+        const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+        const cloneWorkspace = ScriptField.MakeScript(`cloneWorkspace()`);
+        workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneWorkspace!]);
+        workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Workspace Layout"]);
+
+        Doc.AddDocToList(workspaces, "data", workspaceDoc);
+        // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+    }
+
     stop = (e: React.MouseEvent) => {
         e.stopPropagation();
     }
@@ -431,7 +463,7 @@ export class MobileInterface extends React.Component {
         }
     }
 
-    // Button for switching between pan and ink mode
+    // Button for switching between pen and ink mode
     @action
     onSwitchInking = () => {
         const button = document.getElementById("inkButton") as HTMLElement;
@@ -465,6 +497,16 @@ export class MobileInterface extends React.Component {
         if (this._activeDoc._viewType === "docking") {
             return (
                 <>
+                    {/* <div className="docButton"
+                        id="undoButton"
+                        title="undo"
+                        onClick={(e: React.MouseEvent) => {
+                            UndoManager.Undo();
+                            e.stopPropagation();
+                        }}>
+                        <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="undo-alt"
+                        />
+                    </div> */}
                     <div className="docButton"
                         id="inkButton"
                         title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
@@ -472,6 +514,16 @@ export class MobileInterface extends React.Component {
                         <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="pen-nib"
                         />
                     </div>
+                    {/* <div className="docButton"
+                        id="redoButton"
+                        title="redo"
+                        onClick={(e: React.MouseEvent) => {
+                            UndoManager.Redo();
+                            e.stopPropagation();
+                        }}>
+                        <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="redo-alt"
+                        />
+                    </div> */}
                 </>);
         }
     }
@@ -645,6 +697,12 @@ export class MobileInterface extends React.Component {
         );
     }
 
+    displayRadialMenu = () => {
+        if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc) {
+            return <RadialMenu />;
+        }
+    }
+
     onDragOver = (e: React.DragEvent) => {
         e.preventDefault();
         e.stopPropagation();
@@ -669,12 +727,14 @@ export class MobileInterface extends React.Component {
                     {this.displayWorkspaces()}
                     {this.renderDefaultContent()}
                 </GestureOverlay>
-                <RadialMenu />
+                {this.displayRadialMenu()}
             </div>
         );
     }
 }
 
+
+
 Scripting.addGlobal(function switchMobileView(doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); });
 Scripting.addGlobal(function openMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); });
 Scripting.addGlobal(function toggleMobileSidebar() { return MobileInterface.Instance.toggleSidebar(); });
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index d2a9e9cce..b0b5a484a 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -55,7 +55,8 @@ async function CreateThumbnail(coreFilename: string, pageNum: number, res: expre
     const documentProxy = await Pdfjs.getDocument(sourcePath).promise;
     const factory = new NodeCanvasFactory();
     const page = await documentProxy.getPage(pageNum);
-    const viewport = page.getViewport(1 as any);
+    const scale = 1;
+    const viewport = page.getViewport(scale as any);
     const { canvas, context } = factory.create(viewport.width, viewport.height);
     const renderContext = {
         canvasContext: context,
-- 
cgit v1.2.3-70-g09d2


From 48e841282b896a0961d595281211b276b7a9d345 Mon Sep 17 00:00:00 2001
From: geireann <60007097+geireann@users.noreply.github.com>
Date: Fri, 26 Jun 2020 22:06:33 +0800
Subject: audio dictation

---
 src/client/util/CurrentUserUtils.ts                |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   4 +
 src/mobile/AudioUpload.tsx                         |  10 +-
 src/mobile/MobileInterface.tsx                     | 116 +++++++--------------
 4 files changed, 48 insertions(+), 84 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 823a99436..e402718bf 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -474,7 +474,7 @@ export class CurrentUserUtils {
 
     static setupMobileButtons(doc?: Doc, buttons?: string[]) {
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
-            { title: "WORKSPACES", icon: "bars", click: 'openMobileWorkspaces()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " },
+            { title: "WORKSPACES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " },
             { title: "UPLOAD", icon: "upload", click: 'uploadImageMobile()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
             { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploads()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." },
             { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." },
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3eecc98a4..b5591ca02 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -505,6 +505,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     recordDictation = () => {
+        console.log("recording dictation");
+        console.log(this._editorView);
         DictationManager.Controls.listen({
             interimHandler: this.setCurrentBulletContent,
             continuous: { indefinite: false },
@@ -523,6 +525,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     recordBullet = async () => {
+        console.log("recording bullet");
         const completedCue = "end session";
         const results = await DictationManager.Controls.listen({
             interimHandler: this.setCurrentBulletContent,
@@ -539,6 +542,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     setCurrentBulletContent = (value: string) => {
         if (this._editorView) {
+            console.log("this._editorView");
             const state = this._editorView.state;
             const now = Date.now();
             let mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(now / 1000) });
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index f859b0ba3..83da41d90 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -19,6 +19,9 @@ import { nullAudio } from '../fields/URLField';
 import { Transform } from '../client/util/Transform';
 import { DocumentView } from '../client/views/nodes/DocumentView';
 import { MobileInterface } from './MobileInterface';
+import { DictationOverlay } from '../client/views/DictationOverlay';
+import RichTextMenu from '../client/views/nodes/formattedText/RichTextMenu';
+import { ContextMenu } from '../client/views/ContextMenu';
 
 export interface ImageUploadProps {
     Document: Doc;
@@ -57,6 +60,9 @@ export class AudioUpload extends React.Component {
     private get uploadInterface() {
         return (
             <>
+                <ContextMenu />
+                <DictationOverlay />
+                <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
                 <div className="closeUpload" onClick={() => this.closeUpload()}>
                     <FontAwesomeIcon icon="window-close" size={"lg"} />
                 </div>
@@ -66,10 +72,10 @@ export class AudioUpload extends React.Component {
                         Document={this._audioCol}
                         DataDoc={undefined}
                         LibraryPath={emptyPath}
-                        addDocument={returnFalse}
+                        addDocument={undefined}
                         addDocTab={returnFalse}
                         pinToPres={emptyFunction}
-                        rootSelected={returnFalse}
+                        rootSelected={returnTrue}
                         removeDocument={undefined}
                         docFilters={returnEmptyFilter}
                         onClick={undefined}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index ed5076402..90672e19f 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -45,6 +45,8 @@ import { Cast, FieldValue } from '../fields/Types';
 import { CollectionView } from '../client/views/collections/CollectionView';
 import { InkingStroke } from '../client/views/InkingStroke';
 import RichTextMenu from "../client/views/nodes/formattedText/RichTextMenu";
+import { AudioBox } from "../client/views/nodes/AudioBox";
+import { FormattedTextBox } from "../client/views/nodes/formattedText/FormattedTextBox";
 
 library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
     faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
@@ -80,13 +82,22 @@ export class MobileInterface extends React.Component {
 
     @action
     componentDidMount = () => {
-        Doc.UserDoc().activeMobile = this._homeDoc;
+        // Doc.UserDoc().activeMobile = this._homeDoc;
         this._homeDoc._viewType === "stacking" ? this.menuListView = true : this.menuListView = false;
         Doc.SetSelectedTool(InkTool.None);
-        this.switchCurrentView((userDoc: Doc) => this._homeDoc);
+        Doc.UserDoc().activeMobile = this._homeDoc;
+        AudioBox.Enabled = true;
 
         document.removeEventListener("dblclick", this.onReactDoubleClick);
         document.addEventListener("dblclick", this.onReactDoubleClick);
+        document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on 
+            const id = FormattedTextBox.GetDocFromUrl(e.detail);
+            DocServer.GetRefField(id).then(doc => {
+                if (doc instanceof Doc) {
+                    DocumentManager.Instance.jumpToDocument(doc, false, undefined);
+                }
+            });
+        });
     }
 
     @action
@@ -101,10 +112,14 @@ export class MobileInterface extends React.Component {
 
     // Switch the mobile view to the given doc
     @action
-    switchCurrentView = (doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => {
+    switchCurrentView = (doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => {
         if (!this.userDoc) return;
-
-        Doc.UserDoc().activeMobile = doc(this.userDoc);
+        if (this._activeDoc === this._homeDoc) {
+            this._parents.push(this._activeDoc);
+            this._homeMenu = false;
+        }
+        this._activeDoc = doc;
+        Doc.UserDoc().activeMobile = doc;
         onSwitch && onSwitch();
 
         this.renderView = renderView;
@@ -118,21 +133,10 @@ export class MobileInterface extends React.Component {
      * Method called when 'Library' button is pressed on the home screen
      */
     switchToLibrary = async () => {
-        this._parents.push(this._activeDoc);
-        this.switchCurrentView((userDoc: Doc) => this._library);
-        this._activeDoc = this._library;
+        this.switchCurrentView(this._library);
         this._homeMenu = false;
         this.toggleSidebar();
         //setTimeout(this.toggleSidebar, 300);
-
-    }
-
-    openWorkspaces = () => {
-        this._parents.push(this._activeDoc);
-        this.switchCurrentView((userDoc: Doc) => this._library);
-        this._activeDoc = this._library;
-        this._homeMenu = false;
-        this.sidebarActive = true;
     }
 
     /**
@@ -144,24 +148,20 @@ export class MobileInterface extends React.Component {
 
         if (doc === Cast(this._library, Doc) as Doc) {
             this._child = null;
-            this.userDoc.activeMobile = this._library;
+            this.switchCurrentView(this._library);
         } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
             this._homeMenu = true;
             this._parents = [];
-            this._activeDoc = this._homeDoc;
             this._child = null;
-            this.switchCurrentView((userDoc: Doc) => this._homeDoc);
+            this.switchCurrentView(this._homeDoc);
         } else {
             if (doc) {
                 this._child = doc;
-                this.switchCurrentView((userDoc: Doc) => doc);
+                this.switchCurrentView(doc);
                 this._homeMenu = false;
                 header.textContent = String(doc.title);
             }
         }
-        if (doc) {
-            this._activeDoc = doc;
-        }
         this._ink = false;
     }
 
@@ -172,9 +172,8 @@ export class MobileInterface extends React.Component {
         if (!this._homeMenu || this.sidebarActive) {
             this._homeMenu = true;
             this._parents = [];
-            this._activeDoc = this._homeDoc;
             this._child = null;
-            this.switchCurrentView((userDoc: Doc) => this._homeDoc);
+            this.switchCurrentView(this._homeDoc);
         }
         if (this.sidebarActive) {
             this.toggleSidebar();
@@ -186,8 +185,7 @@ export class MobileInterface extends React.Component {
      */
     returnMain = () => {
         this._parents = [this._homeDoc];
-        this._activeDoc = this._library;
-        this.switchCurrentView((userDoc: Doc) => this._library);
+        this.switchCurrentView(this._library);
         this._homeMenu = false;
         this._child = null;
     }
@@ -244,21 +242,17 @@ export class MobileInterface extends React.Component {
      * Navigates to the given doc and updates the sidebar.
      * @param doc: doc for which the method is called
      */
-    @undoBatch
     handleClick = async (doc: Doc) => {
         const children = DocListCast(doc.data);
         if (doc.type !== "collection" && this.sidebarActive) {
             this._parents.push(this._activeDoc);
-            this._activeDoc = doc;
-            this.switchCurrentView((userDoc: Doc) => doc);
+            this.switchCurrentView(doc);
             this._homeMenu = false;
             this.toggleSidebar();
         }
-        else if (doc.type === "collection" && children.length === 0) this.openFromSidebar(doc);
         else {
             this._parents.push(this._activeDoc);
-            this._activeDoc = doc;
-            this.switchCurrentView((userDoc: Doc) => doc);
+            this.switchCurrentView(doc);
             this._homeMenu = false;
             this._child = doc;
         }
@@ -266,8 +260,7 @@ export class MobileInterface extends React.Component {
 
     openFromSidebar = (doc: Doc) => {
         this._parents.push(this._activeDoc);
-        this._activeDoc = doc;
-        this.switchCurrentView((userDoc: Doc) => doc);
+        this.switchCurrentView(doc);
         this._homeMenu = false;
         this._child = doc; //?
         this.toggleSidebar();
@@ -354,16 +347,14 @@ export class MobileInterface extends React.Component {
     // Handles when user clicks on a document in the pathbar
     handlePathClick = (doc: Doc, index: number) => {
         if (doc === this._library) {
-            this._activeDoc = doc;
             this._child = null;
-            this.switchCurrentView((userDoc: Doc) => doc);
+            this.switchCurrentView(doc);
             this._parents.length = index;
         } else if (doc === this._homeDoc) {
             this.returnHome();
         } else {
-            this._activeDoc = doc;
             this._child = doc;
-            this.switchCurrentView((userDoc: Doc) => doc);
+            this.switchCurrentView(doc);
             this._parents.length = index;
         }
     }
@@ -610,31 +601,6 @@ export class MobileInterface extends React.Component {
         }
     }
 
-    // Mobile audio doc
-    recordAudio = async () => {
-        // upload to server with known URL
-        if (this._activeDoc.title !== "mobile audio") {
-            this._parents.push(this._activeDoc);
-        }
-        const audioDoc = Cast(Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "mobile audio" }), Doc) as Doc;
-        if (audioDoc) {
-            this._activeDoc = audioDoc;
-            this.switchCurrentView((userDoc: Doc) => audioDoc);
-            this._homeMenu = false;
-        }
-    }
-
-    // // Pushing the audio doc onto Dash Web through the right side bar
-    // uploadAudio = () => {
-    //     const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
-    //     const audioDoc = this._activeDoc;
-    //     const data = Cast(audioRightSidebar.data, listSpec(Doc));
-
-    //     if (data) {
-    //         data.push(audioDoc);
-    //     }
-    // }
-
     // Button for pinning images to presentation
     pinToPresentation = () => {
         // Only making button available if it is an image
@@ -719,15 +685,11 @@ export class MobileInterface extends React.Component {
 
     // For setting up the presentation document for the home menu
     setupDefaultPresentation = () => {
-        if (this._activeDoc.title !== "Presentation") {
-            this._parents.push(this._activeDoc);
-        }
 
         const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
 
         if (presentation) {
-            this._activeDoc = presentation;
-            this.switchCurrentView((userDoc: Doc) => presentation);
+            this.switchCurrentView(presentation);
             this._homeMenu = false;
         }
     }
@@ -801,13 +763,8 @@ export class MobileInterface extends React.Component {
     }
 
     switchToMobileUploads = () => {
-        if (this._activeDoc.title !== "Presentation") {
-            this._parents.push(this._activeDoc);
-        }
         const mobileUpload = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
-        console.log(mobileUpload.title);
-        this._activeDoc = mobileUpload;
-        this.switchCurrentView((userDoc: Doc) => mobileUpload);
+        this.switchCurrentView(mobileUpload);
         this._homeMenu = false;
     }
 
@@ -831,9 +788,7 @@ export class MobileInterface extends React.Component {
                         {this.undo()}
                         {this.drawInk()}
                         {this.redo()}
-                        {/* {this.upload()} */}
                         {this.uploadImageButton()}
-                        {/* {this.uploadAudioButton()} */}
                     </div>
                     {this.displayWorkspaces()}
                     {this.renderDefaultContent()}
@@ -846,14 +801,13 @@ export class MobileInterface extends React.Component {
 
 
 
-Scripting.addGlobal(function switchMobileView(doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
+Scripting.addGlobal(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
     "changes the active document displayed on the mobile, (doc: any)");
 Scripting.addGlobal(function openMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); },
     "opens the presentation on mobile");
 Scripting.addGlobal(function toggleMobileSidebar() { return MobileInterface.Instance.toggleSidebar(); });
 Scripting.addGlobal(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); });
 Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Instance.open(); });
-Scripting.addGlobal(function openMobileWorkspaces() { return MobileInterface.Instance.openWorkspaces(); });
 Scripting.addGlobal(function uploadImageMobile() { return MobileInterface.Instance.toggleUpload(); });
 Scripting.addGlobal(function switchToMobileUploads() { return MobileInterface.Instance.switchToMobileUploads(); });
-Scripting.addGlobal(function switchToLibrary() { return MobileInterface.Instance.switchToLibrary(); });
\ No newline at end of file
+Scripting.addGlobal(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 24ac6997048932c4f2717651e992f744e6a5276a Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Wed, 1 Jul 2020 17:42:38 -0500
Subject: started clicking mode popup

---
 src/client/views/MainView.tsx                      |  4 +--
 .../views/collections/CollectionLinearView.scss    | 42 +++++++++++++++-------
 .../views/collections/CollectionLinearView.tsx     | 25 ++++++++++++-
 src/client/views/linking/LinkMenu.tsx              |  6 ++--
 src/client/views/linking/LinkMenuItem.tsx          |  3 +-
 src/client/views/nodes/DocumentLinksButton.tsx     | 31 +++++++++++++---
 src/client/views/nodes/DocumentView.tsx            |  1 +
 .../views/nodes/formattedText/FormattedTextBox.tsx | 36 +++++++++++++++++++
 8 files changed, 124 insertions(+), 24 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f6db1af66..662232810 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -4,7 +4,7 @@ import {
     faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
     faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
     faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
-    faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
+    faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle,
     faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight
 } from '@fortawesome/free-solid-svg-icons';
 import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
@@ -138,7 +138,7 @@ export class MainView extends React.Component {
         }
 
         library.add(faTasks, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
-            faTerminal, faToggleOn, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, fileSolid,
+            faTerminal, faToggleOn, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faTimesCircle, faWindowMaximize, faAddressCard, fileSolid,
             faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
             faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
             faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell,
diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss
index 123a27deb..90c96e08b 100644
--- a/src/client/views/collections/CollectionLinearView.scss
+++ b/src/client/views/collections/CollectionLinearView.scss
@@ -1,12 +1,27 @@
 @import "../globalCssVariables";
 @import "../_nodeModuleOverrides";
 
-.collectionLinearView-outer{
+.collectionLinearView-outer {
     overflow: hidden;
-    height:100%;
+    height: 100%;
+
     .collectionLinearView {
-        display:flex;
+        display: flex;
         height: 100%;
+
+        >span {
+            margin-top: "auto";
+            margin-bottom: "auto";
+            background: $dark-color;
+            color: $light-color;
+            display: inline-block;
+            border-radius: 18px;
+            margin-top: auto;
+            margin-bottom: auto;
+            margin-right: 3px;
+            cursor: pointer;
+        }
+
         >label {
             margin-top: "auto";
             margin-bottom: "auto";
@@ -17,15 +32,15 @@
             font-size: 12.5px;
             width: 18px;
             height: 18px;
-            margin-top:auto;
-            margin-bottom:auto;
+            margin-top: auto;
+            margin-bottom: auto;
             margin-right: 3px;
             cursor: pointer;
             transition: transform 0.2s;
         }
 
         label p {
-            padding-left:5px;
+            padding-left: 5px;
         }
 
         label:hover {
@@ -36,13 +51,14 @@
         >input {
             display: none;
         }
+
         >input:not(:checked)~.collectionLinearView-content {
             display: none;
         }
 
         >input:checked~label {
             transform: rotate(45deg);
-            transition: transform 0.5s;
+transition: transform 0.5s;
             cursor: pointer;
         }
 
@@ -52,12 +68,14 @@
             position: relative;
             margin-top: auto;
 
-            .collectionLinearView-docBtn, .collectionLinearView-docBtn-scalable {
-                position:relative;
-                margin:auto;
+            .collectionLinearView-docBtn,
+            .collectionLinearView-docBtn-scalable {
+                position: relative;
+                margin: auto;
                 margin-left: 3px;
                 transform-origin: center 80%;
             }
+
             .collectionLinearView-docBtn-scalable:hover {
                 transform: scale(1.15);
             }
@@ -68,10 +86,10 @@
                 border-radius: 18px;
                 font-size: 15px;
             }
-            
+
             .collectionLinearView-round-button:hover {
                 transform: scale(1.15);
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index f38eeaf41..29deb906a 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -13,6 +13,8 @@ import { CollectionSubView } from './CollectionSubView';
 import { DocumentView } from '../nodes/DocumentView';
 import { documentSchema } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
+import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 
 
 type LinearDocument = makeInterface<[typeof documentSchema,]>;
@@ -75,6 +77,11 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
         return new Transform(-translateX, -translateY, 1);
     }
 
+    @action
+    exitLongLinks = () => {
+        DocumentLinksButton.StartLink = undefined
+    }
+
     render() {
         const guid = Utils.GenerateGuid();
         const flexDir: any = StrCast(this.Document.flexDirection);
@@ -82,7 +89,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
         const color = StrCast(this.props.Document.color, "white");
         return <div className="collectionLinearView-outer">
             <div className="collectionLinearView" ref={this.createDashEventsTarget} >
-                <label htmlFor={`${guid}`} title="Close Menu" style={{ background: backgroundColor === color ? "black" : backgroundColor }}
+                <label htmlFor={`${guid}`} title="Close Menu" style={{
+                    background: backgroundColor === color ? "black" : backgroundColor,
+                    // width: "18px", height: "18px", fontSize: "12.5px",
+                    // transition: this.props.Document.linearViewIsExpanded ? "transform 0.2s" : "transform 0.5s",
+                    // transform: this.props.Document.linearViewIsExpanded ? "" : "rotate(45deg)"
+                }}
                     onPointerDown={e => e.stopPropagation()} >
                     <p>+</p>
                 </label>
@@ -130,6 +142,17 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                         </div>;
                     })}
                 </div>
+                {DocumentLinksButton.StartLink ? <span style={{
+                    background: backgroundColor === color ? "black" : backgroundColor,
+                }}
+                    onPointerDown={e => e.stopPropagation()} >
+                    <span style={{ fontSize: "7px", display: "inline-block" }}>
+                        Creating Link From: {DocumentLinksButton.StartLink.title} </span>
+
+                    <FontAwesomeIcon icon="times-circle" size="lg" style={{ color: "red" }}
+                        onClick={this.exitLongLinks} />
+
+                </span> : null}
             </div>
         </div>;
     }
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 2a9bbed39..c36e7b67c 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -32,9 +32,9 @@ export class LinkMenu extends React.Component<Props> {
             DocumentLinksButton.EditLink = undefined;
         }
 
-        // if (this._linkMenuRef && !this._linkMenuRef.contains(e.target)) {
-        //     DocumentLinksButton.EditLink = undefined;
-        // }
+        if (this._linkMenuRef && !this._linkMenuRef.contains(e.target as any)) {
+            DocumentLinksButton.EditLink = undefined;
+        }
     }
     @action
     componentDidMount() {
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index d5d31d1ba..54dca261d 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -137,6 +137,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
     @action.bound
     async followDefault() {
+        console.log("FOLLOWWW");
         DocumentLinksButton.EditLink = undefined;
         LinkDocPreview.LinkInfo = undefined;
         DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false);
@@ -174,7 +175,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                             {/* <div title="Edit link" className="button" ref={this._editRef} onPointerDown={this.onEdit}><FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div> */}
                             <div title="Delete link" className="button" ref={this._editRef} onPointerDown={this.deleteLink}>
                                 <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" /></div>
-                            <div title="Follow link" className="button" onClick={this.followDefault} onContextMenu={this.onContextMenu}>
+                            <div title="Follow link" className="button" onClick={e => this.followDefault()} onContextMenu={this.onContextMenu}>
                                 <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" />
                             </div>
                         </div>
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index c625c4cd6..d04cdd34c 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -24,6 +24,8 @@ interface DocumentLinksButtonProps {
 export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
     private _linkButton = React.createRef<HTMLDivElement>();
 
+    @observable public static StartLink: DocumentView | undefined;
+
     @action
     onLinkButtonMoved = (e: PointerEvent) => {
         if (this._linkButton.current !== null) {
@@ -51,11 +53,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         return false;
     }
 
-    @observable static StartLink: DocumentView | undefined;
     onLinkButtonDown = (e: React.PointerEvent): void => {
-
         setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
-            if (doubleTap) {
+            if (doubleTap && this.props.InMenu) {
                 DocumentLinksButton.StartLink = this.props.View;
             } else if (!!!this.props.InMenu) {
                 DocumentLinksButton.EditLink = this.props.View;
@@ -63,6 +63,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             }
         }));
     }
+
+    @action
+    onLinkClick = (e: React.MouseEvent): void => {
+        if (this.props.InMenu) {
+            DocumentLinksButton.StartLink = this.props.View;
+        } else if (!!!this.props.InMenu) {
+            DocumentLinksButton.EditLink = this.props.View;
+            DocumentLinksButton.EditLinkLoc = [e.clientX + 10, e.clientY];
+        }
+    }
+
     completeLink = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e, doubleTap) => {
             if (doubleTap) {
@@ -76,6 +87,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         }));
     }
 
+    finishLinkClick = () => {
+        if (DocumentLinksButton.StartLink === this.props.View) {
+            DocumentLinksButton.StartLink = undefined;
+        } else {
+            DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
+                DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
+        }
+    }
+
     @observable
     public static EditLink: DocumentView | undefined;
     public static EditLinkLoc: number[] = [0, 0];
@@ -86,7 +106,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         return (!links.length || links[0].hidden) && !this.props.AlwaysOn ? (null) :
             <div title="Drag(create link) Tap(view links)" ref={this._linkButton} style={{ minWidth: 20, minHeight: 20, position: "absolute", left: this.props.Offset?.[0] }}>
                 <div className={"documentLinksButton"} style={{ backgroundColor: DocumentLinksButton.StartLink ? "transparent" : "" }}
-                    onPointerDown={this.onLinkButtonDown}
+                    onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick}
                 // onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
                 // onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = {
                 //     addDocTab: this.props.View.props.addDocTab,
@@ -97,7 +117,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 >
                     {links.length && !!!this.props.InMenu ? links.length : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
                 </div>
-                {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ? <div className={"documentLinksButton-endLink"} onPointerDown={this.completeLink} /> : (null)}
+                {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ? <div className={"documentLinksButton-endLink"}
+                    onPointerDown={this.completeLink} onClick={this.finishLinkClick} /> : (null)}
                 {DocumentLinksButton.StartLink === this.props.View ? <div className={"documentLinksButton-startLink"} /> : (null)}
             </div>;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 09eeaee36..6820b2047 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -116,6 +116,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     private holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
+    public get title() { return this.props.Document.title }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
     public get ContentDiv() { return this._mainCont.current; }
     get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2a79e2f13..dc7d8c7ca 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -58,6 +58,7 @@ import { FieldView, FieldViewProps } from "../FieldView";
 import "./FormattedTextBox.scss";
 import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment';
 import React = require("react");
+import { DocumentManager } from '../../../util/DocumentManager';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -1047,6 +1048,40 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
+    @action
+    onDoubleClick = (e: React.MouseEvent): void => {
+
+        this.doLinkOnDeselect();
+        FormattedTextBox._downEvent = true;
+        FormattedTextBoxComment.textBox = this;
+        if (this.props.onClick && e.button === 0 && !this.props.isSelected(false)) {
+            e.preventDefault();
+        }
+        if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+            if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar
+                e.stopPropagation();  // if the text box is selected, then it consumes all down events
+            }
+        }
+        if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
+            e.preventDefault();
+        }
+        FormattedTextBoxComment.Hide();
+        if (FormattedTextBoxComment.linkDoc) {
+            if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
+                this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+            } else {
+                DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document,
+                    (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+            }
+        }
+
+        (e.nativeEvent as any).formattedHandled = true;
+
+        if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) {
+            e.stopPropagation();
+        }
+    }
+
     @action
     onFocused = (e: React.FocusEvent): void => {
         console.log("FOUCSS")
@@ -1317,6 +1352,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                             }
                         }
                     })}
+                    onDoubleClick={this.onDoubleClick}
                 >
                     <div className={`formattedTextBox-outer`} ref={this._scrollRef}
                         style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !this.props.isSelected() ? "none" : undefined }}
-- 
cgit v1.2.3-70-g09d2


From fcbf3a161839111ac609ec737cd56c71efde18a1 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 1 Jul 2020 21:13:10 -0400
Subject: added basic synchronizatoin between links and their hypertext
 anchors.  more work is needed to handle overlapping anchors.

---
 src/client/util/LinkManager.ts                     |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 39 ++++++++++++++-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 32 ++++---------
 .../nodes/formattedText/prosemirrorPatches.js      | 55 ++++++++++++++++++++++
 4 files changed, 103 insertions(+), 25 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 9b4dc2630..749fabfcc 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -49,7 +49,7 @@ export class LinkManager {
     }
 
     public deleteLink(linkDoc: Doc): boolean {
-        if (LinkManager.Instance.LinkManagerDoc) {
+        if (LinkManager.Instance.LinkManagerDoc && linkDoc instanceof Doc) {
             Doc.RemoveDocFromList(LinkManager.Instance.LinkManagerDoc, "data", linkDoc);
             return true;
         }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 664141607..f32c17563 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -8,7 +8,7 @@ import { baseKeymap, selectAll } from "prosemirror-commands";
 import { history } from "prosemirror-history";
 import { inputRules } from 'prosemirror-inputrules';
 import { keymap } from "prosemirror-keymap";
-import { Fragment, Mark, Node, Slice } from "prosemirror-model";
+import { Fragment, Mark, Node, Slice, Schema } from "prosemirror-model";
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state";
 import { ReplaceStep } from 'prosemirror-transform';
 import { EditorView } from "prosemirror-view";
@@ -16,6 +16,7 @@ import { DateField } from '../../../../fields/DateField';
 import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclSym } from "../../../../fields/Doc";
 import { documentSchema } from '../../../../fields/documentSchemas';
 import applyDevTools = require("prosemirror-dev-tools");
+import { removeMarkWithAttrs } from "./prosemirrorPatches";
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
@@ -56,7 +57,7 @@ import { DocumentButtonBar } from '../../DocumentButtonBar';
 import { AudioBox } from '../AudioBox';
 import { FieldView, FieldViewProps } from "../FieldView";
 import "./FormattedTextBox.scss";
-import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment';
+import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark } from './FormattedTextBoxComment';
 import React = require("react");
 
 library.add(faEdit);
@@ -87,6 +88,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     private _editorView: Opt<EditorView>;
     private _applyingChange: boolean = false;
     private _searchIndex = 0;
+    private _cachedLinks: Doc[] = [];
     private _undoTyping?: UndoManager.Batch;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _dropDisposer?: DragManager.DragDropDisposer;
@@ -141,6 +143,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
 
+    // removes all hyperlink anchors for the removed linkDoc
+    // TODO: bcz: Argh... if a section of text has multiple anchors, this should just remove the intended one. 
+    // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing.
+    public RemoveLinkFromDoc(linkDoc?: Doc) {
+        const state = this._editorView?.state;
+        if (state && linkDoc && this._editorView) {
+            var allHrefs: any[] = [];
+            state.doc.nodesBetween(0, state.doc.nodeSize - 2, (node: any, pos: number, parent: any) => {
+                const foundMark = findLinkMark(node.marks);
+                const newHrefs = foundMark?.attrs.allHrefs.filter((a: any) => a.href.includes(linkDoc[Id])) || [];
+                allHrefs = newHrefs.length ? newHrefs : allHrefs;
+                return true;
+            });
+            if (allHrefs.length) {
+                this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.link, { allHrefs }));
+            }
+        }
+    }
+    // removes all the specified link referneces from the selection. 
+    // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references.
+    public RemoveLinkFromSelection(allHrefs: { href: string, title: string, linkId: string, targetId: string }[]) {
+        const state = this._editorView?.state;
+        if (state && this._editorView) {
+            this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allHrefs }));
+        }
+    }
+
     linkOnDeselect: Map<string, string> = new Map();
 
     doLinkOnDeselect() {
@@ -626,6 +655,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
     componentDidMount() {
+        this._cachedLinks = DocListCast(this.Document.links);
+        this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
+            newLinks => {
+                this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l));
+                this._cachedLinks = newLinks;
+            });
         this._disposers.buttonBar = reaction(
             () => DocumentButtonBar.Instance,
             instance => {
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 0fcddfc43..6366f299d 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -772,28 +772,16 @@ export default class RichTextMenu extends AntimodeMenu {
     }
 
     deleteLink = () => {
-        if (!this.view) return;
-
-        const node = this.view.state.selection.$from.nodeAfter;
-        const link = node && node.marks.find(m => m.type === this.view!.state.schema.marks.link);
-        const href = link!.attrs.allHrefs.length > 0 ? link!.attrs.allHrefs[0].href : undefined;
-        if (href) {
-            if (href.indexOf(Utils.prepend("/doc/")) === 0) {
-                const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
-                if (linkclicked) {
-                    DocServer.GetRefField(linkclicked).then(async linkDoc => {
-                        if (linkDoc instanceof Doc) {
-                            LinkManager.Instance.deleteLink(linkDoc);
-                            this.view!.dispatch(this.view!.state.tr.removeMark(this.view!.state.selection.from, this.view!.state.selection.to, this.view!.state.schema.marks.link));
-                        }
-                    });
-                }
-            } else {
-                if (node) {
-                    const { tr, schema, selection } = this.view.state;
-                    const extension = this.linkExtend(selection.$anchor, href);
-                    this.view.dispatch(tr.removeMark(extension.from, extension.to, schema.marks.link));
-                }
+        if (this.view) {
+            const link = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.link);
+            if (link) {
+                const allHrefs = link.attrs.allHrefs.slice();
+                this.TextView.RemoveLinkFromSelection(link.attrs.allHrefs);
+                // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected.
+                allHrefs.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => {
+                    const linkId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+                    linkId && DocServer.GetRefField(linkId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc))
+                });
             }
         }
     }
diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js
index 763961958..0969ea4ef 100644
--- a/src/client/views/nodes/formattedText/prosemirrorPatches.js
+++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js
@@ -9,6 +9,7 @@ var prosemirrorModel = require('prosemirror-model');
 exports.liftListItem = liftListItem;
 exports.sinkListItem = sinkListItem;
 exports.wrappingInputRule = wrappingInputRule;
+exports.removeMarkWithAttrs = removeMarkWithAttrs;
 // :: (NodeType) → (state: EditorState, dispatch: ?(tr: Transaction)) → bool
 // Create a command to lift the list item around the selection up into
 // a wrapping list.
@@ -139,3 +140,57 @@ function wrappingInputRule(regexp, nodeType, getAttrs, joinPredicate, customWith
 }
 
 
+// :: ([Mark]) → ?Mark
+// Tests whether there is a mark of this type in the given set.
+function isInSetWithAttrs(mark, set, attrs) {
+    for (var i = 0; i < set.length; i++) {
+        if (set[i].type == mark) {
+            if (Array.from(Object.keys(attrs)).reduce((p, akey) => {
+                return p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey]);
+            }, true)) {
+                return set[i];
+            }
+        }
+    }
+};
+
+// :: (number, number, ?union<Mark, MarkType>) → this
+// Remove marks from inline nodes between `from` and `to`. When `mark`
+// is a single mark, remove precisely that mark. When it is a mark type,
+// remove all marks of that type. When it is null, remove all marks of
+// any type.
+function removeMarkWithAttrs(tr, from, to, mark, attrs) {
+    if (mark === void 0) mark = null;
+
+    var matched = [], step = 0;
+    tr.doc.nodesBetween(from, to, function (node, pos) {
+        if (!node.isInline) { return }
+        step++;
+        var toRemove = null;
+        if (mark) {
+            if (isInSetWithAttrs(mark, node.marks, attrs)) { toRemove = [mark]; }
+        } else {
+            toRemove = node.marks;
+        }
+        if (toRemove && toRemove.length) {
+            var end = Math.min(pos + node.nodeSize, to);
+            for (var i = 0; i < toRemove.length; i++) {
+                var style = toRemove[i], found$1 = (void 0);
+                for (var j = 0; j < matched.length; j++) {
+                    var m = matched[j];
+                    if (m.step == step - 1 && style.eq(matched[j].style)) { found$1 = m; }
+                }
+                if (found$1) {
+                    found$1.to = end;
+                    found$1.step = step;
+                } else {
+                    matched.push({ style: style, from: Math.max(pos, from), to: end, step: step });
+                }
+            }
+        }
+    });
+    matched.forEach(function (m) { return tr.step(new prosemirrorTransform.RemoveMarkStep(m.from, m.to, m.style)); });
+    return tr
+};
+
+
-- 
cgit v1.2.3-70-g09d2


From 19a48bfe9b2087d23ab6bbb384f8edc18ed07d81 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 1 Jul 2020 21:26:35 -0400
Subject: renamed link to linkAnchor and allHrefs to allLinks in rich text code

---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 36 +++++++++++-----------
 .../formattedText/FormattedTextBoxComment.tsx      |  4 +--
 .../views/nodes/formattedText/RichTextMenu.tsx     | 18 +++++------
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/client/views/nodes/formattedText/marks_rts.ts  | 20 ++++++------
 src/fields/RichTextUtils.ts                        |  2 +-
 6 files changed, 41 insertions(+), 41 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f32c17563..9c7c46ae9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -149,24 +149,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     public RemoveLinkFromDoc(linkDoc?: Doc) {
         const state = this._editorView?.state;
         if (state && linkDoc && this._editorView) {
-            var allHrefs: any[] = [];
+            var allLinks: any[] = [];
             state.doc.nodesBetween(0, state.doc.nodeSize - 2, (node: any, pos: number, parent: any) => {
                 const foundMark = findLinkMark(node.marks);
-                const newHrefs = foundMark?.attrs.allHrefs.filter((a: any) => a.href.includes(linkDoc[Id])) || [];
-                allHrefs = newHrefs.length ? newHrefs : allHrefs;
+                const newHrefs = foundMark?.attrs.allLinks.filter((a: any) => a.href.includes(linkDoc[Id])) || [];
+                allLinks = newHrefs.length ? newHrefs : allLinks;
                 return true;
             });
-            if (allHrefs.length) {
-                this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.link, { allHrefs }));
+            if (allLinks.length) {
+                this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allLinks }));
             }
         }
     }
     // removes all the specified link referneces from the selection. 
     // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references.
-    public RemoveLinkFromSelection(allHrefs: { href: string, title: string, linkId: string, targetId: string }[]) {
+    public RemoveLinkFromSelection(allLinks: { href: string, title: string, linkId: string, targetId: string }[]) {
         const state = this._editorView?.state;
         if (state && this._editorView) {
-            this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allHrefs }));
+            this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allLinks }));
         }
     }
 
@@ -206,8 +206,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     this.linkOnDeselect.set(key, value);
 
                     const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
-                    const allHrefs = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }];
-                    const link = this._editorView.state.schema.marks.link.create({ allHrefs, location: "onRight", title: value });
+                    const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }];
+                    const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value });
                     const mval = this._editorView.state.schema.marks.metadataVal.create();
                     const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
                     tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
@@ -275,8 +275,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const lastSel = Math.min(flattened.length - 1, this._searchIndex);
             this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
             const alink = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: target }, "automatic")!;
-            const allHrefs = [{ href: Utils.prepend("/doc/" + alink[Id]), title: "a link", targetId: target[Id], linkId: alink[Id] }];
-            const link = this._editorView.state.schema.marks.link.create({ allHrefs, title: "a link", location });
+            const allLinks = [{ href: Utils.prepend("/doc/" + alink[Id]), title: "a link", targetId: target[Id], linkId: alink[Id] }];
+            const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, title: "a link", location });
             this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link));
         }
     }
@@ -643,9 +643,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
             sel.from !== sel.to && tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
                 if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
-                    const allHrefs = [{ href, title, targetId, linkId }];
-                    allHrefs.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.link.name)?.attrs.allHrefs ?? []));
-                    const link = state.schema.marks.link.create({ allHrefs, title, location, linkId });
+                    const allLinks = [{ href, title, targetId, linkId }];
+                    allLinks.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.linkAnchor.name)?.attrs.allLinks ?? []));
+                    const link = state.schema.marks.linkAnchor.create({ allLinks, title, location, linkId });
                     tr = tr.addMark(pos, pos + node.nodeSize, link);
                 }
             });
@@ -766,8 +766,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         return node.copy(content.frag);
                     }
                     const marks = [...node.marks];
-                    const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.link);
-                    return linkIndex !== -1 && marks[linkIndex].attrs.allHrefs.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined;
+                    const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor);
+                    return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined;
                 };
 
                 let start = 0;
@@ -949,8 +949,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
             const marks = [...node.marks];
             const linkIndex = marks.findIndex(mark => mark.type.name === "link");
-            const allHrefs = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
-            const link = view.state.schema.mark(view.state.schema.marks.link, { allHrefs, location: "onRight", title, docref: true });
+            const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
+            const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true });
             marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
             return node.mark(marks);
         }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 90f2c0aa6..4c90b6afd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -27,7 +27,7 @@ export function findUserMark(marks: Mark[]): Mark | undefined {
     return marks.find(m => m.attrs.userid);
 }
 export function findLinkMark(marks: Mark[]): Mark | undefined {
-    return marks.find(m => m.type === schema.marks.link);
+    return marks.find(m => m.type === schema.marks.linkAnchor);
 }
 export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) {
     let before = 0;
@@ -182,7 +182,7 @@ export class FormattedTextBoxComment {
             state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
             child = child || (nbef && state.selection.$from.nodeBefore);
             const mark = child ? findLinkMark(child.marks) : undefined;
-            const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allHrefs.find((item: { href: string }) => item.href)?.href || forceUrl;
+            const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href || forceUrl;
             if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
                 FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
                 (FormattedTextBoxComment.tooltipText as any).href = href;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 6366f299d..3ec75e680 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -737,7 +737,7 @@ export default class RichTextMenu extends AntimodeMenu {
         const node = this.view.state.selection.$from.nodeAfter;
         const link = node && node.marks.find(m => m.type.name === "link");
         if (link) {
-            const href = link.attrs.allHrefs.length > 0 ? link.attrs.allHrefs[0].href : undefined;
+            const href = link.attrs.allLinks.length > 0 ? link.attrs.allLinks[0].href : undefined;
             if (href) {
                 if (href.indexOf(Utils.prepend("/doc/")) === 0) {
                     const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
@@ -773,27 +773,27 @@ export default class RichTextMenu extends AntimodeMenu {
 
     deleteLink = () => {
         if (this.view) {
-            const link = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.link);
+            const link = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.linkAnchor);
             if (link) {
-                const allHrefs = link.attrs.allHrefs.slice();
-                this.TextView.RemoveLinkFromSelection(link.attrs.allHrefs);
+                const allLinks = link.attrs.allLinks.slice();
+                this.TextView.RemoveLinkFromSelection(link.attrs.allLinks);
                 // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected.
-                allHrefs.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => {
+                allLinks.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => {
                     const linkId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
-                    linkId && DocServer.GetRefField(linkId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc))
+                    linkId && DocServer.GetRefField(linkId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc));
                 });
             }
         }
     }
 
     linkExtend($start: ResolvedPos, href: string) {
-        const mark = this.view!.state.schema.marks.link;
+        const mark = this.view!.state.schema.marks.linkAnchor;
 
         let startIndex = $start.index();
         let endIndex = $start.indexAfter();
 
-        while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.allHrefs.find((item: { href: string }) => item.href === href)).length) startIndex--;
-        while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.allHrefs.find((item: { href: string }) => item.href === href)).length) endIndex++;
+        while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.allLinks.find((item: { href: string }) => item.href === href)).length) startIndex--;
+        while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.allLinks.find((item: { href: string }) => item.href === href)).length) endIndex++;
 
         let startPos = $start.start();
         let endPos = startPos;
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 612a824fc..ca30dde9d 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -279,7 +279,7 @@ export class RichTextRules {
                                 DocUtils.Publish(target, docid, returnFalse, returnFalse);
                                 DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
                             });
-                            const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
+                            const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
                             return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
                         }
                         return state.tr;
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 54b61aa20..3d7d71b14 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -17,12 +17,12 @@ export const marks: { [index: string]: MarkSpec } = {
             return ["div", { className: "dummy" }, 0];
         }
     },
-    // :: MarkSpec A link. Has `href` and `title` attributes. `title`
+    // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each link has an href URL and a title for use in menus and hover (Dash links have linkIDs & targetIDs). `title`
     // defaults to the empty string. Rendered and parsed as an `<a>`
     // element.
-    link: {
+    linkAnchor: {
         attrs: {
-            allHrefs: { default: [] as { href: string, title: string, linkId: string, targetId: string }[] },
+            allLinks: { default: [] as { href: string, title: string, linkId: string, targetId: string }[] },
             showPreview: { default: true },
             location: { default: null },
             title: { default: null },
@@ -31,22 +31,22 @@ export const marks: { [index: string]: MarkSpec } = {
         inclusive: false,
         parseDOM: [{
             tag: "a[href]", getAttrs(dom: any) {
-                return { allHrefs: [{ href: dom.getAttribute("href"), title: dom.getAttribute("title"), linkId: dom.getAttribute("linkids"), targetId: dom.getAttribute("targetids") }], location: dom.getAttribute("location"), };
+                return { allLinks: [{ href: dom.getAttribute("href"), title: dom.getAttribute("title"), linkId: dom.getAttribute("linkids"), targetId: dom.getAttribute("targetids") }], location: dom.getAttribute("location"), };
             }
         }],
         toDOM(node: any) {
-            const targetids = node.attrs.allHrefs.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, "");
-            const linkids = node.attrs.allHrefs.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, "");
+            const targetids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, "");
+            const linkids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, "");
             return node.attrs.docref && node.attrs.title ?
-                ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allHrefs[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
-                node.attrs.allHrefs.length === 1 ?
-                    ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}`, href: node.attrs.allHrefs[0].href }, 0] :
+                ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allLinks[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
+                node.attrs.allLinks.length === 1 ?
+                    ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href }, 0] :
                     ["div", { class: "prosemirror-anchor" },
                         ["span", { class: "prosemirror-linkBtn" },
                             ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}` }, 0],
                             ["input", { class: "prosemirror-hrefoptions" }],
                         ],
-                        ["div", { class: "prosemirror-links" }, ...node.attrs.allHrefs.map((item: { href: string, title: string }) =>
+                        ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
                             ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
                         )]
                     ];
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 7c7bf3e12..a590c88c4 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -392,7 +392,7 @@ export namespace RichTextUtils {
                     const { attrs } = mark;
                     switch (converted) {
                         case "link":
-                            let url = attrs.allHrefs.length ? attrs.allHrefs[0].href : "";
+                            let url = attrs.allLinks.length ? attrs.allLinks[0].href : "";
                             const delimiter = "/doc/";
                             const alreadyShared = "?sharing=true";
                             if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
-- 
cgit v1.2.3-70-g09d2


From 713b200804fa84d654aeb0051e086641d55d95d9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 2 Jul 2020 10:17:26 -0400
Subject: started some cleanup for Mobile  ..  lookslike workspaces view is
 broken now

---
 src/client/util/CurrentUserUtils.ts                |   3 +-
 src/client/views/AntimodeMenu.tsx                  |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   7 +-
 src/client/views/nodes/PDFBox.tsx                  |   3 -
 src/client/views/nodes/RadialMenu.tsx              |   2 -
 .../views/nodes/formattedText/FormattedTextBox.tsx |   6 +-
 src/mobile/MobileInterface.tsx                     | 393 +++++++++------------
 7 files changed, 170 insertions(+), 246 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e86f72684..6906c0bb3 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -32,7 +32,6 @@ export class CurrentUserUtils {
     public static get MainDocId() { return this.mainDocId; }
     public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
     @computed public static get UserDocument() { return Doc.UserDoc(); }
-    @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).inkPen as Doc; }
 
     @observable public static GuestTarget: Doc | undefined;
     @observable public static GuestWorkspace: Doc | undefined;
@@ -365,7 +364,7 @@ export class CurrentUserUtils {
         } else {
             const templateIconsDoc = Cast(doc["template-icons"], Doc, null);
             const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc,
-            doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc];
+            doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc];
             DocListCastAsync(templateIconsDoc.data).then(async curIcons => {
                 await Promise.all(curIcons!);
                 requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype));
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 3e4d20fea..b8f77932d 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -131,7 +131,7 @@ export default abstract class AntimodeMenu extends React.Component {
     }
 
     protected getDragger = () => {
-        return <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />;
+        return <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />;
     }
 
     protected getElement(buttons: JSX.Element[]) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0ec676379..8d122baeb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -29,7 +29,6 @@ import { SelectionManager } from "../../util/SelectionManager";
 import SharingManager from '../../util/SharingManager';
 import { Transform } from "../../util/Transform";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { CollectionDockingView, DockedFrameRenderer } from "../collections/CollectionDockingView";
 import { CollectionView, CollectionViewType } from '../collections/CollectionView';
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -185,7 +184,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 });
         RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
         // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Pin", event: () => DockedFrameRenderer.PinDoc(this.props.Document), icon: "map-pin", selected: -1 });
+        RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
         RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 });
 
         SelectionManager.DeselectAll();
@@ -945,7 +944,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         recommendations.documentIconHeight = 150;
         recommendations.sourceDoc = this.props.Document;
         recommendations.sourceDocContext = this.props.ContainingCollectionView!.props.Document;
-        CollectionDockingView.AddRightSplit(recommendations, undefined);
+        this.props.addDocTab(recommendations, "onRight");
 
         // RecommendationsBox.Instance.displayRecommendations(e.pageX + 100, e.pageY);
     }
@@ -977,7 +976,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             body.href = urls[i];
             bodies.push(body);
         }
-        CollectionDockingView.AddRightSplit(Docs.Create.SchemaDocument(headers, bodies, { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), undefined);
+        this.props.addDocTab(Docs.Create.SchemaDocument(headers, bodies, { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), "onRight");
         this._showKPQuery = true;
         this._queries = kps.toString();
     }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 54f44a0f9..1c5825a8f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -226,9 +226,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
 
     @computed get contentScaling() { return this.props.ContentScaling(); }
     @computed get renderTitleBox() {
-        console.log("fitWidth ?: " + !(this.props.Document._fitWidth) && (window.screen.width > 600));
-        console.log("_nativeHeight: " + this.Document._nativeHeight);
-        console.log("%: " + `${100 / this.contentScaling}%`);
         const classname = "pdfBox" + (this.active() ? "-interactive" : "");
         return <div className={classname} style={{
             width: !this.props.Document._fitWidth ? this.Document._nativeWidth || 0 : `${100 / this.contentScaling}%`,
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index 2bbb80b2a..a3ac09a11 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -3,7 +3,6 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from "mobx-react";
 import "./RadialMenu.scss";
 import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem";
-import { MobileInterface } from "../../../mobile/MobileInterface";
 
 @observer
 export class RadialMenu extends React.Component {
@@ -26,7 +25,6 @@ export class RadialMenu extends React.Component {
 
 
     catchTouch = (te: React.TouchEvent) => {
-        console.log("caught");
         te.stopPropagation();
         te.preventDefault();
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ae1db76de..270fcb01c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -190,7 +190,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }
             const state = this._editorView.state.apply(tx);
-            this?._editorView?.updateState(state);
+            this._editorView?.updateState(state);
             (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks);
 
             const tsel = this._editorView.state.selection.$from;
@@ -512,8 +512,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     recordDictation = () => {
-        console.log("recording dictation");
-        console.log(this._editorView);
         DictationManager.Controls.listen({
             interimHandler: this.setCurrentBulletContent,
             continuous: { indefinite: false },
@@ -532,7 +530,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     recordBullet = async () => {
-        console.log("recording bullet");
         const completedCue = "end session";
         const results = await DictationManager.Controls.listen({
             interimHandler: this.setCurrentBulletContent,
@@ -549,7 +546,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     setCurrentBulletContent = (value: string) => {
         if (this._editorView) {
-            console.log("this._editorView");
             const state = this._editorView.state;
             const now = Date.now();
             let mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(now / 1000) });
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 1b934df93..34894250a 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -13,7 +13,7 @@ import { action, computed, observable, reaction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../fields/Doc';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils';
 import { Docs, DocumentOptions } from '../client/documents/Documents';
 import { Scripting } from '../client/util/Scripting';
 import { DocumentView } from '../client/views/nodes/DocumentView';
@@ -35,7 +35,6 @@ import { AudioUpload } from "./AudioUpload";
 import { Cast, FieldValue } from '../fields/Types';
 import RichTextMenu from "../client/views/nodes/formattedText/RichTextMenu";
 import { AudioBox } from "../client/views/nodes/AudioBox";
-import { Compute } from "google-auth-library";
 
 library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
     faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
@@ -47,22 +46,22 @@ library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngl
 @observer
 export class MobileInterface extends React.Component {
     @observable static Instance: MobileInterface;
-    @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
-    @observable private mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(this.userDoc);
-    @observable private renderView?: () => JSX.Element;
-    @observable private sidebarActive: boolean = false; //to toggle sidebar display
-    @observable private imageUploadActive: boolean = false; //to toggle image upload
-    @observable private audioUploadActive: boolean = false;
-    @observable private menuListView: boolean = false; //to switch between menu view (list / icon)
+    @observable private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc());
+    @observable private _sidebarActive: boolean = false; //to toggle sidebar display
+    @observable private _imageUploadActive: boolean = false; //to toggle image upload
+    @observable private _audioUploadActive: boolean = false;
+    @observable private _menuListView: boolean = false; //to switch between menu view (list / icon)
     @observable private _ink: boolean = false; //toggle whether ink is being dispalyed
+    @observable private renderView?: () => JSX.Element;
 
-    public _activeDoc: Doc = this.mainDoc; // doc updated as the active mobile page is updated (initially home menu)
-    public _homeDoc: Doc = this.mainDoc; // home menu as a document
+    private _activeDoc: Doc = this._mainDoc; // doc updated as the active mobile page is updated (initially home menu)
+    private _homeDoc: Doc = this._mainDoc; // home menu as a document
     private _homeMenu: boolean = true; // to determine whether currently at home menu
     private _child: Doc | null = null; // currently selected document
     private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
-    private _library: Doc = CurrentUserUtils.setupLibrary(this.userDoc); // to access documents in Dash Web
+    private _library: Doc = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web
+
+    @computed private get mainContainer() { return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -72,7 +71,7 @@ export class MobileInterface extends React.Component {
     @action
     componentDidMount = () => {
         // if the home menu is in list view -> adjust the menu toggle appropriately
-        this._homeDoc._viewType === "stacking" ? this.menuListView = true : this.menuListView = false;
+        this._menuListView = this._homeDoc._viewType === "stacking" ? true : false;
         Doc.SetSelectedTool(InkTool.None); // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
@@ -95,14 +94,14 @@ export class MobileInterface extends React.Component {
     // Switch the mobile view to the given doc
     @action
     switchCurrentView = (doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => {
-        if (!this.userDoc) return;
+        if (!Doc.UserDoc()) return;
         if (this._activeDoc === this._homeDoc) {
             this._parents.push(this._activeDoc);
             this._homeMenu = false;
         }
         this._activeDoc = doc;
         Doc.UserDoc().activeMobile = doc;
-        onSwitch && onSwitch();
+        onSwitch?.();
 
         this.renderView = renderView;
         // Ensures that switching to home is not registed
@@ -113,7 +112,7 @@ export class MobileInterface extends React.Component {
     // For toggling the hamburger menu
     @action
     toggleSidebar = () => {
-        this.sidebarActive = !this.sidebarActive;
+        this._sidebarActive = !this._sidebarActive;
 
         if (this._ink) {
             this.onSwitchInking();
@@ -126,7 +125,6 @@ export class MobileInterface extends React.Component {
         this.switchCurrentView(this._library);
         this._homeMenu = false;
         this.toggleSidebar();
-        //setTimeout(this.toggleSidebar, 300);
     }
 
     /**
@@ -146,13 +144,11 @@ export class MobileInterface extends React.Component {
             this._child = null;
             this.switchCurrentView(this._homeDoc);
             // Case 3: Parent document is any document
-        } else {
-            if (doc) {
-                this._child = doc;
-                this.switchCurrentView(doc);
-                this._homeMenu = false;
-                header.textContent = String(doc.title);
-            }
+        } else if (doc) {
+            this._child = doc;
+            this.switchCurrentView(doc);
+            this._homeMenu = false;
+            header.textContent = String(doc.title);
         }
         this._ink = false; // turns ink off
     }
@@ -161,13 +157,13 @@ export class MobileInterface extends React.Component {
      * Return 'Home", which implies returning to 'Home' menu buttons
      */
     returnHome = () => {
-        if (!this._homeMenu || this.sidebarActive) {
+        if (!this._homeMenu || this._sidebarActive) {
             this._homeMenu = true;
             this._parents = [];
             this._child = null;
             this.switchCurrentView(this._homeDoc);
         }
-        if (this.sidebarActive) {
+        if (this._sidebarActive) {
             this.toggleSidebar();
         }
     }
@@ -185,42 +181,38 @@ export class MobileInterface extends React.Component {
     /**
      * DocumentView for graphic display of all documents
      */
+    whitebackground = () => "white";
     @computed get displayWorkspaces() {
-        if (this.mainContainer) {
-            const backgroundColor = () => "white";
-            return (
-                <div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
-                    <DocumentView
-                        Document={this.mainContainer}
-                        DataDoc={undefined}
-                        LibraryPath={emptyPath}
-                        addDocument={returnFalse}
-                        addDocTab={returnFalse}
-                        pinToPres={emptyFunction}
-                        rootSelected={returnFalse}
-                        removeDocument={undefined}
-                        onClick={undefined}
-                        ScreenToLocalTransform={Transform.Identity}
-                        ContentScaling={returnOne}
-                        PanelWidth={this.returnWidth}
-                        PanelHeight={this.returnHeight}
-                        NativeHeight={returnZero}
-                        NativeWidth={returnZero}
-                        renderDepth={0}
-                        focus={emptyFunction}
-                        backgroundColor={backgroundColor}
-                        parentActive={returnTrue}
-                        whenActiveChanged={emptyFunction}
-                        bringToFront={emptyFunction}
-                        docFilters={returnEmptyFilter}
-                        ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
-                    />
-                </div>
-            );
-        }
+        return !this.mainContainer ? (null) :
+            <div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
+                <DocumentView
+                    Document={this.mainContainer}
+                    DataDoc={undefined}
+                    LibraryPath={emptyPath}
+                    addDocument={returnFalse}
+                    addDocTab={returnFalse}
+                    pinToPres={emptyFunction}
+                    rootSelected={returnFalse}
+                    removeDocument={undefined}
+                    onClick={undefined}
+                    ScreenToLocalTransform={Transform.Identity}
+                    ContentScaling={returnOne}
+                    PanelWidth={this.returnWidth}
+                    PanelHeight={this.returnHeight}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
+                    renderDepth={0}
+                    focus={emptyFunction}
+                    backgroundColor={this.whitebackground}
+                    parentActive={returnTrue}
+                    whenActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                />
+            </div>;
     }
-
     /**
      * Note: window.innerWidth and window.screen.width compute different values.
      * window.screen.width is the display size, however window.innerWidth is the
@@ -235,8 +227,7 @@ export class MobileInterface extends React.Component {
      * @param doc: doc for which the method is called
      */
     handleClick = async (doc: Doc) => {
-        const children = DocListCast(doc.data);
-        if (doc.type !== "collection" && this.sidebarActive) {
+        if (doc.type !== "collection" && this._sidebarActive) {
             this._parents.push(this._activeDoc);
             this.switchCurrentView(doc);
             this._homeMenu = false;
@@ -267,12 +258,7 @@ export class MobileInterface extends React.Component {
      * Handles creation of array which is then rendered in renderPathbar()
      */
     createPathname = () => {
-        const docArray = [];
-        this._parents.map((doc: Doc, index: any) => {
-            docArray.push(doc);
-        });
-        docArray.push(this._activeDoc);
-        return docArray;
+        return [...this._parents, this._activeDoc];
     }
 
     // Renders the graphical pathbar
@@ -280,24 +266,21 @@ export class MobileInterface extends React.Component {
         const docArray = this.createPathname();
         const items = docArray.map((doc: Doc, index: any) => {
             if (index === 0) {
-                return (
-                    <>
-                        {this._homeMenu ?
-                            <div className="pathbarItem">
-                                <div className="pathbarText"
-                                    style={{ backgroundColor: "rgb(119, 37, 37)" }}
-                                    key={index}
-                                    onClick={() => this.handlePathClick(doc, index)}>{doc.title}
-                                </div>
-                            </div>
-                            :
-                            <div className="pathbarItem">
-                                <div className="pathbarText"
-                                    key={index}
-                                    onClick={() => this.handlePathClick(doc, index)}>{doc.title}
-                                </div>
-                            </div>}
-                    </>);
+                return this._homeMenu ?
+                    <div className="pathbarItem" key={index}>
+                        <div className="pathbarText"
+                            style={{ backgroundColor: "rgb(119, 37, 37)" }}
+                            key={index}
+                            onClick={() => this.handlePathClick(doc, index)}>{doc.title}
+                        </div>
+                    </div>
+                    :
+                    <div className="pathbarItem" key={index}>
+                        <div className="pathbarText"
+                            key={index}
+                            onClick={() => this.handlePathClick(doc, index)}>{doc.title}
+                        </div>
+                    </div>;
 
             } else if (doc === this._activeDoc) {
                 return (
@@ -357,14 +340,14 @@ export class MobileInterface extends React.Component {
     }
 
     // Renders the contents of the menu and sidebar
-    renderDefaultContent = () => {
+    @computed get renderDefaultContent() {
         if (this._homeMenu) {
             return (
                 <div>
                     <div className="navbar">
                         <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
                         <div className="header" id="header">{this._homeDoc.title}</div>
-                        <div className="cover" id="cover" onClick={(e) => this.stop(e)}></div>
+                        <div className="cover" id="cover" onClick={e => e.stopPropagation()}></div>
                         <div className="toggle-btn" id="menuButton" onClick={this.toggleSidebar}>
                             <span></span>
                             <span></span>
@@ -376,7 +359,7 @@ export class MobileInterface extends React.Component {
             );
         }
         // stores workspace documents as 'workspaces' variable
-        let workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
+        let workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc) as Doc;
         if (this._child) {
             workspaces = this._child;
         }
@@ -400,16 +383,16 @@ export class MobileInterface extends React.Component {
             <div>
                 <div className="navbar">
                     <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
-                    <div className="header" id="header">{this.sidebarActive ? "library" : this._activeDoc.title}</div>
-                    <div className={`toggle-btn ${this.sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
+                    <div className="header" id="header">{this._sidebarActive ? "library" : this._activeDoc.title}</div>
+                    <div className={`toggle-btn ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
                         <span></span>
                         <span></span>
                         <span></span>
                     </div>
-                    <div className={`background ${this.sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}></div>
+                    <div className={`background ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}></div>
                 </div>
                 {this.renderPathbar()}
-                <div className={`sidebar ${this.sidebarActive ? "active" : ""}`}>
+                <div className={`sidebar ${this._sidebarActive ? "active" : ""}`}>
                     <div className="sidebarButtons">
                         {this._child ?
                             <>
@@ -435,7 +418,7 @@ export class MobileInterface extends React.Component {
                         }
                     </div>
                 </div>
-                <div className={`blanket ${this.sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
+                <div className={`blanket ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
                 </div>
             </div>
         );
@@ -446,7 +429,7 @@ export class MobileInterface extends React.Component {
      */
     @action
     createNewWorkspace = async (id?: string) => {
-        const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
+        const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc) as Doc;
         const workspaceCount = DocListCast(workspaces.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
@@ -465,10 +448,6 @@ export class MobileInterface extends React.Component {
         Doc.AddDocToList(workspaces, "data", workspaceDoc);
     }
 
-    stop = (e: React.MouseEvent) => {
-        e.stopPropagation();
-    }
-
     // Button for switching between pen and ink mode
     @action
     onSwitchInking = () => {
@@ -486,152 +465,121 @@ export class MobileInterface extends React.Component {
     }
 
     // The static ink menu that appears at the top
-    inkMenu = () => {
-        if (this._activeDoc._viewType === "docking") {
-            if (this._ink) {
-                return <div className="colorSelector">
-                    <InkOptionsMenu />
-                </div>;
-
-            }
-        }
+    @computed get inkMenu() {
+        return this._activeDoc._viewType !== "docking" || !this._ink ? (null) :
+            <div className="colorSelector">
+                <InkOptionsMenu />
+            </div>;
     }
 
     // DocButton that uses UndoManager and handles the opacity change if CanUndo is true
     @computed get undo() {
-        if (this.mainContainer) {
-            if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc && this._activeDoc !== this.userDoc.rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
-                return (<>
-                    <div className="docButton"
-                        style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }}
-                        id="undoButton"
-                        title="undo"
-                        onClick={(e: React.MouseEvent) => {
-                            UndoManager.Undo();
-                            e.stopPropagation();
-                        }}>
-                        <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="undo-alt" />
-                    </div>
-                </>);
-            }
-        }
+        if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
+            this._activeDoc !== Doc.UserDoc().rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
+            return (
+                <div className="docButton"
+                    style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }}
+                    id="undoButton"
+                    title="undo"
+                    onClick={(e: React.MouseEvent) => {
+                        UndoManager.Undo();
+                        e.stopPropagation();
+                    }}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="undo-alt" />
+                </div>);
+        } else return (null);
     }
 
     // DocButton that uses UndoManager and handles the opacity change if CanRedo is true
     @computed get redo() {
-        if (this.mainContainer) {
-            if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc && this._activeDoc !== this.userDoc.rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
-                return (<>
-                    <div className="docButton"
-                        style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }}
-                        id="undoButton"
-                        title="redo"
-                        onClick={(e: React.MouseEvent) => {
-                            UndoManager.Redo();
-                            e.stopPropagation();
-                        }}>
-                        <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="redo-alt" />
-                    </div>
-                </>);
-            }
-        }
+        if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
+            this._activeDoc !== Doc.UserDoc().rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
+            return (
+                <div className="docButton"
+                    style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }}
+                    id="undoButton"
+                    title="redo"
+                    onClick={(e: React.MouseEvent) => {
+                        UndoManager.Redo();
+                        e.stopPropagation();
+                    }}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="redo-alt" />
+                </div>);
+        } else return (null);
     }
 
     // DocButton for switching into ink mode
     @computed get drawInk() {
-        if (this.mainContainer) {
-            if (this._activeDoc._viewType === "docking") {
-                return (
-                    <>
-                        <div className="docButton"
-                            id="inkButton"
-                            title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
-                            onClick={this.onSwitchInking}>
-                            <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="pen-nib"
-                            />
-                        </div>
-                    </>);
-            }
-        }
+        return !this.mainContainer || this._activeDoc._viewType !== "docking" ? (null) :
+            <div className="docButton"
+                id="inkButton"
+                title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
+                onClick={this.onSwitchInking}>
+                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="pen-nib" />
+            </div>;
     }
 
     // DocButton: Button that appears on the bottom of the screen to initiate image upload
-    uploadImageButton = () => {
+    @computed get uploadImageButton() {
         if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== "docking" && this._activeDoc.title !== "WORKSPACES") {
             return <div className="docButton"
                 id="imageButton"
                 title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
                 onClick={this.toggleUpload}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload"
-                />
+                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" />
             </div>;
-        }
+        } else return (null);
     }
 
     // DocButton to download images on the mobile
-    downloadDocument = () => {
+    @computed get downloadDocument() {
         if (this._activeDoc.type === "image" || this._activeDoc.type === "pdf" || this._activeDoc.type === "video") {
-            const url = this._activeDoc["data-path"]?.toString();
             return <div className="docButton"
                 title={"Download Image"}
                 style={{ backgroundColor: "white", color: "black" }}
-                onClick={e => {
-                    window.open(url);
-                }}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download"
-                />
+                onClick={e => window.open(this._activeDoc["data-path"]?.toString())}> // daa-path holds the url
+                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download" />
             </div>;
-        }
+        } else return (null);
     }
 
     // DocButton for pinning images to presentation
-    pinToPresentation = () => {
+    @computed get pinToPresentation() {
         // Only making button available if it is an image
         if (!(this._activeDoc.type === "collection" || this._activeDoc.type === "presentation")) {
             const isPinned = this._activeDoc && Doc.isDocPinned(this._activeDoc);
             return <div className="docButton"
                 title={Doc.isDocPinned(this._activeDoc) ? "Unpin from presentation" : "Pin to presentation"}
                 style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
-                onClick={e => {
-                    if (isPinned) {
-                        DockedFrameRenderer.UnpinDoc(this._activeDoc);
-                    }
-                    else {
-                        DockedFrameRenderer.PinDoc(this._activeDoc);
-                    }
-                }}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin"
-                />
+                onClick={e => DockedFrameRenderer.PinDoc(this._activeDoc, isPinned)}>
+                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>;
-        }
+        } else return (null);
     }
 
     // Buttons for switching the menu between large and small icons
-    switchMenuView = () => {
-        if (this._activeDoc.title === this._homeDoc.title) {
-            return (
-                <div className="homeSwitch">
-                    <div className={`list ${!this.menuListView ? "active" : ""}`} onClick={this.changeToIconView}>
-                        <FontAwesomeIcon size="sm" icon="th-large" />
-                    </div>
-                    <div className={`list ${this.menuListView ? "active" : ""}`} onClick={this.changeToListView}>
-                        <FontAwesomeIcon size="sm" icon="bars" />
-                    </div>
+    @computed get switchMenuView() {
+        return this._activeDoc.title !== this._homeDoc.title ? (null) :
+            <div className="homeSwitch">
+                <div className={`list ${!this._menuListView ? "active" : ""}`} onClick={this.changeToIconView}>
+                    <FontAwesomeIcon size="sm" icon="th-large" />
                 </div>
-            );
-        }
+                <div className={`list ${this._menuListView ? "active" : ""}`} onClick={this.changeToListView}>
+                    <FontAwesomeIcon size="sm" icon="bars" />
+                </div>
+            </div>;
     }
 
     // Logic for switching the menu into the icons
     @action
     changeToIconView = () => {
         if (this._homeDoc._viewType = "stacking") {
-            this.menuListView = false;
+            this._menuListView = false;
             this._homeDoc._viewType = "masonry";
             this._homeDoc.columnWidth = 300;
             this._homeDoc._columnWidth = 300;
             const menuButtons = DocListCast(this._homeDoc.data);
-            menuButtons.map((doc: Doc, index: any) => {
+            menuButtons.map(doc => {
                 const buttonData = DocListCast(doc.data);
                 buttonData[1]._nativeWidth = 0.1;
                 buttonData[1]._width = 0.1;
@@ -647,9 +595,9 @@ export class MobileInterface extends React.Component {
     changeToListView = () => {
         if (this._homeDoc._viewType = "masonry") {
             this._homeDoc._viewType = "stacking";
-            this.menuListView = true;
+            this._menuListView = true;
             const menuButtons = DocListCast(this._homeDoc.data);
-            menuButtons.map((doc: Doc, index: any) => {
+            menuButtons.map(doc => {
                 const buttonData = DocListCast(doc.data);
                 buttonData[1]._nativeWidth = 450;
                 buttonData[1]._dimMagnitude = 2;
@@ -671,49 +619,37 @@ export class MobileInterface extends React.Component {
 
     // For toggling image upload pop up
     @action
-    toggleUpload = () => this.imageUploadActive = !this.imageUploadActive
+    toggleUpload = () => this._imageUploadActive = !this._imageUploadActive
 
     // For toggling audio record and dictate pop up
     @action
-    toggleAudio = () => this.audioUploadActive = !this.audioUploadActive
+    toggleAudio = () => this._audioUploadActive = !this._audioUploadActive
 
     // Button for toggling the upload pop up in a collection
     @action
     toggleUploadInCollection = () => {
         const button = document.getElementById("imageButton") as HTMLElement;
-        button.style.backgroundColor = this.imageUploadActive ? "white" : "black";
-        button.style.color = this.imageUploadActive ? "black" : "white";
+        button.style.backgroundColor = this._imageUploadActive ? "white" : "black";
+        button.style.color = this._imageUploadActive ? "black" : "white";
 
-        this.imageUploadActive = !this.imageUploadActive;
+        this._imageUploadActive = !this._imageUploadActive;
     }
 
     // For closing the image upload pop up
     @action
     closeUpload = () => {
-        this.imageUploadActive = false;
+        this._imageUploadActive = false;
     }
 
     // Returns the image upload pop up
-    uploadImage = () => {
-        let doc;
-        let toggle;
-        if (this._homeMenu === false) {
-            doc = this._activeDoc;
-            toggle = this.toggleUploadInCollection;
-        } else {
-            doc = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
-            toggle = this.toggleUpload;
-        }
-        return (
-            <Uploader Document={doc} />
-        );
+    @computed get uploadImage() {
+        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        return <Uploader Document={doc} />;
     }
 
     // Radial menu can only be used if it is a colleciton and it is not a homeDoc
-    displayRadialMenu = () => {
-        if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc) {
-            return <RadialMenu />;
-        }
+    @computed get displayRadialMenu() {
+        return this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc ? <RadialMenu /> : (null);
     }
 
     onDragOver = (e: React.DragEvent) => {
@@ -733,32 +669,31 @@ export class MobileInterface extends React.Component {
     }
 
     render() {
-        trace();
         return (
             <div className="mobileInterface-container" onDragOver={this.onDragOver}>
                 <SettingsManager />
-                <div className={`image-upload ${this.imageUploadActive ? "active" : ""}`}>
-                    {this.uploadImage()}
+                <div className={`image-upload ${this._imageUploadActive ? "active" : ""}`}>
+                    {this.uploadImage}
                 </div>
-                <div className={`audio-upload ${this.audioUploadActive ? "active" : ""}`}>
+                <div className={`audio-upload ${this._audioUploadActive ? "active" : ""}`}>
                     <AudioUpload />
                 </div>
-                {this.switchMenuView()}
-                {this.inkMenu()}
+                {this.switchMenuView}
+                {this.inkMenu}
                 <GestureOverlay>
                     <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
                     <div className="docButtonContainer">
-                        {this.pinToPresentation()}
-                        {this.downloadDocument()}
+                        {this.pinToPresentation}
+                        {this.downloadDocument}
                         {this.undo}
                         {this.redo}
                         {this.drawInk}
-                        {this.uploadImageButton()}
+                        {this.uploadImageButton}
                     </div>
                     {this.displayWorkspaces}
-                    {this.renderDefaultContent()}
+                    {this.renderDefaultContent}
                 </GestureOverlay>
-                {this.displayRadialMenu()}
+                {this.displayRadialMenu}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 96001ff5b299326de8742a6540acd5017d0f751a Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Thu, 2 Jul 2020 10:40:17 -0500
Subject: started link created popup, tried hihglighting source

---
 package-lock.json                                  | 331 ++++++++++++++++++++-
 package.json                                       |   2 +
 src/client/documents/Documents.ts                  |   9 +-
 src/client/views/GlobalKeyHandler.ts               |   8 +
 src/client/views/MainView.tsx                      |  17 ++
 .../views/collections/CollectionLinearView.scss    |   2 +-
 .../views/collections/CollectionLinearView.tsx     |  17 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   9 +
 src/client/views/nodes/DocumentView.tsx            |   7 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   4 +-
 src/fields/Doc.ts                                  |   2 +
 11 files changed, 398 insertions(+), 10 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index d42e286e2..ad181758c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -372,6 +372,101 @@
             "resolved": "https://registry.npmjs.org/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz",
             "integrity": "sha512-6SJfx949YEWooh/CUPpJ+F491y4BYJmknz4hUN1+RHvKoUEynKbRmhnwbk/VLmh4OthLLDNCyWXfbh4DG1cTXA=="
         },
+        "@material-ui/core": {
+            "version": "4.11.0",
+            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz",
+            "integrity": "sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g==",
+            "requires": {
+                "@babel/runtime": "^7.4.4",
+                "@material-ui/styles": "^4.10.0",
+                "@material-ui/system": "^4.9.14",
+                "@material-ui/types": "^5.1.0",
+                "@material-ui/utils": "^4.10.2",
+                "@types/react-transition-group": "^4.2.0",
+                "clsx": "^1.0.4",
+                "hoist-non-react-statics": "^3.3.2",
+                "popper.js": "1.16.1-lts",
+                "prop-types": "^15.7.2",
+                "react-is": "^16.8.0",
+                "react-transition-group": "^4.4.0"
+            },
+            "dependencies": {
+                "dom-helpers": {
+                    "version": "5.1.4",
+                    "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz",
+                    "integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==",
+                    "requires": {
+                        "@babel/runtime": "^7.8.7",
+                        "csstype": "^2.6.7"
+                    }
+                },
+                "popper.js": {
+                    "version": "1.16.1-lts",
+                    "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
+                    "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA=="
+                },
+                "react-transition-group": {
+                    "version": "4.4.1",
+                    "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
+                    "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
+                    "requires": {
+                        "@babel/runtime": "^7.5.5",
+                        "dom-helpers": "^5.0.1",
+                        "loose-envify": "^1.4.0",
+                        "prop-types": "^15.6.2"
+                    }
+                }
+            }
+        },
+        "@material-ui/styles": {
+            "version": "4.10.0",
+            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz",
+            "integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==",
+            "requires": {
+                "@babel/runtime": "^7.4.4",
+                "@emotion/hash": "^0.8.0",
+                "@material-ui/types": "^5.1.0",
+                "@material-ui/utils": "^4.9.6",
+                "clsx": "^1.0.4",
+                "csstype": "^2.5.2",
+                "hoist-non-react-statics": "^3.3.2",
+                "jss": "^10.0.3",
+                "jss-plugin-camel-case": "^10.0.3",
+                "jss-plugin-default-unit": "^10.0.3",
+                "jss-plugin-global": "^10.0.3",
+                "jss-plugin-nested": "^10.0.3",
+                "jss-plugin-props-sort": "^10.0.3",
+                "jss-plugin-rule-value-function": "^10.0.3",
+                "jss-plugin-vendor-prefixer": "^10.0.3",
+                "prop-types": "^15.7.2"
+            }
+        },
+        "@material-ui/system": {
+            "version": "4.9.14",
+            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz",
+            "integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==",
+            "requires": {
+                "@babel/runtime": "^7.4.4",
+                "@material-ui/utils": "^4.9.6",
+                "csstype": "^2.5.2",
+                "prop-types": "^15.7.2"
+            }
+        },
+        "@material-ui/types": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
+            "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A=="
+        },
+        "@material-ui/utils": {
+            "version": "4.10.2",
+            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz",
+            "integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==",
+            "requires": {
+                "@babel/runtime": "^7.4.4",
+                "prop-types": "^15.7.2",
+                "react-is": "^16.8.0"
+            }
+        },
         "@rkusa/linebreak": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/@rkusa/linebreak/-/linebreak-1.0.0.tgz",
@@ -1075,7 +1170,6 @@
             "version": "4.4.0",
             "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz",
             "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==",
-            "dev": true,
             "requires": {
                 "@types/react": "*"
             }
@@ -2478,6 +2572,11 @@
             "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
             "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
         },
+        "bowser": {
+            "version": "1.9.4",
+            "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz",
+            "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ=="
+        },
         "boxen": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
@@ -2878,6 +2977,11 @@
                 "type-detect": "^4.0.5"
             }
         },
+        "chain-function": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz",
+            "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg=="
+        },
         "chalk": {
             "version": "2.4.2",
             "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -2888,6 +2992,11 @@
                 "supports-color": "^5.3.0"
             }
         },
+        "change-emitter": {
+            "version": "0.1.6",
+            "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
+            "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
+        },
         "character-parser": {
             "version": "2.2.0",
             "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
@@ -3614,6 +3723,11 @@
                 "shallow-clone": "^3.0.0"
             }
         },
+        "clsx": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+            "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
+        },
         "code-point-at": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -4263,6 +4377,15 @@
             "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
             "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
         },
+        "css-in-js-utils": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz",
+            "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==",
+            "requires": {
+                "hyphenate-style-name": "^1.0.2",
+                "isobject": "^3.0.1"
+            }
+        },
         "css-loader": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz",
@@ -4317,6 +4440,15 @@
                 }
             }
         },
+        "css-vendor": {
+            "version": "2.0.8",
+            "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
+            "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
+            "requires": {
+                "@babel/runtime": "^7.8.3",
+                "is-in-browser": "^1.0.2"
+            }
+        },
         "css-what": {
             "version": "2.1.3",
             "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
@@ -7032,6 +7164,11 @@
                 }
             }
         },
+        "hyphenate-style-name": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
+            "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
+        },
         "i": {
             "version": "0.3.6",
             "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz",
@@ -7247,6 +7384,15 @@
             "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
             "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
         },
+        "inline-style-prefixer": {
+            "version": "3.0.8",
+            "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz",
+            "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=",
+            "requires": {
+                "bowser": "^1.7.3",
+                "css-in-js-utils": "^2.0.0"
+            }
+        },
         "inspect-function": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
@@ -7555,6 +7701,11 @@
                 "is-extglob": "^2.1.1"
             }
         },
+        "is-in-browser": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
+            "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
+        },
         "is-installed-globally": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
@@ -7922,6 +8073,84 @@
                 "verror": "1.10.0"
             }
         },
+        "jss": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss/-/jss-10.3.0.tgz",
+            "integrity": "sha512-B5sTRW9B6uHaUVzSo9YiMEOEp3UX8lWevU0Fsv+xtRnsShmgCfIYX44bTH8bPJe6LQKqEXku3ulKuHLbxBS97Q==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "csstype": "^2.6.5",
+                "is-in-browser": "^1.1.3",
+                "tiny-warning": "^1.0.2"
+            }
+        },
+        "jss-plugin-camel-case": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.3.0.tgz",
+            "integrity": "sha512-tadWRi/SLWqLK3EUZEdDNJL71F3ST93Zrl9JYMjV0QDqKPAl0Liue81q7m/nFUpnSTXczbKDy4wq8rI8o7WFqA==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "hyphenate-style-name": "^1.0.3",
+                "jss": "^10.3.0"
+            }
+        },
+        "jss-plugin-default-unit": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.3.0.tgz",
+            "integrity": "sha512-tT5KkIXAsZOSS9WDSe8m8lEHIjoEOj4Pr0WrG0WZZsMXZ1mVLFCSsD2jdWarQWDaRNyMj/I4d7czRRObhOxSuw==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "^10.3.0"
+            }
+        },
+        "jss-plugin-global": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.3.0.tgz",
+            "integrity": "sha512-etYTG/y3qIR/vxZnKY+J3wXwObyBDNhBiB3l/EW9/pE3WHE//BZdK8LFvQcrCO48sZW1Z6paHo6klxUPP7WbzA==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "^10.3.0"
+            }
+        },
+        "jss-plugin-nested": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.3.0.tgz",
+            "integrity": "sha512-qWiEkoXNEkkZ+FZrWmUGpf+zBsnEOmKXhkjNX85/ZfWhH9dfGxUCKuJFuOWFM+rjQfxV4csfesq4hY0jk8Qt0w==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "^10.3.0",
+                "tiny-warning": "^1.0.2"
+            }
+        },
+        "jss-plugin-props-sort": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.3.0.tgz",
+            "integrity": "sha512-boetORqL/lfd7BWeFD3K+IyPqyIC+l3CRrdZr+NPq7Noqp+xyg/0MR7QisgzpxCEulk+j2CRcEUoZsvgPC4nTg==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "^10.3.0"
+            }
+        },
+        "jss-plugin-rule-value-function": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.3.0.tgz",
+            "integrity": "sha512-7WiMrKIHH3rwxTuJki9+7nY11r1UXqaUZRhHvqTD4/ZE+SVhvtD5Tx21ivNxotwUSleucA/8boX+NF21oXzr5Q==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "^10.3.0",
+                "tiny-warning": "^1.0.2"
+            }
+        },
+        "jss-plugin-vendor-prefixer": {
+            "version": "10.3.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.3.0.tgz",
+            "integrity": "sha512-sZQbrcZyP5V0ADjCLwUA1spVWoaZvM7XZ+2fSeieZFBj31cRsnV7X70FFDerMHeiHAXKWzYek+67nMDjhrZAVQ==",
+            "requires": {
+                "@babel/runtime": "^7.3.1",
+                "css-vendor": "^2.0.8",
+                "jss": "^10.3.0"
+            }
+        },
         "jstransformer": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
@@ -7955,6 +8184,11 @@
             "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz",
             "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw=="
         },
+        "keycode": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
+            "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
+        },
         "keygrip": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@@ -8201,6 +8435,11 @@
             "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
             "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
         },
+        "lodash.merge": {
+            "version": "4.6.2",
+            "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+            "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+        },
         "lodash.padend": {
             "version": "4.6.1",
             "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
@@ -8212,6 +8451,11 @@
             "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
             "dev": true
         },
+        "lodash.throttle": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+            "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+        },
         "lodash.union": {
             "version": "4.6.0",
             "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
@@ -8371,6 +8615,38 @@
             "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
             "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
         },
+        "material-ui": {
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.20.2.tgz",
+            "integrity": "sha512-VeqgQkdvtK193w+FFvXDEwlVxI4rWk83eWbpYLeOIHDPWr3rbB9B075JRnJt/8IsI2X8q5Aia5W3+7m4KkleDg==",
+            "requires": {
+                "babel-runtime": "^6.23.0",
+                "inline-style-prefixer": "^3.0.8",
+                "keycode": "^2.1.8",
+                "lodash.merge": "^4.6.0",
+                "lodash.throttle": "^4.1.1",
+                "prop-types": "^15.5.7",
+                "react-event-listener": "^0.6.2",
+                "react-transition-group": "^1.2.1",
+                "recompose": "^0.26.0",
+                "simple-assign": "^0.1.0",
+                "warning": "^3.0.0"
+            },
+            "dependencies": {
+                "react-transition-group": {
+                    "version": "1.2.1",
+                    "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz",
+                    "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==",
+                    "requires": {
+                        "chain-function": "^1.0.0",
+                        "dom-helpers": "^3.2.0",
+                        "loose-envify": "^1.3.1",
+                        "prop-types": "^15.5.6",
+                        "warning": "^3.0.0"
+                    }
+                }
+            }
+        },
         "md5.js": {
             "version": "1.3.5",
             "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -13888,6 +14164,26 @@
                 "prop-types": "^15.6.0"
             }
         },
+        "react-event-listener": {
+            "version": "0.6.6",
+            "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz",
+            "integrity": "sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==",
+            "requires": {
+                "@babel/runtime": "^7.2.0",
+                "prop-types": "^15.6.0",
+                "warning": "^4.0.1"
+            },
+            "dependencies": {
+                "warning": {
+                    "version": "4.0.3",
+                    "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+                    "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+                    "requires": {
+                        "loose-envify": "^1.0.0"
+                    }
+                }
+            }
+        },
         "react-grid-layout": {
             "version": "0.18.3",
             "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-0.18.3.tgz",
@@ -14224,6 +14520,24 @@
                 "resolve": "^1.1.6"
             }
         },
+        "recompose": {
+            "version": "0.26.0",
+            "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz",
+            "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==",
+            "requires": {
+                "change-emitter": "^0.1.2",
+                "fbjs": "^0.8.1",
+                "hoist-non-react-statics": "^2.3.1",
+                "symbol-observable": "^1.0.4"
+            },
+            "dependencies": {
+                "hoist-non-react-statics": {
+                    "version": "2.5.5",
+                    "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+                    "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+                }
+            }
+        },
         "redent": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
@@ -14988,6 +15302,11 @@
             "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
             "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
         },
+        "simple-assign": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz",
+            "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o="
+        },
         "simple-concat": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
@@ -15827,6 +16146,11 @@
                 "has-flag": "^3.0.0"
             }
         },
+        "symbol-observable": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+            "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
+        },
         "symbol-tree": {
             "version": "3.2.4",
             "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -16122,6 +16446,11 @@
             "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
             "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
         },
+        "tiny-warning": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+            "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+        },
         "tinycolor2": {
             "version": "1.4.1",
             "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
diff --git a/package.json b/package.json
index 5e7598376..75dc817f5 100644
--- a/package.json
+++ b/package.json
@@ -121,6 +121,7 @@
         "@hig/flyout": "^1.2.1",
         "@hig/theme-context": "^2.1.3",
         "@hig/theme-data": "^2.16.1",
+        "@material-ui/core": "^4.11.0",
         "@types/google-maps": "^3.2.2",
         "@types/webscopeio__react-textarea-autocomplete": "^4.6.1",
         "@webscopeio/react-textarea-autocomplete": "^4.7.0",
@@ -172,6 +173,7 @@
         "jsonschema": "^1.2.5",
         "libxmljs": "^0.19.7",
         "lodash": "^4.17.15",
+        "material-ui": "^0.20.2",
         "mobile-detect": "^1.4.4",
         "mobx": "^5.15.3",
         "mobx-react": "^5.3.5",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 85da621e0..2e0323ede 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,4 +1,4 @@
-import { runInAction } from "mobx";
+import { runInAction, action } from "mobx";
 import { extname, basename } from "path";
 import { DateField } from "../../fields/DateField";
 import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
@@ -50,6 +50,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
 import { Networking } from "../Network";
 import { Upload } from "../../server/SharedMediaTypes";
+import { MainView } from "../views/MainView";
 const path = require('path');
 
 export interface DocumentOptions {
@@ -866,6 +867,7 @@ export namespace DocUtils {
     export function MakeLinkToActiveAudio(doc: Doc) {
         DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
     }
+
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", id?: string) {
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
@@ -877,6 +879,11 @@ export namespace DocUtils {
 
         Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)");
         Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)");
+
+        runInAction(() => { MainView.linkCreated = true; });
+        console.log("link created");
+        runInAction(() => { setTimeout("MainView.changeLinkCreated()", 2000); });
+
         return linkDoc;
     }
 
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c85849adb..369d6f4ae 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -79,7 +79,15 @@ export default class KeyManager {
                 // MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
                 break;
             case "escape":
+                // if (DocumentLinksButton.StartLink) {
+                //     if (DocumentLinksButton.StartLink.Document) {
+                //         action((e: React.PointerEvent<HTMLDivElement>) => {
+                //             Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
+                //         });
+                //     }
+                // }
                 DocumentLinksButton.StartLink = undefined;
+
                 const main = MainView.Instance;
                 Doc.SetSelectedTool(InkTool.None);
                 var doDeselect = true;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 662232810..4898e114a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -59,6 +59,7 @@ import { DocumentManager } from '../util/DocumentManager';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { LinkMenu } from './linking/LinkMenu';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
+import { Fade } from '@material-ui/core';
 
 @observer
 export class MainView extends React.Component {
@@ -82,6 +83,8 @@ export class MainView extends React.Component {
 
     public isPointerDown = false;
 
+    @observable public static linkCreated: boolean = false;
+
 
     componentDidMount() {
         DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType"]); // can play with these fields on someone else's
@@ -362,6 +365,11 @@ export class MainView extends React.Component {
         }
     }
 
+    @action
+    public static changeLinkCreated = () => {
+        MainView.linkCreated = !MainView.linkCreated;
+    }
+
     @action
     onPointerMove = (e: PointerEvent) => {
         this.flyoutWidth = Math.max(e.clientX, 0);
@@ -606,6 +614,15 @@ export class MainView extends React.Component {
                 {this.mainContent}
             </GestureOverlay>
             <PreviewCursor />
+            {MainView.linkCreated ? <div className="mainView-linkCreated" style={{
+                border: "2px solid red", top: screenTop / 2,
+                left: screenLeft / 2, width: "50px", height: "20px", zIndex: 10000,
+                fontSize: "12px"
+            }}>
+                {/* <Fade in={MainView.linkCreated}> */}
+                link created!
+                {/* </Fade> */}
+            </div> : null}
             {DocumentLinksButton.EditLink ? <LinkMenu location={DocumentLinksButton.EditLinkLoc} docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} backgroundColor={this.defaultBackgroundColors}
                 linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href}
diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss
index 647ce1b01..134d62d4f 100644
--- a/src/client/views/collections/CollectionLinearView.scss
+++ b/src/client/views/collections/CollectionLinearView.scss
@@ -10,6 +10,7 @@
         height: 100%;
 
         >span {
+
             margin-top: 8px;
             margin-left: 4px;
             //margin-bottom: 2px;
@@ -18,7 +19,6 @@
             display: inline-block;
             border-radius: 18px;
             margin-right: 6px;
-            padding: "4px";
             cursor: pointer;
         }
 
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index df8e2c58e..a180991d0 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -79,7 +79,14 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
 
     @action
     exitLongLinks = () => {
-        DocumentLinksButton.StartLink = undefined
+        if (DocumentLinksButton.StartLink) {
+            if (DocumentLinksButton.StartLink.Document) {
+                action((e: React.PointerEvent<HTMLDivElement>) => {
+                    Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
+                });
+            }
+        }
+        DocumentLinksButton.StartLink = undefined;
     }
 
     render() {
@@ -146,8 +153,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                     background: backgroundColor === color ? "black" : backgroundColor,
                 }}
                     onPointerDown={e => e.stopPropagation()} >
-                    <span style={{ fontSize: "12.5px", display: "inline", whiteSpace: "nowrap" }}>
-                        Creating Link From: {DocumentLinksButton.StartLink.title} </span>
+                    <span style={{
+                        fontSize: "12.5px", display: "inline", whiteSpace: "nowrap",
+                        paddingLeft: "8px", paddingRight: "8px", paddingTop: "4px",
+                        paddingBottom: "10px"
+                    }}>
+                        Creating link from: {DocumentLinksButton.StartLink.title} </span>
 
                     <FontAwesomeIcon icon="times-circle" size="lg" style={{ color: "red" }}
                         onClick={this.exitLongLinks} />
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index d04cdd34c..27fbbc7fc 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -10,6 +10,7 @@ import React = require("react");
 import { DocUtils } from "../../documents/Documents";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { LinkDocPreview } from "./LinkDocPreview";
+import { MainView } from "../MainView";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -56,6 +57,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     onLinkButtonDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
             if (doubleTap && this.props.InMenu) {
+                //action(() => Doc.BrushDoc(this.props.View.Document));
                 DocumentLinksButton.StartLink = this.props.View;
             } else if (!!!this.props.InMenu) {
                 DocumentLinksButton.EditLink = this.props.View;
@@ -68,6 +70,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     onLinkClick = (e: React.MouseEvent): void => {
         if (this.props.InMenu) {
             DocumentLinksButton.StartLink = this.props.View;
+            //action(() => Doc.BrushDoc(this.props.View.Document));
         } else if (!!!this.props.InMenu) {
             DocumentLinksButton.EditLink = this.props.View;
             DocumentLinksButton.EditLinkLoc = [e.clientX + 10, e.clientY];
@@ -79,6 +82,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             if (doubleTap) {
                 if (DocumentLinksButton.StartLink === this.props.View) {
                     DocumentLinksButton.StartLink = undefined;
+                    // action((e: React.PointerEvent<HTMLDivElement>) => {
+                    //     Doc.UnBrushDoc(this.props.View.Document);
+                    // });
                 } else {
                     DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
                         DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
@@ -90,6 +96,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     finishLinkClick = () => {
         if (DocumentLinksButton.StartLink === this.props.View) {
             DocumentLinksButton.StartLink = undefined;
+            // action((e: React.PointerEvent<HTMLDivElement>) => {
+            //     Doc.UnBrushDoc(this.props.View.Document);
+            // });
         } else {
             DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
                 DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6820b2047..0d7029576 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -116,7 +116,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     private holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
-    public get title() { return this.props.Document.title }
+    public get title() { return this.props.Document.title; }
     public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
     public get ContentDiv() { return this._mainCont.current; }
     get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
@@ -1191,7 +1191,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             id={this.props.Document[Id]}
             ref={this._mainCont} onKeyDown={this.onKeyDown}
             onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-            onPointerEnter={action(() => Doc.BrushDoc(this.props.Document))}
+            onPointerEnter={action(() => { Doc.BrushDoc(this.props.Document); })}
             onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
                 let entered = false;
                 const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
@@ -1200,7 +1200,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                         entered = true;
                     }
                 }
+                // if (this.props.Document !== DocumentLinksButton.StartLink?.Document) {
                 !entered && Doc.UnBrushDoc(this.props.Document);
+                //}
+
             })}
             style={{
                 transformOrigin: this._animateScalingTo ? "center center" : undefined,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index dc7d8c7ca..276c79453 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1084,7 +1084,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     @action
     onFocused = (e: React.FocusEvent): void => {
-        console.log("FOUCSS")
+        console.log("FOUCSS");
         FormattedTextBox.FocusedBox = this;
         this.tryUpdateHeight();
 
@@ -1216,7 +1216,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     public static HadSelection: boolean = false;
     onBlur = (e: any) => {
-        console.log("BLURRR")
+        console.log("BLURRR");
         FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
         //DictationManager.Controls.stop(false);
         this.endUndoTypingBatch();
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e4d11dd4d..8a7b91e49 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -832,12 +832,14 @@ export namespace Doc {
         })(doc);
     }
     export function BrushDoc(doc: Doc) {
+        console.log("brushed");
         if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc;
         brushManager.BrushedDoc.set(doc, true);
         brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
         return doc;
     }
     export function UnBrushDoc(doc: Doc) {
+        console.log("unbrushed");
         if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc;
         brushManager.BrushedDoc.delete(doc);
         brushManager.BrushedDoc.delete(Doc.GetProto(doc));
-- 
cgit v1.2.3-70-g09d2


From 1daf0a496175267d86f495bb24aa5140b6a9eeab Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 2 Jul 2020 14:05:33 -0400
Subject: small clanup in Mobile related code

---
 src/client/views/GestureOverlay.tsx                |  2 +-
 src/client/views/collections/CollectionView.tsx    | 21 ++++++++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 11 ++++----
 src/client/views/nodes/DocumentView.tsx            | 29 +++++++++++-----------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 5 files changed, 33 insertions(+), 32 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 839ab09c7..487467b2b 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -264,7 +264,7 @@ export default class GestureOverlay extends Touchable {
     handleHandDown = async (e: React.TouchEvent) => {
         this._holdTimer && clearTimeout(this._holdTimer);
 
-        // this chunk of code helps us keep track of which touch events ar e associated with a hand event
+        // this chunk of code helps us keep track of which touch events are associated with a hand event
         // so that if a hand is held down, but a second hand is interacting with dash, the second hand's events
         // won't interfere with the first hand's events.
         const fingers = new Array<React.Touch>();
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 35196b634..300309882 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -231,12 +231,12 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
 
 
     setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
-        const existingVm = ContextMenu.Instance?.findByDescription(category);
+        const existingVm = ContextMenu.Instance.findByDescription(category);
         const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
 
         subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" });
         if (addExtras && CollectionView._safeMode) {
-            ContextMenu.Instance?.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" });
+            ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" });
         }
         subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" });
         subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" });
@@ -255,11 +255,12 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
         }
         addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
-        !existingVm && ContextMenu.Instance?.addItem({ description: category, subitems: subItems, icon: "eye" });
+        !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" });
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+        const cm = ContextMenu.Instance;
+        if (cm && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             this.setupViewTypes("Add a Perspective...", vtype => {
                 const newRendition = Doc.MakeAlias(this.props.Document);
                 newRendition._viewType = vtype;
@@ -267,7 +268,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 return newRendition;
             }, false);
 
-            const existing = ContextMenu.Instance?.findByDescription("Options...");
+            const existing = cm.findByDescription("Options...");
             const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
             layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
             if (this.props.Document.childLayout instanceof Doc) {
@@ -278,9 +279,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             }
             layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
 
-            !existing && ContextMenu.Instance?.addItem({ description: "Options...", subitems: layoutItems, icon: "hand-point-right" });
+            !existing && cm.addItem({ description: "Options...", subitems: layoutItems, icon: "hand-point-right" });
 
-            const existingOnClick = ContextMenu.Instance?.findByDescription("OnClick...");
+            const existingOnClick = cm.findByDescription("OnClick...");
             const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
             const funcs = [
                 { key: "onChildClick", name: "On Child Clicked" },
@@ -296,13 +297,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                     icon: "edit",
                     event: () => this.props.Document[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)),
                 }));
-            !existingOnClick && ContextMenu.Instance?.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
+            !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
 
             if (!Doc.UserDoc().noviceMode) {
-                const more = ContextMenu.Instance?.findByDescription("More...");
+                const more = cm.findByDescription("More...");
                 const moreItems = more && "subitems" in more ? more.subitems : [];
                 moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
-                !more && ContextMenu.Instance?.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+                !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
             }
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ae7213d0b..21b0473c9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -602,7 +602,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
         // bcz: theres should be a better way of doing these than referencing these static instances directly
         MarqueeOptionsMenu.Instance?.fadeOut(true);// I think it makes sense for the marquee menu to go away when panned. -syip2
-        // PDFMenu.Instance.fadeOut(true);
 
         const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
         this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, undefined, true);
@@ -1204,17 +1203,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private thumbIdentifier?: number;
 
     onContextMenu = (e: React.MouseEvent) => {
-        if (this.props.annotationsKey) return;
+        if (this.props.annotationsKey || !ContextMenu.Instance) return;
 
-        const appearance = ContextMenu.Instance?.findByDescription("Appearance...");
+        const appearance = ContextMenu.Instance.findByDescription("Appearance...");
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         appearanceItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
         appearanceItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
         appearanceItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
-        !appearance && ContextMenu.Instance?.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
+        !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
 
-        const options = ContextMenu.Instance?.findByDescription("Options...");
+        const options = ContextMenu.Instance.findByDescription("Options...");
         const optionItems = options && "subitems" in options ? options.subitems : [];
         !this.props.isAnnotationOverlay &&
             optionItems.push({ description: (this.showTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this.showTimeline = !this.showTimeline), icon: faEye });
@@ -1254,7 +1253,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 }
             });
         }
-        !options && ContextMenu.Instance?.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
+        !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
 
     }
     @observable showTimeline = false;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8d122baeb..732089068 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -736,21 +736,22 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
 
         const cm = ContextMenu.Instance;
+        if (!cm) return;
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
         Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
-            cm?.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
+            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
         this.props.contextMenuItems?.().forEach(item =>
-            cm?.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
+            cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
 
         const options = cm.findByDescription("Options...");
         const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
         const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
         templateDoc && optionItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
         optionItems.push({ description: "Toggle Show Each Link Dot", event: () => this.layoutDoc.showLinks = !this.layoutDoc.showLinks, icon: "eye" });
-        !options && cm?.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+        !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
 
-        const existingOnClick = cm?.findByDescription("OnClick...");
+        const existingOnClick = cm.findByDescription("OnClick...");
         const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
         onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
         onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" });
@@ -759,17 +760,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" });
         onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
         onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" });
-        !existingOnClick && cm?.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
+        !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
 
         const funcs: ContextMenuProps[] = [];
         if (this.layoutDoc.onDragStart) {
             funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
             funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
             funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
-            cm?.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
+            cm.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
         }
 
-        const more = cm?.findByDescription("More...");
+        const more = cm.findByDescription("More...");
         const moreItems = more && "subitems" in more ? more.subitems : [];
         if (!Doc.UserDoc().noviceMode) {
             moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
@@ -798,16 +799,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
         moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
         moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" });
-        !more && cm?.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
-        cm?.moveAfter(cm?.findByDescription("More...")!, cm?.findByDescription("OnClick...")!);
+        !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+        cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
 
-        const help = cm?.findByDescription("Help...");
+        const help = cm.findByDescription("Help...");
         const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
         helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("http://localhost:1050/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
         helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
-        cm?.addItem({ description: "Help...", subitems: helpItems, icon: "question" });
+        cm.addItem({ description: "Help...", subitems: helpItems, icon: "question" });
 
-        const existingAcls = cm?.findByDescription("Privacy...");
+        const existingAcls = cm.findByDescription("Privacy...");
         const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : [];
         aclItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" });
         aclItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" });
@@ -815,7 +816,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         aclItems.push({ description: "Make Editable", event: () => this.setAcl("write"), icon: "concierge-bell" });
         aclItems.push({ description: "Test Private", event: () => this.testAcl("ownerOnly"), icon: "concierge-bell" });
         aclItems.push({ description: "Test Readonly", event: () => this.testAcl("readOnly"), icon: "concierge-bell" });
-        !existingAcls && cm?.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" });
+        !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" });
 
         // const recommender_subitems: ContextMenuProps[] = [];
 
@@ -876,7 +877,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 // DocumentViews should stop propagation of this event
                 e.stopPropagation();
             }
-            ContextMenu.Instance?.displayMenu(e.pageX - 15, e.pageY - 15);
+            cm.displayMenu(e.pageX - 15, e.pageY - 15);
             !SelectionManager.IsSelected(this, true) && SelectionManager.SelectDoc(this, false);
         });
         const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), "");
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 270fcb01c..b127cd024 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -190,7 +190,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }
             const state = this._editorView.state.apply(tx);
-            this._editorView?.updateState(state);
+            this._editorView.updateState(state);
             (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks);
 
             const tsel = this._editorView.state.selection.$from;
-- 
cgit v1.2.3-70-g09d2


From 5022535496730c0b02db5fb74a1e1e224bcaf745 Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Thu, 2 Jul 2020 14:46:24 -0500
Subject: cleaned text link preview

---
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 .../formattedText/FormattedTextBoxComment.scss     |  47 ++++++++--
 .../formattedText/FormattedTextBoxComment.tsx      | 100 ++++++++++++++-------
 3 files changed, 105 insertions(+), 44 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1ffa16e43..d4222b4bc 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1430,7 +1430,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                                 setTimeout(() => this._editorView!.focus(), 500);
                                 e.stopPropagation();
                             }} >
-                            <FontAwesomeIcon className="formattedTExtBox-audioFont"
+                            <FontAwesomeIcon className="formattedTextBox-audioFont"
                                 style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} icon={"microphone"} size="sm" />
                         </div>}
                 </div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
index adef7f81f..9089e7039 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
@@ -36,32 +36,61 @@
     border-top-color: white;
 }
 
-.FormattedTextBox-buttons {
+.FormattedTextBoxComment-buttons {
     display: none;
     position: absolute;
     top: 50%;
     right: 0;
     transform: translateY(-50%);
 
-    .FormattedTextBox-button {
+    .FormattedTextBoxComment-button {
         width: 20px;
         height: 20px;
         margin: 0;
         margin-right: 6px;
         border-radius: 50%;
         pointer-events: auto;
-        background-color: rgb(53, 146, 199);
-        color: rgb(150, 211, 248);
+        background-color: rgb(38, 40, 41);
+        color: rgb(178, 181, 184);
         font-size: 65%;
         transition: transform 0.2s;
         text-align: center;
         position: relative;
 
-        .FormattedTextBox-fa-icon {
-            position: absolute;
-            top: 50%;
-            left: 50%;
-            transform: translate(-50%, -50%);
+        // margin-top: "auto";
+        //     margin-bottom: "auto";
+        //     background: black;
+        //     color: white;
+        //     display: inline-block;
+        //     border-radius: 18px;
+        //     font-size: 12.5px;
+        //     width: 18px;
+        //     height: 18px;
+        //     margin-top: auto;
+        //     margin-bottom: auto;
+        //     margin-right: 3px;
+        //     cursor: pointer;
+        //     transition: transform 0.2s;
+
+        .FormattedTextBoxComment-fa-icon {
+            margin-top: "auto";
+            margin-bottom: "auto";
+            background: black;
+            color: white;
+            display: inline-block;
+            border-radius: 18px;
+            font-size: 12.5px;
+            width: 18px;
+            height: 18px;
+            margin-top: auto;
+            margin-bottom: auto;
+            margin-right: 3px;
+            cursor: pointer;
+            transition: transform 0.2s;
+            // position: absolute;
+            // top: 50%;
+            // left: 50%;
+            // transform: translate(-50%, -50%);
         }
 
         &:last-child {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 21033f315..56826e5c7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -84,8 +84,8 @@ export class FormattedTextBoxComment {
             FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
             FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
             FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
-            FormattedTextBoxComment.tooltip.style.maxWidth = "350px";
-            FormattedTextBoxComment.tooltip.style.maxHeight = "250px";
+            FormattedTextBoxComment.tooltip.style.maxWidth = "200px";
+            FormattedTextBoxComment.tooltip.style.maxHeight = "206px";
             FormattedTextBoxComment.tooltip.style.width = "100%";
             FormattedTextBoxComment.tooltip.style.height = "100%";
             FormattedTextBoxComment.tooltip.style.overflow = "hidden";
@@ -241,50 +241,82 @@ export class FormattedTextBoxComment {
                             }
                             if (target?.author) {
                                 FormattedTextBoxComment.showCommentbox("", view, nbef);
-                                const docPreview = <div style={{ backgroundColor: "white", border: "7px solid white" }}>
+                                const docPreview = <div style={{ backgroundColor: "white", border: "8px solid white" }}>
                                     {target.title}
                                     <div className="wrapper" style={{ float: "right" }}>
                                         <div title="Delete link" className="FormattedTextBoxComment-button" style={{
                                             display: "inline",
-                                            paddingLeft: "5px",
-                                            paddingRight: "5px"
+                                            paddingLeft: "6px",
+                                            paddingRight: "6px",
+                                            paddingTop: "2.5px",
+                                            paddingBottom: "2.5px",
+                                            width: "20px",
+                                            height: "20px",
+                                            margin: 0,
+                                            marginRight: "6px",
+                                            borderRadius: "50%",
+                                            pointerEvents: "auto",
+                                            backgroundColor: "rgb(38, 40, 41)",
+                                            color: "rgb(178, 181, 184)",
+                                            transition: "transform 0.2s",
+                                            textAlign: "center",
+                                            position: "relative"
                                         }} ref={(r) => this._deleteRef = r}>
-                                            <FontAwesomeIcon className="FormattedTextBox-fa-icon" icon="trash" size="sm"
-                                            /></div>
+                                            <FontAwesomeIcon className="FormattedTextBox-fa-icon" icon="trash"
+                                                size="sm" /></div>
                                         <div title="Follow link" className="FormattedTextBoxComment-button" style={{
                                             display: "inline",
-                                            paddingRight: "5px"
+                                            paddingLeft: "6px",
+                                            paddingRight: "6px",
+                                            paddingTop: "2.5px",
+                                            paddingBottom: "2.5px",
+                                            width: "20px",
+                                            height: "20px",
+                                            margin: 0,
+                                            marginRight: "6px",
+                                            borderRadius: "50%",
+                                            pointerEvents: "auto",
+                                            backgroundColor: "rgb(38, 40, 41)",
+                                            color: "rgb(178, 181, 184)",
+                                            transition: "transform 0.2s",
+                                            textAlign: "center",
+                                            position: "relative"
                                         }} ref={(r) => this._followRef = r}>
                                             <FontAwesomeIcon className="FormattedTextBox-fa-icon" icon="arrow-right"
                                                 size="sm" />
                                         </div>
                                     </div>
-                                    <ContentFittingDocumentView
-                                        Document={target}
-                                        LibraryPath={emptyPath}
-                                        fitToBox={true}
-                                        moveDocument={returnFalse}
-                                        rootSelected={returnFalse}
-                                        ScreenToLocalTransform={Transform.Identity}
-                                        parentActive={returnFalse}
-                                        addDocument={returnFalse}
-                                        removeDocument={returnFalse}
-                                        addDocTab={returnFalse}
-                                        pinToPres={returnFalse}
-                                        dontRegisterView={true}
-                                        docFilters={returnEmptyFilter}
-                                        ContainingCollectionDoc={undefined}
-                                        ContainingCollectionView={undefined}
-                                        renderDepth={0}
-                                        PanelWidth={() => Math.min(350, NumCast(target._width, 350))}
-                                        PanelHeight={() => Math.min(250, NumCast(target._height, 250))}
-                                        focus={emptyFunction}
-                                        whenActiveChanged={returnFalse}
-                                        bringToFront={returnFalse}
-                                        ContentScaling={returnOne}
-                                        NativeWidth={returnZero}
-                                        NativeHeight={returnZero}
-                                    />
+                                    <div className="wrapper" style={{
+                                        maxWidth: "180px", maxHeight: "168px", overflow: "hidden",
+                                        overflowY: "hidden", paddingTop: "5px"
+                                    }}>
+                                        <ContentFittingDocumentView
+                                            Document={target}
+                                            LibraryPath={emptyPath}
+                                            fitToBox={true}
+                                            moveDocument={returnFalse}
+                                            rootSelected={returnFalse}
+                                            ScreenToLocalTransform={Transform.Identity}
+                                            parentActive={returnFalse}
+                                            addDocument={returnFalse}
+                                            removeDocument={returnFalse}
+                                            addDocTab={returnFalse}
+                                            pinToPres={returnFalse}
+                                            dontRegisterView={true}
+                                            docFilters={returnEmptyFilter}
+                                            ContainingCollectionDoc={undefined}
+                                            ContainingCollectionView={undefined}
+                                            renderDepth={0}
+                                            PanelWidth={() => Math.min(350, NumCast(target._width, 350))}
+                                            PanelHeight={() => Math.min(250, NumCast(target._height, 250))}
+                                            focus={emptyFunction}
+                                            whenActiveChanged={returnFalse}
+                                            bringToFront={returnFalse}
+                                            ContentScaling={returnOne}
+                                            NativeWidth={returnZero}
+                                            NativeHeight={returnZero}
+                                        />
+                                    </div>
                                 </div>;
                                 FormattedTextBoxComment.showCommentbox("", view, nbef);
 
-- 
cgit v1.2.3-70-g09d2

