aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/DocServer.ts39
-rw-r--r--src/client/util/DragManager.ts27
-rw-r--r--src/client/util/InteractionUtils.tsx17
-rw-r--r--src/client/views/GestureOverlay.tsx51
-rw-r--r--src/client/views/InkingStroke.tsx4
-rw-r--r--src/client/views/MainView.tsx13
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx1
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx1
-rw-r--r--src/client/views/collections/CollectionSubView.tsx14
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx16
-rw-r--r--src/client/views/nodes/DocumentView.tsx1
-rw-r--r--src/client/views/nodes/WebBox.scss15
-rw-r--r--src/client/views/nodes/WebBox.tsx108
16 files changed, 261 insertions, 54 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 5fcd2547e..33f7c1d35 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,10 +1,12 @@
import * as OpenSocket from 'socket.io-client';
-import { MessageStore, YoutubeQueryTypes } 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';
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
@@ -64,6 +66,26 @@ export namespace DocServer {
}
}
+ export namespace Mobile {
+
+ export function dispatchGesturePoints(content: GestureContent) {
+ Utils.Emit(_socket, MessageStore.GesturePoints, content);
+ }
+
+ export function dispatchOverlayTrigger(content: MobileInkOverlayContent) {
+ // _socket.emit("dispatchBoxTrigger");
+ Utils.Emit(_socket, MessageStore.MobileInkOverlayTrigger, content);
+ }
+
+ export function dispatchOverlayPositionUpdate(content: UpdateMobileInkOverlayPositionContent) {
+ Utils.Emit(_socket, MessageStore.UpdateMobileInkOverlayPosition, content);
+ }
+
+ export function dispatchMobileDocumentUpload(content: MobileDocumentUploadContent) {
+ Utils.Emit(_socket, MessageStore.MobileDocumentUpload, content);
+ }
+ }
+
const instructions = "This page will automatically refresh after this alert is closed. Expect to reconnect after about 30 seconds.";
function alertUser(connectionTerminationReason: string) {
switch (connectionTerminationReason) {
@@ -101,6 +123,21 @@ export namespace DocServer {
Utils.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete);
Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete);
Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, alertUser);
+
+ // mobile ink overlay socket events to communicate between mobile view and desktop view
+ _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => {
+ MobileInkOverlay.Instance.drawStroke(content);
+ });
+ _socket.addEventListener("receiveOverlayTrigger", (content: MobileInkOverlayContent) => {
+ GestureOverlay.Instance.enableMobileInkOverlay(content);
+ MobileInkOverlay.Instance.initMobileInkOverlay(content);
+ });
+ _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 5d2f1bc06..bf64e0bdb 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -180,7 +180,7 @@ export namespace DragManager {
);
}
element.dataset.canDrop = "true";
- const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
+ const handler = (e: Event) => { dropFunc(e, (e as CustomEvent<DropEvent>).detail); };
element.addEventListener("dashOnDrop", handler);
return () => {
element.removeEventListener("dashOnDrop", handler);
@@ -196,7 +196,7 @@ export namespace DragManager {
dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d)
);
- e.docDragData?.droppedDocuments.forEach((drop: Doc, i: number) =>
+ e.docDragData ?.droppedDocuments.forEach((drop: Doc, i: number) =>
Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => drop[prop] = undefined));
};
dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded
@@ -266,6 +266,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) {
@@ -285,6 +289,7 @@ export namespace DragManager {
if (!ele.parentNode) dragDiv.appendChild(ele);
const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement;
const rect = ele.getBoundingClientRect();
+ console.log("boudning", rect);
const scaleX = rect.width / ele.offsetWidth,
scaleY = rect.height / ele.offsetHeight;
xs.push(rect.left);
@@ -302,7 +307,7 @@ export namespace DragManager {
dragElement.style.transformOrigin = "0 0";
dragElement.style.borderRadius = getComputedStyle(ele).borderRadius;
dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000";
- dragElement.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`;
+ dragElement.style.transform = `translate(${rect.left + (options ?.offsetX || 0)}px, ${rect.top + (options ?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`;
dragElement.style.width = `${rect.width / scaleX}px`;
dragElement.style.height = `${rect.height / scaleY}px`;
@@ -331,8 +336,8 @@ export namespace DragManager {
return dragElement;
});
- const hideSource = options?.hideSource ? true : false;
- eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = hideSource) : (ele.hidden = hideSource));
+ const hideSource = options ?.hideSource ? true : false;
+ eles.map(ele => ele.parentElement && ele.parentElement ?.className === dragData.dragDivName ? (ele.parentElement.hidden = hideSource) : (ele.hidden = hideSource));
let lastX = downX;
let lastY = downY;
@@ -343,7 +348,7 @@ export namespace DragManager {
}
if (e.shiftKey && CollectionDockingView.Instance && dragData.droppedDocuments.length === 1) {
AbortDrag();
- finishDrag?.(new DragCompleteEvent(true, dragData));
+ finishDrag ?.(new DragCompleteEvent(true, dragData));
CollectionDockingView.Instance.StartOtherDrag({
pageX: e.pageX,
pageY: e.pageY,
@@ -357,13 +362,13 @@ export namespace DragManager {
lastX = e.pageX;
lastY = e.pageY;
dragElements.map((dragElement, i) => (dragElement.style.transform =
- `translate(${(xs[i] += moveX) + (options?.offsetX || 0)}px, ${(ys[i] += moveY) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
+ `translate(${(xs[i] += moveX) + (options ?.offsetX || 0)}px, ${(ys[i] += moveY) + (options ?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
);
};
const hideDragShowOriginalElements = () => {
dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
- eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = false) : (ele.hidden = false));
+ eles.map(ele => ele.parentElement && ele.parentElement ?.className === dragData.dragDivName ? (ele.parentElement.hidden = false) : (ele.hidden = false));
};
const endDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
@@ -373,14 +378,14 @@ export namespace DragManager {
AbortDrag = () => {
hideDragShowOriginalElements();
SelectionManager.SetIsDragging(false);
- options?.dragComplete?.(new DragCompleteEvent(true, dragData));
+ options ?.dragComplete ?.(new DragCompleteEvent(true, dragData));
endDrag();
};
const upHandler = (e: PointerEvent) => {
hideDragShowOriginalElements();
dispatchDrag(eles, e, dragData, options, finishDrag);
SelectionManager.SetIsDragging(false);
- options?.dragComplete?.(new DragCompleteEvent(false, dragData));
+ options ?.dragComplete ?.(new DragCompleteEvent(false, dragData));
endDrag();
};
document.addEventListener("pointermove", moveHandler, true);
@@ -401,7 +406,7 @@ export namespace DragManager {
});
if (target) {
const complete = new DragCompleteEvent(false, dragData);
- finishDrag?.(complete);
+ finishDrag ?.(complete);
console.log(complete.aborted);
target.dispatchEvent(
new CustomEvent<DropEvent>("dashOnDrop", {
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 8fc5e8098..2eec02a42 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -1,3 +1,5 @@
+import React = require("react");
+
export namespace InteractionUtils {
export const MOUSETYPE = "mouse";
export const TOUCHTYPE = "touch";
@@ -90,6 +92,21 @@ export namespace InteractionUtils {
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 (
+ <polyline
+ points={pts}
+ style={{
+ fill: "none",
+ stroke: color,
+ strokeWidth: width
+ }}
+ />
+ );
+ }
+
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
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 5c4dd0c0a..52fa6dc22 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -27,6 +27,9 @@ import { listSpec } from "../../new_fields/Schema";
import { List } from "../../new_fields/List";
import { CollectionViewType } from "./collections/CollectionView";
import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
+import MobileInterface from "../../mobile/MobileInterface";
+import { MobileInkOverlayContent } from "../../server/Message";
+import MobileInkOverlay from "../../mobile/MobileInkOverlay";
@observer
export default class GestureOverlay extends Touchable {
@@ -53,6 +56,8 @@ export default class GestureOverlay extends Touchable {
@computed private get height(): number { return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 300, 300); }
@computed private get showBounds() { return this.Tool !== ToolglassTools.None; }
+ @observable private showMobileInkOverlay: boolean = false;
+
private _d1: Doc | undefined;
private _inkToTextDoc: Doc | undefined;
private _thumbDoc: Doc | undefined;
@@ -71,7 +76,7 @@ export default class GestureOverlay extends Touchable {
componentDidMount = () => {
this._thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc));
- this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
+ this._inkToTextDoc = FieldValue(Cast(this._thumbDoc ?.inkToTextDoc, Doc));
}
getNewTouches(e: React.TouchEvent | TouchEvent) {
@@ -130,7 +135,7 @@ export default class GestureOverlay extends Touchable {
if (nts.nt.length < 5) {
const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY);
- target?.dispatchEvent(
+ target ?.dispatchEvent(
new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>("dashOnTouchStart",
{
bubbles: true,
@@ -304,7 +309,7 @@ export default class GestureOverlay extends Touchable {
if (pt.radiusX > 1 && pt.radiusY > 1) {
for (let j = 0; j < e.targetTouches.length; j++) {
const tPt = e.targetTouches.item(j);
- if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) {
+ if (tPt ?.screenX === pt ?.screenX && tPt ?.screenY === pt ?.screenY) {
if (pt && this.prevPoints.has(pt.identifier)) {
fingers.push(pt);
}
@@ -327,9 +332,9 @@ export default class GestureOverlay extends Touchable {
else {
console.log("not hand");
}
- this.pointerIdentifier = pointer?.identifier;
+ this.pointerIdentifier = pointer ?.identifier;
runInAction(() => {
- this._pointerY = pointer?.clientY;
+ this._pointerY = pointer ?.clientY;
if (thumb.identifier === this.thumbIdentifier) {
this._thumbX = thumb.clientX;
this._thumbY = thumb.clientY;
@@ -338,7 +343,7 @@ export default class GestureOverlay extends Touchable {
}
});
- this.thumbIdentifier = thumb?.identifier;
+ this.thumbIdentifier = thumb ?.identifier;
this._hands.set(thumb.identifier, fingers);
const others = fingers.filter(f => f !== thumb);
const minX = Math.min(...others.map(f => f.clientX));
@@ -372,7 +377,7 @@ export default class GestureOverlay extends Touchable {
if (pt.radiusX > 1 && pt.radiusY > 1) {
for (let j = 0; j < e.targetTouches.length; j++) {
const tPt = e.targetTouches.item(j);
- if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) {
+ if (tPt ?.screenX === pt ?.screenX && tPt ?.screenY === pt ?.screenY) {
if (pt && this.prevPoints.has(pt.identifier)) {
this._hands.forEach(hand => hand.some(f => {
if (f.identifier === pt.identifier) {
@@ -385,7 +390,7 @@ export default class GestureOverlay extends Touchable {
}
}
const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
- if (thumb?.identifier && thumb?.identifier === this.thumbIdentifier) {
+ if (thumb ?.identifier && thumb ?.identifier === this.thumbIdentifier) {
this._hands.set(thumb.identifier, fingers);
}
@@ -435,7 +440,7 @@ export default class GestureOverlay extends Touchable {
this._thumbDoc = undefined;
let scriptWorked = false;
- if (NumCast(this._inkToTextDoc?.selectedIndex) > -1) {
+ if (NumCast(this._inkToTextDoc ?.selectedIndex) > -1) {
const selectedButton = this._possibilities[this._selectedIndex];
if (selectedButton) {
selectedButton.props.onClick();
@@ -523,8 +528,8 @@ export default class GestureOverlay extends Touchable {
callbackFn: callback
}
});
- target1?.dispatchEvent(ge);
- target2?.dispatchEvent(ge);
+ target1 ?.dispatchEvent(ge);
+ target2 ?.dispatchEvent(ge);
return actionPerformed;
}
@@ -534,6 +539,16 @@ 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 && MobileInterface.Instance.drawingInk) {
+ const { selectedColor, selectedWidth } = InkingControl.Instance;
+ DocServer.Mobile.dispatchGesturePoints({
+ points: this._points,
+ bounds: B,
+ color: selectedColor,
+ width: selectedWidth
+ });
+ }
+
const initialPoint = this._points[0.];
const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + (this.height);
const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - (this.height) && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER);
@@ -551,10 +566,10 @@ export default class GestureOverlay extends Touchable {
const possibilities: string[] = [];
for (const wR of wordResults) {
console.log(wR);
- if (wR?.recognizedText) {
- possibilities.push(wR?.recognizedText)
+ if (wR ?.recognizedText) {
+ possibilities.push(wR ?.recognizedText)
}
- possibilities.push(...wR?.alternates?.map((a: any) => a.recognizedString));
+ possibilities.push(...wR ?.alternates ?.map((a: any) => a.recognizedString));
}
console.log(possibilities);
const r = Math.max(this.svgBounds.right, ...this._strokes.map(s => this.getBounds(s).right));
@@ -613,7 +628,7 @@ export default class GestureOverlay extends Touchable {
dispatchGesture = (gesture: GestureUtils.Gestures, stroke?: InkData, data?: any) => {
const target = document.elementFromPoint((stroke ?? this._points)[0].X, (stroke ?? this._points)[0].Y);
- target?.dispatchEvent(
+ target ?.dispatchEvent(
new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
{
bubbles: true,
@@ -693,10 +708,16 @@ export default class GestureOverlay extends Touchable {
this._clipboardDoc = undefined;
}
+ @action
+ enableMobileInkOverlay = (content: MobileInkOverlayContent) => {
+ this.showMobileInkOverlay = content.enableOverlay;
+ }
+
render() {
trace();
return (
<div className="gestureOverlay-cont" onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
+ {this.showMobileInkOverlay ? <MobileInkOverlay /> : <></>}
{this.elements}
<div className="clipboardDoc-cont" style={{
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index f315ce12a..a791eed40 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -29,13 +29,13 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
@computed get PanelHeight() { return this.props.PanelHeight(); }
private analyzeStrokes = () => {
- const data: InkData = Cast(this.Document.data, InkField)?.inkData ?? [];
+ const data: InkData = Cast(this.Document.data, InkField) ?.inkData ?? [];
CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.Document, ["inkAnalysis", "handwriting"], [data]);
}
render() {
TraceMobx();
- const data: InkData = Cast(this.Document.data, InkField)?.inkData ?? [];
+ const data: InkData = Cast(this.Document.data, InkField) ?.inkData ?? [];
const xs = data.map(p => p.X);
const ys = data.map(p => p.Y);
const left = Math.min(...xs);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 92b26b9bc..1b886da2e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -56,6 +56,7 @@ export class MainView extends React.Component {
private _flyoutSizeOnDown = 0;
private _urlState: HistoryUtil.DocUrl;
private _docBtnRef = React.createRef<HTMLDivElement>();
+ private _mainViewRef = React.createRef<HTMLDivElement>();
@observable private _panelWidth: number = 0;
@observable private _panelHeight: number = 0;
@@ -484,7 +485,7 @@ export class MainView extends React.Component {
return new Transform(-translateX, -translateY, 1 / scale);
}
@computed get docButtons() {
- if (CurrentUserUtils.UserDocument?.expandingButtons instanceof Doc) {
+ if (CurrentUserUtils.UserDocument ?.expandingButtons instanceof Doc) {
return <div className="mainView-docButtons" ref={this._docBtnRef}
style={{ height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} >
<MainViewNotifs />
@@ -519,8 +520,16 @@ export class MainView extends React.Component {
return (null);
}
+ get mainViewElement() {
+ return document.getElementById("mainView-container");
+ }
+
+ get mainViewRef() {
+ return this._mainViewRef;
+ }
+
render() {
- return (<div id="mainView-container">
+ return (<div id="mainView-container" ref={this._mainViewRef}>
<DictationOverlay />
<SharingManager />
<SettingsManager />
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index e25a2f5eb..e84b3b0dd 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -73,6 +73,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@undoBatch
rowDrop = action((e: Event, de: DragManager.DropEvent) => {
+ console.log("masronry row drop");
this._createAliasSelected = false;
if (de.complete.docDragData) {
(this.props.parent.Document.dropConverter instanceof ScriptField) &&
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 91c7ca76e..7592712e4 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -227,6 +227,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;
@@ -257,6 +258,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@undoBatch
@action
onDrop = async (e: React.DragEvent): Promise<void> => {
+ console.log("DROP STACKING");
const where = [e.clientX, e.clientY];
let targInd = -1;
this._docXfs.map((cd, i) => {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 2a9f903bb..c4680fc28 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -63,6 +63,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@undoBatch
columnDrop = action((e: Event, de: DragManager.DropEvent) => {
+ console.log("column drop stacking");
this._createAliasSelected = false;
if (de.complete.docDragData) {
const key = StrCast(this.props.parent.props.Document.sectionFilter);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8679c8bd1..f125403f9 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";
@@ -52,9 +52,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
private _childLayoutDisposer?: IReactionDisposer;
protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
- this.dropDisposer?.();
- this.gestureDisposer?.();
- this.multiTouchDisposer?.();
+ this.dropDisposer ?.();
+ this.gestureDisposer ?.();
+ this.multiTouchDisposer ?.();
if (ele) {
this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
@@ -66,7 +66,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
componentDidMount() {
- this._childLayoutDisposer = reaction(() => [this.childDocs, (Cast(this.props.Document.childLayout, Doc) as Doc)?.[Id]],
+ this._childLayoutDisposer = reaction(() => [this.childDocs, (Cast(this.props.Document.childLayout, Doc) as Doc) ?.[Id]],
(args) => {
const childLayout = Cast(this.props.Document.childLayout, Doc);
if (childLayout instanceof Doc) {
@@ -171,7 +171,6 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {
-
}
@undoBatch
@@ -194,7 +193,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
const movedDocs = docDragData.draggedDocuments;
added = movedDocs.reduce((added: boolean, d, i) =>
docDragData.droppedDocuments[i] !== d ? this.props.addDocument(docDragData.droppedDocuments[i]) :
- docDragData.moveDocument?.(d, this.props.Document, this.props.addDocument) || added, false);
+ docDragData.moveDocument ?.(d, this.props.Document, this.props.addDocument) || added, false);
} else {
added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
}
@@ -217,6 +216,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
const html = e.dataTransfer.getData("text/html");
const text = e.dataTransfer.getData("text/plain");
+ console.log(html);
if (text && text.startsWith("<div")) {
return;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index dab0ce08e..ab6ac0eaf 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -145,7 +145,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
- ContextMenu.Instance.clearItems();
+ ContextMenu.Instance && ContextMenu.Instance.clearItems();
if (index !== -1) {
value.splice(index, 1);
return true;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 073a30330..0811654bf 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -166,7 +166,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
this._viewSpecsOpen = true;
//@ts-ignore
- if (!e.target?.classList[0]?.startsWith("qs")) {
+ if (!e.target ?.classList[0] ?.startsWith("qs")) {
this.closeDatePicker();
}
@@ -315,7 +315,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
if (de.complete.docDragData && de.complete.docDragData.draggedDocuments.length) {
- this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData?.draggedDocuments || []));
+ this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData ?.draggedDocuments || []));
e.stopPropagation();
}
return true;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4b1bb09a9..d988ff241 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -81,7 +81,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private _layoutComputeReaction: IReactionDisposer | undefined;
private _layoutPoolData = new ObservableMap<string, any>();
- public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
+ public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title ?.toString() + ")"; } // this makes mobx trace() statements more descriptive
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@observable _clusterSets: (Doc[])[] = [];
@@ -250,7 +250,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
}
childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
- childLayouts.map(child => Doc.GetProto(child).clusterStr = child.cluster?.toString());
+ childLayouts.map(child => Doc.GetProto(child).clusterStr = child.cluster ?.toString());
}
}
@@ -429,9 +429,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
console.log("end");
if (this._inkToTextStartX && this._inkToTextStartY) {
const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
- const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === "text" && s.color);
+ const setDocs = this.getActiveDocuments().filter(s => s.proto ?.type === "text" && s.color);
const sets = setDocs.map((sd) => {
- return Cast(sd.data, RichTextField)?.Text as string;
+ return Cast(sd.data, RichTextField) ?.Text as string;
});
if (sets.length && sets[0]) {
this._wordPalette.clear();
@@ -505,7 +505,7 @@ 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;
@@ -814,8 +814,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } {
- const result = this.Document.arrangeScript?.script.run(params, console.log);
- if (result?.success) {
+ const result = this.Document.arrangeScript ?.script.run(params, console.log);
+ if (result ?.success) {
return { ...result, transition: "transform 1s" };
}
const layoutDoc = Doc.Layout(params.doc);
@@ -1045,7 +1045,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
@computed get placeholder() {
return <div className="collectionfreeformview-placeholder" style={{ background: this.Document.backgroundColor }}>
- <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span>
+ <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title ?.toString()}</span>
</div>;
}
@computed get marqueeView() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 765077c4d..9ce0324ed 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -453,6 +453,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(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/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index fbe9bf063..226103a53 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -90,4 +90,19 @@
width: 100%;
margin-right: 10px;
height: 100%;
+}
+
+.touch-iframe-overlay {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ pointer-events: all;
+
+ .indicator {
+ position: absolute;
+
+ &.active {
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a48dc286e..082a9a965 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,13 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
@observable private collapsed: boolean = true;
@observable private url: string = "";
+ private _longPressSecondsHack?: NodeJS.Timeout;
+ private _iframeRef = React.createRef<HTMLIFrameElement>();
+ private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
+ private _iframeDragRef = React.createRef<HTMLDivElement>();
+ @observable private _pressX: number = 0;
+ @observable private _pressY: number = 0;
+
componentDidMount() {
const field = Cast(this.props.Document[this.props.fieldKey], WebField);
@@ -49,6 +59,14 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
}
this.setURL();
+
+ document.addEventListener("pointerup", this.onLongPressUp);
+ document.addEventListener("pointermove", this.onLongPressMove);
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener("pointerup", this.onLongPressUp);
+ document.removeEventListener("pointermove", this.onLongPressMove);
}
@action
@@ -164,6 +182,78 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
}
}
+ onLongPressDown = (e: React.PointerEvent) => {
+ this._pressX = e.clientX;
+ this._pressY = e.clientY;
+
+ // find the pressed element in the iframe (currently only works if its an img)
+ let pressedElement: HTMLElement | undefined;
+ let pressedBound: ClientRect | undefined;
+ if (this._iframeRef.current) {
+ const B = this._iframeRef.current.getBoundingClientRect();
+ const iframeDoc = this._iframeRef.current.contentDocument;
+ if (B && iframeDoc) {
+ // TODO: this only works when scale = 1
+ const element = iframeDoc.elementFromPoint(this._pressX - B.left, this._pressY - B.top);
+ if (element && element.nodeName === "IMG") {
+ pressedBound = element.getBoundingClientRect();
+ pressedElement = element.cloneNode(true) as HTMLElement;
+ }
+ }
+ }
+
+ // mark the pressed element
+ if (pressedElement && pressedBound) {
+ console.log("clones b", pressedElement.getBoundingClientRect(), pressedBound);
+ if (this._iframeIndicatorRef.current) {
+ this._iframeIndicatorRef.current.style.top = pressedBound.top + "px";
+ this._iframeIndicatorRef.current.style.left = pressedBound.left + "px";
+ this._iframeIndicatorRef.current.style.width = pressedBound.width + "px";
+ this._iframeIndicatorRef.current.style.height = pressedBound.height + "px";
+ this._iframeIndicatorRef.current.classList.add("active");
+ }
+ }
+
+ // start dragging the pressed element if long pressed
+ this._longPressSecondsHack = setTimeout(() => {
+ if (pressedElement && pressedBound) {
+ e.stopPropagation();
+ e.preventDefault();
+ if (pressedElement.nodeName === "IMG") {
+ const src = pressedElement.getAttribute("src"); // TODO: may not always work
+ if (src) {
+ const doc = Docs.Create.ImageDocument(src, { width: 300 });
+ ImageUtils.ExtractExif(doc);
+
+ // add clone to div so that dragging ghost is placed properly
+ if (this._iframeDragRef.current) this._iframeDragRef.current.appendChild(pressedElement);
+
+ const dragData = new DragManager.DocumentDragData([doc]);
+ DragManager.StartDocumentDrag([pressedElement], dragData, this._pressX, this._pressY, { hideSource: true });
+ }
+ }
+ }
+ }, 1500);
+ }
+
+ onLongPressMove = (e: PointerEvent) => {
+ // this._pressX = e.clientX;
+ // this._pressY = e.clientY;
+ }
+
+ onLongPressUp = (e: PointerEvent) => {
+ if (this._longPressSecondsHack) {
+ clearTimeout(this._longPressSecondsHack);
+ }
+ if (this._iframeIndicatorRef.current) {
+ this._iframeIndicatorRef.current.classList.remove("active");
+ }
+ if (this._iframeDragRef.current) {
+ while (this._iframeDragRef.current.firstChild) this._iframeDragRef.current.removeChild(this._iframeDragRef.current.firstChild);
+ }
+ }
+
+
@computed
get content() {
const field = this.dataDoc[this.props.fieldKey];
@@ -171,9 +261,9 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
if (field instanceof HtmlField) {
view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
- view = <iframe src={Utils.CorsProxy(field.url.href)} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+ view = <iframe ref={this._iframeRef} src={Utils.CorsProxy(field.url.href)} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
} else {
- view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+ view = <iframe ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
}
const content =
<div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
@@ -181,15 +271,23 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
{view}
</div>;
- const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+ const decInteracting = DocumentDecorations.Instance && DocumentDecorations.Instance.Interacting;
+
+ const frozen = !this.props.isSelected() || decInteracting;
- const classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+ const classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !decInteracting ? "-interactive" : "");
return (
<>
<div className={classname} >
{content}
</div>
- {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
+ {!frozen ? (null) :
+ <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
+ <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
+ <div className="indicator" ref={this._iframeIndicatorRef}></div>
+ <div className="dragger" ref={this._iframeDragRef}></div>
+ </div>
+ </div>}
</>);
}
render() {