From b4c2d2add2b863060ce559e49b3953f24050f162 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Tue, 14 Jan 2020 14:47:44 -0500
Subject: can add strokes to mobile ink

---
 src/client/views/collections/CollectionSubView.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 73dc7edc6..d8b575092 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -53,7 +53,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
             this.dropDisposer && this.dropDisposer();
             this.gestureDisposer && this.gestureDisposer();
+            console.log("create drop", ele);
             if (ele) {
+                console.log("create drop 2", ele);
                 this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
                 this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
             }
@@ -135,7 +137,6 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
 
         @undoBatch
         protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {
-
         }
 
         @undoBatch
-- 
cgit v1.2.3-70-g09d2


From 3cca58612cde96a3082ca8e190fe2166d531d556 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Tue, 14 Jan 2020 17:40:50 -0500
Subject: drawn strokes get added to mobile ink collection

---
 src/client/views/GestureOverlay.tsx                |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  2 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  8 +--
 src/mobile/MobileInterface.scss                    |  7 +++
 src/mobile/MobileInterface.tsx                     | 62 ++++++++++++++--------
 .../authentication/models/current_user_utils.ts    |  2 +-
 6 files changed, 53 insertions(+), 30 deletions(-)
 create mode 100644 src/mobile/MobileInterface.scss

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 632f2f0d6..4d487c032 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -231,9 +231,9 @@ export default class GestureOverlay extends Touchable {
     render() {
         return (
             <div className="gestureOverlay-cont" onPointerDown={this.onPointerDown} onTouchStart={this.onTouchStart}>
+                {this.currentStroke}
                 {this.props.children}
                 {this._palette}
-                {this.currentStroke}
             </div>);
     }
 }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d8b575092..e94f24f2c 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -53,9 +53,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
             this.dropDisposer && this.dropDisposer();
             this.gestureDisposer && this.gestureDisposer();
