From ce98e69b0030eff0fbb1bf09137571e7b37ef77c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 25 Jun 2020 14:29:27 -0400
Subject: fixed linkicon dragging feedback

---
 src/client/views/nodes/DocumentLinksButton.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index f35b48ce4..4f4f12521 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -82,16 +82,16 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     get linkButton() {
         const links = DocListCast(this.props.View.props.Document.links);
         return (!links.length || links[0].hidden) && !this.props.AlwaysOn ? (null) :
-            <div title="Drag(create link) Tap(view links)" ref={this._linkButton} style={{ position: "absolute", left: this.props.Offset?.[0] }}>
+            <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}
                     onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
-                    onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
+                    onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = {
                         addDocTab: this.props.View.props.addDocTab,
                         linkSrc: this.props.View.props.Document,
                         linkDoc: links[0],
                         Location: [e.clientX, e.clientY + 20]
-                    })} >
+                    }))} >
                     {links.length ? 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)}
-- 
cgit v1.2.3-70-g09d2


From e7372931b9d28c141aaec9552041b5644c2f415a Mon Sep 17 00:00:00 2001
From: geireann <60007097+geireann@users.noreply.github.com>
Date: Fri, 26 Jun 2020 03:37:08 +0800
Subject: Merge branch 'master' into mobile_revision_direct

