From a23fb4733c250b2be0124ad456af7da71f4fca00 Mon Sep 17 00:00:00 2001 From: Fawn Date: Mon, 13 Jan 2020 17:03:19 -0500 Subject: started creating mobile ink view --- src/mobile/MobileInterface.tsx | 74 ++++++++++++++-------- .../authentication/models/current_user_utils.ts | 1 + 2 files changed, 48 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index ea2fc917f..1e0920686 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -1,6 +1,6 @@ import React = require('react'); import { observer } from 'mobx-react'; -import { computed, action } from 'mobx'; +import { computed, action, observable } from 'mobx'; import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; import { FieldValue, Cast } from '../new_fields/Types'; import { Doc } from '../new_fields/Doc'; @@ -11,11 +11,19 @@ import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, re import { Transform } from '../client/util/Transform'; import { library } from '@fortawesome/fontawesome-svg-core'; import { faPenNib, faHighlighter, faEraser, faMousePointer } from '@fortawesome/free-solid-svg-icons'; +import { Scripting } from '../client/util/Scripting'; @observer export default class MobileInterface extends React.Component { + @observable static Instance: MobileInterface; @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" | "library" = "main"; + + constructor(props: Readonly<{}>) { + super(props); + MobileInterface.Instance = this; + } @action componentDidMount = () => { @@ -27,43 +35,55 @@ export default class MobileInterface extends React.Component { } } + @action switchCurrentView = (view: "main" | "ink" | "library") => { this.currentView = view; } + @computed get mainContent() { if (this.mainContainer) { - return window.screen.width} - PanelHeight={() => window.screen.height} - renderDepth={0} - focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - zoomToScale={emptyFunction} - getScale={returnOne}> - ; + switch (this.currentView) { + case "main": + return window.screen.width} + PanelHeight={() => window.screen.height} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + ; + case "ink": + return
INK
; + case "library": + return
LIBRARY
; + } } return "hello"; } render() { + console.log("rendering mobile"); return (
{this.mainContent}
); } -} \ No newline at end of file +} + +Scripting.addGlobal(function switchMobileView(view: "main" | "ink" | "library") { return MobileInterface.Instance.switchCurrentView(view); }); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 2ade6f102..71369b625 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -101,6 +101,7 @@ export class CurrentUserUtils { { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, { 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: "mouse-pointer", click: 'switchMobileView("ink");', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "red", activePen: doc }, ]; return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, -- cgit v1.2.3-70-g09d2 From c78409b7cb6c4ab828bce0eef1525032958219f7 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 14 Jan 2020 11:48:19 -0500 Subject: temp fix for inkingstroke type error when on mobile view --- src/client/views/GestureOverlay.tsx | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 830c06f1f..632f2f0d6 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -1,9 +1,9 @@ import React = require("react"); import { Touchable } from "./Touchable"; import { observer } from "mobx-react"; -import "./GestureOverlay.scss" +import "./GestureOverlay.scss"; import { computed, observable, action, runInAction } from "mobx"; -import { CreatePolyline } from "./InkingStroke"; +// import { CreatePolyline } from "./InkingStroke"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { InteractionUtils } from "../util/InteractionUtils"; import { InkingControl } from "./InkingControl"; @@ -199,6 +199,21 @@ export default class GestureOverlay extends Touchable { return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top }; } + // TODO: find a way to reference this function from InkingStroke instead of copy pastign here. copied bc of weird error when on mobile view + CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color?: string, width?: number) { + const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); + return ( + + ); + } + @computed get currentStroke() { if (this._points.length <= 1) { return (null); @@ -208,7 +223,7 @@ export default class GestureOverlay extends Touchable { return ( - {CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} + {this.CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} ); } -- cgit v1.2.3-70-g09d2 From b4c2d2add2b863060ce559e49b3953f24050f162 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 14 Jan 2020 14:47:44 -0500 Subject: can add strokes to mobile ink --- src/client/views/collections/CollectionSubView.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + src/mobile/MobileInterface.tsx | 108 +++++++++++++++------ .../authentication/models/current_user_utils.ts | 4 + 4 files changed, 87 insertions(+), 29 deletions(-) (limited to 'src') 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(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(schemaCtor: (doc: Doc) => T) { @undoBatch protected onGesture(e: Event, ge: GestureUtils.GestureEvent) { - } @undoBatch diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 84945c6e6..c3e131184 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -356,6 +356,7 @@ 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; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 1e0920686..8d342d749 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -10,8 +10,10 @@ 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 } from '@fortawesome/free-solid-svg-icons'; +import { faPenNib, faHighlighter, faEraser, faMousePointer, faBreadSlice } 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'; @observer export default class MobileInterface extends React.Component { @@ -35,52 +37,102 @@ export default class MobileInterface extends React.Component { } } - @action switchCurrentView = (view: "main" | "ink" | "library") => { this.currentView = view; } + @action + switchCurrentView = (view: "main" | "ink" | "library") => { + this.currentView = view; + + if (this.userDoc) { + switch (view) { + case "main": { + const doc = CurrentUserUtils.setupMobileDoc(this.userDoc); + this.userDoc.activeMobile = doc; + break; + } + case "ink": { + const doc = CurrentUserUtils.setupMobileInkingDoc(this.userDoc); + this.userDoc.activeMobile = doc; + break; + } + } + } + } @computed get mainContent() { if (this.mainContainer) { - switch (this.currentView) { - case "main": - return window.screen.width} + PanelHeight={() => window.screen.height} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + ; + } + return "hello"; + } + + @computed + get inkContent() { + // return
INK
; + if (this.mainContainer) { + return ( + + window.screen.width} PanelHeight={() => window.screen.height} - renderDepth={0} + PanelWidth={() => window.screen.width} + annotationsKey={""} + isAnnotationOverlay={false} focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} + isSelected={returnTrue} // + select={emptyFunction} + active={returnTrue} // + ContentScaling={returnOne} + whenActiveChanged={returnFalse} + CollectionView={undefined} + ScreenToLocalTransform={Transform.Identity} + ruleProvider={undefined} + renderDepth={0} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} - zoomToScale={emptyFunction} - getScale={returnOne}> - ; - case "ink": - return
INK
; - case "library": - return
LIBRARY
; - } + chromeCollapsed={true}> +
+
+ ); } - return "hello"; } render() { - console.log("rendering mobile"); + const content = this.currentView === "main" ? this.mainContent : this.currentView === "ink" ? this.inkContent : <>; return (
- {this.mainContent} + {content}
); } diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 71369b625..b40179fbc 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -140,6 +140,10 @@ export class CurrentUserUtils { }); } + static setupMobileInkingDoc(userDoc: Doc) { + return Docs.Create.FreeformDocument([], { x: 0, y: 0, width: 10, height: 20, title: "Mobile Inking", backgroundColor: "red" }); + } + // 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) static setupToolsPanel(sidebarContainer: Doc, doc: Doc) { // setup a masonry view of all he creators -- cgit v1.2.3-70-g09d2 From 3cca58612cde96a3082ca8e190fe2166d531d556 Mon Sep 17 00:00:00 2001 From: Fawn 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') 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 (
+ {this.currentStroke} {this.props.children} {this._palette} - {this.currentStroke}
); } } 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(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
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
INK
; + // TODO: get props from active collection and pass to the new freeform if (this.mainContainer) { return ( - + + +
+ 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}> - + ContainingCollectionDoc={undefined}> + ); } 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 65e164eaec42d1850de7f5e1eba1d4302c3e8230 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 15 Jan 2020 16:08:48 -0500 Subject: mobile interface emits events when switched to inking view and when strokes are drawn, currently with dummy callbacks --- src/client/DocServer.ts | 22 ++++++++++++++++++- src/client/views/GestureOverlay.tsx | 21 ++++++++++++++++++ src/client/views/MainView.tsx | 13 +++++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 --- src/mobile/MobileInterface.tsx | 25 +++++++++++++++------- src/server/Message.ts | 13 +++++++++++ src/server/Websocket/Websocket.ts | 12 ++++++++++- 7 files changed, 94 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index ed7fbd7ba..cfd9612ed 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,10 +1,11 @@ import * as OpenSocket from 'socket.io-client'; -import { MessageStore, YoutubeQueryTypes } from "./../server/Message"; +import { MessageStore, YoutubeQueryTypes, GestureContent } from "./../server/Message"; import { Opt, Doc } from '../new_fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../new_fields/RefField'; import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; +import GestureOverlay from './views/GestureOverlay'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -64,6 +65,19 @@ export namespace DocServer { } } + export namespace Mobile { + + export function dispatchGesturePoints(content: GestureContent) { + Utils.Emit(_socket, MessageStore.GesturePoints, content); + } + + export function dispatchBoxTrigger(enableBox: boolean) { + // _socket.emit("dispatchBoxTrigger"); + Utils.Emit(_socket, MessageStore.MobileInkBoxTrigger, enableBox); + } + + } + export function init(protocol: string, hostname: string, port: number, identifier: string) { _cache = {}; GUID = identifier; @@ -85,6 +99,12 @@ export namespace DocServer { Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, () => { alert("Your connection to the server has been terminated."); }); + _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => { + GestureOverlay.Instance.manualDispatch(content); + }); + _socket.addEventListener("receiveBoxTrigger", (enableBox: boolean) => { + GestureOverlay.Instance.showBox(enableBox); + }); } function errorFunc(): never { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 4d487c032..4b8dfa119 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -16,6 +16,10 @@ import { Scripting } from "../util/Scripting"; import { FieldValue, Cast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; +import MobileInterface from "../../mobile/MobileInterface"; +import { MainView } from "./MainView"; +import { DocServer } from "../DocServer"; +import { GestureContent } from "../../server/Message"; @observer export default class GestureOverlay extends Touchable { @@ -35,6 +39,14 @@ export default class GestureOverlay extends Touchable { GestureOverlay.Instance = this; } + manualDispatch = (content: GestureContent) => { + console.log(content); + } + + showBox = (enableBox: boolean) => { + console.log("enable box?", enableBox); + } + @action handleHandDown = (e: React.TouchEvent) => { const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); @@ -139,6 +151,15 @@ export default class GestureOverlay extends Touchable { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); + if (MobileInterface.Instance.drawingInk) { + const { selectedColor, selectedWidth } = InkingControl.Instance; + DocServer.Mobile.dispatchGesturePoints({ + points: this._points, + color: selectedColor, + width: selectedWidth + }); + } + const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); let actionPerformed = false; if (result && result.Score > 0.7) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 93fb0a07c..f1f7c37a3 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -49,6 +49,7 @@ export class MainView extends React.Component { private _flyoutSizeOnDown = 0; private _urlState: HistoryUtil.DocUrl; private _docBtnRef = React.createRef(); + private _mainViewRef = React.createRef(); @observable private _panelWidth: number = 0; @observable private _panelHeight: number = 0; @@ -507,8 +508,16 @@ export class MainView extends React.Component { return (null); } + get mainViewElement() { + return document.getElementById("mainView-container"); + } + + get mainViewRef() { + return this._mainViewRef; + } + render() { - return (
+ return (
@@ -518,7 +527,7 @@ export class MainView extends React.Component { - + diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8d376fb57..bbfd22f50 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -114,7 +114,6 @@ 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; @@ -362,7 +361,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { 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; @@ -991,7 +989,6 @@ 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
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 3cb08afa7..1429226b4 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -2,7 +2,7 @@ import React = require('react'); import { observer } from 'mobx-react'; import { computed, action, observable } from 'mobx'; import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; -import { FieldValue, Cast } from '../new_fields/Types'; +import { FieldValue, Cast, StrCast } from '../new_fields/Types'; import { Doc } from '../new_fields/Doc'; import { Docs } from '../client/documents/Documents'; import { CollectionView } from '../client/views/collections/CollectionView'; @@ -18,6 +18,10 @@ import { InkingControl } from '../client/views/InkingControl'; import { InkTool } from '../new_fields/InkField'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import "./MobileInterface.scss"; +import { SelectionManager } from '../client/util/SelectionManager'; +import { DateField } from '../new_fields/DateField'; +import { GestureUtils } from '../pen-gestures/GestureUtils'; +import { DocServer } from '../client/DocServer'; library.add(faTrash, faCheck); @@ -30,6 +34,7 @@ export default class MobileInterface extends React.Component { private mainDoc = CurrentUserUtils.setupMobileDoc(this.userDoc); private inkDoc?: Doc; + public drawingInk: boolean = false; constructor(props: Readonly<{}>) { super(props); @@ -61,6 +66,10 @@ export default class MobileInterface extends React.Component { this.inkDoc = CurrentUserUtils.setupMobileInkingDoc(this.userDoc); this.userDoc.activeMobile = this.inkDoc; InkingControl.Instance.switchTool(InkTool.Pen); + this.drawingInk = true; + + DocServer.Mobile.dispatchBoxTrigger(true); + break; } } @@ -100,17 +109,17 @@ export default class MobileInterface extends React.Component { } 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 + + DocServer.Mobile.dispatchBoxTrigger(false); + + this.inkDoc = undefined; + this.drawingInk = false; } @computed get inkContent() { - // TODO: get props from active collection and pass to the new freeform if (this.mainContainer) { return ( @@ -128,9 +137,9 @@ export default class MobileInterface extends React.Component { PanelHeight={() => window.innerHeight} PanelWidth={() => window.innerWidth} focus={emptyFunction} - isSelected={returnTrue} + isSelected={returnFalse} select={emptyFunction} - active={returnTrue} + active={returnFalse} ContentScaling={returnOne} whenActiveChanged={returnFalse} ScreenToLocalTransform={Transform.Identity} diff --git a/src/server/Message.ts b/src/server/Message.ts index 621abfd1e..fe74dafa5 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -1,4 +1,5 @@ import { Utils } from "../Utils"; +import { Point } from "../pen-gestures/ndollar"; export class Message { private _name: string; @@ -42,6 +43,16 @@ export interface Diff extends Reference { readonly diff: any; } +export interface GestureContent { + readonly points: Array; + readonly width?: string; + readonly color?: string; +} + +export interface MobileInkBoxTriggerContent { + readonly enableBox: boolean; +} + export namespace MessageStore { export const Foo = new Message("Foo"); export const Bar = new Message("Bar"); @@ -51,6 +62,8 @@ export namespace MessageStore { export const GetDocument = new Message("Get Document"); export const DeleteAll = new Message("Delete All"); export const ConnectionTerminated = new Message("Connection Terminated"); + export const GesturePoints = new Message("Gesture Points"); + export const MobileInkBoxTrigger = new Message("Trigger Mobile Ink Box"); export const GetRefField = new Message("Get Ref Field"); export const GetRefFields = new Message("Get Ref Fields"); diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 578147d60..67777f023 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 } from "../Message"; +import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent } from "../Message"; import { Client } from "../Client"; import { Socket } from "socket.io"; import { Database } from "../database"; @@ -54,6 +54,8 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff)); Utils.AddServerHandler(socket, MessageStore.DeleteField, id => DeleteField(socket, id)); Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); + Utils.AddServerHandler(socket, MessageStore.GesturePoints, content => processGesturePoints(socket, content)); + Utils.AddServerHandler(socket, MessageStore.MobileInkBoxTrigger, enableBox => processBoxTrigger(socket, enableBox)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); @@ -68,6 +70,14 @@ export namespace WebSocket { logPort("websocket", socketPort); } + function processGesturePoints(socket: Socket, content: GestureContent) { + socket.broadcast.emit("receiveGesturePoints", content); + } + + function processBoxTrigger(socket: Socket, enableBox: boolean) { + socket.broadcast.emit("receiveBoxTrigger", enableBox); + } + function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) { const { ProjectCredentials } = GoogleCredentialsLoader; switch (query.type) { -- cgit v1.2.3-70-g09d2 From 43c4ff69d515b874f71f9c638a267cd896036854 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 15 Jan 2020 17:20:09 -0500 Subject: strokes from mobile interface get properly inserted into desktop with offset of the mobile ink overlay --- src/client/DocServer.ts | 12 +++---- src/client/views/GestureOverlay.scss | 4 +++ src/client/views/GestureOverlay.tsx | 70 ++++++++++++++++++++++++++++++------ src/mobile/MobileInterface.tsx | 12 +++++-- src/server/Message.ts | 7 ++-- src/server/Websocket/Websocket.ts | 8 ++--- 6 files changed, 89 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index cfd9612ed..132128182 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 } from "./../server/Message"; +import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkBoxContent } from "./../server/Message"; import { Opt, Doc } from '../new_fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; @@ -71,9 +71,9 @@ export namespace DocServer { Utils.Emit(_socket, MessageStore.GesturePoints, content); } - export function dispatchBoxTrigger(enableBox: boolean) { + export function dispatchBoxTrigger(content: MobileInkBoxContent) { // _socket.emit("dispatchBoxTrigger"); - Utils.Emit(_socket, MessageStore.MobileInkBoxTrigger, enableBox); + Utils.Emit(_socket, MessageStore.MobileInkBoxTrigger, content); } } @@ -100,10 +100,10 @@ export namespace DocServer { alert("Your connection to the server has been terminated."); }); _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => { - GestureOverlay.Instance.manualDispatch(content); + GestureOverlay.Instance.drawStrokeToMobileInkBox(content); }); - _socket.addEventListener("receiveBoxTrigger", (enableBox: boolean) => { - GestureOverlay.Instance.showBox(enableBox); + _socket.addEventListener("receiveBoxTrigger", (content: MobileInkBoxContent) => { + GestureOverlay.Instance.enableMobileInkBox(content); }); } diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index 31601efd4..2996b7073 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -5,4 +5,8 @@ top: 0; left: 0; touch-action: none; +} + +.mobileInkOverlay { + border: 5px dashed red; } \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 4b8dfa119..7e74d1d33 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -19,7 +19,8 @@ import Palette from "./Palette"; import MobileInterface from "../../mobile/MobileInterface"; import { MainView } from "./MainView"; import { DocServer } from "../DocServer"; -import { GestureContent } from "../../server/Message"; +import { GestureContent, MobileInkBoxContent } from "../../server/Message"; +import { Point } from "../northstar/model/idea/idea"; @observer export default class GestureOverlay extends Touchable { @@ -30,6 +31,10 @@ export default class GestureOverlay extends Touchable { @observable public Color: string = "rgb(244, 67, 54)"; @observable public Width: number = 5; + @observable private showMobileInkOverlay: boolean = false; + @observable private mobileInkOverlaySize: { width: number, height: number } = { width: 500, height: 700 }; + @observable private mobileInkOverlayPosition: { x: number, y: number } = { x: 300, y: 0 }; + private _d1: Doc | undefined; private thumbIdentifier?: number; @@ -39,14 +44,6 @@ export default class GestureOverlay extends Touchable { GestureOverlay.Instance = this; } - manualDispatch = (content: GestureContent) => { - console.log(content); - } - - showBox = (enableBox: boolean) => { - console.log("enable box?", enableBox); - } - @action handleHandDown = (e: React.TouchEvent) => { const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); @@ -151,10 +148,11 @@ export default class GestureOverlay extends Touchable { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); - if (MobileInterface.Instance.drawingInk) { + if (MobileInterface.Instance && MobileInterface.Instance.drawingInk) { const { selectedColor, selectedWidth } = InkingControl.Instance; DocServer.Mobile.dispatchGesturePoints({ points: this._points, + bounds: B, color: selectedColor, width: selectedWidth }); @@ -249,12 +247,64 @@ export default class GestureOverlay extends Touchable { ); } + drawStrokeToMobileInkBox = (content: GestureContent) => { + console.log(content); + const { points, bounds } = content; + + const B = { + right: bounds.right + this.mobileInkOverlayPosition.x, + left: bounds.left + this.mobileInkOverlayPosition.x, + bottom: bounds.bottom + this.mobileInkOverlayPosition.y, + top: bounds.top + this.mobileInkOverlayPosition.y, + width: bounds.width, + height: bounds.height + }; + + const target = document.elementFromPoint(points[0].X, points[0].Y); + target?.dispatchEvent( + new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: points, + gesture: GestureUtils.Gestures.Stroke, + bounds: B + } + } + ) + ); + } + + @action + enableMobileInkBox = (content: MobileInkBoxContent) => { + const { enableBox, width, height } = content; + console.log("enable box?", enableBox, width, height); + this.showMobileInkOverlay = enableBox; + this.mobileInkOverlaySize = { width: width ? width : 0, height: height ? height : 0 }; + this.mobileInkOverlayPosition = { x: 300, y: 25 }; // TODO put at center of screen + + } + + @computed get mobileInkOverlay() { + return ( +
+ ); + } + render() { return (
{this.currentStroke} {this.props.children} {this._palette} + {this.showMobileInkOverlay ? this.mobileInkOverlay : <>}
); } } diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 1429226b4..4840ea374 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -68,7 +68,11 @@ export default class MobileInterface extends React.Component { InkingControl.Instance.switchTool(InkTool.Pen); this.drawingInk = true; - DocServer.Mobile.dispatchBoxTrigger(true); + DocServer.Mobile.dispatchBoxTrigger({ + enableBox: true, + width: window.innerWidth, + height: window.innerHeight + }); break; } @@ -112,7 +116,11 @@ export default class MobileInterface extends React.Component { this.switchCurrentView("main"); InkingControl.Instance.switchTool(InkTool.None); // TODO: switch to previous tool - DocServer.Mobile.dispatchBoxTrigger(false); + DocServer.Mobile.dispatchBoxTrigger({ + enableBox: false, + width: window.innerWidth, + height: window.innerHeight + }); this.inkDoc = undefined; this.drawingInk = false; diff --git a/src/server/Message.ts b/src/server/Message.ts index fe74dafa5..1958286df 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -45,12 +45,15 @@ export interface Diff extends Reference { export interface GestureContent { readonly points: Array; + readonly bounds: { right: number, left: number, bottom: number, top: number, width: number, height: number }; readonly width?: string; readonly color?: string; } -export interface MobileInkBoxTriggerContent { +export interface MobileInkBoxContent { readonly enableBox: boolean; + readonly width?: number; + readonly height?: number; } export namespace MessageStore { @@ -63,7 +66,7 @@ export namespace MessageStore { export const DeleteAll = new Message("Delete All"); export const ConnectionTerminated = new Message("Connection Terminated"); export const GesturePoints = new Message("Gesture Points"); - export const MobileInkBoxTrigger = new Message("Trigger Mobile Ink Box"); + export const MobileInkBoxTrigger = new Message("Trigger Mobile Ink Box"); export const GetRefField = new Message("Get Ref Field"); export const GetRefFields = new Message("Get Ref Fields"); diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 67777f023..16e34bdfc 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 } from "../Message"; +import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkBoxContent } from "../Message"; import { Client } from "../Client"; import { Socket } from "socket.io"; import { Database } from "../database"; @@ -55,7 +55,7 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteField, id => DeleteField(socket, id)); Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); Utils.AddServerHandler(socket, MessageStore.GesturePoints, content => processGesturePoints(socket, content)); - Utils.AddServerHandler(socket, MessageStore.MobileInkBoxTrigger, enableBox => processBoxTrigger(socket, enableBox)); + Utils.AddServerHandler(socket, MessageStore.MobileInkBoxTrigger, content => processBoxTrigger(socket, content)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); @@ -74,8 +74,8 @@ export namespace WebSocket { socket.broadcast.emit("receiveGesturePoints", content); } - function processBoxTrigger(socket: Socket, enableBox: boolean) { - socket.broadcast.emit("receiveBoxTrigger", enableBox); + function processBoxTrigger(socket: Socket, content: MobileInkBoxContent) { + socket.broadcast.emit("receiveBoxTrigger", content); } function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) { -- cgit v1.2.3-70-g09d2 From e08c0d5b228c8ca1103bb0d7368f3f65bb24b675 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 15 Jan 2020 17:49:37 -0500 Subject: abstracted mobile ink overlay stuff into its own component --- src/client/DocServer.ts | 4 +- src/client/views/GestureOverlay.tsx | 53 ++------------------------ src/mobile/MobileInkOverlay.tsx | 75 +++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 51 deletions(-) create mode 100644 src/mobile/MobileInkOverlay.tsx (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 132128182..a1cb42df2 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -6,6 +6,7 @@ import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../new_fields/RefField'; import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; import GestureOverlay from './views/GestureOverlay'; +import MobileInkOverlay from '../mobile/MobileInkOverlay'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -100,10 +101,11 @@ export namespace DocServer { alert("Your connection to the server has been terminated."); }); _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => { - GestureOverlay.Instance.drawStrokeToMobileInkBox(content); + MobileInkOverlay.Instance.drawStroke(content); }); _socket.addEventListener("receiveBoxTrigger", (content: MobileInkBoxContent) => { GestureOverlay.Instance.enableMobileInkBox(content); + MobileInkOverlay.Instance.initMobileInkOverlay(content); }); } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 7e74d1d33..81284b543 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -21,6 +21,7 @@ import { MainView } from "./MainView"; import { DocServer } from "../DocServer"; import { GestureContent, MobileInkBoxContent } from "../../server/Message"; import { Point } from "../northstar/model/idea/idea"; +import MobileInkOverlay from "../../mobile/MobileInkOverlay"; @observer export default class GestureOverlay extends Touchable { @@ -32,8 +33,6 @@ export default class GestureOverlay extends Touchable { @observable public Width: number = 5; @observable private showMobileInkOverlay: boolean = false; - @observable private mobileInkOverlaySize: { width: number, height: number } = { width: 500, height: 700 }; - @observable private mobileInkOverlayPosition: { x: number, y: number } = { x: 300, y: 0 }; private _d1: Doc | undefined; private thumbIdentifier?: number; @@ -247,55 +246,9 @@ export default class GestureOverlay extends Touchable { ); } - drawStrokeToMobileInkBox = (content: GestureContent) => { - console.log(content); - const { points, bounds } = content; - - const B = { - right: bounds.right + this.mobileInkOverlayPosition.x, - left: bounds.left + this.mobileInkOverlayPosition.x, - bottom: bounds.bottom + this.mobileInkOverlayPosition.y, - top: bounds.top + this.mobileInkOverlayPosition.y, - width: bounds.width, - height: bounds.height - }; - - const target = document.elementFromPoint(points[0].X, points[0].Y); - target?.dispatchEvent( - new CustomEvent("dashOnGesture", - { - bubbles: true, - detail: { - points: points, - gesture: GestureUtils.Gestures.Stroke, - bounds: B - } - } - ) - ); - } - @action enableMobileInkBox = (content: MobileInkBoxContent) => { - const { enableBox, width, height } = content; - console.log("enable box?", enableBox, width, height); - this.showMobileInkOverlay = enableBox; - this.mobileInkOverlaySize = { width: width ? width : 0, height: height ? height : 0 }; - this.mobileInkOverlayPosition = { x: 300, y: 25 }; // TODO put at center of screen - - } - - @computed get mobileInkOverlay() { - return ( -
- ); + this.showMobileInkOverlay = content.enableBox; } render() { @@ -304,7 +257,7 @@ export default class GestureOverlay extends Touchable { {this.currentStroke} {this.props.children} {this._palette} - {this.showMobileInkOverlay ? this.mobileInkOverlay : <>} + {this.showMobileInkOverlay ? : <>}
); } } diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx new file mode 100644 index 000000000..240d23b32 --- /dev/null +++ b/src/mobile/MobileInkOverlay.tsx @@ -0,0 +1,75 @@ +import React = require('react'); +import { observer } from "mobx-react"; +import { MobileInkBoxContent, GestureContent } from "../server/Message"; +import { observable, action } from "mobx"; +import { GestureUtils } from "../pen-gestures/GestureUtils"; + + +@observer +export default class MobileInkOverlay extends React.Component { + public static Instance: MobileInkOverlay; + + private _mobileWidth: number = 0; + private _mobileHeight: number = 0; + @observable private _width: number = 0; + @observable private _height: number = 0; + @observable private _x: number = -300; + @observable private _y: number = -300; + + constructor(props: Readonly<{}>) { + super(props); + MobileInkOverlay.Instance = this; + } + + @action + initMobileInkOverlay(content: MobileInkBoxContent) { + const { width, height } = content; + this._mobileWidth = width ? width : 0; + this._mobileHeight = height ? height : 0; + this._width = width ? width : 0; + this._height = height ? height : 0; + this._x = 300; // TODO: center on screen + this._y = 25; // TODO: center on screen + } + + drawStroke = (content: GestureContent) => { + const { points, bounds } = content; + const scale = 1; + + const B = { + right: bounds.right + this._x, + left: bounds.left + this._x, // TODO: scale + bottom: bounds.bottom + this._y, + top: bounds.top + this._y, // TODO: scale + width: bounds.width * scale, + height: bounds.height * scale, + }; + + const target = document.elementFromPoint(points[0].X, points[0].Y); + target?.dispatchEvent( + new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: points, + gesture: GestureUtils.Gestures.Stroke, + bounds: B + } + } + ) + ); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From d2530f906d68e47cc25359cd8d85684a0e29637a Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 15 Jan 2020 18:06:05 -0500 Subject: strokes drawn on mobile interface are translated properly to desktop mobile ink overlay --- src/mobile/MobileInkOverlay.tsx | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx index 240d23b32..71dd20c51 100644 --- a/src/mobile/MobileInkOverlay.tsx +++ b/src/mobile/MobileInkOverlay.tsx @@ -9,8 +9,7 @@ import { GestureUtils } from "../pen-gestures/GestureUtils"; export default class MobileInkOverlay extends React.Component { public static Instance: MobileInkOverlay; - private _mobileWidth: number = 0; - private _mobileHeight: number = 0; + @observable private _scale: number = 1; @observable private _width: number = 0; @observable private _height: number = 0; @observable private _x: number = -300; @@ -21,28 +20,34 @@ export default class MobileInkOverlay extends React.Component { MobileInkOverlay.Instance = this; } + initialSize(mobileWidth: number, mobileHeight: number) { + const maxWidth = window.innerWidth - 30; // TODO: may not be window ?? figure out how to not include library ???? + const maxHeight = window.innerHeight - 30; // -30 for padding + const scale = Math.min(maxWidth / mobileWidth, maxHeight / mobileHeight); + return { width: mobileWidth * scale, height: mobileHeight * scale, scale: scale }; + } + @action initMobileInkOverlay(content: MobileInkBoxContent) { const { width, height } = content; - this._mobileWidth = width ? width : 0; - this._mobileHeight = height ? height : 0; - this._width = width ? width : 0; - this._height = height ? height : 0; + const scaledSize = this.initialSize(width ? width : 0, height ? height : 0); + this._width = scaledSize.width; + this._height = scaledSize.height; + this._scale = scaledSize.scale; this._x = 300; // TODO: center on screen this._y = 25; // TODO: center on screen } drawStroke = (content: GestureContent) => { const { points, bounds } = content; - const scale = 1; const B = { - right: bounds.right + this._x, - left: bounds.left + this._x, // TODO: scale - bottom: bounds.bottom + this._y, - top: bounds.top + this._y, // TODO: scale - width: bounds.width * scale, - height: bounds.height * scale, + right: (bounds.right * this._scale) + this._x, + left: (bounds.left * this._scale) + this._x, // TODO: scale + bottom: (bounds.bottom * this._scale) + this._y, + top: (bounds.top * this._scale) + this._y, // TODO: scale + width: bounds.width * this._scale, + height: bounds.height * this._scale, }; const target = document.elementFromPoint(points[0].X, points[0].Y); -- cgit v1.2.3-70-g09d2 From ecf0f5b8f426db9e66c05e759f61294811b15fca Mon Sep 17 00:00:00 2001 From: vellichora Date: Sat, 1 Feb 2020 14:56:19 -0500 Subject: mobile ink overlay is draggable from desktop --- src/client/DocServer.ts | 19 ++- src/client/util/InteractionUtils.ts | 165 ------------------- src/client/util/InteractionUtils.tsx | 182 +++++++++++++++++++++ src/client/views/GestureOverlay.scss | 4 - src/client/views/GestureOverlay.tsx | 25 +-- src/client/views/InkingStroke.tsx | 17 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 1 + src/mobile/MobileInkOverlay.scss | 38 +++++ src/mobile/MobileInkOverlay.tsx | 91 +++++++++-- src/mobile/MobileInterface.scss | 2 +- src/mobile/MobileInterface.tsx | 90 ++++++---- src/server/Message.ts | 14 +- src/server/Websocket/Websocket.ts | 8 +- 14 files changed, 393 insertions(+), 264 deletions(-) delete mode 100644 src/client/util/InteractionUtils.ts create mode 100644 src/client/util/InteractionUtils.tsx create mode 100644 src/mobile/MobileInkOverlay.scss (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index a1cb42df2..b20cd3521 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, MobileInkBoxContent } from "./../server/Message"; +import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPosition } from "./../server/Message"; import { Opt, Doc } from '../new_fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; @@ -72,9 +72,13 @@ export namespace DocServer { Utils.Emit(_socket, MessageStore.GesturePoints, content); } - export function dispatchBoxTrigger(content: MobileInkBoxContent) { + export function dispatchOverlayTrigger(content: MobileInkOverlayContent) { // _socket.emit("dispatchBoxTrigger"); - Utils.Emit(_socket, MessageStore.MobileInkBoxTrigger, content); + Utils.Emit(_socket, MessageStore.MobileInkOverlayTrigger, content); + } + + export function dispatchOverlayPositionUpdate(content: UpdateMobileInkOverlayPosition) { + Utils.Emit(_socket, MessageStore.UpdateMobileInkOverlayPosition, content); } } @@ -100,13 +104,18 @@ export namespace DocServer { Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, () => { alert("Your connection to the server has been terminated."); }); + + // mobile ink overlay socket events to communicate between mobile view and desktop view _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => { MobileInkOverlay.Instance.drawStroke(content); }); - _socket.addEventListener("receiveBoxTrigger", (content: MobileInkBoxContent) => { - GestureOverlay.Instance.enableMobileInkBox(content); + _socket.addEventListener("receiveOverlayTrigger", (content: MobileInkOverlayContent) => { + GestureOverlay.Instance.enableMobileInkOverlay(content); MobileInkOverlay.Instance.initMobileInkOverlay(content); }); + _socket.addEventListener("updateMobileOverlayPosition", (content: UpdateMobileInkOverlayPosition) => { + MobileInkOverlay.Instance.updatePosition(content); + }); } function errorFunc(): never { diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts deleted file mode 100644 index 76b43da3c..000000000 --- a/src/client/util/InteractionUtils.ts +++ /dev/null @@ -1,165 +0,0 @@ -export namespace InteractionUtils { - export const MOUSETYPE = "mouse"; - export const TOUCHTYPE = "touch"; - export const PENTYPE = "pen"; - export const ERASERTYPE = "eraser"; - - const POINTER_PEN_BUTTON = -1; - const REACT_POINTER_PEN_BUTTON = 0; - const ERASER_BUTTON = 5; - - export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { - const myTouches = new Array(); - for (let i = 0; i < e.targetTouches.length; i++) { - const pt: any = e.targetTouches.item(i); - if (pt && prevPoints.has(pt.identifier)) { - if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { - myTouches.push(pt); - } - } - } - return myTouches; - } - - export function IsType(e: PointerEvent | React.PointerEvent, type: string): boolean { - switch (type) { - // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2 - case PENTYPE: - return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0); - case ERASERTYPE: - return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON); - default: - return e.pointerType === type; - } - } - - export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number { - return Math.sqrt(Math.pow(pt1.clientX - pt2.clientX, 2) + Math.pow(pt1.clientY - pt2.clientY, 2)); - } - - /** - * Returns the centroid of an n-arbitrary long list of points (takes the average the x and y components of each point) - * @param pts - n-arbitrary long list of points - */ - export function CenterPoint(pts: React.Touch[]): { X: number, Y: number } { - const centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length; - const centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length; - return { X: centerX, Y: centerY }; - } - - /** - * Returns -1 if pinching out, 0 if not pinching, and 1 if pinching in - * @param pt1 - new point that corresponds to oldPoint1 - * @param pt2 - new point that corresponds to oldPoint2 - * @param oldPoint1 - previous point 1 - * @param oldPoint2 - previous point 2 - */ - export function Pinching(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { - const threshold = 4; - const oldDist = TwoPointEuclidist(oldPoint1, oldPoint2); - const newDist = TwoPointEuclidist(pt1, pt2); - - /** if they have the same sign, then we are either pinching in or out. - * threshold it by 10 (it has to be pinching by at least threshold to be a valid pinch) - * so that it can still pan without freaking out - */ - if (Math.sign(oldDist) === Math.sign(newDist) && Math.abs(oldDist - newDist) > threshold) { - return Math.sign(oldDist - newDist); - } - return 0; - } - - /** - * Returns -1 if pinning and pinching out, 0 if not pinning, and 1 if pinching in - * @param pt1 - new point that corresponds to oldPoint1 - * @param pt2 - new point that corresponds to oldPoint2 - * @param oldPoint1 - previous point 1 - * @param oldPoint2 - previous point 2 - */ - export function Pinning(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { - const threshold = 4; - - const pt1Dist = TwoPointEuclidist(oldPoint1, pt1); - const pt2Dist = TwoPointEuclidist(oldPoint2, pt2); - - const pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2); - - if (pinching !== 0) { - if ((pt1Dist < threshold && pt2Dist > threshold) || (pt1Dist > threshold && pt2Dist < threshold)) { - return pinching; - } - } - return 0; - } - - export function IsDragging(oldTouches: Map, newTouches: React.Touch[], leniency: number): boolean { - for (const touch of newTouches) { - if (touch) { - const oldTouch = oldTouches.get(touch.identifier); - if (oldTouch) { - if (TwoPointEuclidist(touch, oldTouch) >= leniency) { - return true; - } - } - } - } - return false; - } - - // These might not be very useful anymore, but I'll leave them here for now -syip2 - { - - - /** - * Returns the type of Touch Interaction from a list of points. - * Also returns any data that is associated with a Touch Interaction - * @param pts - List of points - */ - // export function InterpretPointers(pts: React.Touch[]): { type: Opt, data?: any } { - // const leniency = 200; - // switch (pts.length) { - // case 1: - // return { type: OneFinger }; - // case 2: - // return { type: TwoSeperateFingers }; - // case 3: - // let pt1 = pts[0]; - // let pt2 = pts[1]; - // let pt3 = pts[2]; - // if (pt1 && pt2 && pt3) { - // let dist12 = TwoPointEuclidist(pt1, pt2); - // let dist23 = TwoPointEuclidist(pt2, pt3); - // let dist13 = TwoPointEuclidist(pt1, pt3); - // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`); - // let dist12close = dist12 < leniency; - // let dist23close = dist23 < leniency; - // let dist13close = dist13 < leniency; - // let xor2313 = dist23close ? !dist13close : dist13close; - // let xor = dist12close ? !xor2313 : xor2313; - // // three input xor because javascript doesn't have logical xor's - // if (xor) { - // let points: number[] = []; - // let min = Math.min(dist12, dist23, dist13); - // switch (min) { - // case dist12: - // points = [0, 1, 2]; - // break; - // case dist23: - // points = [1, 2, 0]; - // break; - // case dist13: - // points = [0, 2, 1]; - // break; - // } - // return { type: TwoToOneFingers, data: points }; - // } - // else { - // return { type: ThreeSeperateFingers, data: null }; - // } - // } - // default: - // return { type: undefined }; - // } - // } - } -} \ No newline at end of file diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx new file mode 100644 index 000000000..34c7cfa74 --- /dev/null +++ b/src/client/util/InteractionUtils.tsx @@ -0,0 +1,182 @@ +import React = require("react"); + +export namespace InteractionUtils { + export const MOUSETYPE = "mouse"; + export const TOUCHTYPE = "touch"; + export const PENTYPE = "pen"; + export const ERASERTYPE = "eraser"; + + const POINTER_PEN_BUTTON = -1; + const REACT_POINTER_PEN_BUTTON = 0; + const ERASER_BUTTON = 5; + + export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { + const myTouches = new Array(); + for (let i = 0; i < e.targetTouches.length; i++) { + const pt: any = e.targetTouches.item(i); + if (pt && prevPoints.has(pt.identifier)) { + if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + myTouches.push(pt); + } + } + } + return myTouches; + } + + // TODO: find a way to reference this function from InkingStroke instead of copy pastign here. copied bc of weird error when on mobile view + export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: number) { + const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); + return ( + + ); + } + + export function IsType(e: PointerEvent | React.PointerEvent, type: string): boolean { + switch (type) { + // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2 + case PENTYPE: + return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0); + case ERASERTYPE: + return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON); + default: + return e.pointerType === type; + } + } + + export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number { + return Math.sqrt(Math.pow(pt1.clientX - pt2.clientX, 2) + Math.pow(pt1.clientY - pt2.clientY, 2)); + } + + /** + * Returns the centroid of an n-arbitrary long list of points (takes the average the x and y components of each point) + * @param pts - n-arbitrary long list of points + */ + export function CenterPoint(pts: React.Touch[]): { X: number, Y: number } { + const centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length; + const centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length; + return { X: centerX, Y: centerY }; + } + + /** + * Returns -1 if pinching out, 0 if not pinching, and 1 if pinching in + * @param pt1 - new point that corresponds to oldPoint1 + * @param pt2 - new point that corresponds to oldPoint2 + * @param oldPoint1 - previous point 1 + * @param oldPoint2 - previous point 2 + */ + export function Pinching(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { + const threshold = 4; + const oldDist = TwoPointEuclidist(oldPoint1, oldPoint2); + const newDist = TwoPointEuclidist(pt1, pt2); + + /** if they have the same sign, then we are either pinching in or out. + * threshold it by 10 (it has to be pinching by at least threshold to be a valid pinch) + * so that it can still pan without freaking out + */ + if (Math.sign(oldDist) === Math.sign(newDist) && Math.abs(oldDist - newDist) > threshold) { + return Math.sign(oldDist - newDist); + } + return 0; + } + + /** + * Returns -1 if pinning and pinching out, 0 if not pinning, and 1 if pinching in + * @param pt1 - new point that corresponds to oldPoint1 + * @param pt2 - new point that corresponds to oldPoint2 + * @param oldPoint1 - previous point 1 + * @param oldPoint2 - previous point 2 + */ + export function Pinning(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number { + const threshold = 4; + + const pt1Dist = TwoPointEuclidist(oldPoint1, pt1); + const pt2Dist = TwoPointEuclidist(oldPoint2, pt2); + + const pinching = Pinching(pt1, pt2, oldPoint1, oldPoint2); + + if (pinching !== 0) { + if ((pt1Dist < threshold && pt2Dist > threshold) || (pt1Dist > threshold && pt2Dist < threshold)) { + return pinching; + } + } + return 0; + } + + export function IsDragging(oldTouches: Map, newTouches: React.Touch[], leniency: number): boolean { + for (const touch of newTouches) { + if (touch) { + const oldTouch = oldTouches.get(touch.identifier); + if (oldTouch) { + if (TwoPointEuclidist(touch, oldTouch) >= leniency) { + return true; + } + } + } + } + return false; + } + + // These might not be very useful anymore, but I'll leave them here for now -syip2 + { + + + /** + * Returns the type of Touch Interaction from a list of points. + * Also returns any data that is associated with a Touch Interaction + * @param pts - List of points + */ + // export function InterpretPointers(pts: React.Touch[]): { type: Opt, data?: any } { + // const leniency = 200; + // switch (pts.length) { + // case 1: + // return { type: OneFinger }; + // case 2: + // return { type: TwoSeperateFingers }; + // case 3: + // let pt1 = pts[0]; + // let pt2 = pts[1]; + // let pt3 = pts[2]; + // if (pt1 && pt2 && pt3) { + // let dist12 = TwoPointEuclidist(pt1, pt2); + // let dist23 = TwoPointEuclidist(pt2, pt3); + // let dist13 = TwoPointEuclidist(pt1, pt3); + // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`); + // let dist12close = dist12 < leniency; + // let dist23close = dist23 < leniency; + // let dist13close = dist13 < leniency; + // let xor2313 = dist23close ? !dist13close : dist13close; + // let xor = dist12close ? !xor2313 : xor2313; + // // three input xor because javascript doesn't have logical xor's + // if (xor) { + // let points: number[] = []; + // let min = Math.min(dist12, dist23, dist13); + // switch (min) { + // case dist12: + // points = [0, 1, 2]; + // break; + // case dist23: + // points = [1, 2, 0]; + // break; + // case dist13: + // points = [0, 2, 1]; + // break; + // } + // return { type: TwoToOneFingers, data: points }; + // } + // else { + // return { type: ThreeSeperateFingers, data: null }; + // } + // } + // default: + // return { type: undefined }; + // } + // } + } +} \ No newline at end of file diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index 2996b7073..31601efd4 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -5,8 +5,4 @@ top: 0; left: 0; touch-action: none; -} - -.mobileInkOverlay { - border: 5px dashed red; } \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 81284b543..a01a86b53 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -19,7 +19,7 @@ import Palette from "./Palette"; import MobileInterface from "../../mobile/MobileInterface"; import { MainView } from "./MainView"; import { DocServer } from "../DocServer"; -import { GestureContent, MobileInkBoxContent } from "../../server/Message"; +import { GestureContent, MobileInkOverlayContent } from "../../server/Message"; import { Point } from "../northstar/model/idea/idea"; import MobileInkOverlay from "../../mobile/MobileInkOverlay"; @@ -217,21 +217,6 @@ export default class GestureOverlay extends Touchable { return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top }; } - // TODO: find a way to reference this function from InkingStroke instead of copy pastign here. copied bc of weird error when on mobile view - CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color?: string, width?: number) { - const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); - return ( - - ); - } - @computed get currentStroke() { if (this._points.length <= 1) { return (null); @@ -241,23 +226,23 @@ export default class GestureOverlay extends Touchable { return ( - {this.CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} + {InteractionUtils.CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} ); } @action - enableMobileInkBox = (content: MobileInkBoxContent) => { - this.showMobileInkOverlay = content.enableBox; + enableMobileInkOverlay = (content: MobileInkOverlayContent) => { + this.showMobileInkOverlay = content.enableOverlay; } render() { return (
+ {this.showMobileInkOverlay ? : <>} {this.currentStroke} {this.props.children} {this._palette} - {this.showMobileInkOverlay ? : <>}
); } } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 8b346d5d9..aca507147 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -10,24 +10,11 @@ import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); import { TraceMobx } from "../../new_fields/util"; +import { InteractionUtils } from "../util/InteractionUtils"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); -export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color?: string, width?: number) { - const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); - return ( - - ); -} - @observer export class InkingStroke extends DocExtendableComponent(InkDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } @@ -44,7 +31,7 @@ export class InkingStroke extends DocExtendableComponent(Docu if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { e.stopPropagation(); + // TODO: check here for panning/inking } return; } diff --git a/src/mobile/MobileInkOverlay.scss b/src/mobile/MobileInkOverlay.scss new file mode 100644 index 000000000..2e45d0441 --- /dev/null +++ b/src/mobile/MobileInkOverlay.scss @@ -0,0 +1,38 @@ +.mobileInkOverlay { + border: 5px dashed red; +} + +.mobileInkOverlay-border { + background-color: rgba(0, 255, 0, .4); + position: absolute; + pointer-events: auto; + cursor: pointer; + + &.top { + width: calc(100% + 20px); + height: 10px; + top: -10px; + left: -10px; + } + + &.left { + width: 10px; + height: calc(100% + 20px); + top: -10px; + left: -10px; + } + + &.right { + width: 10px; + height: calc(100% + 20px); + top: -10px; + right: -10px; + } + + &.bottom { + width: calc(100% + 20px); + height: 10px; + bottom: -10px; + left: -10px; + } +} \ No newline at end of file diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx index 71dd20c51..5efc7b83a 100644 --- a/src/mobile/MobileInkOverlay.tsx +++ b/src/mobile/MobileInkOverlay.tsx @@ -1,8 +1,9 @@ import React = require('react'); import { observer } from "mobx-react"; -import { MobileInkBoxContent, GestureContent } from "../server/Message"; +import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPosition } from "../server/Message"; import { observable, action } from "mobx"; import { GestureUtils } from "../pen-gestures/GestureUtils"; +import "./MobileInkOverlay.scss"; @observer @@ -15,6 +16,11 @@ export default class MobileInkOverlay extends React.Component { @observable private _x: number = -300; @observable private _y: number = -300; + @observable private _offsetX: number = 0; + @observable private _offsetY: number = 0; + @observable private _isDragging: boolean = false; + private _mainCont: React.RefObject = React.createRef(); + constructor(props: Readonly<{}>) { super(props); MobileInkOverlay.Instance = this; @@ -28,18 +34,27 @@ export default class MobileInkOverlay extends React.Component { } @action - initMobileInkOverlay(content: MobileInkBoxContent) { + initMobileInkOverlay(content: MobileInkOverlayContent) { const { width, height } = content; const scaledSize = this.initialSize(width ? width : 0, height ? height : 0); - this._width = scaledSize.width; - this._height = scaledSize.height; - this._scale = scaledSize.scale; + this._width = scaledSize.width * .5; + this._height = scaledSize.height * .5; + this._scale = .5; //scaledSize.scale; this._x = 300; // TODO: center on screen this._y = 25; // TODO: center on screen } + @action + updatePosition(content: UpdateMobileInkOverlayPosition) { + const { dx, dy, dsize } = content; + console.log(dx, dy, dsize); + } + drawStroke = (content: GestureContent) => { + // TODO: figure out why strokes drawn in corner of mobile interface dont get inserted + const { points, bounds } = content; + console.log("received points", points, bounds); const B = { right: (bounds.right * this._scale) + this._x, @@ -65,16 +80,66 @@ export default class MobileInkOverlay extends React.Component { ); } + @action + dragStart = (e: React.PointerEvent) => { + console.log("pointer down"); + document.removeEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + document.addEventListener("pointermove", this.dragging); + document.addEventListener("pointerup", this.dragEnd); + + this._isDragging = true; + this._offsetX = e.pageX - this._mainCont.current!.getBoundingClientRect().left; + this._offsetY = e.pageY - this._mainCont.current!.getBoundingClientRect().top; + + e.preventDefault(); + e.stopPropagation(); + } + + @action + dragging = (e: PointerEvent) => { + const x = e.pageX - this._offsetX; + const y = e.pageY - this._offsetY; + + // TODO: don't allow drag over library? + this._x = Math.min(Math.max(x, 0), window.innerWidth - this._width); + this._y = Math.min(Math.max(y, 0), window.innerHeight - this._height); + + e.preventDefault(); + e.stopPropagation(); + } + + @action + dragEnd = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + + this._isDragging = false; + + e.preventDefault(); + e.stopPropagation(); + } + render() { + return ( -
+
+
+
+
+
+
); } } \ No newline at end of file diff --git a/src/mobile/MobileInterface.scss b/src/mobile/MobileInterface.scss index e4cc919a5..8abe5a40d 100644 --- a/src/mobile/MobileInterface.scss +++ b/src/mobile/MobileInterface.scss @@ -1,4 +1,4 @@ -.mobileInterface-topButtons { +.mobileInterface-inkInterfaceButtons { position: absolute; display: flex; justify-content: space-between; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 4840ea374..b191b3afb 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -10,7 +10,7 @@ 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, faTrash, faCheck } from '@fortawesome/free-solid-svg-icons'; +import { faPenNib, faHighlighter, faEraser, faMousePointer, faBreadSlice, faTrash, faCheck, faLongArrowAltLeft } 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'; @@ -23,7 +23,7 @@ import { DateField } from '../new_fields/DateField'; import { GestureUtils } from '../pen-gestures/GestureUtils'; import { DocServer } from '../client/DocServer'; -library.add(faTrash, faCheck); +library.add(faLongArrowAltLeft); @observer export default class MobileInterface extends React.Component { @@ -68,8 +68,8 @@ export default class MobileInterface extends React.Component { InkingControl.Instance.switchTool(InkTool.Pen); this.drawingInk = true; - DocServer.Mobile.dispatchBoxTrigger({ - enableBox: true, + DocServer.Mobile.dispatchOverlayTrigger({ + enableOverlay: true, width: window.innerWidth, height: window.innerHeight }); @@ -112,12 +112,12 @@ export default class MobileInterface extends React.Component { return "hello"; } - onClick = (e: React.MouseEvent) => { + onBack = (e: React.MouseEvent) => { this.switchCurrentView("main"); InkingControl.Instance.switchTool(InkTool.None); // TODO: switch to previous tool - DocServer.Mobile.dispatchBoxTrigger({ - enableBox: false, + DocServer.Mobile.dispatchOverlayTrigger({ + enableOverlay: false, width: window.innerWidth, height: window.innerHeight }); @@ -126,37 +126,61 @@ export default class MobileInterface extends React.Component { this.drawingInk = false; } + shiftLeft = (e: React.MouseEvent) => { + DocServer.Mobile.dispatchOverlayPositionUpdate({ + dx: -10 + }); + } + + shiftRight = (e: React.MouseEvent) => { + DocServer.Mobile.dispatchOverlayPositionUpdate({ + dx: 10 + }); + } + @computed get inkContent() { + // TODO: support panning and zooming + // TODO: handle moving of ink strokes if (this.mainContainer) { return ( - -
- - +
+
+
+ +
+
+ +
+
+ + +
- 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}> - - + + 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}> + + +
); } } diff --git a/src/server/Message.ts b/src/server/Message.ts index 1958286df..064a19653 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -50,12 +50,18 @@ export interface GestureContent { readonly color?: string; } -export interface MobileInkBoxContent { - readonly enableBox: boolean; +export interface MobileInkOverlayContent { + readonly enableOverlay: boolean; readonly width?: number; readonly height?: number; } +export interface UpdateMobileInkOverlayPosition { + readonly dx?: number; + readonly dy?: number; + readonly dsize?: number; +} + export namespace MessageStore { export const Foo = new Message("Foo"); export const Bar = new Message("Bar"); @@ -65,8 +71,10 @@ export namespace MessageStore { export const GetDocument = new Message("Get Document"); export const DeleteAll = new Message("Delete All"); export const ConnectionTerminated = new Message("Connection Terminated"); + export const GesturePoints = new Message("Gesture Points"); - export const MobileInkBoxTrigger = new Message("Trigger Mobile Ink Box"); + export const MobileInkOverlayTrigger = new Message("Trigger Mobile Ink Overlay"); + export const UpdateMobileInkOverlayPosition = new Message("Update Mobile Ink Overlay Position"); export const GetRefField = new Message("Get Ref Field"); export const GetRefFields = new Message("Get Ref Fields"); diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 16e34bdfc..fe253400c 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, MobileInkBoxContent } from "../Message"; +import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent } from "../Message"; import { Client } from "../Client"; import { Socket } from "socket.io"; import { Database } from "../database"; @@ -55,7 +55,7 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteField, id => DeleteField(socket, id)); Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); Utils.AddServerHandler(socket, MessageStore.GesturePoints, content => processGesturePoints(socket, content)); - Utils.AddServerHandler(socket, MessageStore.MobileInkBoxTrigger, content => processBoxTrigger(socket, content)); + Utils.AddServerHandler(socket, MessageStore.MobileInkOverlayTrigger, content => processOverlayTrigger(socket, content)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); @@ -74,8 +74,8 @@ export namespace WebSocket { socket.broadcast.emit("receiveGesturePoints", content); } - function processBoxTrigger(socket: Socket, content: MobileInkBoxContent) { - socket.broadcast.emit("receiveBoxTrigger", content); + function processOverlayTrigger(socket: Socket, content: MobileInkOverlayContent) { + socket.broadcast.emit("receiveOverlayTrigger", content); } function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) { -- cgit v1.2.3-70-g09d2 From 7249f3f2bd12794ca86775853aee700e398eeb00 Mon Sep 17 00:00:00 2001 From: vellichora Date: Sun, 2 Feb 2020 16:13:23 -0500 Subject: started creating upload from web on mobile --- src/client/DocServer.ts | 6 +- src/mobile/MobileInkOverlay.tsx | 15 ++--- src/mobile/MobileInterface.tsx | 72 ++++++++++++++++++++-- src/server/Message.ts | 4 +- src/server/Websocket/Websocket.ts | 7 ++- .../authentication/models/current_user_utils.ts | 14 ++++- 6 files changed, 99 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index b20cd3521..c03764471 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, UpdateMobileInkOverlayPosition } from "./../server/Message"; +import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent } from "./../server/Message"; import { Opt, Doc } from '../new_fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; @@ -77,7 +77,7 @@ export namespace DocServer { Utils.Emit(_socket, MessageStore.MobileInkOverlayTrigger, content); } - export function dispatchOverlayPositionUpdate(content: UpdateMobileInkOverlayPosition) { + export function dispatchOverlayPositionUpdate(content: UpdateMobileInkOverlayPositionContent) { Utils.Emit(_socket, MessageStore.UpdateMobileInkOverlayPosition, content); } @@ -113,7 +113,7 @@ export namespace DocServer { GestureOverlay.Instance.enableMobileInkOverlay(content); MobileInkOverlay.Instance.initMobileInkOverlay(content); }); - _socket.addEventListener("updateMobileOverlayPosition", (content: UpdateMobileInkOverlayPosition) => { + _socket.addEventListener("receiveUpdateOverlayPosition", (content: UpdateMobileInkOverlayPositionContent) => { MobileInkOverlay.Instance.updatePosition(content); }); } diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx index 5efc7b83a..600e8a91b 100644 --- a/src/mobile/MobileInkOverlay.tsx +++ b/src/mobile/MobileInkOverlay.tsx @@ -1,6 +1,6 @@ import React = require('react'); import { observer } from "mobx-react"; -import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPosition } from "../server/Message"; +import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPositionContent } from "../server/Message"; import { observable, action } from "mobx"; import { GestureUtils } from "../pen-gestures/GestureUtils"; import "./MobileInkOverlay.scss"; @@ -37,17 +37,19 @@ export default class MobileInkOverlay extends React.Component { initMobileInkOverlay(content: MobileInkOverlayContent) { const { width, height } = content; const scaledSize = this.initialSize(width ? width : 0, height ? height : 0); - this._width = scaledSize.width * .5; - this._height = scaledSize.height * .5; - this._scale = .5; //scaledSize.scale; + this._width = scaledSize.width * .8; + this._height = scaledSize.height * .8; + this._scale = .8; //scaledSize.scale; this._x = 300; // TODO: center on screen this._y = 25; // TODO: center on screen } @action - updatePosition(content: UpdateMobileInkOverlayPosition) { + updatePosition(content: UpdateMobileInkOverlayPositionContent) { const { dx, dy, dsize } = content; - console.log(dx, dy, dsize); + if (dx) this._x += dx; + if (dy) this._y += dy; + // TODO: scale dsize } drawStroke = (content: GestureContent) => { @@ -82,7 +84,6 @@ export default class MobileInkOverlay extends React.Component { @action dragStart = (e: React.PointerEvent) => { - console.log("pointer down"); document.removeEventListener("pointermove", this.dragging); document.removeEventListener("pointerup", this.dragEnd); document.addEventListener("pointermove", this.dragging); diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index b191b3afb..03bcbca80 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -30,12 +30,15 @@ export default class MobileInterface extends React.Component { @observable static Instance: MobileInterface; @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" | "library" = "main"; + @observable private currentView: "main" | "ink" | "upload" = "main"; private mainDoc = CurrentUserUtils.setupMobileDoc(this.userDoc); + private inkDoc?: Doc; public drawingInk: boolean = false; + private uploadDoc?: Doc; + constructor(props: Readonly<{}>) { super(props); MobileInterface.Instance = this; @@ -52,7 +55,7 @@ export default class MobileInterface extends React.Component { } @action - switchCurrentView = (view: "main" | "ink" | "library") => { + switchCurrentView = (view: "main" | "ink" | "upload") => { this.currentView = view; if (this.userDoc) { @@ -76,6 +79,11 @@ export default class MobileInterface extends React.Component { break; } + case "upload": { + this.uploadDoc = CurrentUserUtils.setupMobileUploadDoc(this.userDoc); + this.userDoc.activeMobile = this.uploadDoc; + + } } } } @@ -127,15 +135,20 @@ export default class MobileInterface extends React.Component { } shiftLeft = (e: React.MouseEvent) => { + console.log("shift left!"); DocServer.Mobile.dispatchOverlayPositionUpdate({ dx: -10 }); + e.preventDefault(); + e.stopPropagation(); } shiftRight = (e: React.MouseEvent) => { DocServer.Mobile.dispatchOverlayPositionUpdate({ dx: 10 }); + e.preventDefault(); + e.stopPropagation(); } @computed @@ -147,7 +160,7 @@ export default class MobileInterface extends React.Component {
- +
@@ -185,8 +198,57 @@ export default class MobileInterface extends React.Component { } } + upload = () => { + + } + + @computed + get uploadContent() { + if (this.mainContainer) { + return ( +
+
+
+ +
+
+ +
+
+ window.screen.width} + PanelHeight={() => window.screen.height} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + +
+ ); + } + } + render() { - const content = this.currentView === "main" ? this.mainContent : this.currentView === "ink" ? this.inkContent : <>; + const content = this.currentView === "main" ? this.mainContent : + this.currentView === "ink" ? this.inkContent : + this.currentView === "upload" ? this.uploadContent : <>; return (
{content} @@ -195,4 +257,4 @@ export default class MobileInterface extends React.Component { } } -Scripting.addGlobal(function switchMobileView(view: "main" | "ink" | "library") { return MobileInterface.Instance.switchCurrentView(view); }); +Scripting.addGlobal(function switchMobileView(view: "main" | "ink" | "upload") { return MobileInterface.Instance.switchCurrentView(view); }); diff --git a/src/server/Message.ts b/src/server/Message.ts index 064a19653..236df3f3c 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -56,7 +56,7 @@ export interface MobileInkOverlayContent { readonly height?: number; } -export interface UpdateMobileInkOverlayPosition { +export interface UpdateMobileInkOverlayPositionContent { readonly dx?: number; readonly dy?: number; readonly dsize?: number; @@ -74,7 +74,7 @@ export namespace MessageStore { export const GesturePoints = new Message("Gesture Points"); export const MobileInkOverlayTrigger = new Message("Trigger Mobile Ink Overlay"); - export const UpdateMobileInkOverlayPosition = new Message("Update Mobile Ink Overlay Position"); + export const UpdateMobileInkOverlayPosition = new Message("Update Mobile Ink Overlay Position"); export const GetRefField = new Message("Get Ref Field"); export const GetRefFields = new Message("Get Ref Fields"); diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index fe253400c..77816c897 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 } from "../Message"; +import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent } from "../Message"; import { Client } from "../Client"; import { Socket } from "socket.io"; import { Database } from "../database"; @@ -56,6 +56,7 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); 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.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); @@ -78,6 +79,10 @@ export namespace WebSocket { socket.broadcast.emit("receiveOverlayTrigger", content); } + function processUpdateOverlayPosition(socket: Socket, content: UpdateMobileInkOverlayPositionContent) { + socket.broadcast.emit("receiveUpdateOverlayPosition", 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 98092ddfe..82bed76f7 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -101,7 +101,8 @@ export class CurrentUserUtils { { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, { 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: "mouse-pointer", click: 'switchMobileView("ink");', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "red", activePen: doc }, + { title: "draw", icon: "pen-nib", click: 'switchMobileView("ink");', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "red", activePen: doc }, + { title: "upload", icon: "upload", click: 'switchMobileView("upload");', backgroundColor: "orange" }, ]; return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, @@ -144,6 +145,17 @@ export class CurrentUserUtils { return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white" }); } + 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", ignoreClick: true }); + 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 + }); + } + // 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) static setupToolsPanel(sidebarContainer: Doc, doc: Doc) { // setup a masonry view of all he creators -- cgit v1.2.3-70-g09d2 From 28efd6f2b5b79d1f25fa66e5d9f69d77a7594fee Mon Sep 17 00:00:00 2001 From: vellichora Date: Sat, 8 Feb 2020 20:48:51 -0500 Subject: refactored mobile interface so that current views are created via scripting --- src/client/views/nodes/WebBox.tsx | 6 +- src/mobile/ImageUpload.tsx | 10 +- src/mobile/MobileInkOverlay.tsx | 6 +- src/mobile/MobileInterface.tsx | 115 +++++++++++++-------- .../authentication/models/current_user_utils.ts | 13 ++- 5 files changed, 92 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index b35ea0bb0..0b23c3bec 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -182,9 +182,11 @@ export class WebBox extends DocAnnotatableComponent {view}
; - const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; + const decInteracting = DocumentDecorations.Instance && DocumentDecorations.Instance.Interacting; - const classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); + const frozen = !this.props.isSelected() || decInteracting; + + const classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !decInteracting ? "-interactive" : ""); return ( <>
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 0b0280519..3304e8e22 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -13,6 +13,7 @@ import { observable } from 'mobx'; import { Utils } from '../Utils'; import MobileInterface from './MobileInterface'; import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; +import { Scripting } from '../client/util/Scripting'; @@ -27,12 +28,11 @@ const inputRef = React.createRef(); @observer class Uploader extends React.Component { - @observable - error: string = ""; - @observable - status: string = ""; + @observable error: string = ""; + @observable status: string = ""; onClick = async () => { + console.log("uploader click"); try { this.status = "initializing protos"; await Docs.Prototypes.initialize(); @@ -105,6 +105,8 @@ class Uploader extends React.Component { } +// Scripting.addGlobal(function uploadImageMobile() { return Uploader.onClick(); }); + // DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload"); (async () => { diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx index 600e8a91b..6b65aa436 100644 --- a/src/mobile/MobileInkOverlay.tsx +++ b/src/mobile/MobileInkOverlay.tsx @@ -37,9 +37,9 @@ export default class MobileInkOverlay extends React.Component { initMobileInkOverlay(content: MobileInkOverlayContent) { const { width, height } = content; const scaledSize = this.initialSize(width ? width : 0, height ? height : 0); - this._width = scaledSize.width * .8; - this._height = scaledSize.height * .8; - this._scale = .8; //scaledSize.scale; + this._width = scaledSize.width; + this._height = scaledSize.height; + this._scale = scaledSize.scale; //scaledSize.scale; this._x = 300; // TODO: center on screen this._y = 25; // TODO: center on screen } diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 03bcbca80..665d9a168 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -30,14 +30,14 @@ export default class MobileInterface extends React.Component { @observable static Instance: MobileInterface; @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"; + // @observable private currentView: "main" | "ink" | "upload" = "main"; + private mainDoc: Doc = CurrentUserUtils.setupMobileDoc(this.userDoc); + @observable private renderView?: () => JSX.Element; - private mainDoc = CurrentUserUtils.setupMobileDoc(this.userDoc); - - private inkDoc?: Doc; + // private inkDoc?: Doc; public drawingInk: boolean = false; - private uploadDoc?: Doc; + // private uploadDoc?: Doc; constructor(props: Readonly<{}>) { super(props); @@ -55,41 +55,63 @@ export default class MobileInterface extends React.Component { } @action - switchCurrentView = (view: "main" | "ink" | "upload") => { - this.currentView = view; + switchCurrentView = (doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => { + if (!this.userDoc) return; - 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; + this.userDoc.activeMobile = doc(this.userDoc); + onSwitch && onSwitch(); - DocServer.Mobile.dispatchOverlayTrigger({ - enableOverlay: true, - width: window.innerWidth, - height: window.innerHeight - }); + this.renderView = renderView; + console.log("switching current view", renderView); + } - break; - } - case "upload": { - this.uploadDoc = CurrentUserUtils.setupMobileUploadDoc(this.userDoc); - this.userDoc.activeMobile = this.uploadDoc; + onSwitchInking = () => { + InkingControl.Instance.switchTool(InkTool.Pen); + MobileInterface.Instance.drawingInk = true; - } - } - } + DocServer.Mobile.dispatchOverlayTrigger({ + enableOverlay: true, + width: window.innerWidth, + height: window.innerHeight + }); } - @computed - get mainContent() { + // @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; + + // } + // } + // } + // } + + renderDefaultContent = () => { + console.log("rendering default content"); if (this.mainContainer) { return { - this.switchCurrentView("main"); + this.switchCurrentView((userDoc: Doc) => this.mainDoc); InkingControl.Instance.switchTool(InkTool.None); // TODO: switch to previous tool DocServer.Mobile.dispatchOverlayTrigger({ @@ -130,7 +152,7 @@ export default class MobileInterface extends React.Component { height: window.innerHeight }); - this.inkDoc = undefined; + // this.inkDoc = undefined; this.drawingInk = false; } @@ -151,8 +173,8 @@ export default class MobileInterface extends React.Component { e.stopPropagation(); } - @computed - get inkContent() { + renderInkingContent = () => { + console.log("rendering inking content"); // TODO: support panning and zooming // TODO: handle moving of ink strokes if (this.mainContainer) { @@ -202,8 +224,7 @@ export default class MobileInterface extends React.Component { } - @computed - get uploadContent() { + renderUploadContent() { if (this.mainContainer) { return (
@@ -246,15 +267,19 @@ export default class MobileInterface extends React.Component { } render() { - const content = this.currentView === "main" ? this.mainContent : - this.currentView === "ink" ? this.inkContent : - this.currentView === "upload" ? this.uploadContent : <>; + // const content = this.currentView === "main" ? this.mainContent : + // this.currentView === "ink" ? this.inkContent : + // this.currentView === "upload" ? this.uploadContent : <>; return (
- {content} + {this.renderView ? this.renderView() : this.renderDefaultContent()}
); } } -Scripting.addGlobal(function switchMobileView(view: "main" | "ink" | "upload") { return MobileInterface.Instance.switchCurrentView(view); }); +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 renderMobileUpload() { return MobileInterface.Instance.renderUploadContent(); }); + diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 82bed76f7..817cf40b1 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -14,6 +14,7 @@ import { Utils } from "../../../Utils"; import { nullAudio } from "../../../new_fields/URLField"; import { DragManager } from "../../../client/util/DragManager"; import { InkingControl } from "../../../client/views/InkingControl"; +import { Scripting } from "../../../client/util/Scripting"; export class CurrentUserUtils { private static curr_id: string; @@ -101,8 +102,9 @@ export class CurrentUserUtils { { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, { 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("ink");', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "red", activePen: doc }, - { title: "upload", icon: "upload", click: 'switchMobileView("upload");', backgroundColor: "orange" }, + { 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: 'uploadImageMobile();', backgroundColor: "cyan" }, ]; return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, @@ -147,7 +149,7 @@ 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", ignoreClick: true }); + 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", @@ -388,4 +390,7 @@ export class CurrentUserUtils { }; return recurs([] as Attribute[], schema ? schema.rootAttributeGroup : undefined); } -} \ No newline at end of file +} + +Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); +Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From e8fcbbf57b2a2f443d9c280ce10558cf9d51c632 Mon Sep 17 00:00:00 2001 From: vellichora 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') 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).detail); + const handler = (e: Event) => { console.log("drop target reveied docs"); dropFunc(e, (e as CustomEvent).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 { + 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 => { + 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 { + 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 { 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 { + 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("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("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} >
-
+
); } } \ 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 { 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 {
- - 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}> - - + 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}> +
); } } - 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 ( -
+
@@ -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 ( -
- {this.renderView ? this.renderView() : this.renderDefaultContent()} +
+ {/* + + {this.renderView ? this.renderView() : this.renderDefaultContent()} + */} + + {/* + + */} + + + {this.renderView ? this.renderView() : this.renderDefaultContent()} + + + {/* */} + + {/* + + */}
); } @@ -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 { 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("Foo"); export const Bar = new Message("Bar"); @@ -75,6 +80,7 @@ export namespace MessageStore { export const GesturePoints = new Message("Gesture Points"); export const MobileInkOverlayTrigger = new Message("Trigger Mobile Ink Overlay"); export const UpdateMobileInkOverlayPosition = new Message("Update Mobile Ink Overlay Position"); + export const MobileDocumentUpload = new Message("Upload Document From Mobile"); export const GetRefField = new Message("Get Ref Field"); export const GetRefFields = new Message("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 bede55e2f8504a4b45c3adb96d6ba2d13c17b96f Mon Sep 17 00:00:00 2001 From: vellichora Date: Sun, 9 Feb 2020 17:43:54 -0500 Subject: option to upload as collection for mobile --- src/mobile/MobileInkOverlay.tsx | 15 +++++++++++---- src/mobile/MobileInterface.tsx | 8 +++++--- src/server/Message.ts | 1 + 3 files changed, 17 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx index ed4cca5b9..9f810424a 100644 --- a/src/mobile/MobileInkOverlay.tsx +++ b/src/mobile/MobileInkOverlay.tsx @@ -4,10 +4,11 @@ import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPosition import { observable, action } from "mobx"; import { GestureUtils } from "../pen-gestures/GestureUtils"; import "./MobileInkOverlay.scss"; -import { StrCast } from '../new_fields/Types'; +import { StrCast, Cast } from '../new_fields/Types'; import { DragManager } from "../client/util/DragManager"; import { DocServer } from '../client/DocServer'; -import { Doc } from '../new_fields/Doc'; +import { Doc, DocListCastAsync } from '../new_fields/Doc'; +import { listSpec } from '../new_fields/Schema'; @observer @@ -87,7 +88,7 @@ export default class MobileInkOverlay extends React.Component { } uploadDocument = async (content: MobileDocumentUploadContent) => { - const { docId } = content; + const { docId, asCollection } = content; console.log("receive upload document id", docId); const doc = await DocServer.GetRefField(docId); @@ -97,7 +98,13 @@ export default class MobileInkOverlay extends React.Component { const target = document.elementFromPoint(this._x + 10, this._y + 10); console.log("the target is", target); - const dragData = new DragManager.DocumentDragData([doc]); + let uploadDocs = [doc]; + if (!asCollection) { + const children = await DocListCastAsync(doc.data); + console.log("uploading children", children); + uploadDocs = children ? children : []; + } + const dragData = new DragManager.DocumentDragData(uploadDocs); const complete = new DragManager.DragCompleteEvent(false, dragData); console.log("the drag data is", dragData); diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index a1ef0a5d1..e21258c62 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -203,7 +203,7 @@ export default class MobileInterface extends React.Component { } } - upload = async (e: React.MouseEvent) => { + upload = async (e: React.MouseEvent, asCollection: boolean) => { if (this.mainContainer) { const data = Cast(this.mainContainer.data, listSpec(Doc)); if (data) { @@ -212,7 +212,8 @@ export default class MobileInterface extends React.Component { console.log("UPLOADING DOCUMENT FROM MOBILE", uploadDoc[Id], StrCast(uploadDoc.proto!.title)); if (uploadDoc) { DocServer.Mobile.dispatchMobileDocumentUpload({ - docId: uploadDoc[Id] + docId: uploadDoc[Id], + asCollection: asCollection }); } } @@ -230,7 +231,8 @@ export default class MobileInterface extends React.Component {
- + +
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') 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).detail); }; + const handler = (e: Event) => { dropFunc(e, (e as CustomEvent).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(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("(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 @observable private collapsed: boolean = true; @observable private url: string = ""; + private _longPressSecondsHack?: NodeJS.Timeout; + private _iframeRef = React.createRef(); + private _iframeDragRef = React.createRef(); + @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 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) => { this.url = e.target.value; @@ -165,6 +217,62 @@ export class WebBox extends DocAnnotatableComponent } } + // 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 if (field instanceof HtmlField) { view = ; } else if (field instanceof WebField) { - view =