-            console.log("create drop", ele);
             if (ele) {
-                console.log("create drop 2", ele);
                 this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
                 this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
             }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c3e131184..8d376fb57 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -114,6 +114,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
     private addDocument = (newBox: Doc) => {
         const added = this.props.addDocument(newBox);
+        console.log("adding doc from freeform", added);
         added && this.bringToFront(newBox);
         added && this.updateCluster(newBox);
         return added;
@@ -356,12 +357,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @undoBatch
     onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
-        console.log("on gesture");
         switch (ge.gesture) {
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { x: B.x, y: B.y, width: B.width, height: B.height });
+                console.log("make stroke", inkDoc);
                 this.addDocument(inkDoc);
                 e.stopPropagation();
                 break;
@@ -403,14 +404,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @action
     pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
         // I think it makes sense for the marquee menu to go away when panned. -syip2
-        MarqueeOptionsMenu.Instance.fadeOut(true);
+        MarqueeOptionsMenu.Instance && MarqueeOptionsMenu.Instance.fadeOut(true);
 
         let x = this.Document.panX || 0;
         let y = this.Document.panY || 0;
         const docs = this.childLayoutPairs.map(pair => pair.layout);
         const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
         if (!this.isAnnotationOverlay) {
-            PDFMenu.Instance.fadeOut(true);
+            PDFMenu.Instance && PDFMenu.Instance.fadeOut(true);
             const minx = docs.length ? NumCast(docs[0].x) : 0;
             const maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
             const miny = docs.length ? NumCast(docs[0].y) : 0;
@@ -990,6 +991,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         // otherwise, they are stored in fieldKey.  All annotations to this document are stored in the extension document
         if (!this.extensionDoc) return (null);
         // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale;
+        console.log("height freeform", this.isAnnotationOverlay, this.Document.scrollHeight, this.props.PanelHeight());
         return <div className={"collectionfreeformview-container"} ref={this.createDropAndGestureTarget} onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
             style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
             onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onTouchStart={this.onTouchStart}>
diff --git a/src/mobile/MobileInterface.scss b/src/mobile/MobileInterface.scss
new file mode 100644
index 000000000..e4cc919a5
--- /dev/null
+++ b/src/mobile/MobileInterface.scss
@@ -0,0 +1,7 @@
+.mobileInterface-topButtons {
+    position: absolute;
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+    z-index: 9999;
+}
\ No newline at end of file
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 8d342d749..3cb08afa7 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -10,10 +10,16 @@ import { DocumentView } from '../client/views/nodes/DocumentView';
 import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from '../Utils';
 import { Transform } from '../client/util/Transform';
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faPenNib, faHighlighter, faEraser, faMousePointer, faBreadSlice } from '@fortawesome/free-solid-svg-icons';
+import { faPenNib, faHighlighter, faEraser, faMousePointer, faBreadSlice, faTrash, faCheck } from '@fortawesome/free-solid-svg-icons';
 import { Scripting } from '../client/util/Scripting';
 import { CollectionFreeFormView } from '../client/views/collections/collectionFreeForm/CollectionFreeFormView';
 import GestureOverlay from '../client/views/GestureOverlay';
+import { InkingControl } from '../client/views/InkingControl';
+import { InkTool } from '../new_fields/InkField';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import "./MobileInterface.scss";
+
+library.add(faTrash, faCheck);
 
 @observer
 export default class MobileInterface extends React.Component {
@@ -22,6 +28,9 @@ export default class MobileInterface extends React.Component {
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
     @observable private currentView: "main" | "ink" | "library" = "main";
 
+    private mainDoc = CurrentUserUtils.setupMobileDoc(this.userDoc);
+    private inkDoc?: Doc;
+
     constructor(props: Readonly<{}>) {
         super(props);
         MobileInterface.Instance = this;
@@ -32,8 +41,8 @@ export default class MobileInterface extends React.Component {
         library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer]);
 
         if (this.userDoc && !this.mainContainer) {
-            const doc = CurrentUserUtils.setupMobileDoc(this.userDoc);
-            this.userDoc.activeMobile = doc;
+            // const doc = CurrentUserUtils.setupMobileDoc(this.userDoc);
+            this.userDoc.activeMobile = this.mainDoc;
         }
     }
 
@@ -44,13 +53,14 @@ export default class MobileInterface extends React.Component {
         if (this.userDoc) {
             switch (view) {
                 case "main": {
-                    const doc = CurrentUserUtils.setupMobileDoc(this.userDoc);
-                    this.userDoc.activeMobile = doc;
+                    // const doc = CurrentUserUtils.setupMobileDoc(this.userDoc);
+                    this.userDoc.activeMobile = this.mainDoc;
                     break;
                 }
                 case "ink": {
-                    const doc = CurrentUserUtils.setupMobileInkingDoc(this.userDoc);
-                    this.userDoc.activeMobile = doc;
+                    this.inkDoc = CurrentUserUtils.setupMobileInkingDoc(this.userDoc);
+                    this.userDoc.activeMobile = this.inkDoc;
+                    InkingControl.Instance.switchTool(InkTool.Pen);
                     break;
                 }
             }
@@ -64,7 +74,7 @@ export default class MobileInterface extends React.Component {
                 Document={this.mainContainer}
                 DataDoc={undefined}
                 LibraryPath={emptyPath}
-                addDocument={undefined}
+                addDocument={returnFalse}
                 addDocTab={returnFalse}
                 pinToPres={emptyFunction}
                 removeDocument={undefined}
@@ -89,40 +99,46 @@ export default class MobileInterface extends React.Component {
         return "hello";
     }
 
+    onClick = (e: React.MouseEvent) => {
+        // insert ink as collection into active displayed doc view
+        // reset ink collection
+        this.inkDoc = undefined;
+        console.log("clicked");
+        this.switchCurrentView("main");
+        InkingControl.Instance.switchTool(InkTool.None); // TODO: switch to previous tool
+    }
+
     @computed
     get inkContent() {
-        // return <div>INK</div>;
+        // TODO: get props from active collection and pass to the new freeform
         if (this.mainContainer) {
             return (
                 <GestureOverlay>
-                    <CollectionFreeFormView
+                    <div className="mobileInterface-topButtons">
+                        <button className="mobileInterface-button cancel" onClick={this.onClick} title="Cancel drawing"><FontAwesomeIcon icon="trash" /></button>
+                        <button className="mobileInterface-button cancel" onClick={this.onClick} title="Insert drawing"><FontAwesomeIcon icon="check" /></button>
+                    </div>
+                    <CollectionView
                         Document={this.mainContainer}
                         DataDoc={undefined}
                         LibraryPath={emptyPath}
                         fieldKey={""}
-                        addDocument={returnFalse}
-                        removeDocument={returnFalse}
-                        moveDocument={returnFalse}
                         addDocTab={returnFalse}
                         pinToPres={emptyFunction}
-                        PanelHeight={() => window.screen.height}
-                        PanelWidth={() => window.screen.width}
-                        annotationsKey={""}
-                        isAnnotationOverlay={false}
+                        PanelHeight={() => window.innerHeight}
+                        PanelWidth={() => window.innerWidth}
                         focus={emptyFunction}
-                        isSelected={returnTrue} //
+                        isSelected={returnTrue}
                         select={emptyFunction}
-                        active={returnTrue} //
+                        active={returnTrue}
                         ContentScaling={returnOne}
                         whenActiveChanged={returnFalse}
-                        CollectionView={undefined}
                         ScreenToLocalTransform={Transform.Identity}
                         ruleProvider={undefined}
                         renderDepth={0}
                         ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined}
-                        chromeCollapsed={true}>
-                    </CollectionFreeFormView>
+                        ContainingCollectionDoc={undefined}>
+                    </CollectionView>
                 </GestureOverlay>
             );
         }
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index b40179fbc..98092ddfe 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -141,7 +141,7 @@ export class CurrentUserUtils {
     }
 
     static setupMobileInkingDoc(userDoc: Doc) {
-        return Docs.Create.FreeformDocument([], { x: 0, y: 0, width: 10, height: 20, title: "Mobile Inking", backgroundColor: "red" });
+        return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white" });
     }
 
     // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker.  when clicked, this panel will be displayed in the target container (ie, sidebarContainer)  
-- 
cgit v1.2.3-70-g09d2


From e8fcbbf57b2a2f443d9c280ce10558cf9d51c632 Mon Sep 17 00:00:00 2001
From: vellichora <fangrui_tong@brown.edu>
Date: Sun, 9 Feb 2020 17:11:24 -0500
Subject: can upload collection from mobile to desktop

---
 src/client/DocServer.ts                            |   9 +-
 src/client/util/DragManager.ts                     |   2 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |   1 +
 .../views/collections/CollectionStackingView.tsx   |   2 +
 .../CollectionStackingViewFieldColumn.tsx          |   1 +
 src/client/views/collections/CollectionSubView.tsx |   2 +-
 src/client/views/collections/CollectionView.tsx    |   2 +-
 .../views/collections/CollectionViewChromes.tsx    |   1 +
 src/mobile/ImageUpload.tsx                         |   1 +
 src/mobile/MobileInkOverlay.tsx                    |  53 +++++++-
 src/mobile/MobileInterface.scss                    |   8 ++
 src/mobile/MobileInterface.tsx                     | 150 ++++++++++++---------
 src/server/ApiManagers/UploadManager.ts            |   1 +
 src/server/Message.ts                              |   6 +
 src/server/Websocket/Websocket.ts                  |   7 +-
 .../authentication/models/current_user_utils.ts    |  21 +--
 src/server/server_Initialization.ts                |  24 ++--
 17 files changed, 195 insertions(+), 96 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index c03764471..e09251855 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,5 +1,5 @@
 import * as OpenSocket from 'socket.io-client';
-import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent } from "./../server/Message";
+import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
 import { Opt, Doc } from '../new_fields/Doc';
 import { Utils, emptyFunction } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
@@ -81,6 +81,10 @@ export namespace DocServer {
             Utils.Emit(_socket, MessageStore.UpdateMobileInkOverlayPosition, content);
         }
 
+        export function dispatchMobileDocumentUpload(content: MobileDocumentUploadContent) {
+            Utils.Emit(_socket, MessageStore.MobileDocumentUpload, content);
+        }
+
     }
 
     export function init(protocol: string, hostname: string, port: number, identifier: string) {
@@ -116,6 +120,9 @@ export namespace DocServer {
         _socket.addEventListener("receiveUpdateOverlayPosition", (content: UpdateMobileInkOverlayPositionContent) => {
             MobileInkOverlay.Instance.updatePosition(content);
         });
+        _socket.addEventListener("receiveMobileDocumentUpload", (content: MobileDocumentUploadContent) => {
+            MobileInkOverlay.Instance.uploadDocument(content);
+        });
     }
 
     function errorFunc(): never {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index df2f5fe3c..0bb8b531d 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -179,7 +179,7 @@ export namespace DragManager {
             );
         }
         element.dataset.canDrop = "true";
-        const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
+        const handler = (e: Event) => { console.log("drop target reveied docs"); dropFunc(e, (e as CustomEvent<DropEvent>).detail); };
         element.addEventListener("dashOnDrop", handler);
         return () => {
             element.removeEventListener("dashOnDrop", handler);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 80752303c..26e0cc35a 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -73,6 +73,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
 
     @undoBatch
     rowDrop = action((e: Event, de: DragManager.DropEvent) => {
+        console.log("masronry row drop");
         this._createAliasSelected = false;
         if (de.complete.docDragData) {
             (this.props.parent.Document.dropConverter instanceof ScriptField) &&
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 992820fc7..83c90810e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -240,6 +240,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
+        console.log("DROP STACKIN G2");
         const where = [de.x, de.y];
         let targInd = -1;
         let plusOne = 0;
@@ -270,6 +271,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @undoBatch
     @action
     onDrop = async (e: React.DragEvent): Promise<void> => {
+        console.log("DROP STACKING");
         const where = [e.clientX, e.clientY];
         let targInd = -1;
         this._docXfs.map((cd, i) => {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 39b4e4e1d..65c4b3195 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -57,6 +57,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
 
     @undoBatch
     columnDrop = action((e: Event, de: DragManager.DropEvent) => {
+        console.log("column drop stacking");
         this._createAliasSelected = false;
         if (de.complete.docDragData) {
             const key = StrCast(this.props.parent.props.Document.sectionFilter);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e94f24f2c..b35af2314 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 88023783b..1d399e26f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -138,7 +138,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
         let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
         index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
 
-        ContextMenu.Instance.clearItems();
+        ContextMenu.Instance && ContextMenu.Instance.clearItems();
         if (index !== -1) {
             value.splice(index, 1);
             return true;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index a870b6043..01dc21f95 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -231,6 +231,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
 
     @action
     toggleCollapse = () => {
+        console.log("toggle collapse");
         this.props.CollectionView.props.Document.chromeStatus = this.props.CollectionView.props.Document.chromeStatus === "enabled" ? "collapsed" : "enabled";
         if (this.props.collapse) {
             this.props.collapse(this.props.CollectionView.props.Document.chromeStatus !== "enabled");
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index 3304e8e22..10bd78075 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -47,6 +47,7 @@ class Uploader extends React.Component {
 
                     const upload = window.location.origin + "/upload";
                     this.status = "uploading image";
+                    console.log("uploading image", formData);
                     const res = await fetch(upload, {
                         method: 'POST',
                         body: formData
diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx
index 6b65aa436..ed4cca5b9 100644
--- a/src/mobile/MobileInkOverlay.tsx
+++ b/src/mobile/MobileInkOverlay.tsx
@@ -1,9 +1,13 @@
 import React = require('react');
 import { observer } from "mobx-react";
-import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPositionContent } from "../server/Message";
+import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "../server/Message";
 import { observable, action } from "mobx";
 import { GestureUtils } from "../pen-gestures/GestureUtils";
 import "./MobileInkOverlay.scss";
+import { StrCast } from '../new_fields/Types';
+import { DragManager } from "../client/util/DragManager";
+import { DocServer } from '../client/DocServer';
+import { Doc } from '../new_fields/Doc';
 
 
 @observer
@@ -67,7 +71,7 @@ export default class MobileInkOverlay extends React.Component {
             height: bounds.height * this._scale,
         };
 
-        const target = document.elementFromPoint(points[0].X, points[0].Y);
+        const target = document.elementFromPoint(this._x + 10, this._y + 10);
         target?.dispatchEvent(
             new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
                 {
@@ -82,6 +86,43 @@ export default class MobileInkOverlay extends React.Component {
         );
     }
 
+    uploadDocument = async (content: MobileDocumentUploadContent) => {
+        const { docId } = content;
+        console.log("receive upload document id", docId);
+        const doc = await DocServer.GetRefField(docId);
+
+        if (doc && doc instanceof Doc) {
+            console.log("parsed upload document into doc", StrCast(doc.proto!.title));
+
+            const target = document.elementFromPoint(this._x + 10, this._y + 10);
+            console.log("the target is", target);
+
+            const dragData = new DragManager.DocumentDragData([doc]);
+            const complete = new DragManager.DragCompleteEvent(false, dragData);
+            console.log("the drag data is", dragData);
+
+            if (target) {
+                target.dispatchEvent(
+                    new CustomEvent<DragManager.DropEvent>("dashOnDrop",
+                        {
+                            bubbles: true,
+                            detail: {
+                                x: this._x,
+                                y: this._y,
+                                complete: complete,
+                                altKey: false,
+                                metaKey: false,
+                                ctrlKey: false
+                            }
+                        }
+                    )
+                );
+            } else {
+                alert("TARGET IS UNDEFINED");
+            }
+        }
+    }
+
     @action
     dragStart = (e: React.PointerEvent) => {
         document.removeEventListener("pointermove", this.dragging);
@@ -132,15 +173,17 @@ export default class MobileInkOverlay extends React.Component {
                     transform: `translate(${this._x}px, ${this._y}px)`,
                     zIndex: 30000,
                     pointerEvents: "none",
-                    borderStyle: this._isDragging ? "solid" : "dashed"
-                }}
+                    borderStyle: this._isDragging ? "solid" : "dashed",
+                    backgroundColor: "rgba(255, 0, 0, 0.3)"
+                }
+                }
                 ref={this._mainCont}
             >
                 <div className="mobileInkOverlay-border top" onPointerDown={this.dragStart}></div>
                 <div className="mobileInkOverlay-border bottom" onPointerDown={this.dragStart}></div>
                 <div className="mobileInkOverlay-border left" onPointerDown={this.dragStart}></div>
                 <div className="mobileInkOverlay-border right" onPointerDown={this.dragStart}></div>
-            </div>
+            </div >
         );
     }
 }
\ No newline at end of file
diff --git a/src/mobile/MobileInterface.scss b/src/mobile/MobileInterface.scss
index 8abe5a40d..8b0ebcd53 100644
--- a/src/mobile/MobileInterface.scss
+++ b/src/mobile/MobileInterface.scss
@@ -1,7 +1,15 @@
 .mobileInterface-inkInterfaceButtons {
     position: absolute;
+    top: -50px;
     display: flex;
     justify-content: space-between;
     width: 100%;
     z-index: 9999;
+    height: 50px;
+}
+
+.mobileInterface-container {
+    height: calc(100% - 50px);
+    margin-top: 50px;
+    position: relative;
 }
\ No newline at end of file
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 665d9a168..a1ef0a5d1 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -22,6 +22,18 @@ import { SelectionManager } from '../client/util/SelectionManager';
 import { DateField } from '../new_fields/DateField';
 import { GestureUtils } from '../pen-gestures/GestureUtils';
 import { DocServer } from '../client/DocServer';
+import { DocumentDecorations } from '../client/views/DocumentDecorations';
+import { OverlayView } from '../client/views/OverlayView';
+import { DictationOverlay } from '../client/views/DictationOverlay';
+import SharingManager from '../client/util/SharingManager';
+import { PreviewCursor } from '../client/views/PreviewCursor';
+import { ContextMenu } from '../client/views/ContextMenu';
+import { RadialMenu } from '../client/views/nodes/RadialMenu';
+import PDFMenu from '../client/views/pdf/PDFMenu';
+import MarqueeOptionsMenu from '../client/views/collections/collectionFreeForm/MarqueeOptionsMenu';
+import GoogleAuthenticationManager from '../client/apis/GoogleAuthenticationManager';
+import { listSpec } from '../new_fields/Schema';
+import { Id } from '../new_fields/FieldSymbols';
 
 library.add(faLongArrowAltLeft);
 
@@ -31,7 +43,7 @@ export default class MobileInterface extends React.Component {
     @computed private get userDoc() { return CurrentUserUtils.UserDocument; }
     @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
     // @observable private currentView: "main" | "ink" | "upload" = "main";
-    private mainDoc: Doc = CurrentUserUtils.setupMobileDoc(this.userDoc);
+    private mainDoc: any = CurrentUserUtils.setupMobileDoc(this.userDoc);
     @observable private renderView?: () => JSX.Element;
 
     // private inkDoc?: Doc;
@@ -49,7 +61,6 @@ export default class MobileInterface extends React.Component {
         library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer]);
 
         if (this.userDoc && !this.mainContainer) {
-            // const doc = CurrentUserUtils.setupMobileDoc(this.userDoc);
             this.userDoc.activeMobile = this.mainDoc;
         }
     }
@@ -76,48 +87,22 @@ export default class MobileInterface extends React.Component {
         });
     }
 
-    // @action
-    // switchCurrentView = (view: "main" | "ink" | "upload") => {
-    //     this.currentView = view;
-
-    //     if (this.userDoc) {
-    //         switch (view) {
-    //             case "main": {
-    //                 // const doc = CurrentUserUtils.setupMobileDoc(this.userDoc);
-    //                 this.userDoc.activeMobile = this.mainDoc;
-    //                 break;
-    //             }
-    //             case "ink": {
-    //                 this.inkDoc = CurrentUserUtils.setupMobileInkingDoc(this.userDoc);
-    //                 this.userDoc.activeMobile = this.inkDoc;
-    //                 InkingControl.Instance.switchTool(InkTool.Pen);
-    //                 this.drawingInk = true;
-
-    //                 DocServer.Mobile.dispatchOverlayTrigger({
-    //                     enableOverlay: true,
-    //                     width: window.innerWidth,
-    //                     height: window.innerHeight
-    //                 });
-
-    //                 break;
-    //             }
-    //             case "upload": {
-    //                 this.uploadDoc = CurrentUserUtils.setupMobileUploadDoc(this.userDoc);
-    //                 this.userDoc.activeMobile = this.uploadDoc;
-
-    //             }
-    //         }
-    //     }
-    // }
+    onSwitchUpload = () => {
+        DocServer.Mobile.dispatchOverlayTrigger({
+            enableOverlay: true,
+            width: 100,
+            height: 100
+        });
+    }
 
     renderDefaultContent = () => {
-        console.log("rendering default content");
+        console.log("rendering default content", this.mainContainer);
         if (this.mainContainer) {
             return <DocumentView
                 Document={this.mainContainer}
                 DataDoc={undefined}
                 LibraryPath={emptyPath}
-                addDocument={returnFalse}
+                addDocument={(doc: Doc) => { console.log("want to add doc to default content", StrCast(doc.title)); return false; }}
                 addDocTab={returnFalse}
                 pinToPres={emptyFunction}
                 removeDocument={undefined}
@@ -192,42 +177,54 @@ export default class MobileInterface extends React.Component {
                             <button className="mobileInterface-button" onClick={this.shiftRight} title="Shift right">right</button>
                         </div>
                     </div>
-                    <GestureOverlay>
-                        <CollectionView
-                            Document={this.mainContainer}
-                            DataDoc={undefined}
-                            LibraryPath={emptyPath}
-                            fieldKey={""}
-                            addDocTab={returnFalse}
-                            pinToPres={emptyFunction}
-                            PanelHeight={() => window.innerHeight}
-                            PanelWidth={() => window.innerWidth}
-                            focus={emptyFunction}
-                            isSelected={returnFalse}
-                            select={emptyFunction}
-                            active={returnFalse}
-                            ContentScaling={returnOne}
-                            whenActiveChanged={returnFalse}
-                            ScreenToLocalTransform={Transform.Identity}
-                            ruleProvider={undefined}
-                            renderDepth={0}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}>
-                        </CollectionView>
-                    </GestureOverlay>
+                    <CollectionView
+                        Document={this.mainContainer}
+                        DataDoc={undefined}
+                        LibraryPath={emptyPath}
+                        fieldKey={""}
+                        addDocTab={returnFalse}
+                        pinToPres={emptyFunction}
+                        PanelHeight={() => window.innerHeight}
+                        PanelWidth={() => window.innerWidth}
+                        focus={emptyFunction}
+                        isSelected={returnFalse}
+                        select={emptyFunction}
+                        active={returnFalse}
+                        ContentScaling={returnOne}
+                        whenActiveChanged={returnFalse}
+                        ScreenToLocalTransform={Transform.Identity}
+                        ruleProvider={undefined}
+                        renderDepth={0}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}>
+                    </CollectionView>
                 </div>
             );
         }
     }
 
-    upload = () => {
+    upload = async (e: React.MouseEvent) => {
+        if (this.mainContainer) {
+            const data = Cast(this.mainContainer.data, listSpec(Doc));
+            if (data) {
+                const uploadDoc = await data[1]; // TODO: ensure this is the collection to upload
 
+                console.log("UPLOADING DOCUMENT FROM MOBILE", uploadDoc[Id], StrCast(uploadDoc.proto!.title));
+                if (uploadDoc) {
+                    DocServer.Mobile.dispatchMobileDocumentUpload({
+                        docId: uploadDoc[Id]
+                    });
+                }
+            }
+        }
+        e.stopPropagation();
+        e.preventDefault();
     }
 
     renderUploadContent() {
         if (this.mainContainer) {
             return (
-                <div className="mobileInterface">
+                <div className="mobileInterface" onDragOver={this.onDragOver}>
                     <div className="mobileInterface-inkInterfaceButtons">
                         <div className="navButtons">
                             <button className="mobileInterface-button cancel" onClick={this.onBack} title="Back">BACK</button>
@@ -240,7 +237,7 @@ export default class MobileInterface extends React.Component {
                         Document={this.mainContainer}
                         DataDoc={undefined}
                         LibraryPath={emptyPath}
-                        addDocument={returnFalse}
+                        addDocument={(doc: Doc) => { console.log("want to add doc", StrCast(doc.title)); return false; }}
                         addDocTab={returnFalse}
                         pinToPres={emptyFunction}
                         removeDocument={undefined}
@@ -266,13 +263,35 @@ export default class MobileInterface extends React.Component {
         }
     }
 
+    onDragOver = (e: React.DragEvent) => {
+        e.preventDefault();
+        e.stopPropagation();
+    }
+
     render() {
         // const content = this.currentView === "main" ? this.mainContent :
         //     this.currentView === "ink" ? this.inkContent :
         //         this.currentView === "upload" ? this.uploadContent : <></>;
         return (
-            <div className="mobile-container">
-                {this.renderView ? this.renderView() : this.renderDefaultContent()}
+            <div className="mobileInterface-container" onDragOver={this.onDragOver}>
+                {/* <DocumentDecorations />
+                <GestureOverlay>
+                    {this.renderView ? this.renderView() : this.renderDefaultContent()}
+                </GestureOverlay> */}
+
+                {/* <DictationOverlay />
+                <SharingManager />
+                <GoogleAuthenticationManager /> */}
+                <DocumentDecorations />
+                <GestureOverlay>
+                    {this.renderView ? this.renderView() : this.renderDefaultContent()}
+                </GestureOverlay>
+                <PreviewCursor />
+                {/* <ContextMenu /> */}
+                <RadialMenu />
+                {/* <PDFMenu />
+                <MarqueeOptionsMenu />
+                <OverlayView /> */}
             </div>
         );
     }
@@ -281,5 +300,6 @@ export default class MobileInterface extends React.Component {
 Scripting.addGlobal(function switchMobileView(doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); });
 Scripting.addGlobal(function onSwitchMobileInking() { return MobileInterface.Instance.onSwitchInking(); });
 Scripting.addGlobal(function renderMobileInking() { return MobileInterface.Instance.renderInkingContent(); });
+Scripting.addGlobal(function onSwitchMobileUpload() { return MobileInterface.Instance.onSwitchUpload(); });
 Scripting.addGlobal(function renderMobileUpload() { return MobileInterface.Instance.renderUploadContent(); });
 
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 74f45ae62..e76d9b7a2 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -42,6 +42,7 @@ export default class UploadManager extends ApiManager {
             method: Method.POST,
             subscription: "/upload",
             secureHandler: async ({ req, res }) => {
+                console.log("/upload register");
                 const form = new formidable.IncomingForm();
                 form.uploadDir = pathToDirectory(Directory.parsed_files);
                 form.keepExtensions = true;
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 236df3f3c..c23a2f0a8 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -1,5 +1,6 @@
 import { Utils } from "../Utils";
 import { Point } from "../pen-gestures/ndollar";
+import { Doc } from "../new_fields/Doc";
 
 export class Message<T> {
     private _name: string;
@@ -62,6 +63,10 @@ export interface UpdateMobileInkOverlayPositionContent {
     readonly dsize?: number;
 }
 
+export interface MobileDocumentUploadContent {
+    readonly docId: string;
+}
+
 export namespace MessageStore {
     export const Foo = new Message<string>("Foo");
     export const Bar = new Message<string>("Bar");
@@ -75,6 +80,7 @@ export namespace MessageStore {
     export const GesturePoints = new Message<GestureContent>("Gesture Points");
     export const MobileInkOverlayTrigger = new Message<MobileInkOverlayContent>("Trigger Mobile Ink Overlay");
     export const UpdateMobileInkOverlayPosition = new Message<UpdateMobileInkOverlayPositionContent>("Update Mobile Ink Overlay Position");
+    export const MobileDocumentUpload = new Message<MobileDocumentUploadContent>("Upload Document From Mobile");
 
     export const GetRefField = new Message<string>("Get Ref Field");
     export const GetRefFields = new Message<string[]>("Get Ref Fields");
diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts
index 77816c897..798bdae67 100644
--- a/src/server/Websocket/Websocket.ts
+++ b/src/server/Websocket/Websocket.ts
@@ -1,5 +1,5 @@
 import { Utils } from "../../Utils";
-import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent } from "../Message";
+import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "../Message";
 import { Client } from "../Client";
 import { Socket } from "socket.io";
 import { Database } from "../database";
@@ -57,6 +57,7 @@ export namespace WebSocket {
             Utils.AddServerHandler(socket, MessageStore.GesturePoints, content => processGesturePoints(socket, content));
             Utils.AddServerHandler(socket, MessageStore.MobileInkOverlayTrigger, content => processOverlayTrigger(socket, content));
             Utils.AddServerHandler(socket, MessageStore.UpdateMobileInkOverlayPosition, content => processUpdateOverlayPosition(socket, content));
+            Utils.AddServerHandler(socket, MessageStore.MobileDocumentUpload, content => processMobileDocumentUpload(socket, content));
             Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField);
             Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields);
 
@@ -83,6 +84,10 @@ export namespace WebSocket {
         socket.broadcast.emit("receiveUpdateOverlayPosition", content);
     }
 
+    function processMobileDocumentUpload(socket: Socket, content: MobileDocumentUploadContent) {
+        socket.broadcast.emit("receiveMobileDocumentUpload", content);
+    }
+
     function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) {
         const { ProjectCredentials } = GoogleCredentialsLoader;
         switch (query.type) {
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 817cf40b1..3e5953ac1 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -103,7 +103,7 @@ export class CurrentUserUtils {
             { title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc },
             { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc },
             { title: "draw", icon: "pen-nib", click: 'switchMobileView(setupMobileInkingDoc, renderMobileInking, onSwitchMobileInking);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "red", activePen: doc },
-            { title: "upload", icon: "upload", click: 'switchMobileView(setupMobileUploadDoc, renderMobileUpload);', backgroundColor: "orange" },
+            { title: "upload", icon: "upload", click: 'switchMobileView(setupMobileUploadDoc, renderMobileUpload, onSwitchMobileUpload);', backgroundColor: "orange" },
             { title: "upload", icon: "upload", click: 'uploadImageMobile();', backgroundColor: "cyan" },
         ];
         return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({
@@ -138,7 +138,9 @@ export class CurrentUserUtils {
     }
 
     static setupMobileDoc(userDoc: Doc) {
-        return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), {
+        const webDoc = Docs.Create.WebDocument("https://wikipedia.com", { title: "Mobile Upload Web", chromeStatus: "enabled" });
+
+        return userDoc.activeMoble ?? Docs.Create.MasonryDocument([webDoc, ...CurrentUserUtils.setupMobileButtons(userDoc)], {
             columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5
         });
     }
@@ -148,13 +150,14 @@ export class CurrentUserUtils {
     }
 
     static setupMobileUploadDoc(userDoc: Doc) {
-        console.log("setup mobile upload", window.innerWidth, window.innerHeight);
-        const webDoc = Docs.Create.WebDocument("https://wikipedia.com", { title: "Mobile Upload Web", chromeStatus: "enabled" });
-        const uploadDoc = Docs.Create.StackingDocument([], { title: "Mobile Upload", backgroundColor: "pink" });
-        return Docs.Create.StackingDocument([webDoc, uploadDoc], {
-            title: "Mobile Upload", backgroundColor: "white",
-            columnWidth: window.innerWidth, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", autoHeight: true, yMargin: 5,
-            width: window.innerWidth, height: window.innerHeight
+        const webDoc = Docs.Create.WebDocument("https://yahoo.com", { title: "Upload Images From the Web", chromeStatus: "enabled" });
+        const uploadDoc = Docs.Create.StackingDocument([], { title: "Mobile Upload Collection", backgroundColor: "pink" });
+        console.log("window size", window.innerWidth, window.innerHeight);
+        // return Docs.Create.StackingDocument([webDoc, uploadDoc], {
+        //     columnWidth: window.innerWidth, //ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "Mobile Upload", autoHeight: true, yMargin: 5
+        // });
+        return Docs.Create.StackingDocument([webDoc, uploadDoc], {//...CurrentUserUtils.setupMobileButtons(userDoc)], {
+            columnWidth: 100, lockedPosition: true, chromeStatus: "disabled", title: "Upload", autoHeight: true, yMargin: 30
         });
     }
 
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index cbe070293..5f1ecc733 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -42,18 +42,18 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
         }
     };
     app.use(cors(corsOptions));
-    app.use("*", ({ user, originalUrl }, res, next) => {
-        if (user && !originalUrl.includes("Heartbeat")) {
-            const userEmail = (user as any).email;
-            if (userEmail) {
-                timeMap[userEmail] = Date.now();
-            }
-        }
-        if (!user && originalUrl === "/") {
-            return res.redirect("/login");
-        }
-        next();
-    });
+    // app.use("*", ({ user, originalUrl }, res, next) => {
+    //     if (user && !originalUrl.includes("Heartbeat")) {
+    //         const userEmail = (user as any).email;
+    //         if (userEmail) {
+    //             timeMap[userEmail] = Date.now();
+    //         }
+    //     }
+    //     if (!user && originalUrl === "/") {
+    //         return res.redirect("/login");
+    //     }
+    //     next();
+    // });
 
     app.use(wdm(compiler, { publicPath: config.output.publicPath }));
     app.use(whm(compiler));
-- 
cgit v1.2.3-70-g09d2


From dba26063754e6a882c8a3eb238b04a935989f406 Mon Sep 17 00:00:00 2001
From: kimdahey <claire_kim1@brown.edu>
Date: Mon, 10 Feb 2020 04:52:04 -0500
Subject: started long press for iframe to copy image

---
 src/client/util/DragManager.ts                     |   6 +-
 src/client/views/collections/CollectionSubView.tsx |   2 +
 src/client/views/nodes/WebBox.tsx                  | 114 ++++++++++++++++++++-
 3 files changed, 119 insertions(+), 3 deletions(-)

(limited to 'src/client/views/collections/CollectionSubView.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0bb8b531d..745540ff9 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -179,7 +179,7 @@ export namespace DragManager {
             );
         }
         element.dataset.canDrop = "true";
-        const handler = (e: Event) => { console.log("drop target reveied docs"); dropFunc(e, (e as CustomEvent<DropEvent>).detail); };
+        const handler = (e: Event) => { dropFunc(e, (e as CustomEvent<DropEvent>).detail); };
         element.addEventListener("dashOnDrop", handler);
         return () => {
             element.removeEventListener("dashOnDrop", handler);
@@ -265,6 +265,10 @@ export namespace DragManager {
         StartDrag([ele], dragData, downX, downY, options);
     }
 
+    export function StartImgDrag(ele: HTMLElement, downX: number, downY: number) {
+        StartDrag([ele], {}, downX, downY);
+    }
+
     function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
         eles = eles.filter(e => e);
         if (!dragDiv) {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b35af2314..b5067ea9e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -180,6 +180,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             }
             const html = e.dataTransfer.getData("text/html");
             const text = e.dataTransfer.getData("text/plain");
+            console.log(html);
 
             if (text && text.startsWith("<div")) {
                 return;
@@ -212,6 +213,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
                 if (img) {
                     const split = img.split("src=\"")[1].split("\"")[0];
+                    console.log("creating image", split);
                     const doc = Docs.Create.ImageDocument(split, { ...options, width: 300 });
                     ImageUtils.ExtractExif(doc);
                     this.props.addDocument(doc);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 0b23c3bec..c405dd9d4 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -21,6 +21,9 @@ import "./WebBox.scss";
 import React = require("react");
 import { DocAnnotatableComponent } from "../DocComponent";
 import { documentSchema } from "../../../new_fields/documentSchemas";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { DragManager } from "../../util/DragManager";
+import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 
 library.add(faStickyNote);
 
@@ -34,6 +37,12 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
     @observable private collapsed: boolean = true;
     @observable private url: string = "";
 
+    private _longPressSecondsHack?: NodeJS.Timeout;
+    private _iframeRef = React.createRef<HTMLIFrameElement>();
+    private _iframeDragRef = React.createRef<HTMLDivElement>();
+    @observable private _pressX: number = 0;
+    @observable private _pressY: number = 0;
+
     componentWillMount() {
 
         const field = Cast(this.props.Document[this.props.fieldKey], WebField);
@@ -51,6 +60,49 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
         this.setURL();
     }
 
+    componentDidMount() {
+        document.addEventListener("pointerup", this.onLongPressUp);
+        document.addEventListener("pointermove", this.onLongPressMove);
+        // this._iframeRef.current?.contentWindow?.document.addEventListener("mousedown", (event: MouseEvent) => {
+        //     console.log("clicked inside the iframe?");
+        // });
+        // const iframe = document.getElementById(this.props.Document.proto![Id]);
+        // if (iframe) {
+        //     iframe.addEventListener('pointerdown', function (event) {
+        //         const B = iframe.getBoundingClientRect();
+        //         const e = new CustomEvent('pointerdown', { bubbles: true, cancelable: false });
+        //         // e.clientX = e.clientX + B?.left;
+        //         // e.clientY = e.clientY + B?.top;
+        //         console.log("custom event pointer down");
+        //         iframe.dispatchEvent(e);
+        //     })
+        // }
+        // if (this._iframeRef.current) {
+        //     console.log("resetting iframes events");
+        //     const self = this;
+        //     this._iframeRef.current.addEventListener('pointermove', function (event) {
+        //         const B = self._iframeRef.current?.getBoundingClientRect();
+        //         const e = new CustomEvent('pointermove', { bubbles: true, cancelable: false });
+        //         // e.clientX = e.clientX + B?.left;
+        //         // e.clientY = e.clientY + B?.top;
+        //         self._iframeRef.current?.dispatchEvent(e);
+        //     });
+        //     this._iframeRef.current.addEventListener('pointerdown', function (event) {
+        //         const B = self._iframeRef.current?.getBoundingClientRect();
+        //         const e = new CustomEvent('pointerdown', { bubbles: true, cancelable: false });
+        //         // e.clientX = e.clientX + B?.left;
+        //         // e.clientY = e.clientY + B?.top;
+        //         console.log("custom event pointer down");
+        //         self._iframeRef.current?.dispatchEvent(e);
+        //     })
+        // }
+    }
+
+    componentWillUnmount() {
+        document.removeEventListener("pointerup", this.onLongPressUp);
+        document.removeEventListener("pointermove", this.onLongPressMove);
+    }
+
     @action
     onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.url = e.target.value;
@@ -165,6 +217,62 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
         }
     }
 
+    // TODO: make this actually a long press
+    onLongPressDown = (e: React.PointerEvent) => {
+
+        console.log("press down", e.clientX, e.clientX);
+        this._pressX = e.clientX;
+        this._pressY = e.clientY;
+        this._longPressSecondsHack = setTimeout(() => {
+            console.log("start the drag!!");
+            const B = this._iframeRef.current?.getBoundingClientRect();
+            const iframeDoc = this._iframeRef.current?.contentDocument;
+            if (B && iframeDoc) {
+                console.log("frame doc", iframeDoc);
+                console.log("get point", this._pressX, B.left, this._pressY, B.top);
+                const element = iframeDoc.elementFromPoint(this._pressX - B.left, this._pressY - B.top);
+                console.log("found element", element);
+                if (element) {
+                    e.stopPropagation();
+                    e.preventDefault();
+                    const clone = element.cloneNode(true) as HTMLElement;
+
+                    if (clone.nodeName === "IMG") {
+                        const src = clone.getAttribute("src"); // TODO: may not always work
+
+                        if (src) {
+                            const doc = Docs.Create.ImageDocument(src, { width: 300 });
+                            ImageUtils.ExtractExif(doc);
+
+                            console.log("start image drag", this._pressX, this._pressY, doc);
+                            // document.dispatchEvent()
+                            const dragData = new DragManager.DocumentDragData([doc]);
+                            DragManager.StartDocumentDrag([clone], dragData, this._pressX, this._pressY);
+                        }
+                    }
+                }
+            }
+        }, 1500);
+        // e.stopPropagation();
+        // e.preventDefault();
+    }
+
+    onLongPressMove = (e: PointerEvent) => {
+        this._pressX = e.clientX;
+        this._pressY = e.clientY;
+    }
+
+    onLongPressUp = (e: PointerEvent) => {
+        console.log("press up");
+        if (this._longPressSecondsHack) {
+            clearTimeout(this._longPressSecondsHack);
+            console.log("long press cancelled");
+        }
+        // e.stopPropagation();
+        // e.preventDefault();
+    }
+
+
     @computed
     get content() {
         const field = this.dataDoc[this.props.fieldKey];
@@ -172,9 +280,9 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
         if (field instanceof HtmlField) {
             view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
         } else if (field instanceof WebField) {
-            view = <iframe src={Utils.CorsProxy(field.url.href)} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+            view = <iframe ref={this._iframeRef} src={Utils.CorsProxy(field.url.href)} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
         } else {
-            view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+            view = <iframe ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
         }
         const content =
             <div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
@@ -192,7 +300,9 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
                 <div className={classname}  >
                     {content}
                 </div>
+                <div className="mobileIframeDragger" ref={this._iframeDragRef} draggable></div>
                 {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
+                {/* onPointerDown={(e) => { this.onPrePointer(e); this.onLongPressDown(e) }} onPointerMove={this.onPrePointer} onPointerUp={(e) => { this.onPrePointer(e); }} />} */}
             </>);
     }
     render() {
-- 
cgit v1.2.3-70-g09d2