And changes to UI of record and image upload
---
 src/client/util/CurrentUserUtils.ts                |  12 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/views/DocumentButtonBar.tsx             |  63 +-----
 src/client/views/DocumentDecorations.tsx           |   2 +-
 src/client/views/GlobalKeyHandler.ts               |   2 +
 src/client/views/MainView.tsx                      |  11 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   4 +-
 src/client/views/globalCssVariables.scss           |   1 +
 src/client/views/linking/LinkMenu.scss             |   4 +
 src/client/views/linking/LinkMenu.tsx              |  37 +--
 src/client/views/nodes/AudioBox.scss               |  71 +-----
 src/client/views/nodes/AudioBox.tsx                |   2 +-
 src/client/views/nodes/DocumentLinksButton.scss    |  38 ++++
 src/client/views/nodes/DocumentLinksButton.tsx     | 104 +++++++++
 src/client/views/nodes/DocumentView.tsx            |  28 ++-
 src/client/views/nodes/LinkAnchorBox.tsx           |   8 +
 src/client/views/nodes/LinkDocPreview.tsx          | 107 +++++++++
 .../views/nodes/formattedText/RichTextMenu.tsx     |   9 +-
 src/mobile/AudioUpload.scss                        |  41 +++-
 src/mobile/AudioUpload.tsx                         | 249 +++++++--------------
 src/mobile/ImageUpload.scss                        |  61 ++---
 src/mobile/ImageUpload.tsx                         |  66 +++---
 src/mobile/MobileInterface.tsx                     |  29 ++-
 src/mobile/MobileMenu.scss                         |   1 +
 24 files changed, 549 insertions(+), 403 deletions(-)
 create mode 100644 src/client/views/nodes/DocumentLinksButton.scss
 create mode 100644 src/client/views/nodes/DocumentLinksButton.tsx
 create mode 100644 src/client/views/nodes/LinkDocPreview.tsx

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index efdc943fa..823a99436 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -474,12 +474,12 @@ 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: "folder-open", click: 'openWorkspaces()', backgroundColor: "#ffd6d6", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " },
-            { 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: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploads()', backgroundColor: "#ff7373", info: "Access the collection of your mobile uploads." },
-            { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "#ff5e5e", info: "Change your password, log out, or manage your account security." }
+            { title: "WORKSPACES", icon: "bars", click: 'openMobileWorkspaces()', 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." },
+            { title: "PRESENTATION", icon: "desktop", click: 'openMobilePresentation()', backgroundColor: "lightgrey", info: "Use your phone as a remote for you presentation." },
+            { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "lightgrey", info: "Change your password, log out, or manage your account security." }
         ];
         return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data =>
             this.mobileButton({
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 91bc51101..417ddf989 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -353,7 +353,7 @@ export namespace DragManager {
             const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
             const rect = ele.getBoundingClientRect();
             const scaleX = rect.width / ele.offsetWidth,
-                scaleY = rect.height / ele.offsetHeight;
+                scaleY = ele.offsetHeight ? rect.height / ele.offsetHeight : scaleX;
             elesCont.left = Math.min(rect.left, elesCont.left);
             elesCont.top = Math.min(rect.top, elesCont.top);
             elesCont.right = Math.max(rect.right, elesCont.right);
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 62a95116f..fcaea0f0b 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,28 +1,28 @@
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowAltCircleDown, faPhotoVideo, faArrowAltCircleUp, faArrowAltCircleRight, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faPhotoVideo, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast } from "../../fields/Doc";
 import { RichTextField } from '../../fields/RichTextField';
-import { NumCast, StrCast, Cast } from "../../fields/Types";
+import { Cast, NumCast } from "../../fields/Types";
 import { emptyFunction, setupMoveUpEvents } from "../../Utils";
+import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
+import { Docs, DocUtils } from '../documents/Documents';
+import { DragManager } from '../util/DragManager';
 import { UndoManager } from "../util/UndoManager";
 import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView';
 import { ParentDocSelector } from './collections/ParentDocumentSelector';
 import './collections/ParentDocumentSelector.scss';
 import './DocumentButtonBar.scss';
-import { LinkMenu } from "./linking/LinkMenu";
+import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { DocumentView } from './nodes/DocumentView';
 import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
 import { TemplateMenu } from "./TemplateMenu";
 import { Template, Templates } from "./Templates";
 import React = require("react");
-import { DragManager } from '../util/DragManager';
-import { MetadataEntryMenu } from './MetadataEntryMenu';
-import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
-import { Docs } from '../documents/Documents';
+import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -113,39 +113,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
 
     get view0() { return this.props.views()?.[0]; }
 
-    @action
-    onLinkButtonMoved = (e: PointerEvent) => {
-        if (this._linkButton.current !== null) {
-            const linkDrag = UndoManager.StartBatch("Drag Link");
-            this.view0 && DragManager.StartLinkDrag(this._linkButton.current, this.view0.props.Document, e.pageX, e.pageY, {
-                dragComplete: dropEv => {
-                    const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
-                    if (this.view0 && linkDoc) {
-                        !linkDoc.linkRelationship && (Doc.GetProto(linkDoc).linkRelationship = "hyperlink");
-
-                        // we want to allow specific views to handle the link creation in their own way (e.g., rich text makes text hyperlinks)
-                        // the dragged view can regiser a linkDropCallback to be notified that the link was made and to update their data structures
-                        // however, the dropped document isn't so accessible.  What we do is set the newly created link document on the documentView
-                        // The documentView passes a function prop returning this link doc to its descendants who can react to changes to it.
-                        dropEv.linkDragData?.linkDropCallback?.(dropEv.linkDragData);
-                        runInAction(() => this.view0!._link = linkDoc);
-                        setTimeout(action(() => this.view0!._link = undefined), 0);
-                    }
-                    linkDrag?.end();
-                },
-                hideSource: false
-            });
-            return true;
-        }
-        return false;
-    }
-
-
-    onLinkButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, emptyFunction);
-    }
-
-
     @computed
     get considerGoogleDocsPush() {
         const targetDoc = this.view0?.props.Document;
@@ -235,20 +202,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         </div>;
     }
 
-    @computed
-    get linkButton() {
-        const view0 = this.view0;
-        const linkCount = view0 && DocListCast(view0.props.Document.links).length;
-        return !view0 ? (null) : <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
-            <Flyout anchorPoint={anchorPoints.LEFT_TOP}
-                content={<LinkMenu docView={view0} addDocTab={view0.props.addDocTab} changeFlyout={emptyFunction} />}>
-                <div className={"documentButtonBar-linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
-                    {linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
-                </div>
-            </Flyout>
-        </div>;
-    }
-
     @computed
     get metadataButton() {
         const view0 = this.view0;
@@ -318,7 +271,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const considerPush = isText && this.considerGoogleDocsPush;
         return <div className="documentButtonBar">
             <div className="documentButtonBar-button">
-                {this.linkButton}
+                <DocumentLinksButton View={this.view0!} AlwaysOn={true} />
             </div>
             <div className="documentButtonBar-button">
                 {this.templateButton}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index beb6155ca..d7b0ab7a9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -552,7 +552,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
 
             </div >
-            <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2, top: bounds.b + this._resizeBorderWidth / 2 }}>
+            <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
                 <DocumentButtonBar views={SelectionManager.SelectedDocuments} />
             </div>
         </div >
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 27755737e..7bc8cf6a7 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -19,6 +19,7 @@ import { MarqueeView } from "./collections/collectionFreeForm/MarqueeView";
 import { DocumentDecorations } from "./DocumentDecorations";
 import { MainView } from "./MainView";
 import { DocumentView } from "./nodes/DocumentView";
+import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -77,6 +78,7 @@ export default class KeyManager {
                 // MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
                 break;
             case "escape":
+                DocumentLinksButton.StartLink = undefined;
                 const main = MainView.Instance;
                 Doc.SetSelectedTool(InkTool.None);
                 if (main.isPointerDown) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 97953452d..4e2c067ac 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,7 +5,7 @@ import {
     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,
-    faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft
+    faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft, faSortAmountDown
 } from '@fortawesome/free-solid-svg-icons';
 import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -55,6 +55,9 @@ import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { SnappingManager } from '../util/SnappingManager';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { DocumentManager } from '../util/DocumentManager';
+import { DocumentLinksButton } from './nodes/DocumentLinksButton';
+import { LinkMenu } from './linking/LinkMenu';
+import { LinkDocPreview } from './nodes/LinkDocPreview';
 
 @observer
 export class MainView extends React.Component {
@@ -135,7 +138,7 @@ export class MainView extends React.Component {
             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,
-            faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft);
+            faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faQuoteLeft, faSortAmountDown);
         this.initEventListeners();
         this.initAuthenticationRouters();
     }
@@ -578,6 +581,10 @@ export class MainView extends React.Component {
                 {this.mainContent}
             </GestureOverlay>
             <PreviewCursor />
+            {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}
+                addDocTab={LinkDocPreview.LinkInfo.addDocTab} /> : (null)}
             <ContextMenu />
             <RadialMenu />
             <PDFMenu />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dc62c48b7..42aa0f58e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -47,6 +47,7 @@ import { Timeline } from "../../animationtimeline/Timeline";
 import { SnappingManager } from "../../../util/SnappingManager";
 import { ActiveInkColor, ActiveInkWidth, ActiveInkBezierApprox } from "../../InkingStroke";
 import { DocumentType } from "../../../documents/DocumentTypes";
+import { DocumentLinksButton } from "../../nodes/DocumentLinksButton";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -245,7 +246,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             // const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
             // this.props.addDocument(source);
             // linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation"); // TODODO this is where in text links get passed
-           return false;
+            return false;
         } else {
             const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
             this.props.addDocument(source);
@@ -589,6 +590,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     onClick = (e: React.MouseEvent) => {
         if (this.layoutDoc.targetScale && (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
             if (Date.now() - this._lastTap < 300) {
+                runInAction(() => DocumentLinksButton.StartLink = undefined);
                 const docpt = this.getTransform().transformPoint(e.clientX, e.clientY);
                 this.scaleAtPt(docpt, 1);
                 e.stopPropagation();
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 4e85fe0ca..3e54d001b 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -12,6 +12,7 @@ $lighter-alt-accent: rgb(207, 220, 240);
 $darker-alt-accent: rgb(178, 206, 248);
 $intermediate-color: #9c9396;
 $dark-color: #121721;
+$link-color: lightBlue;
 $antimodemenu-height: 35px;
 // fonts
 $sans-serif: "Noto Sans",
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index 7dee22f66..c372e7098 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -8,6 +8,10 @@
 .linkMenu-list {
     max-height: 200px;
     overflow-y: scroll;
+    position: absolute;
+    z-index: 10;
+    background: $link-color;
+    min-width: 150px
 }
 
 .linkMenu-group {
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 56f40ad69..0fcc0f0b9 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -4,11 +4,12 @@ import { DocumentView } from "../nodes/DocumentView";
 import { LinkEditor } from "./LinkEditor";
 import './LinkMenu.scss';
 import React = require("react");
-import { Doc } from "../../../fields/Doc";
+import { Doc, Opt } from "../../../fields/Doc";
 import { LinkManager } from "../../util/LinkManager";
 import { LinkMenuGroup } from "./LinkMenuGroup";
 import { faTrash } from '@fortawesome/free-solid-svg-icons';
 import { library } from "@fortawesome/fontawesome-svg-core";
+import { DocumentLinksButton } from "../nodes/DocumentLinksButton";
 
 library.add(faTrash);
 
@@ -16,16 +17,29 @@ interface Props {
     docView: DocumentView;
     changeFlyout: () => void;
     addDocTab: (document: Doc, where: string) => boolean;
+    location: number[];
 }
 
 @observer
 export class LinkMenu extends React.Component<Props> {
 
     @observable private _editingLink?: Doc;
+    @observable private _linkMenuRef: Opt<HTMLDivElement | null>;
 
+    @action
+    onClick = (e: PointerEvent) => {
+        if (!Array.from(this._linkMenuRef?.getElementsByTagName((e.target as HTMLElement).tagName) || []).includes(e.target as any)) {
+            DocumentLinksButton.EditLink = undefined;
+        }
+    }
     @action
     componentDidMount() {
         this._editingLink = undefined;
+        document.addEventListener("pointerdown", this.onClick);
+    }
+
+    componentWillUnmount() {
+        document.removeEventListener("pointerdown", this.onClick);
     }
 
     clearAllLinks = () => {
@@ -56,20 +70,11 @@ export class LinkMenu extends React.Component<Props> {
     render() {
         const sourceDoc = this.props.docView.props.Document;
         const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
-        if (this._editingLink === undefined) {
-            return (
-                <div className="linkMenu">
-                    {/* <button className="linkEditor-button linkEditor-clearButton" onClick={() => this.clearAllLinks()} title="Clear all links"><FontAwesomeIcon icon="trash" size="sm" /></button> */}
-                    {/* <input id="linkMenu-searchBar" type="text" placeholder="Search..."></input> */}
-                    <div className="linkMenu-list">
-                        {this.renderAllGroups(groups)}
-                    </div>
-                </div>
-            );
-        } else {
-            return (
-                <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor>
-            );
-        }
+        return <div className="linkMenu-list" ref={(r) => this._linkMenuRef = r} style={{ left: this.props.location[0], top: this.props.location[1] }}>
+            {!this._editingLink ?
+                this.renderAllGroups(groups) :
+                <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)} />
+            }
+        </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index 43dd698ad..b5d113ca0 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -175,76 +175,29 @@
 
 
 @media only screen and (max-device-width: 480px) {
-
-    .audiobox-container,
-    .audiobox-container-interactive {
-        width: max-content;
-        /* align-self: center; */
-        left: 50%;
-        top: 40%;
-        transform: translate(-50%, 0);
-        height: 100px;
-        position: absolute;
-        display: flex;
-        pointer-events: all;
-        /* cursor: default; */
-    }
-
-    .audiobox-container .audiobox-buttons,
-    .audiobox-container-interactive .audiobox-buttons {
-        display: flex;
-        width: 500px;
-        /* left: 50%; */
-        /* transform: translate(-50%,0); */
-        align-items: center;
-    }
-
     .audiobox-dictation {
+        font-size: 5em;
+        display: flex;
         width: 100;
-        height: 100;
-        font-size: 50;
-        /* background-color: white; */
+        justify-content: center;
+        flex-direction: column;
+        align-items: center;
     }
 
-    .audiobox-container .audiobox-record-interactive,
-    .audiobox-container-interactive .audiobox-record-interactive {
-        width: 95%;
-        user-select: none;
-        font-size: 50px;
-        font-weight: 100;
-        left: 5%;
+    .audiobox-container .audiobox-record,
+    .audiobox-container-interactive .audiobox-record {
+        pointer-events: all;
+        font-size: 3em;
+        width: 100%;
         height: 100%;
         position: relative;
+        pointer-events: none;
     }
 
     .audiobox-container .audiobox-controls .audiobox-player .audiobox-playhead,
     .audiobox-container .audiobox-controls .audiobox-player .audiobox-dictation,
     .audiobox-container-interactive .audiobox-controls .audiobox-player .audiobox-playhead,
     .audiobox-container-interactive .audiobox-controls .audiobox-player .audiobox-dictation {
-        position: relative;
-        align-self: center;
-        width: 50px;
-        height: max-content;
-        padding: 0;
-    }
-
-    .audiobox-container .audiobox-controls .audiobox-player,
-    .audiobox-container-interactive .audiobox-controls .audiobox-player {
-        margin-top: auto;
-        margin-bottom: auto;
-        width: 100%;
-        height: 100%;
-        position: relative;
-        padding-right: 5px;
-        display: flex;
-    }
-
-    .audiobox-container .audiobox-controls,
-    .audiobox-container-interactive .audiobox-controls {
-        width: 500px;
-        height: 100%;
-        position: relative;
-        display: flex;
-        padding-left: 2px;
+        width: 70px;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index cb0582d87..d5288fff6 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -193,7 +193,7 @@ export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument
         const newDoc = Docs.Create.TextDocument("", {
             title: "", _chromeStatus: "disabled",
             x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
-            _width: NumCast(this.props.Document._width), _height: 3 * NumCast(this.props.Document._height)
+            _width: NumCast(this.props.Document._width), _height: 2 * NumCast(this.props.Document._height)
         });
         Doc.GetProto(newDoc).recordingSource = this.dataDoc;
         Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
new file mode 100644
index 000000000..484f8c469
--- /dev/null
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -0,0 +1,38 @@
+@import "../globalCssVariables.scss";
+
+
+.documentLinksButton,
+.documentLinksButton-endLink,
+.documentLinksButton-startLink {
+    height: 20px;
+    width: 20px;
+    position: absolute;
+    border-radius: 50%;
+    opacity: 0.9;
+    pointer-events: auto;
+    color: black;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &:hover {
+        background: deepskyblue;
+        transform: scale(1.05);
+        cursor: default;
+    }
+}
+.documentLinksButton {
+    background-color: $link-color;
+}
+.documentLinksButton-endLink {
+    border: red solid 2px;
+}
+.documentLinksButton-startLink {
+    border: red solid 2px;
+    background-color: rgba(255, 192, 203, 0.5);
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
new file mode 100644
index 000000000..f35b48ce4
--- /dev/null
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -0,0 +1,104 @@
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { emptyFunction, setupMoveUpEvents, returnFalse } from "../../../Utils";
+import { DragManager } from "../../util/DragManager";
+import { UndoManager } from "../../util/UndoManager";
+import './DocumentLinksButton.scss';
+import { DocumentView } from "./DocumentView";
+import React = require("react");
+import { DocUtils } from "../../documents/Documents";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { LinkDocPreview } from "./LinkDocPreview";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+interface DocumentLinksButtonProps {
+    View: DocumentView;
+    Offset?: number[];
+    AlwaysOn?: boolean;
+}
+@observer
+export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
+    private _linkButton = React.createRef<HTMLDivElement>();
+
+    @action
+    onLinkButtonMoved = (e: PointerEvent) => {
+        if (this._linkButton.current !== null) {
+            const linkDrag = UndoManager.StartBatch("Drag Link");
+            this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View.props.Document, e.pageX, e.pageY, {
+                dragComplete: dropEv => {
+                    const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
+                    if (this.props.View && linkDoc) {
+                        !linkDoc.linkRelationship && (Doc.GetProto(linkDoc).linkRelationship = "hyperlink");
+
+                        // we want to allow specific views to handle the link creation in their own way (e.g., rich text makes text hyperlinks)
+                        // the dragged view can regiser a linkDropCallback to be notified that the link was made and to update their data structures
+                        // however, the dropped document isn't so accessible.  What we do is set the newly created link document on the documentView
+                        // The documentView passes a function prop returning this link doc to its descendants who can react to changes to it.
+                        dropEv.linkDragData?.linkDropCallback?.(dropEv.linkDragData);
+                        runInAction(() => this.props.View._link = linkDoc);
+                        setTimeout(action(() => this.props.View._link = undefined), 0);
+                    }
+                    linkDrag?.end();
+                },
+                hideSource: false
+            });
+            return true;
+        }
+        return false;
+    }
+
+    @observable static StartLink: DocumentView | undefined;
+    onLinkButtonDown = (e: React.PointerEvent): void => {
+        setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
+            if (doubleTap) {
+                DocumentLinksButton.StartLink = this.props.View;
+            } else {
+                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) {
+                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];
+
+    @computed
+    get linkButton() {
+        const links = DocListCast(this.props.View.props.Document.links);
+        return (!links.length || links[0].hidden) && !this.props.AlwaysOn ? (null) :
+            <div title="Drag(create link) Tap(view links)" ref={this._linkButton} style={{ position: "absolute", left: this.props.Offset?.[0] }}>
+                <div className={"documentLinksButton"} style={{ backgroundColor: DocumentLinksButton.StartLink ? "transparent" : "" }}
+                    onPointerDown={this.onLinkButtonDown}
+                    onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
+                    onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
+                        addDocTab: this.props.View.props.addDocTab,
+                        linkSrc: this.props.View.props.Document,
+                        linkDoc: links[0],
+                        Location: [e.clientX, e.clientY + 20]
+                    })} >
+                    {links.length ? 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 === this.props.View ? <div className={"documentLinksButton-startLink"} /> : (null)}
+            </div>;
+    }
+    render() {
+        return this.linkButton;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d31ec8c1a..afd28534e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -42,6 +42,7 @@ import { LinkAnchorBox } from './LinkAnchorBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
 import { MobileInterface } from '../../../mobile/MobileInterface';
+import { DocumentLinksButton } from './DocumentLinksButton';
 
 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,
@@ -585,10 +586,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     toggleLinkButtonBehavior = (): void => {
         if (this.Document.isLinkButton || this.layoutDoc.onClick || this.Document.ignoreClick) {
             this.Document.isLinkButton = false;
+            const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
+            first && (first.hidden = false);
             this.Document.ignoreClick = false;
             this.layoutDoc.onClick = undefined;
         } else {
             this.Document.isLinkButton = true;
+            const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
+            first && (first.hidden = true);
             this.Document.followLinkZoom = false;
             this.Document.followLinkLocation = undefined;
         }
@@ -598,8 +603,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     toggleFollowInPlace = (): void => {
         if (this.Document.isLinkButton) {
             this.Document.isLinkButton = false;
+            const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
+            first && (first.hidden = false);
         } else {
             this.Document.isLinkButton = true;
+            const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
+            first && (first.hidden = true);
             this.Document.followLinkZoom = true;
             this.Document.followLinkLocation = "inPlace";
         }
@@ -609,6 +618,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     toggleFollowOnRight = (): void => {
         if (this.Document.isLinkButton) {
             this.Document.isLinkButton = false;
+            const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
+            first && (first.hidden = false);
         } else {
             this.Document.isLinkButton = true;
             this.Document.followLinkZoom = false;
@@ -737,10 +748,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         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" });
-        if (!options) {
-            options = { description: "Options...", subitems: optionItems, icon: "compass" };
-            cm?.addItem(options);
-        }
+        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" });
 
         const existingOnClick = cm?.findByDescription("OnClick...");
         const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
@@ -1039,7 +1048,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 select={this.select}
                 onClick={this.onClickHandler}
                 layoutKey={this.finalLayoutKey} />
-            {this.anchors}
+            {this.layoutDoc.showLinks ? this.anchors : (null)}
+            {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.props.dontRegisterView ? (null) : <DocumentLinksButton View={this} Offset={[-15, 0]} />}
         </div>
         );
     }
@@ -1063,7 +1073,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
     @computed get anchors() {
         TraceMobx();
-        return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
+        return this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.presBox || this.props.dontRegisterView ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
             <DocumentView {...this.props} key={i + 1}
                 Document={d}
                 ContainingCollectionView={this.props.ContainingCollectionView}
@@ -1071,6 +1081,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 PanelWidth={this.anchorPanelWidth}
                 PanelHeight={this.anchorPanelHeight}
                 ContentScaling={returnOne}
+                dontRegisterView={false}
                 forcedBackgroundColor={returnTransparent}
                 removeDocument={this.hideLinkAnchor}
                 pointerEvents={false}
@@ -1194,7 +1205,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 color: StrCast(this.layoutDoc.color, "inherit"),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
                 border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
-                boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined,
+                boxShadow: this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
+                    StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") :
+                    this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
+                        undefined,
                 background: finalColor,
                 opacity: finalOpacity,
                 fontFamily: StrCast(this.Document._fontFamily, "inherit"),
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 2b64cdab6..2bcc6168b 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -18,6 +18,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { SelectionManager } from "../../util/SelectionManager";
 import { TraceMobx } from "../../../fields/util";
 import { Id } from "../../../fields/FieldSymbols";
+import { LinkDocPreview } from "./LinkDocPreview";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -132,6 +133,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
         );
         const small = this.props.PanelWidth() <= 1;
         return <div className={`linkAnchorBox-cont${small ? "-small" : ""} ${this.rootDoc[Id]}`}
+            onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
+            onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
+                addDocTab: this.props.addDocTab,
+                linkSrc: this.props.ContainingCollectionDoc!,
+                linkDoc: this.rootDoc,
+                Location: [e.clientX, e.clientY + 20]
+            })}
             onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
             ref={this._ref} style={{
                 background: c,
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
new file mode 100644
index 000000000..126dc240a
--- /dev/null
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -0,0 +1,107 @@
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from "mobx-react";
+import wiki from "wikijs";
+import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { Cast, FieldValue, NumCast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
+import { Transform } from "../../util/Transform";
+import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
+import React = require("react");
+import { DocumentView } from './DocumentView';
+
+interface Props {
+    linkDoc?: Doc;
+    linkSrc?: Doc;
+    href?: string;
+    backgroundColor: (doc: Doc) => string;
+    addDocTab: (document: Doc, where: string) => boolean;
+    location: number[];
+}
+@observer
+export class LinkDocPreview extends React.Component<Props> {
+    @observable public static LinkInfo: Opt<{ linkDoc?: Doc; addDocTab: (document: Doc, where: string) => boolean, linkSrc: Doc; href?: string; Location: number[] }>;
+    @observable _targetDoc: Opt<Doc>;
+    @observable _toolTipText = "";
+
+    componentDidUpdate() { this.updatePreview() }
+    componentDidMount() { this.updatePreview() }
+    async updatePreview() {
+        const linkDoc = this.props.linkDoc;
+        const linkSrc = this.props.linkSrc;
+        if (this.props.href) {
+            if (this.props.href.startsWith("https://en.wikipedia.org/wiki/")) {
+                wiki().page(this.props.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
+            } else {
+                runInAction(() => this._toolTipText = "external => " + this.props.href);
+            }
+        } else if (linkDoc && linkSrc) {
+            const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), linkSrc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
+            const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
+            runInAction(() => {
+                this._toolTipText = "";
+                this._targetDoc = target;
+                if (anchor !== this._targetDoc && anchor && this._targetDoc) {
+                    this._targetDoc._scrollY = NumCast(anchor?.y);
+                }
+            });
+        }
+    }
+    pointerDown = (e: React.PointerEvent) => {
+        if (this.props.linkDoc && this.props.linkSrc) {
+            DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc,
+                (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+        } else if (this.props.href) {
+            this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight");
+        }
+    }
+    width = () => Math.min(350, NumCast(this._targetDoc?.[WidthSym](), 350));
+    height = () => Math.min(350, NumCast(this._targetDoc?.[HeightSym](), 350));
+    @computed get targetDocView() {
+        return !this._targetDoc ?
+            <div style={{ pointerEvents: "all", maxWidth: 350, maxHeight: 250, width: "100%", height: "100%", overflow: "hidden" }}>
+                <div style={{ width: "100%", height: "100%", textOverflow: "ellipsis", }} onPointerDown={this.pointerDown}>
+                    {this._toolTipText}
+                </div>
+            </div> :
+            <ContentFittingDocumentView
+                Document={this._targetDoc}
+                LibraryPath={emptyPath}
+                fitToBox={true}
+                backgroundColor={this.props.backgroundColor}
+                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={this.width}
+                PanelHeight={this.height}
+                focus={emptyFunction}
+                whenActiveChanged={returnFalse}
+                bringToFront={returnFalse}
+                ContentScaling={returnOne}
+                NativeWidth={returnZero}
+                NativeHeight={returnZero}
+            />;
+    }
+
+    render() {
+        return <div className="linkDocPreview"
+            style={{
+                position: "absolute", left: this.props.location[0],
+                top: this.props.location[1], width: this.width(), height: this.height(),
+                boxShadow: "black 2px 2px 1em"
+            }}>
+            {this.targetDocView}
+        </div>;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 5e32b3ef8..839943aac 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -11,7 +11,7 @@ import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { Doc } from "../../../../fields/Doc";
 import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast } from "../../../../fields/Types";
+import { Cast, StrCast, BoolCast } from "../../../../fields/Types";
 import { unimplementedFunction, Utils } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { LinkManager } from "../../../util/LinkManager";
@@ -72,7 +72,7 @@ export default class RichTextMenu extends AntimodeMenu {
         super(props);
         RichTextMenu.Instance = this;
         this._canFade = false;
-        this.Pinned = true;
+        this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
 
         this.fontSizeOptions = [
             { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
@@ -147,7 +147,6 @@ export default class RichTextMenu extends AntimodeMenu {
         this.updateFromDash(view, lastState, this.editorProps);
     }
 
-
     public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => {
         if (this.view) {
             const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, linkId: linkDocId, targetId: targetDocId });
@@ -750,7 +749,7 @@ export default class RichTextMenu extends AntimodeMenu {
 
     @action
     toggleMenuPin = (e: React.MouseEvent) => {
-        this.Pinned = !this.Pinned;
+        Doc.UserDoc()["menuRichText-pinned"] = this.Pinned = !this.Pinned;
         if (!this.Pinned) {
             this.fadeOut(true);
         }
@@ -779,7 +778,7 @@ export default class RichTextMenu extends AntimodeMenu {
             this.createHighlighterButton(),
             this.createLinkButton(),
             this.createBrushButton(),
-            this.createButton("indent", "Summarize", undefined, this.insertSummarizer),
+            this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
             this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
         ]}</div>;
 
diff --git a/src/mobile/AudioUpload.scss b/src/mobile/AudioUpload.scss
index 9fe442e55..6e64d9e2e 100644
--- a/src/mobile/AudioUpload.scss
+++ b/src/mobile/AudioUpload.scss
@@ -5,8 +5,45 @@
     justify-content: center;
     flex-direction: column;
     align-items: center;
-    max-width: 400px;
-    min-width: 400px;
+    margin-top: 10px;
+    height: 400px;
+    width: 600px;
+}
+
+.upload_label {
+    position: relative;
+    font-weight: 700;
+    color: black;
+    background-color: rgba(0, 0, 0, 0);
+    border: solid 3px black;
+    margin: 10px;
+    font-size: 30;
+    height: 70px;
+    width: 60%;
+    display: inline-flex;
+    font-family: sans-serif;
+    text-transform: uppercase;
+    justify-content: center;
+    flex-direction: column;
+    border-radius: 10px;
+}
+
+.restart_label {
+    position: relative;
+    font-weight: 700;
+    color: black;
+    background-color: rgba(0, 0, 0, 0);
+    border: solid 3px black;
+    margin: 10px;
+    font-size: 30;
+    height: 70px;
+    width: 60%;
+    display: inline-flex;
+    font-family: sans-serif;
+    text-transform: uppercase;
+    justify-content: center;
+    flex-direction: column;
+    border-radius: 10px;
 }
 
 .audio-upload {
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 7ea11ee84..f859b0ba3 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -5,11 +5,11 @@ import "./ImageUpload.scss";
 import React = require('react');
 import { DocServer } from '../client/DocServer';
 import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
-import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue } from '../Utils';
+import { observable, action, computed } from 'mobx';
+import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue, returnEmptyFilter } from '../Utils';
 import { Networking } from '../client/Network';
 import { Doc, Opt } from '../fields/Doc';
-import { Cast } from '../fields/Types';
+import { Cast, FieldValue } from '../fields/Types';
 import { listSpec } from '../fields/Schema';
 import { List } from '../fields/List';
 import { Scripting } from '../client/util/Scripting';
@@ -38,185 +38,100 @@ export class AudioUpload extends React.Component {
     @observable status: string = "";
     @observable nm: string = "Choose files";
     @observable process: string = "";
+    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
 
-    onClick = async () => {
-        try {
-            await Docs.Prototypes.initialize();
-            const imgPrev = document.getElementById("img_preview");
-            const slab1 = document.getElementById("slab1");
-            if (slab1) {
-                slab1.style.opacity = "1";
-            }
-            if (imgPrev) {
-                const files: FileList | null = inputRef.current!.files;
-                const slab2 = document.getElementById("slab2");
-                if (slab2) {
-                    slab2.style.opacity = "1";
-                }
-                if (files && files.length !== 0) {
-                    this.process = "Uploading Files";
-                    for (let index = 0; index < files.length; ++index) {
-                        const file = files[index];
-                        const res = await Networking.UploadFilesToServer(file);
-                        const slab3 = document.getElementById("slab3");
-                        if (slab3) {
-                            slab3.style.opacity = "1";
-                        }
-                        res.map(async ({ result }) => {
-                            const name = file.name;
-                            if (result instanceof Error) {
-                                return;
-                            }
-                            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 });
-                            }
-                            const slab4 = document.getElementById("slab4");
-                            if (slab4) {
-                                slab4.style.opacity = "1";
-                            }
-                            const res = await rp.get(Utils.prepend("/getUserDocumentId"));
-                            if (!res) {
-                                throw new Error("No user id returned");
-                            }
-                            const field = await DocServer.GetRefField(res);
-                            let pending: Opt<Doc>;
-                            if (field instanceof Doc) {
-                                pending = await Cast(field.mobileUpload, Doc);
-                            }
-                            if (pending) {
-                                const data = await Cast(pending.data, listSpec(Doc));
-                                if (data) {
-                                    data.push(doc);
-                                } else {
-                                    pending.data = new List([doc]);
-                                }
-                                this.status = "finished";
-                                const slab5 = document.getElementById("slab5");
-                                if (slab5) {
-                                    slab5.style.opacity = "1";
-                                }
-                                this.process = "File " + (index + 1).toString() + " Uploaded";
-                                const slab6 = document.getElementById("slab6");
-                                if (slab6) {
-                                    slab6.style.opacity = "1";
-                                }
-                                const slab7 = document.getElementById("slab7");
-                                if (slab7) {
-                                    slab7.style.opacity = "1";
-                                }
-
-                            }
-                        });
-                    }
-                } else {
-                    this.process = "No file selected";
-                }
-                setTimeout(this.clearUpload, 3000);
-            }
-        } catch (error) {
-            this.error = JSON.stringify(error);
-        }
-    }
-
-    // Updates label after a files is selected (so user knows a file is uploaded)
-    inputLabel = async () => {
-        const files: FileList | null = inputRef.current!.files;
-        await files;
-        if (files && files.length === 1) {
-            console.log(files);
-            this.nm = files[0].name;
-        } else if (files && files.length > 1) {
-            console.log(files.length);
-            this.nm = files.length.toString() + " files selected";
-        }
-    }
 
     @action
     clearUpload = () => {
-        const slab1 = document.getElementById("slab1");
-        if (slab1) {
-            slab1.style.opacity = "0.4";
-        }
-        const slab2 = document.getElementById("slab2");
-        if (slab2) {
-            slab2.style.opacity = "0.4";
-        }
-        const slab3 = document.getElementById("slab3");
-        if (slab3) {
-            slab3.style.opacity = "0.4";
+        for (let i = 1; i < 8; i++) {
+            this.setOpacity(i, "0.2");
         }
-        const slab4 = document.getElementById("slab4");
-        if (slab4) {
-            slab4.style.opacity = "0.4";
-        }
-        const slab5 = document.getElementById("slab5");
-        if (slab5) {
-            slab5.style.opacity = "0.4";
-        }
-        const slab6 = document.getElementById("slab6");
-        if (slab6) {
-            slab6.style.opacity = "0.4";
-        }
-        const slab7 = document.getElementById("slab7");
-        if (slab7) {
-            slab7.style.
-                opacity = "0.4";
-        }
-        this.nm = "Choose files";
-
-        if (inputRef.current) {
-            inputRef.current.value = "";
-        }
-        this.process = "";
-        console.log(inputRef.current!.files);
+        this._audioCol = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
     }
 
-
+    closeUpload = () => {
+        this.clearUpload();
+        MobileInterface.Instance.toggleAudio();
+    }
 
     private get uploadInterface() {
-        const audioDoc = Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio" }), Doc) as Doc;
-
         return (
-            <div className="imgupload_cont">
-                <div className="closeUpload" onClick={MobileInterface.Instance.toggleAudio}>
+            <>
+                <div className="closeUpload" onClick={() => this.closeUpload()}>
                     <FontAwesomeIcon icon="window-close" size={"lg"} />
                 </div>
-                <DocumentView
-                    Document={audioDoc}
-                    DataDoc={undefined}
-                    LibraryPath={emptyPath}
-                    addDocument={returnFalse}
-                    addDocTab={returnFalse}
-                    pinToPres={emptyFunction}
-                    rootSelected={returnFalse}
-                    removeDocument={undefined}
-                    onClick={undefined}
-                    ScreenToLocalTransform={Transform.Identity}
-                    ContentScaling={returnOne}
-                    PanelWidth={() => 1000}
-                    PanelHeight={() => 1000}
-                    NativeHeight={returnZero}
-                    NativeWidth={returnZero}
-                    renderDepth={0}
-                    focus={emptyFunction}
-                    backgroundColor={() => "white"}
-                    parentActive={returnTrue}
-                    whenActiveChanged={emptyFunction}
-                    bringToFront={emptyFunction}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                />
-            </div>
+                <FontAwesomeIcon icon="microphone" size="lg" style={{ fontSize: "130" }} />
+                <div className="audioUpload_cont">
+                    <DocumentView
+                        Document={this._audioCol}
+                        DataDoc={undefined}
+                        LibraryPath={emptyPath}
+                        addDocument={returnFalse}
+                        addDocTab={returnFalse}
+                        pinToPres={emptyFunction}
+                        rootSelected={returnFalse}
+                        removeDocument={undefined}
+                        docFilters={returnEmptyFilter}
+                        onClick={undefined}
+                        ScreenToLocalTransform={Transform.Identity}
+                        ContentScaling={returnOne}
+                        PanelWidth={() => 600}
+                        PanelHeight={() => 400}
+                        NativeHeight={returnZero}
+                        NativeWidth={returnZero}
+                        renderDepth={0}
+                        focus={emptyFunction}
+                        backgroundColor={() => "rgba(0,0,0,0)"}
+                        parentActive={returnTrue}
+                        whenActiveChanged={emptyFunction}
+                        bringToFront={emptyFunction}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                    />
+                </div>
+                <div className="restart_label" onClick={this.clearUpload}>
+                    Restart
+                </div>
+                <div className="upload_label" onClick={this.uploadAudio}>
+                    Upload
+                </div>
+                <div className="loadingImage">
+                    <div className="loadingSlab" id="slab01" />
+                    <div className="loadingSlab" id="slab02" />
+                    <div className="loadingSlab" id="slab03" />
+                    <div className="loadingSlab" id="slab04" />
+                    <div className="loadingSlab" id="slab05" />
+                    <div className="loadingSlab" id="slab06" />
+                    <div className="loadingSlab" id="slab07" />
+                </div>
+            </>
         );
     }
 
+    setOpacity = (i: number, o: string) => {
+        const slab = document.getElementById("slab0" + i);
+        if (slab) {
+            console.log(slab?.id);
+            slab.style.opacity = o;
+        }
+    }
+
+    // Pushing the audio doc onto Dash Web through the right side bar
+    uploadAudio = () => {
+        console.log("uploading");
+        const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        const audioDoc = this._audioCol;
+        const data = Cast(audioRightSidebar.data, listSpec(Doc));
+        for (let i = 1; i < 8; i++) {
+            setTimeout(() => this.setOpacity(i, "1"), i * 200);
+        }
+        if (data) {
+            data.push(audioDoc);
+        }
+
+        setTimeout(this.clearUpload, 3000);
+    }
+
     @observable private dialogueBoxOpacity = 1;
     @observable private overlayOpacity = 0.4;
 
diff --git a/src/mobile/ImageUpload.scss b/src/mobile/ImageUpload.scss
index b64aac338..890258918 100644
--- a/src/mobile/ImageUpload.scss
+++ b/src/mobile/ImageUpload.scss
@@ -9,13 +9,19 @@
     min-width: 400px;
 
     .upload_label {
-        font-size: 3em;
         font-weight: 700;
-        color: white;
-        background-color: black;
-        display: inline-block;
-        margin: 10;
-        width: 100%;
+        color: black;
+        background-color: rgba(0, 0, 0, 0);
+        border: solid 3px black;
+        margin: 10px;
+        font-size: 30;
+        height: 70px;
+        width: 80%;
+        display: flex;
+        font-family: sans-serif;
+        text-transform: uppercase;
+        justify-content: center;
+        flex-direction: column;
         border-radius: 10px;
     }
 
@@ -26,10 +32,6 @@
         direction: ltr;
     }
 
-    .upload_label:hover {
-        background-color: darkred;
-    }
-
     .button_file {
         text-align: center;
         height: 50%;
@@ -49,20 +51,31 @@
     }
 
     .inputfile+label {
-        font-size: 3em;
         font-weight: 700;
-        color: white;
-        background-color: black;
-        display: inline-block;
+        color: black;
+        background-color: rgba(0, 0, 0, 0);
+        border: solid 3px black;
         margin: 10px;
+        font-size: 30;
+        height: 70px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
         margin-top: 30px;
-        width: 100%;
+        width: 80%;
+        display: flex;
+        font-family: sans-serif;
+        text-transform: uppercase;
+        justify-content: center;
+        flex-direction: column;
         border-radius: 10px;
     }
 
-    .inputfile:focus+label,
-    .inputfile+label:hover {
-        background-color: darkred;
+    .inputfile.active+label {
+        font-style: italic;
+        color: black;
+        background-color: lightgreen;
+        border: solid 3px darkgreen;
     }
 
     .status {
@@ -71,16 +84,6 @@
 
 }
 
-.backgroundUpload {
-    height: 100vh;
-    top: 0;
-    z-index: 999;
-    width: 100vw;
-    position: absolute;
-    background-color: lightgrey;
-    opacity: 0.4;
-}
-
 .image-upload {
     top: 100%;
     opacity: 0;
@@ -129,7 +132,7 @@
     height: 30px;
     margin: 10;
     border-radius: 20px;
-    opacity: 0.3;
+    opacity: 0.2;
     background-color: black;
     transition: all 2s, opacity 1.5s;
 }
\ No newline at end of file
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index b712d52cc..6a5a2dd5b 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -43,21 +43,18 @@ export class Uploader extends React.Component<ImageUploadProps> {
             await Docs.Prototypes.initialize();
             const imgPrev = document.getElementById("img_preview");
             // Slab 1
-            const slab1 = document.getElementById("slab1");
-            if (slab1) slab1.style.opacity = "1";
+            this.setOpacity(1, "1");
             if (imgPrev) {
                 const files: FileList | null = inputRef.current!.files;
                 // Slab 2
-                const slab2 = document.getElementById("slab2");
-                if (slab2) slab2.style.opacity = "1";
+                this.setOpacity(2, "1");
                 if (files && files.length !== 0) {
                     this.process = "Uploading Files";
                     for (let index = 0; index < files.length; ++index) {
                         const file = files[index];
                         const res = await Networking.UploadFilesToServer(file);
                         // Slab 3
-                        const slab3 = document.getElementById("slab3");
-                        if (slab3) slab3.style.opacity = "1";
+                        this.setOpacity(3, "1");
                         res.map(async ({ result }) => {
                             const name = file.name;
                             if (result instanceof Error) {
@@ -74,8 +71,7 @@ export class Uploader extends React.Component<ImageUploadProps> {
                                 doc = Docs.Create.ImageDocument(path, { _nativeWidth: 400, _width: 400, title: name });
                             }
                             // Slab 4
-                            const slab4 = document.getElementById("slab4");
-                            if (slab4) slab4.style.opacity = "1";
+                            this.setOpacity(4, "1");
                             const res = await rp.get(Utils.prepend("/getUserDocumentId"));
                             if (!res) {
                                 throw new Error("No user id returned");
@@ -94,13 +90,15 @@ export class Uploader extends React.Component<ImageUploadProps> {
                                 if (data) data.push(doc);
                                 else pending.data = new List([doc]);
                                 this.status = "finished";
-                                const slab5 = document.getElementById("slab5");
-                                if (slab5) slab5.style.opacity = "1";
+                                this.setOpacity(5, "1"); // Slab 5
                                 this.process = "File " + (index + 1).toString() + " Uploaded";
-                                const slab6 = document.getElementById("slab6");
-                                if (slab6) slab6.style.opacity = "1";
-                                const slab7 = document.getElementById("slab7");
-                                if (slab7) slab7.style.opacity = "1";
+                                this.setOpacity(6, "1"); // Slab 6
+                                this.setOpacity(7, "1"); // Slab 7
+                            }
+                            console.log("i: " + index + 1);
+                            console.log("l: " + files.length);
+                            if ((index + 1) === files.length) {
+                                this.process = "Uploads Completed";
                             }
                         });
                     }
@@ -129,20 +127,9 @@ export class Uploader extends React.Component<ImageUploadProps> {
 
     @action
     clearUpload = () => {
-        const slab1 = document.getElementById("slab1");
-        if (slab1) slab1.style.opacity = "0.4";
-        const slab2 = document.getElementById("slab2");
-        if (slab2) slab2.style.opacity = "0.4";
-        const slab3 = document.getElementById("slab3");
-        if (slab3) slab3.style.opacity = "0.4";
-        const slab4 = document.getElementById("slab4");
-        if (slab4) slab4.style.opacity = "0.4";
-        const slab5 = document.getElementById("slab5");
-        if (slab5) slab5.style.opacity = "0.4";
-        const slab6 = document.getElementById("slab6");
-        if (slab6) slab6.style.opacity = "0.4";
-        const slab7 = document.getElementById("slab7");
-        if (slab7) slab7.style.opacity = "0.4";
+        for (let i = 1; i < 8; i++) {
+            this.setOpacity(i, "0.2");
+        }
         this.nm = "Choose files";
 
         if (inputRef.current) {
@@ -152,24 +139,33 @@ export class Uploader extends React.Component<ImageUploadProps> {
         console.log(inputRef.current!.files);
     }
 
+    closeUpload = () => {
+        this.clearUpload();
+        MobileInterface.Instance.toggleUpload();
+    }
+
+    setOpacity = (i: number, o: string) => {
+        const slab = document.getElementById("slab" + i);
+        if (slab) {
+            console.log(slab?.id);
+            slab.style.opacity = o;
+        }
+    }
 
 
     private get uploadInterface() {
         return (
             <div className="imgupload_cont">
-                <div className="closeUpload" onClick={MobileInterface.Instance.toggleUpload}>
+                <div className="closeUpload" onClick={() => this.closeUpload()}>
                     <FontAwesomeIcon icon="window-close" size={"lg"} />
                 </div>
-                <input type="file" accept="application/pdf, video/*,image/*" className="inputFile" id="input_image_file" ref={inputRef} onChange={this.inputLabel} multiple></input>
+                <FontAwesomeIcon icon="upload" size="lg" style={{ fontSize: "130" }} />
+                <input type="file" accept="application/pdf, video/*,image/*" className={`inputFile ${this.nm !== "Choose files" ? "active" : ""}`} 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" />
-                    &nbsp; &nbsp; Upload
+                    Upload
                 </div>
-                {/* <div onClick={this.onClick} className="upload_button">Upload</div> */}
                 <img id="img_preview" src=""></img>
-                {/* <p>{this.status}</p>
-                <p>{this.error}</p> */}
                 <div className="loadingImage">
                     <div className="loadingSlab" id="slab1" />
                     <div className="loadingSlab" id="slab2" />
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index a1719c015..5894326ab 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, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+    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, 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,
@@ -43,8 +43,9 @@ import { AudioUpload } from "./AudioUpload";
 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";
 
-library.add(faTasks, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+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,
     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,
@@ -387,13 +388,13 @@ export class MobileInterface extends React.Component {
                 return (
                     <div
                         className="item"
-                        key={index}
-                        onClick={() => this.handleClick(doc)}>
-                        <div className="item-title"> {doc.title} </div>
-                        <div className="item-type">{doc.type}</div>
-                        <FontAwesomeIcon className="right" icon="angle-right" size="lg" />
+                        key={index}>
+                        <div className="item-title" onClick={() => this.handleClick(doc)}> {doc.title} </div>
+                        <div className="item-type" onClick={() => this.handleClick(doc)}>{doc.type}</div>
+                        <FontAwesomeIcon onClick={() => this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === "collection" ? "block" : "none"}` }} />
                         <FontAwesomeIcon className="open" onClick={() => this.openFromSidebar(doc)} icon="external-link-alt" size="lg" />
-                    </div>);
+                    </div>
+                );
             }
         });
 
@@ -440,7 +441,7 @@ export class MobileInterface extends React.Component {
     }
 
     /**
-     * Handles the Create New Workspace button in the menu
+     * Handles the Create New Workspace button in the menu (taken from MainView.tsx)
      */
     @action
     createNewWorkspace = async (id?: string) => {
@@ -759,12 +760,7 @@ export class MobileInterface extends React.Component {
             toggle = this.toggleUpload;
         }
         return (
-            <div>
-                <div className="closeUpload" onClick={toggle}>
-                    <FontAwesomeIcon icon="window-close" size={"lg"} />
-                </div>
-                <Uploader Document={doc} />
-            </div>
+            <Uploader Document={doc} />
         );
     }
 
@@ -815,6 +811,7 @@ export class MobileInterface extends React.Component {
                 {this.switchMenuView()}
                 {this.inkMenu()}
                 <GestureOverlay>
+                    <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
                     <div className="docButtonContainer">
                         {this.pinToPresentation()}
                         {this.downloadDocument()}
@@ -843,6 +840,6 @@ Scripting.addGlobal(function openMobilePresentation() { return MobileInterface.I
 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 openWorkspaces() { return MobileInterface.Instance.openWorkspaces(); });
+Scripting.addGlobal(function openMobileWorkspaces() { return MobileInterface.Instance.openWorkspaces(); });
 Scripting.addGlobal(function uploadImageMobile() { return MobileInterface.Instance.toggleUpload(); });
 Scripting.addGlobal(function switchToMobileUploads() { return MobileInterface.Instance.switchToMobileUploads(); });
diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss
index 53bc48034..c499e6d1e 100644
--- a/src/mobile/MobileMenu.scss
+++ b/src/mobile/MobileMenu.scss
@@ -176,6 +176,7 @@ body {
 
 .open {
     right: 20px;
+    font-size: 35;
     position: absolute;
 }
 
-- 
cgit v1.2.3-70-g09d2


From b1777a2b6b5ef34424ba0cdaea03d41617a0eabd Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Mon, 29 Jun 2020 15:39:15 -0500
Subject: adjusting previews

---
 src/client/views/DocumentButtonBar.tsx         |  2 +-
 src/client/views/nodes/DocumentLinksButton.tsx | 18 ++++++++++--------
 2 files changed, 11 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index c05ca33fb..d544e13eb 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -271,7 +271,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         const considerPush = isText && this.considerGoogleDocsPush;
         return <div className="documentButtonBar">
             <div className="documentButtonBar-button">
-                <DocumentLinksButton View={this.view0} AlwaysOn={true} />
+                <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} />
             </div>
             <div className="documentButtonBar-button">
                 {this.templateButton}
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 4f4f12521..0280b0b9b 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -18,6 +18,7 @@ interface DocumentLinksButtonProps {
     View: DocumentView;
     Offset?: number[];
     AlwaysOn?: boolean;
+    InMenu?: boolean;
 }
 @observer
 export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -53,7 +54,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     @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 {
                 DocumentLinksButton.EditLink = this.props.View;
@@ -85,13 +86,14 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             <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}
-                    onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
-                    onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = {
-                        addDocTab: this.props.View.props.addDocTab,
-                        linkSrc: this.props.View.props.Document,
-                        linkDoc: links[0],
-                        Location: [e.clientX, e.clientY + 20]
-                    }))} >
+                // onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
+                // onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = {
+                //     addDocTab: this.props.View.props.addDocTab,
+                //     linkSrc: this.props.View.props.Document,
+                //     linkDoc: links[0],
+                //     Location: [e.clientX, e.clientY + 20]
+                // }))} 
+                >
                     {links.length ? 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)}
-- 
cgit v1.2.3-70-g09d2


From d81d2e814d449043c1cb20512d5e0a61c9351cfc Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Mon, 29 Jun 2020 17:08:25 -0500
Subject: adjusting menu

---
 src/client/views/linking/LinkMenuItem.tsx      | 10 +++++++++-
 src/client/views/nodes/DocumentLinksButton.tsx |  5 +++--
 2 files changed, 12 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index edc18b6a9..10c542536 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -135,6 +135,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false);
     }
 
+    @action
+    deleteLink = (): void => {
+        LinkManager.Instance.deleteLink(this.props.linkDoc);
+        //this.props.showLinks();
+    }
+
     render() {
         const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
         const canExpand = keys ? keys.length > 0 : false;
@@ -147,7 +153,9 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                         <div className="linkMenu-item-buttons">
                             {canExpand ? <div title="Show more" className="button" onPointerDown={e => this.toggleShowMore(e)}>
                                 <FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>}
-                            <div title="Edit link" className="button" ref={this._editRef} onPointerDown={this.onEdit}><FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div>
+
+                            {/* <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}>
                                 <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" />
                             </div>
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 0280b0b9b..69f25093a 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -53,10 +53,11 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
 
     @observable static StartLink: DocumentView | undefined;
     onLinkButtonDown = (e: React.PointerEvent): void => {
+
         setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => {
-            if (doubleTap && !this.props.InMenu) {
+            if (doubleTap) {
                 DocumentLinksButton.StartLink = this.props.View;
-            } else {
+            } else if (!!!this.props.InMenu) {
                 DocumentLinksButton.EditLink = this.props.View;
                 DocumentLinksButton.EditLinkLoc = [e.clientX + 10, e.clientY];
             }
-- 
cgit v1.2.3-70-g09d2


From 087cc4516cb3389324bb0b42c2240face696bfd3 Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Mon, 29 Jun 2020 18:06:42 -0500
Subject: UI changes to menu & prev

---
 src/client/views/linking/LinkMenuGroup.tsx     |  4 ++--
 src/client/views/linking/LinkMenuItem.tsx      | 19 +++++++++++++++++--
 src/client/views/nodes/DocumentLinksButton.tsx |  2 +-
 3 files changed, 20 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 89deb3a55..7892d381b 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -80,11 +80,11 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
 
         return (
             <div className="linkMenu-group">
-                <div className="linkMenu-group-name">
+                {/* <div className="linkMenu-group-name">
                     <p ref={this._drag} onPointerDown={this.onLinkButtonDown}
                         className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p>
                     {this.props.groupType === "*" || this.props.groupType === "" ? <></> : this.viewGroupAsTable(this.props.groupType)}
-                </div>
+                </div> */}
                 <div className="linkMenu-group-wrapper">
                     {groupItems}
                 </div>
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 10c542536..e8db5aff8 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -13,6 +13,8 @@ import React = require("react");
 import { DocumentManager } from '../../util/DocumentManager';
 import { setupMoveUpEvents, emptyFunction } from '../../../Utils';
 import { DocumentView } from '../nodes/DocumentView';
+import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
+import { LinkDocPreview } from '../nodes/LinkDocPreview';
 library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp);
 
 
@@ -124,7 +126,9 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         e.stopPropagation();
     }
 
+    @action
     onContextMenu = (e: React.MouseEvent) => {
+        DocumentLinksButton.EditLink = undefined;
         e.preventDefault();
         ContextMenu.Instance.addItem({ description: "Follow Default Link", event: () => this.followDefault(), icon: "arrow-right" });
         ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
@@ -132,6 +136,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
     @action.bound
     async followDefault() {
+        DocumentLinksButton.EditLink = undefined;
         DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false);
     }
 
@@ -139,6 +144,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     deleteLink = (): void => {
         LinkManager.Instance.deleteLink(this.props.linkDoc);
         //this.props.showLinks();
+        DocumentLinksButton.EditLink = undefined;
     }
 
     render() {
@@ -148,14 +154,23 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         return (
             <div className="linkMenu-item">
                 <div className={canExpand ? "linkMenu-item-content expand-three" : "linkMenu-item-content expand-two"}>
-                    <div ref={this._drag} className="linkMenu-name" title="drag to view target. click to customize." onPointerDown={this.onLinkButtonDown}>
+                    <div ref={this._drag} className="linkMenu-name" title="drag to view target. click to customize."
+                        onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
+                        onPointerEnter={action(e => this.props.linkDoc && (LinkDocPreview.LinkInfo = {
+                            addDocTab: this.props.addDocTab,
+                            linkSrc: this.props.sourceDoc,
+                            linkDoc: this.props.linkDoc,
+                            Location: [e.clientX, e.clientY + 20]
+                        }))}
+                        onPointerDown={this.onLinkButtonDown}>
                         <p >{StrCast(this.props.destinationDoc.title)}</p>
                         <div className="linkMenu-item-buttons">
                             {canExpand ? <div title="Show more" className="button" onPointerDown={e => this.toggleShowMore(e)}>
                                 <FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>}
 
                             {/* <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="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}>
                                 <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" />
                             </div>
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 69f25093a..c625c4cd6 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -95,7 +95,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 //     Location: [e.clientX, e.clientY + 20]
                 // }))} 
                 >
-                    {links.length ? links.length : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
+                    {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 === this.props.View ? <div className={"documentLinksButton-startLink"} /> : (null)}
-- 
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/DocumentLinksButton.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 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/DocumentLinksButton.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 9dfec9ad11210ef5b88d2b76bbe1f369b2e1d77d Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Thu, 2 Jul 2020 12:59:15 -0500
Subject: bottom popup, links button, cursor UI cleanup

---
 src/client/views/collections/CollectionLinearView.scss |  8 ++++----
 src/client/views/collections/CollectionLinearView.tsx  | 12 ++++++++----
 src/client/views/linking/LinkMenuItem.scss             |  1 +
 src/client/views/nodes/DocumentLinksButton.scss        |  2 +-
 src/client/views/nodes/DocumentLinksButton.tsx         |  9 +++++++--
 src/client/views/nodes/LinkAnchorBox.scss              |  7 ++++---
 6 files changed, 25 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss
index 134d62d4f..b3fc5f0e7 100644
--- a/src/client/views/collections/CollectionLinearView.scss
+++ b/src/client/views/collections/CollectionLinearView.scss
@@ -10,15 +10,15 @@
         height: 100%;
 
         >span {
-
-            margin-top: 8px;
-            margin-left: 4px;
+            //margin-top: 8px;
+            //margin-left: 4px;
             //margin-bottom: 2px;
             background: $dark-color;
             color: $light-color;
-            display: inline-block;
+            //display: inline-block;
             border-radius: 18px;
             margin-right: 6px;
+            //height: 100%;
             cursor: pointer;
         }
 
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index a180991d0..3096ef9c8 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -150,13 +150,17 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                     })}
                 </div>
                 {DocumentLinksButton.StartLink ? <span style={{
-                    background: backgroundColor === color ? "black" : backgroundColor,
+                    background: backgroundColor === color ? "black" : backgroundColor, paddingRight: "5px",
+                    height: 30, transform: "translate3d(6px, 5px, 0px)",
+                    paddingTop: "3px",
+                    //textAlign: "center"
                 }}
                     onPointerDown={e => e.stopPropagation()} >
                     <span style={{
-                        fontSize: "12.5px", display: "inline", whiteSpace: "nowrap",
-                        paddingLeft: "8px", paddingRight: "8px", paddingTop: "4px",
-                        paddingBottom: "10px"
+                        display: "inline",
+                        whiteSpace: "nowrap",
+                        paddingLeft: "8px",
+                        verticalAlign: "middle"
                     }}>
                         Creating link from: {DocumentLinksButton.StartLink.title} </span>
 
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
index 1669fc39d..ef53f6a43 100644
--- a/src/client/views/linking/LinkMenuItem.scss
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -81,6 +81,7 @@
         }
         &:hover {
             background: $main-accent;
+            cursor: grab;
         }
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 484f8c469..b58603978 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -23,7 +23,7 @@
     &:hover {
         background: deepskyblue;
         transform: scale(1.05);
-        cursor: default;
+        cursor: grab;
     }
 }
 .documentLinksButton {
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 27fbbc7fc..d67a01d8b 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -114,7 +114,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         const links = DocListCast(this.props.View.props.Document.links);
         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" : "" }}
+                <div className={"documentLinksButton"} style={{
+                    backgroundColor: DocumentLinksButton.StartLink ? "transparent" : "",
+                    width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px", fontWeight: "bold"
+                }}
                     onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick}
                 // onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
                 // onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = {
@@ -127,8 +130,10 @@ 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"}
+                    style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }}
                     onPointerDown={this.completeLink} onClick={this.finishLinkClick} /> : (null)}
-                {DocumentLinksButton.StartLink === this.props.View ? <div className={"documentLinksButton-startLink"} /> : (null)}
+                {DocumentLinksButton.StartLink === this.props.View ? <div className={"documentLinksButton-startLink"}
+                    style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }} /> : (null)}
             </div>;
     }
     render() {
diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss
index 710f2178b..42ef2958e 100644
--- a/src/client/views/nodes/LinkAnchorBox.scss
+++ b/src/client/views/nodes/LinkAnchorBox.scss
@@ -1,4 +1,5 @@
-.linkAnchorBox-cont, .linkAnchorBox-cont-small {
+.linkAnchorBox-cont,
+.linkAnchorBox-cont-small {
     cursor: default;
     position: absolute;
     width: 15;
@@ -24,6 +25,6 @@
 }
 
 .linkAnchorBox-cont-small {
-    width:5px;
-    height:5px;
+    width: 5px;
+    height: 5px;
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 37392f606d29547bed46ffcd0f0190ad38f51148 Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Fri, 3 Jul 2020 01:06:18 -0500
Subject: link created popup

---
 src/client/documents/Documents.ts                  |  8 ++++----
 src/client/views/MainView.tsx                      | 23 +++++++++++++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  5 +++++
 src/client/views/linking/LinkMenu.scss             |  9 ++++-----
 src/client/views/linking/LinkMenu.tsx              |  4 ++++
 src/client/views/linking/LinkMenuItem.tsx          | 11 ++++++++---
 src/client/views/nodes/DocumentLinksButton.tsx     | 14 +++++++++++--
 src/fields/Doc.ts                                  |  4 ++--
 8 files changed, 53 insertions(+), 25 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2e0323ede..0f434d616 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -868,6 +868,10 @@ export namespace DocUtils {
         DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
     }
 
+    function stopLinkCreated() {
+        MainView.linkCreated = false;
+    }
+
     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;
@@ -880,10 +884,6 @@ 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/MainView.tsx b/src/client/views/MainView.tsx
index 4898e114a..617cc19b1 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -84,6 +84,8 @@ export class MainView extends React.Component {
     public isPointerDown = false;
 
     @observable public static linkCreated: boolean = false;
+    @observable public static popupX: number = 600;
+    @observable public static popupY: number = 250;
 
 
     componentDidMount() {
@@ -614,15 +616,18 @@ 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}
+
+            <Fade in={MainView.linkCreated}>
+                <div style={{
+                    border: "1px solid rgb(111, 144, 175)", top: MainView.popupX ? MainView.popupX : 600
+                    , position: "absolute",
+                    left: MainView.popupY ? MainView.popupY : 250
+                    , width: "auto", height: "auto", zIndex: 10000,
+                    fontSize: "13px", whiteSpace: "nowrap", backgroundColor: "rgb(170, 205, 238)",
+                    padding: "5px", //fontWeight: "bold"
+                }}>Link Created</div>
+            </Fade>
+
             {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/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 546a4307c..39bfd8239 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -48,6 +48,7 @@ import { SnappingManager } from "../../../util/SnappingManager";
 import { InkingStroke, ActiveArrowStart, ActiveArrowEnd, ActiveInkColor, ActiveFillColor, ActiveInkWidth, ActiveInkBezierApprox, ActiveDash } from "../../InkingStroke";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { DocumentLinksButton } from "../../nodes/DocumentLinksButton";
+import { MainView } from "../../MainView";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -248,6 +249,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         } else {
             const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
             this.props.addDocument(source);
+            MainView.popupX = xp;
+            MainView.popupY = yp;
+            runInAction(() => { MainView.linkCreated = true; });
+            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
             linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation"); // TODODO this is where in text links get passed
             e.stopPropagation();
             return true;
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index c372e7098..6468ccd3d 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -15,7 +15,7 @@
 }
 
 .linkMenu-group {
-    border-bottom: 0.5px solid lightgray; 
+    border-bottom: 0.5px solid lightgray;
     padding: 5px 0;
 
 
@@ -30,9 +30,11 @@
             p {
                 background-color: lightgray;
             }
+
             p.expand-one {
                 width: calc(100% - 26px);
             }
+
             .linkEditor-tableButton {
                 display: block;
             }
@@ -50,7 +52,4 @@
             display: none;
         }
     }
-}
-
-
-
+}
\ No newline at end of file
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index c36e7b67c..c672511ac 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -10,6 +10,7 @@ import { LinkMenuGroup } from "./LinkMenuGroup";
 import { faTrash } from '@fortawesome/free-solid-svg-icons';
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { DocumentLinksButton } from "../nodes/DocumentLinksButton";
+import { LinkDocPreview } from "../nodes/LinkDocPreview";
 
 library.add(faTrash);
 
@@ -28,6 +29,9 @@ export class LinkMenu extends React.Component<Props> {
 
     @action
     onClick = (e: PointerEvent) => {
+
+        LinkDocPreview.LinkInfo = undefined;
+
         if (this._linkMenuRef?.contains(e.target as any)) {
             DocumentLinksButton.EditLink = undefined;
         }
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 54dca261d..b11a173f3 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -97,6 +97,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         return (<div className="link-metadata">{mdRows}</div>);
     }
 
+    @action
     onLinkButtonDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
         this._downY = e.clientY;
@@ -106,6 +107,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
         document.addEventListener("pointermove", this.onLinkButtonMoved);
         document.removeEventListener("pointerup", this.onLinkButtonUp);
         document.addEventListener("pointerup", this.onLinkButtonUp);
+
+        //if (this._editRef && this._editRef.current?.contains(e.target as any)) {
+        LinkDocPreview.LinkInfo = undefined;
+        //}
     }
 
     onLinkButtonUp = (e: PointerEvent): void => {
@@ -168,14 +173,14 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                         }))}
                         onPointerDown={this.onLinkButtonDown}>
                         <p >{StrCast(this.props.destinationDoc.title)}</p>
-                        <div className="linkMenu-item-buttons">
+                        <div className="linkMenu-item-buttons" ref={this._editRef} >
                             {canExpand ? <div title="Show more" className="button" onPointerDown={e => this.toggleShowMore(e)}>
                                 <FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>}
 
                             {/* <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}>
+                            <div title="Delete link" className="button" onPointerDown={this.deleteLink}>
                                 <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" /></div>
-                            <div title="Follow link" className="button" onClick={e => this.followDefault()} onContextMenu={this.onContextMenu}>
+                            <div title="Follow link" className="button" onPointerDown={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 d67a01d8b..95c2988da 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -77,6 +77,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         }
     }
 
+    @action
     completeLink = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e, doubleTap) => {
             if (doubleTap) {
@@ -88,12 +89,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 } else {
                     DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
                         DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
+                    runInAction(() => { MainView.linkCreated = true; });
+                    runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
+                    MainView.popupX = e.screenX;
+                    MainView.popupY = e.screenY;
                 }
             }
         }));
     }
 
-    finishLinkClick = () => {
+    @action
+    finishLinkClick = (e: React.MouseEvent) => {
         if (DocumentLinksButton.StartLink === this.props.View) {
             DocumentLinksButton.StartLink = undefined;
             // action((e: React.PointerEvent<HTMLDivElement>) => {
@@ -102,6 +108,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         } else {
             DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
                 DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
+            runInAction(() => { MainView.linkCreated = true; });
+            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
+            MainView.popupX = e.screenX;
+            MainView.popupY = e.screenY;
         }
     }
 
@@ -131,7 +141,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 </div>
                 {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ? <div className={"documentLinksButton-endLink"}
                     style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }}
-                    onPointerDown={this.completeLink} onClick={this.finishLinkClick} /> : (null)}
+                    onPointerDown={this.completeLink} onClick={e => this.finishLinkClick(e)} /> : (null)}
                 {DocumentLinksButton.StartLink === this.props.View ? <div className={"documentLinksButton-startLink"}
                     style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }} /> : (null)}
             </div>;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index b4bb556c7..7aa1d528d 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -832,14 +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 c146d9a6165dcfa9ebd98868dfb741a26ede5a9f Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Fri, 3 Jul 2020 12:09:48 -0500
Subject: finished link created popups

---
 src/client/views/MainView.tsx                             | 15 +++++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx         |  4 ----
 src/client/views/nodes/DocumentLinksButton.tsx            |  6 +++---
 src/client/views/nodes/DocumentView.tsx                   | 12 ++++++++++++
 4 files changed, 24 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index acbeb65cc..3d47d09c8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -619,12 +619,15 @@ export class MainView extends React.Component {
 
             <Fade in={MainView.linkCreated}>
                 <div style={{
-                    border: "1px solid rgb(111, 144, 175)", top: MainView.popupX ? MainView.popupX : 600
-                    , position: "absolute",
-                    left: MainView.popupY ? MainView.popupY : 250
-                    , width: "auto", height: "auto", zIndex: 10000,
-                    fontSize: "13px", whiteSpace: "nowrap", backgroundColor: "rgb(170, 205, 238)",
-                    padding: "5px", //fontWeight: "bold"
+                    border: "1px solid rgb(100, 100, 100)",
+                    left: MainView.popupX ? MainView.popupX : 600,
+                    position: "absolute",
+                    top: MainView.popupY ? MainView.popupY : 250, width: "auto",
+                    height: "auto", zIndex: 10000, borderRadius: "13px",
+                    fontSize: "13px", whiteSpace: "nowrap",
+                    color: "rgb(100, 100, 100)", backgroundColor: "rgba(250, 250, 250, 0.85)",
+                    paddingTop: "6.5px", paddingBottom: "6.5px", fontWeight: "bold",
+                    paddingLeft: "9px", paddingRight: "9px"
                 }}>Link Created</div>
             </Fade>
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 25f724449..7f6d1d506 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -249,10 +249,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         } else {
             const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
             this.props.addDocument(source);
-            MainView.popupX = xp;
-            MainView.popupY = yp;
-            runInAction(() => { MainView.linkCreated = true; });
-            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
             linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation"); // TODODO this is where in text links get passed
             e.stopPropagation();
             return true;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 7faae68bf..6a6624aa0 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -109,9 +109,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
                 DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
             runInAction(() => { MainView.linkCreated = true; });
-            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
-            MainView.popupX = e.screenX;
-            MainView.popupY = e.screenY;
+            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2000); });
+            MainView.popupX = e.screenX - 25;
+            MainView.popupY = e.screenY - 140;
         }
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 66af29286..3a9e4fcf0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -42,6 +42,7 @@ import { RadialMenu } from './RadialMenu';
 import React = require("react");
 import { DocumentLinksButton } from './DocumentLinksButton';
 import { MobileInterface } from '../../../mobile/MobileInterface';
+import { MainView } from '../MainView';
 
 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,
@@ -641,12 +642,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             e.stopPropagation();
             de.complete.annoDragData.linkedToDoc = true;
 
+            runInAction(() => MainView.popupX = de.x);
+            runInAction(() => MainView.popupY = de.y);
+            runInAction(() => { MainView.linkCreated = true; });
+            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
+
             DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link");
         }
         if (de.complete.linkDragData) {
             e.stopPropagation();
             // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
             // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
+            runInAction(() => MainView.popupX = de.x);
+            runInAction(() => MainView.popupY = de.y);
+
+            runInAction(() => { MainView.linkCreated = true; });
+            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2000); });
+
             de.complete.linkDragData.linkSourceDocument !== this.props.Document &&
                 (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument },
                     { doc: this.props.Document }, `link`)); // TODODO this is where in text links get passed
-- 
cgit v1.2.3-70-g09d2


From ed74b8b6d6c65a24803c4e0e68aa192a750d60fa Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Fri, 3 Jul 2020 18:47:48 -0500
Subject: restructure

---
 src/client/documents/Documents.ts                  |  5 ----
 src/client/views/MainView.tsx                      | 27 ++---------------
 .../views/collections/CollectionLinearView.scss    | 34 ++++++++++++++++++----
 .../views/collections/CollectionLinearView.tsx     | 32 ++++----------------
 src/client/views/linking/LinkMenuItem.scss         | 34 ++++++++++++----------
 src/client/views/nodes/DocumentLinksButton.tsx     | 24 +++++++++------
 src/client/views/nodes/DocumentView.tsx            | 22 ++++++++------
 src/client/views/nodes/LinkCreatedBox.scss         | 21 +++++++++++++
 src/client/views/nodes/LinkCreatedBox.tsx          | 31 ++++++++++++++++++++
 9 files changed, 134 insertions(+), 96 deletions(-)
 create mode 100644 src/client/views/nodes/LinkCreatedBox.scss
 create mode 100644 src/client/views/nodes/LinkCreatedBox.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0f434d616..f81c25bab 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -50,7 +50,6 @@ 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 {
@@ -868,10 +867,6 @@ export namespace DocUtils {
         DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
     }
 
-    function stopLinkCreated() {
-        MainView.linkCreated = false;
-    }
-
     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;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3d47d09c8..15f818d1f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -60,6 +60,7 @@ import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { LinkMenu } from './linking/LinkMenu';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
 import { Fade } from '@material-ui/core';
+import { LinkCreatedBox } from './nodes/LinkCreatedBox';
 
 @observer
 export class MainView extends React.Component {
@@ -83,11 +84,6 @@ export class MainView extends React.Component {
 
     public isPointerDown = false;
 
-    @observable public static linkCreated: boolean = false;
-    @observable public static popupX: number = 600;
-    @observable public static popupY: number = 250;
-
-
     componentDidMount() {
         DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType"]); // can play with these fields on someone else's
 
@@ -367,11 +363,6 @@ 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);
@@ -616,21 +607,7 @@ export class MainView extends React.Component {
                 {this.mainContent}
             </GestureOverlay>
             <PreviewCursor />
-
-            <Fade in={MainView.linkCreated}>
-                <div style={{
-                    border: "1px solid rgb(100, 100, 100)",
-                    left: MainView.popupX ? MainView.popupX : 600,
-                    position: "absolute",
-                    top: MainView.popupY ? MainView.popupY : 250, width: "auto",
-                    height: "auto", zIndex: 10000, borderRadius: "13px",
-                    fontSize: "13px", whiteSpace: "nowrap",
-                    color: "rgb(100, 100, 100)", backgroundColor: "rgba(250, 250, 250, 0.85)",
-                    paddingTop: "6.5px", paddingBottom: "6.5px", fontWeight: "bold",
-                    paddingLeft: "9px", paddingRight: "9px"
-                }}>Link Created</div>
-            </Fade>
-
+            <LinkCreatedBox />
             {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 b3fc5f0e7..5ada79a28 100644
--- a/src/client/views/collections/CollectionLinearView.scss
+++ b/src/client/views/collections/CollectionLinearView.scss
@@ -10,18 +10,42 @@
         height: 100%;
 
         >span {
-            //margin-top: 8px;
-            //margin-left: 4px;
-            //margin-bottom: 2px;
             background: $dark-color;
             color: $light-color;
-            //display: inline-block;
             border-radius: 18px;
             margin-right: 6px;
-            //height: 100%;
             cursor: pointer;
         }
 
+        .bottomPopup-background {
+            padding-right: 14px;
+            height: 35;
+            transform: translate3d(6px, 5px, 0px);
+            padding-top: 6.5px;
+            padding-bottom: 7px;
+            padding-left: 5px;
+        }
+
+        .bottomPopup-text {
+            display: inline;
+            white-space: nowrap;
+            padding-left: 8px;
+            padding-right: 4px;
+            vertical-align: middle;
+            font-size: 12.5px;
+        }
+
+        .bottomPopup-exit {
+            display: inline;
+            white-space: nowrap;
+            padding-left: 8px;
+            padding-right: 8px;
+            vertical-align: middle;
+            background-color: lightgrey;
+            border-radius: 5.5px;
+            color: black;
+        }
+
         >label {
             margin-top: "auto";
             margin-bottom: "auto";
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 7635dac24..7cbe5c19d 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -149,36 +149,14 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                         </div>;
                     })}
                 </div>
-                {DocumentLinksButton.StartLink ? <span style={{
-                    background: backgroundColor === color ? "black" : backgroundColor,
-                    paddingRight: "14px",
-                    height: "35",
-                    transform: "translate3d(6px, 5px, 0px)",
-                    paddingTop: "6.5px",
-                    paddingBottom: "7px",
-                    paddingLeft: "5px"
+                {DocumentLinksButton.StartLink ? <span className="bottomPopup-background" style={{
+                    background: backgroundColor === color ? "black" : backgroundColor
                 }}
                     onPointerDown={e => e.stopPropagation()} >
-                    <span style={{
-                        display: "inline",
-                        whiteSpace: "nowrap",
-                        paddingLeft: "8px",
-                        paddingRight: "4px",
-                        verticalAlign: "middle",
-                        fontSize: "12.5px"
-                    }}>
+                    <span className="bottomPopup-text" >
                         Creating link from: {DocumentLinksButton.StartLink.title} </span>
-                    <span onClick={this.exitLongLinks}
-                        style={{
-                            display: "inline",
-                            whiteSpace: "nowrap",
-                            paddingLeft: "8px",
-                            paddingRight: "8px",
-                            verticalAlign: "middle",
-                            backgroundColor: "lightgrey",
-                            borderRadius: "5.5px",
-                            color: "black"
-                        }}>Exit</span>
+                    <span className="bottomPopup-exit" onClick={this.exitLongLinks}
+                    >Exit</span>
 
                     {/* <FontAwesomeIcon icon="times-circle" size="lg" style={{ color: "red" }}
                         onClick={this.exitLongLinks} /> */}
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
index c5becae7d..e3ce69cd7 100644
--- a/src/client/views/linking/LinkMenuItem.scss
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -31,21 +31,23 @@
         font-size: 10.5px;
     }
 
-    // &:hover {
-    //     .linkMenu-item-buttons {
-    //         display: flex;
-    //     }
-    //     .linkMenu-item-content {
-    //         &.expand-two p {
-    //             width: calc(100% - 52px);
-    //             background-color: lightgray;
-    //         }
-    //         &.expand-three p {
-    //             width: calc(100% - 84px);
-    //             background-color: lightgray;
-    //         }
-    //     }
-    // }
+    &:hover {
+        .linkMenu-item-buttons {
+            display: flex;
+        }
+
+        .linkMenu-item-content {
+            &.expand-two p {
+                width: calc(100% - 52px);
+                background-color: lightgray;
+            }
+
+            &.expand-three p {
+                width: calc(100% - 84px);
+                background-color: lightgray;
+            }
+        }
+    }
 }
 
 .linkMenu-item-buttons {
@@ -53,7 +55,7 @@
     top: 50%;
     right: 0;
     transform: translateY(-50%);
-    display: flex;
+    display: none;
 
     .button {
         width: 20px;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 6a6624aa0..bfd860f65 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -10,7 +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";
+import { LinkCreatedBox } from "./LinkCreatedBox";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -89,10 +89,13 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 } else {
                     DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
                         DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
-                    runInAction(() => { MainView.linkCreated = true; });
-                    runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
-                    MainView.popupX = e.screenX;
-                    MainView.popupY = e.screenY;
+
+                    runInAction(() => {
+                        LinkCreatedBox.popupX = e.screenX;
+                        LinkCreatedBox.popupY = e.screenY - 120;
+                        LinkCreatedBox.linkCreated = true;
+                        setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500);
+                    });
                 }
             }
         }));
@@ -108,10 +111,13 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         } else {
             DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View &&
                 DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag");
-            runInAction(() => { MainView.linkCreated = true; });
-            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2000); });
-            MainView.popupX = e.screenX - 25;
-            MainView.popupY = e.screenY - 140;
+
+            runInAction(() => {
+                LinkCreatedBox.popupX = e.screenX;
+                LinkCreatedBox.popupY = e.screenY - 120;
+                LinkCreatedBox.linkCreated = true;
+                setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500);
+            });
         }
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3a9e4fcf0..b38db9a1e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -42,7 +42,7 @@ import { RadialMenu } from './RadialMenu';
 import React = require("react");
 import { DocumentLinksButton } from './DocumentLinksButton';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { MainView } from '../MainView';
+import { LinkCreatedBox } from './LinkCreatedBox';
 
 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,
@@ -642,10 +642,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             e.stopPropagation();
             de.complete.annoDragData.linkedToDoc = true;
 
-            runInAction(() => MainView.popupX = de.x);
-            runInAction(() => MainView.popupY = de.y);
-            runInAction(() => { MainView.linkCreated = true; });
-            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2500); });
+            runInAction(() => {
+                LinkCreatedBox.popupX = de.x;
+                LinkCreatedBox.popupY = de.y;
+                LinkCreatedBox.linkCreated = true;
+                setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500);
+            });
 
             DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link");
         }
@@ -653,11 +655,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             e.stopPropagation();
             // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
             // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
-            runInAction(() => MainView.popupX = de.x);
-            runInAction(() => MainView.popupY = de.y);
 
-            runInAction(() => { MainView.linkCreated = true; });
-            runInAction(() => { setTimeout(function () { runInAction(() => MainView.linkCreated = false); }, 2000); });
+            runInAction(() => {
+                LinkCreatedBox.popupX = de.x;
+                LinkCreatedBox.popupY = de.y;
+                LinkCreatedBox.linkCreated = true;
+                setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500);
+            });
 
             de.complete.linkDragData.linkSourceDocument !== this.props.Document &&
                 (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument },
diff --git a/src/client/views/nodes/LinkCreatedBox.scss b/src/client/views/nodes/LinkCreatedBox.scss
new file mode 100644
index 000000000..3cbd38b55
--- /dev/null
+++ b/src/client/views/nodes/LinkCreatedBox.scss
@@ -0,0 +1,21 @@
+.linkCreatedBox-fade {
+    border: 1px solid rgb(100, 100, 100);
+
+
+    width: auto;
+    position: absolute;
+
+    height: auto;
+    z-index: 10000;
+    border-radius: 13px;
+    font-size: 13px;
+    white-space: nowrap;
+
+    color: rgb(100, 100, 100);
+    background-color: rgba(250, 250, 250, 0.85);
+    padding-top: 6.5px;
+    padding-bottom: 6.5px;
+    font-weight: bold;
+    padding-left: 9px;
+    padding-right: 9px;
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkCreatedBox.tsx b/src/client/views/nodes/LinkCreatedBox.tsx
new file mode 100644
index 000000000..d157d3fca
--- /dev/null
+++ b/src/client/views/nodes/LinkCreatedBox.tsx
@@ -0,0 +1,31 @@
+import React = require("react");
+import { observer } from "mobx-react";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface } from "../../../fields/Schema";
+import "./LinkCreatedBox.scss";
+import { observable, action } from "mobx";
+import { Fade } from "@material-ui/core";
+
+
+@observer
+export class LinkCreatedBox extends React.Component<{}> {
+
+    @observable public static linkCreated: boolean = false;
+    @observable public static popupX: number = 600;
+    @observable public static popupY: number = 250;
+
+    @action
+    public static changeLinkCreated = () => {
+        LinkCreatedBox.linkCreated = !LinkCreatedBox.linkCreated;
+    }
+
+    render() {
+        return <Fade in={LinkCreatedBox.linkCreated}>
+            <div className="linkCreatedBox-fade"
+                style={{
+                    left: LinkCreatedBox.popupX ? LinkCreatedBox.popupX : 600,
+                    top: LinkCreatedBox.popupY ? LinkCreatedBox.popupY : 250,
+                }}>Link Created</div>
+        </Fade>;
+    }
+} 
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

