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/server/Websocket/Websocket.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/server/Websocket/Websocket.ts') 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/server/Websocket/Websocket.ts') 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 09da787bb56157ce6ec08f5e835249f8907ef13d Mon Sep 17 00:00:00 2001 From: Mohammad Amoush <47069173+mamoush34@users.noreply.github.com> Date: Thu, 23 Jan 2020 11:07:50 -0500 Subject: Server side coded added back --- src/server/Websocket/Websocket.ts | 56 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) (limited to 'src/server/Websocket/Websocket.ts') diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 6dda6956e..ff208d917 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, RoomMessage } from "../Message"; import { Client } from "../Client"; import { Socket } from "socket.io"; import { Database } from "../database"; @@ -17,6 +17,8 @@ export namespace WebSocket { const clients: { [key: string]: Client } = {}; export const socketMap = new Map(); export let disconnect: Function; + let endpoint: io.Server; + export async function start(isRelease: boolean) { await preliminaryFunctions(); @@ -25,10 +27,9 @@ export namespace WebSocket { async function preliminaryFunctions() { } - function initialize(isRelease: boolean) { - const endpoint = io(); - endpoint.on("connection", function(socket: Socket) { + endpoint = io(); + endpoint.on("connection", function (socket: Socket) { _socket = socket; socket.use((_packet, next) => { @@ -56,6 +57,10 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); + Utils.AddServerHandler(socket, MessageStore.NotifyRoommates, message => HandleRoommateNotification(socket, message)); + Utils.AddServerHandler(socket, MessageStore.HangUpCall, message => HandleHangUp(socket, message)); + Utils.AddRoomHandler(socket, "create or join", HandleCreateOrJoin); + disconnect = () => { socket.broadcast.emit("connection_terminated", Date.now()); @@ -68,6 +73,49 @@ export namespace WebSocket { logPort("websocket", socketPort); } + + function HandleRoommateNotification(socket: Socket, message: RoomMessage) { + //socket.broadcast.emit('message', message); + console.log("The room that sent this: ", message.room, " and message is : ", message.message); + endpoint.sockets.in(message.room).emit('message', message); + + } + + function HandleCreateOrJoin(socket: io.Socket, room: string) { + console.log("Received request to create or join room " + room); + + + let clientsInRoom = endpoint.sockets.adapter.rooms[room]; + let numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; + console.log('Room ' + room + ' now has ' + numClients + ' client(s)'); + + + if (numClients === 0) { + socket.join(room); + console.log('Client ID ' + socket.id + ' created room ' + room); + socket.emit('created', room, socket.id); + + } else if (numClients === 1) { + console.log('Client ID ' + socket.id + ' joined room ' + room); + endpoint.sockets.in(room).emit('join', room); + socket.join(room); + socket.emit('joined', room, socket.id); + endpoint.sockets.in(room).emit('ready'); + + } else { + socket.emit('full', room); + } + + + + + + } + + function HandleHangUp(socket: io.Socket, message: string) { + console.log("Receive bye from someone"); + } + function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) { const { ProjectCredentials } = GoogleCredentialsLoader; switch (query.type) { -- 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/server/Websocket/Websocket.ts') 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/server/Websocket/Websocket.ts') 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 d3db291c2587bf1284f9d0a8ee7d06fd8ec7a252 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush <47069173+mamoush34@users.noreply.github.com> Date: Fri, 7 Feb 2020 21:52:49 -0500 Subject: Another refactor with modules --- src/client/views/webcam/DashWebRTCVideo.scss | 31 +- src/client/views/webcam/DashWebRTCVideo.tsx | 575 +++++++++++++-------------- src/client/views/webcam/WebCamLogic.js | 268 +++++++++++++ src/server/Websocket/Websocket.ts | 51 ++- 4 files changed, 607 insertions(+), 318 deletions(-) create mode 100644 src/client/views/webcam/WebCamLogic.js (limited to 'src/server/Websocket/Websocket.ts') diff --git a/src/client/views/webcam/DashWebRTCVideo.scss b/src/client/views/webcam/DashWebRTCVideo.scss index ccb0a626b..262528d8e 100644 --- a/src/client/views/webcam/DashWebRTCVideo.scss +++ b/src/client/views/webcam/DashWebRTCVideo.scss @@ -27,14 +27,14 @@ border: 1px solid #BBBBBBBB; } - // #localVideo { - // width: 25%; - // height: 20%; - // position: absolute; - // top: 65%; - // z-index: 2; - // right: 5%; - // } + #localVideo { + width: 50%; + height: 50%; + position: relative; + // top: 65%; + // z-index: 2; + // right: 5%; + } .side { width: 25%; @@ -53,13 +53,12 @@ align-self: center; } - // #remoteVideo { - // position: absolute; - // width: 95%; - // height: 75%; - // top: 20%; - // align-self: center; - - // } + #remoteVideo { + position: relative; + width: 50%; + height: 50%; + // top: 20%; + // align-self: center; + } } \ No newline at end of file diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx index 290ca7a48..191f5750d 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -12,9 +12,7 @@ import { DocServer } from "../../DocServer"; import { DocumentView } from "../nodes/DocumentView"; import { Utils } from "../../../Utils"; import { MessageStore } from "../../../server/Message"; - - - +import { initialize } from "./WebCamLogic"; const mediaStreamConstraints = { video: true, @@ -24,358 +22,335 @@ const offerOptions = { offerToReceiveVideo: 1, }; - /** * This models the component that will be rendered, that can be used as a doc that will reflect the video cams. */ @observer export class DashWebRTCVideo extends React.Component { - @observable private localVideoEl: HTMLVideoElement | undefined; @observable private peerVideoEl: HTMLVideoElement | undefined; - private roomText: HTMLInputElement | undefined; - private roomOfCam: string = ""; - private isChannelReady = false; - private isInitiator = false; - private isStarted = false; - @observable remoteVideoAdded: boolean = false; - localStream: MediaStream | undefined; - private pc: any; - remoteStream: MediaStream | undefined; - private turnReady: boolean | undefined; - //localVideo: HTMLVideoElement | undefined; - //remoteVideo: HTMLVideoElement | undefined; - curRoom: string = ""; - - - private pcConfig = { - 'iceServers': [{ - 'urls': 'stun:stun.l.google.com:19302' - }] - }; - - // Set up audio and video regardless of what devices are present. - private sdpConstraints = { - offerToReceiveAudio: true, - offerToReceiveVideo: true - }; + // private roomText: HTMLInputElement | undefined; + // private roomOfCam: string = ""; + // private isChannelReady = false; + // private isInitiator = false; + // private isStarted = false; + // @observable remoteVideoAdded: boolean = false; + // localStream: MediaStream | undefined; + // private pc: any; + // remoteStream: MediaStream | undefined; + // private turnReady: boolean | undefined; + // //localVideo: HTMLVideoElement | undefined; + // //remoteVideo: HTMLVideoElement | undefined; + // curRoom: string = ""; + + // private pcConfig = { + // 'iceServers': [{ + // 'urls': 'stun:stun.l.google.com:19302' + // }] + // }; + + // // Set up audio and video regardless of what devices are present. + // private sdpConstraints = { + // offerToReceiveAudio: true, + // offerToReceiveVideo: true + // }; componentDidMount() { - DocumentDecorations.Instance.addCloseCall(this.closeConnection); - let self = this; - window.onbeforeunload = function () { - self.sendMessage('bye'); - }; + // DocumentDecorations.Instance.addCloseCall(this.closeConnection); + setTimeout(() => initialize(), 10000); + // let self = this; + // window.onbeforeunload = function () { + // self.sendMessage('bye'); + // }; } - closeConnection: CloseCall = () => { - this.hangup(); - } + // closeConnection: CloseCall = () => { + // this.hangup(); + // } - componentWillUnmount() { - } + // componentWillUnmount() { + // } - init(room: string) { + // init(room: string) { + + // this.curRoom = room; + // let self = this; + + // if (room !== '') { + // DocServer._socket.emit('create or join', room); + // console.log('Attempted to create or join room', room); + + // } + + // DocServer._socket.on('created', function (room: string) { + // console.log('Created room ' + room); + // self.isInitiator = true; + // }); + + // DocServer._socket.on('full', function (room: string) { + // console.log('Room ' + room + ' is full'); + // }); + + // DocServer._socket.on('join', function (room: string) { + // console.log('Another peer made a request to join room ' + room); + // console.log('This peer is the initiator of room ' + room + '!'); + // self.isChannelReady = true; + // }); + + + // DocServer._socket.on('joined', function (room: string) { + // console.log('joined: ' + room); + // self.isChannelReady = true; + // }); + + + // DocServer._socket.on('log', function (array: any) { + // console.log.apply(console, array); + // }); + + // // This client receives a message + // DocServer._socket.on('message', async function (message: any) { + // console.log('Client received message:', message); + // if (message.message === 'got user media') { + // self.maybeStart(); + // } else if (message.message.type === 'offer') { + // if (!self.isInitiator && !self.isStarted) { + // self.maybeStart(); + // } + // await self.pc.setRemoteDescription(new RTCSessionDescription(message.message)); + // self.doAnswer(); + // } else if (message.message.type === 'answer' && self.isStarted) { + // await self.pc.setRemoteDescription(new RTCSessionDescription(message.message)); + // } else if (message.message.type === 'candidate' && self.isStarted) { + // let candidate = new RTCIceCandidate({ + // sdpMLineIndex: message.message.label, + // candidate: message.message.candidate + // }); + // self.pc.addIceCandidate(candidate); + // } else if (message.message === 'bye' && self.isStarted) { + // self.handleRemoteHangup(); + // } + // }); + + // navigator.mediaDevices.getUserMedia({ + // audio: true, + // video: true + // }) + // .then(this.gotStream) + // .catch(function (e) { + // alert('getUserMedia() error: ' + e.name); + // }); + + // //Trying this one out!!! + // console.log('Getting user media with constraints', this.constraints); + + // if (location.hostname !== 'localhost') { + // this.requestTurn( + // 'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913' + // ); + // } - this.curRoom = room; - let self = this; - if (room !== '') { - DocServer._socket.emit('create or join', room); - console.log('Attempted to create or join room', room); + // } - } - DocServer._socket.on('created', function (room: string) { - console.log('Created room ' + room); - self.isInitiator = true; - }); - - DocServer._socket.on('full', function (room: string) { - console.log('Room ' + room + ' is full'); - }); - - DocServer._socket.on('join', function (room: string) { - console.log('Another peer made a request to join room ' + room); - console.log('This peer is the initiator of room ' + room + '!'); - self.isChannelReady = true; - }); - - - DocServer._socket.on('joined', function (room: string) { - console.log('joined: ' + room); - self.isChannelReady = true; - }); - - - DocServer._socket.on('log', function (array: any) { - console.log.apply(console, array); - }); - - // This client receives a message - DocServer._socket.on('message', function (message: any) { - console.log('Client received message:', message); - if (message.message === 'got user media') { - self.maybeStart(); - } else if (message.message.type === 'offer') { - if (!self.isInitiator && !self.isStarted) { - self.maybeStart(); - } - self.pc.setRemoteDescription(new RTCSessionDescription(message.message)); - self.doAnswer(); - } else if (message.message.type === 'answer' && self.isStarted) { - self.pc.setRemoteDescription(new RTCSessionDescription(message.message)); - } else if (message.message.type === 'candidate' && self.isStarted) { - let candidate = new RTCIceCandidate({ - sdpMLineIndex: message.message.label, - candidate: message.message.candidate - }); - self.pc.addIceCandidate(candidate); - } else if (message.message === 'bye' && self.isStarted) { - self.handleRemoteHangup(); - } - }); - - navigator.mediaDevices.getUserMedia({ - audio: true, - video: true - }) - .then(this.gotStream) - .catch(function (e) { - alert('getUserMedia() error: ' + e.name); - }); - - //Trying this one out!!! - console.log('Getting user media with constraints', this.constraints); - - if (location.hostname !== 'localhost') { - this.requestTurn( - 'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913' - ); - } + // private sendMessage = (message: any) => { + // console.log('Client sending message: ', message); + // Utils.Emit(DocServer._socket, MessageStore.NotifyRoommates, { message: message, room: this.curRoom }); + // //DocServer._socket.emit('message', message); + // } - } + // private gotStream = (stream: any) => { + // console.log('Adding local stream.'); + // this.localStream = stream; + // this.localVideoEl!.srcObject = stream; + // this.sendMessage('got user media'); + // if (this.isInitiator) { + // this.maybeStart(); + // } + // } - private sendMessage = (message: any) => { - console.log('Client sending message: ', message); - Utils.Emit(DocServer._socket, MessageStore.NotifyRoommates, { message: message, room: this.curRoom }); - //DocServer._socket.emit('message', message); - } + // constraints = { + // video: true, + // audio: true + // }; - // setVideoObjects(localVideo: HTMLVideoElement, remoteVideo: HTMLVideoElement) { - // this.localVideo = localVideo; - // this.remoteVideo = remoteVideo; + // private maybeStart = () => { + // console.log('>>>>>>> maybeStart() ', this.isStarted, this.localStream, this.isChannelReady); + // if (!this.isStarted && typeof this.localStream !== 'undefined' && this.isChannelReady) { + // console.log('>>>>>> creating peer connection'); + // this.createPeerConnection(); + // this.pc.addStream(this.localStream); + // this.isStarted = true; + // console.log('isInitiator', this.isInitiator); + // if (this.isInitiator) { + // this.doCall(); + // } + // } // } - // setLocalVideoObject(localVideoRef: HTMLVideoElement) { - // this.localVideo = localVideoRef; + // // //this will need to be changed to our version of hangUp + // // window.onbeforeunload = function () { + // // sendMessage('bye'); + // // }; + + // private createPeerConnection = () => { + // try { + // this.pc = new RTCPeerConnection(undefined); + // this.pc.onicecandidate = this.handleIceCandidate; + // this.pc.onaddstream = this.handleRemoteStreamAdded; + // this.pc.onremovestream = this.handleRemoteStreamRemoved; + // console.log('Created RTCPeerConnnection'); + // } catch (e) { + // console.log('Failed to create PeerConnection, exception: ' + e.message); + // alert('Cannot create RTCPeerConnection object.'); + // return; + // } // } - // setRemoteVideoObject(remoteVideoRef: HTMLVideoElement) { - // this.remoteVideo = remoteVideoRef; + // private handleIceCandidate = (event: any) => { + // console.log('icecandidate event: ', event); + // if (event.candidate) { + // this.sendMessage({ + // type: 'candidate', + // label: event.candidate.sdpMLineIndex, + // id: event.candidate.sdpMid, + // candidate: event.candidate.candidate + // }); + // } else { + // console.log('End of candidates.'); + // } // } + // private handleCreateOfferError = (event: any) => { + // console.log('createOffer() error: ', event); + // } + // private doCall = () => { + // console.log('Sending offer to peer'); + // this.pc.createOffer(this.setLocalAndSendMessage, this.handleCreateOfferError); + // } + // private doAnswer = () => { + // console.log('Sending answer to peer.'); + // this.pc.createAnswer().then( + // this.setLocalAndSendMessage, + // this.onCreateSessionDescriptionError + // ); + // } - private gotStream = (stream: any) => { - console.log('Adding local stream.'); - this.localStream = stream; - this.localVideoEl!.srcObject = stream; - this.sendMessage('got user media'); - if (this.isInitiator) { - this.maybeStart(); - } - } - - constraints = { - video: true, - audio: true - }; - - - - - - private maybeStart = () => { - console.log('>>>>>>> maybeStart() ', this.isStarted, this.localStream, this.isChannelReady); - if (!this.isStarted && typeof this.localStream !== 'undefined' && this.isChannelReady) { - console.log('>>>>>> creating peer connection'); - this.createPeerConnection(); - this.pc.addStream(this.localStream); - this.isStarted = true; - console.log('isInitiator', this.isInitiator); - if (this.isInitiator) { - this.doCall(); - } - } - } - - - // //this will need to be changed to our version of hangUp - // window.onbeforeunload = function () { - // sendMessage('bye'); - // }; - - private createPeerConnection = () => { - try { - this.pc = new RTCPeerConnection(undefined); - this.pc.onicecandidate = this.handleIceCandidate; - this.pc.onaddstream = this.handleRemoteStreamAdded; - this.pc.onremovestream = this.handleRemoteStreamRemoved; - console.log('Created RTCPeerConnnection'); - } catch (e) { - console.log('Failed to create PeerConnection, exception: ' + e.message); - alert('Cannot create RTCPeerConnection object.'); - return; - } - } - - private handleIceCandidate = (event: any) => { - console.log('icecandidate event: ', event); - if (event.candidate) { - this.sendMessage({ - type: 'candidate', - label: event.candidate.sdpMLineIndex, - id: event.candidate.sdpMid, - candidate: event.candidate.candidate - }); - } else { - console.log('End of candidates.'); - } - } - - private handleCreateOfferError = (event: any) => { - console.log('createOffer() error: ', event); - } - - private doCall = () => { - console.log('Sending offer to peer'); - this.pc.createOffer(this.setLocalAndSendMessage, this.handleCreateOfferError); - } - - private doAnswer = () => { - console.log('Sending answer to peer.'); - this.pc.createAnswer().then( - this.setLocalAndSendMessage, - this.onCreateSessionDescriptionError - ); - } - - private setLocalAndSendMessage = (sessionDescription: any) => { - this.pc.setLocalDescription(sessionDescription); - console.log('setLocalAndSendMessage sending message', sessionDescription); - this.sendMessage(sessionDescription); - } + // private setLocalAndSendMessage = (sessionDescription: any) => { + // this.pc.setLocalDescription(sessionDescription); + // console.log('setLocalAndSendMessage sending message', sessionDescription); + // this.sendMessage(sessionDescription); + // } - private onCreateSessionDescriptionError = (error: any) => { - console.log('Failed to create session description: ' + error.toString()); - } + // private onCreateSessionDescriptionError = (error: any) => { + // console.log('Failed to create session description: ' + error.toString()); + // } - private requestTurn = (turnURL: any) => { - var turnExists = false; - let self = this; - for (var i in this.pcConfig.iceServers) { - if (this.pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') { - turnExists = true; - this.turnReady = true; - break; - } - } - if (!turnExists) { - console.log('Getting TURN server from ', turnURL); - // No TURN server. Get one from computeengineondemand.appspot.com: - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4 && xhr.status === 200) { - var turnServer = JSON.parse(xhr.responseText); - console.log('Got TURN server: ', turnServer); - self.pcConfig.iceServers.push({ - 'urls': 'turn:' + turnServer.username + '@' + turnServer.turn, - //'credential': turnServer.password - }); - self.turnReady = true; - } - }; - xhr.open('GET', turnURL, true); - xhr.send(); - } - } - @action - private handleRemoteStreamAdded = (event: MediaStreamEvent) => { - console.log('Remote stream added.'); - this.remoteStream = event.stream!; - this.peerVideoEl!.srcObject = this.remoteStream; - this.remoteVideoAdded = true; - } + // private requestTurn = (turnURL: any) => { + // var turnExists = false; + // let self = this; + // for (var i in this.pcConfig.iceServers) { + // if (this.pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') { + // turnExists = true; + // this.turnReady = true; + // break; + // } + // } + // if (!turnExists) { + // console.log('Getting TURN server from ', turnURL); + // // No TURN server. Get one from computeengineondemand.appspot.com: + // var xhr = new XMLHttpRequest(); + // xhr.onreadystatechange = function () { + // if (xhr.readyState === 4 && xhr.status === 200) { + // var turnServer = JSON.parse(xhr.responseText); + // console.log('Got TURN server: ', turnServer); + // self.pcConfig.iceServers.push({ + // 'urls': 'turn:' + turnServer.username + '@' + turnServer.turn, + // //'credential': turnServer.password + // }); + // self.turnReady = true; + // } + // }; + // xhr.open('GET', turnURL, true); + // xhr.send(); + // } + // } + // @action + // private handleRemoteStreamAdded = (event: MediaStreamEvent) => { + // console.log('Remote stream added.'); + // this.remoteStream = event.stream!; + // this.peerVideoEl!.srcObject = this.remoteStream; + // this.remoteVideoAdded = true; + // } - private handleRemoteStreamRemoved = (event: MediaStreamEvent) => { - console.log('Remote stream removed. Event: ', event); - } + // private handleRemoteStreamRemoved = (event: MediaStreamEvent) => { + // console.log('Remote stream removed. Event: ', event); + // } - private hangup = () => { - console.log('Hanging up.'); - if (this.pc) { - stop(); - this.sendMessage('bye'); - } + // private hangup = () => { + // console.log('Hanging up.'); + // if (this.pc) { + // stop(); + // this.sendMessage('bye'); + // } - if (this.localStream) { - this.localStream.getTracks().forEach(track => track.stop()); - } + // if (this.localStream) { + // this.localStream.getTracks().forEach(track => track.stop()); + // } - } + // } - private handleRemoteHangup = () => { - console.log('Session terminated.'); - this.stop(); - this.isInitiator = false; + // private handleRemoteHangup = () => { + // console.log('Session terminated.'); + // this.stop(); + // this.isInitiator = false; - if (this.localStream) { - this.localStream.getTracks().forEach(track => track.stop()); - } + // if (this.localStream) { + // this.localStream.getTracks().forEach(track => track.stop()); + // } - } + // } - private stop = () => { - this.isStarted = false; - this.pc.close(); - this.pc = null; - } + // private stop = () => { + // this.isStarted = false; + // this.pc.close(); + // this.pc = null; + // } - /** - * Function that submits the title entered by user on enter press. - */ - private onEnterKeyDown = (e: React.KeyboardEvent) => { - if (e.keyCode === 13) { - let submittedTitle = this.roomText!.value; - this.roomText!.value = ""; - this.roomText!.blur(); - this.roomOfCam = submittedTitle; - this.init(submittedTitle); - } - } + // /** + // * Function that submits the title entered by user on enter press. + // */ + // private onEnterKeyDown = (e: React.KeyboardEvent) => { + // if (e.keyCode === 13) { + // let submittedTitle = this.roomText!.value; + // this.roomText!.value = ""; + // this.roomText!.blur(); + // this.roomOfCam = submittedTitle; + // this.init(submittedTitle); + // } + // } public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DashWebRTCVideo, fieldKey); } - - _ignore = 0; onPreWheel = (e: React.WheelEvent) => { this._ignore = e.timeStamp; @@ -394,17 +369,15 @@ export class DashWebRTCVideo extends React.Component
DashWebRTC
- this.roomText = e!} onKeyDown={this.onEnterKeyDown} /> -
; diff --git a/src/client/views/webcam/WebCamLogic.js b/src/client/views/webcam/WebCamLogic.js index ec8b2e8bf..a7af9c2c4 100644 --- a/src/client/views/webcam/WebCamLogic.js +++ b/src/client/views/webcam/WebCamLogic.js @@ -28,8 +28,6 @@ export function initialize(roomName, handlerUI) { ///////////////////////////////////////////// room = roomName; - // Could prompt for room name: - // room = prompt('Enter room name:'); socket = io.connect(`${window.location.protocol}//${window.location.hostname}:${4321}`); @@ -94,10 +92,6 @@ export function initialize(roomName, handlerUI) { var localVideo = document.querySelector('#localVideo'); var remoteVideo = document.querySelector('#remoteVideo'); - - console.log("Local Video: ", localVideo); - console.log("Remote Video: ", remoteVideo); - const gotStream = (stream) => { console.log('Adding local stream.'); localStream = stream; diff --git a/src/server/Message.ts b/src/server/Message.ts index 6ce5cd96a..79b6fa1e0 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -64,8 +64,6 @@ export namespace MessageStore { export const YoutubeApiQuery = new Message("Youtube Api Query"); export const DeleteField = new Message("Delete field"); export const DeleteFields = new Message("Delete fields"); - export const NotifyRoommates = new Message("message"); - export const HangUpCall = new Message("bye"); } diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index b4cd2dbe2..ba7ca8f35 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -19,7 +19,6 @@ export namespace WebSocket { const clients: { [key: string]: Client } = {}; export const socketMap = new Map(); export let disconnect: Function; - let endpoint: io.Server; export async function start(isRelease: boolean) { @@ -107,9 +106,6 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); - //Utils.AddServerHandler(socket, MessageStore.NotifyRoommates, message => HandleRoommateNotification(socket, message)); - //Utils.AddServerHandler(socket, MessageStore.HangUpCall, message => HandleHangUp(socket, message)); - //Utils.AddRoomHandler(socket, "create or join", HandleCreateOrJoin); disconnect = () => { socket.broadcast.emit("connection_terminated", Date.now()); @@ -122,49 +118,6 @@ export namespace WebSocket { logPort("websocket", socketPort); } - - function HandleRoommateNotification(socket: Socket, message: RoomMessage) { - //socket.broadcast.emit('message', message); - console.log("The room that sent this: ", message.room, " and message is : ", message.message); - endpoint.sockets.in(message.room).emit('message', message); - - } - - function HandleCreateOrJoin(socket: io.Socket, room: string) { - console.log("Received request to create or join room " + room); - - - let clientsInRoom = endpoint.sockets.adapter.rooms[room]; - let numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; - console.log('Room ' + room + ' now has ' + numClients + ' client(s)'); - - - if (numClients === 0) { - socket.join(room); - console.log('Client ID ' + socket.id + ' created room ' + room); - socket.emit('created', room, socket.id); - - } else if (numClients === 1) { - console.log('Client ID ' + socket.id + ' joined room ' + room); - endpoint.sockets.in(room).emit('join', room); - socket.join(room); - socket.emit('joined', room, socket.id); - endpoint.sockets.in(room).emit('ready'); - - } else { - socket.emit('full', room); - } - - - - - - } - - function HandleHangUp(socket: io.Socket, message: string) { - console.log("Receive bye from someone"); - } - function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) { const { ProjectCredentials } = GoogleCredentialsLoader; switch (query.type) { -- 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/server/Websocket/Websocket.ts') 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 2daa348eb760dfd322a271bf3f9f69ebb713a91c Mon Sep 17 00:00:00 2001 From: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> Date: Wed, 12 Feb 2020 04:36:31 -0500 Subject: switched importer to websocket for continual updates --- src/client/documents/Documents.ts | 52 +++++++++++++++-------------- src/scraping/buxton/final/BuxtonImporter.ts | 52 ++++++++++++++++++----------- src/server/ApiManagers/UtilManager.ts | 9 ----- src/server/Message.ts | 5 ++- src/server/Websocket/Websocket.ts | 7 ++++ 5 files changed, 70 insertions(+), 55 deletions(-) (limited to 'src/server/Websocket/Websocket.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b00a1a91d..cc18dc0a6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -55,6 +55,7 @@ import { InkingControl } from "../views/InkingControl"; import { RichTextField } from "../../new_fields/RichTextField"; import { Networking } from "../Network"; import { extname } from "path"; +import { MessageStore } from "../../server/Message"; const requestImageSize = require('../util/request-image-size'); const path = require('path'); @@ -346,6 +347,7 @@ export namespace Docs { export namespace Create { export function Buxton() { + let responded = false; const loading = new Doc; loading.title = "Please wait for the import script..."; const parent = TreeDocument([loading], { @@ -354,36 +356,36 @@ export namespace Docs { _height: 400, _LODdisable: true }); - Networking.FetchFromServer("/buxton").then(response => { - const devices = JSON.parse(response); - if (!Array.isArray(devices)) { - if ("error" in devices) { - loading.title = devices.error; - } else { - console.log(devices); - alert("The importer returned an unexpected import format. Check the console."); - } - return; + const parentProto = Doc.GetProto(parent); + const { _socket } = DocServer; + Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, errors }) => { + if (!responded) { + responded = true; + parentProto.data = new List(); } - const parentProto = Doc.GetProto(parent); - parentProto.data = new List(); - devices.forEach(device => { + if (device) { const { __images } = device; delete device.__images; const { ImageDocument, StackingDocument } = Docs.Create; - if (Array.isArray(__images)) { - const constructed = __images.map(relative => Utils.prepend(relative)); - const hero = constructed[0]; - constructed.splice(0, 1); - const deviceImages = constructed.map((url, i) => ImageDocument(url, { title: `image${i}.${extname(url)}` })); - const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); - const deviceProto = Doc.GetProto(doc); - deviceProto.hero = new ImageField(hero); - Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto); - Doc.AddDocToList(parentProto, "data", doc); - } - }); + const constructed = __images.map(relative => Utils.prepend(relative)); + const deviceImages = constructed.map((url, i) => ImageDocument(url, { title: `image${i}.${extname(url)}` })); + const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); + const deviceProto = Doc.GetProto(doc); + deviceProto.hero = new ImageField(constructed[0]); + Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto); + Doc.AddDocToList(parentProto, "data", doc); + } else if (errors) { + console.log(errors); + } else { + alert("A Buxton document import was completely empty (??)"); + } + }); + Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => { + _socket.off(MessageStore.BuxtonDocumentResult.Message); + _socket.off(MessageStore.BuxtonImportComplete.Message); + alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}.`); }); + Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); return parent; } diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts index 3d7421e90..d9d48d68c 100644 --- a/src/scraping/buxton/final/BuxtonImporter.ts +++ b/src/scraping/buxton/final/BuxtonImporter.ts @@ -8,7 +8,6 @@ const StreamZip = require('node-stream-zip'); const createImageSizeStream = require("image-size-stream"); import { parseXml } from "libxmljs"; import { strictEqual } from "assert"; -import { BatchedArray, TimeUnit } from "array-batcher"; interface DocumentContents { body: string; @@ -24,21 +23,33 @@ export interface DeviceDocument { longDescription: string; company: string; year: number; - originalPrice: number; - degreesOfFreedom: number; + originalPrice?: number; + degreesOfFreedom?: number; dimensions?: string; primaryKey: string; secondaryKey: string; attribute: string; + __images: string[]; + hyperlinks: string[]; + captions: string[]; + embeddedFileNames: string[]; } -interface AnalysisResult { +export interface AnalysisResult { device?: DeviceDocument; - errors?: any; + errors?: { [key: string]: string }; } type Transformer = (raw: string) => { transformed?: T, error?: string }; +export interface ImportResults { + deviceCount: number, + errorCount: number +} + +type ResultCallback = (result: AnalysisResult) => void; +type TerminatorCallback = (result: ImportResults) => void; + interface Processor { exp: RegExp; matchIndex?: number; @@ -168,7 +179,7 @@ const successOut = "buxton.json"; const failOut = "incomplete.json"; const deviceKeys = Array.from(RegexMap.keys()); -export default async function executeImport() { +export default async function executeImport(emitter: ResultCallback, terminator: TerminatorCallback) { try { const contents = readdirSync(sourceDir); const wordDocuments = contents.filter(file => /.*\.docx?$/.test(file)).map(file => `${sourceDir}/${file}`); @@ -176,7 +187,7 @@ export default async function executeImport() { rimraf.sync(dir); mkdirSync(dir); }); - return parseFiles(wordDocuments); + return parseFiles(wordDocuments, emitter, terminator); } catch (e) { const message = [ "Unable to find a source directory.", @@ -188,23 +199,22 @@ export default async function executeImport() { } } -async function parseFiles(wordDocuments: string[]): Promise { - const imported = await BatchedArray.from(wordDocuments, { batchSize: 8 }).batchedMapPatientInterval<{ fileName: string, contents: DocumentContents }>({ magnitude: 10, unit: TimeUnit.Seconds }, async (batch, collector) => { - for (const filePath of batch) { - const fileName = path.basename(filePath).replace("Bill_Notes_", ""); - console.log(cyan(`\nExtracting contents from ${fileName}...`)); - collector.push({ fileName, contents: await extractFileContents(filePath) }); - } - }); - console.log(yellow("\nAnalyzing the extracted document text...\n")); - const results = imported.map(({ fileName, contents }) => analyze(fileName, contents)); +async function parseFiles(wordDocuments: string[], emitter: ResultCallback, terminator: TerminatorCallback): Promise { + const results: AnalysisResult[] = []; + for (const filePath of wordDocuments) { + const fileName = path.basename(filePath).replace("Bill_Notes_", ""); + console.log(cyan(`\nExtracting contents from ${fileName}...`)); + const result = analyze(fileName, await extractFileContents(filePath)); + emitter(result); + results.push(result); + } const masterDevices: DeviceDocument[] = []; - const masterErrors: any[] = []; + const masterErrors: { [key: string]: string }[] = []; results.forEach(({ device, errors }) => { if (device) { masterDevices.push(device); - } else { + } else if (errors) { masterErrors.push(errors); } }); @@ -219,6 +229,8 @@ async function parseFiles(wordDocuments: string[]): Promise { await writeOutputFile(failOut, masterErrors, total, false); console.log(); + terminator({ deviceCount: masterDevices.length, errorCount: masterErrors.length }); + return masterDevices; } @@ -311,7 +323,7 @@ function analyze(fileName: string, contents: DocumentContents): AnalysisResult { embeddedFileNames, __images: imageUrls }; - const errors: any = { fileName }; + const errors: { [key: string]: string } = { fileName }; for (const key of deviceKeys) { const { exp, transformer, matchIndex, required } = RegexMap.get(key)!; diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index 5aac8261e..8adc3da81 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -39,15 +39,6 @@ export default class UtilManager extends ApiManager { } }); - register({ - method: Method.GET, - subscription: "/buxton", - secureHandler: async ({ req, res }) => { - req.setTimeout(300000); - res.send(await executeImport()); - } - }); - register({ method: Method.GET, subscription: "/version", diff --git a/src/server/Message.ts b/src/server/Message.ts index 79b6fa1e0..2a03e2311 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -1,4 +1,5 @@ import { Utils } from "../Utils"; +import { AnalysisResult, ImportResults } from "../scraping/buxton/final/BuxtonImporter"; export class Message { private _name: string; @@ -56,6 +57,9 @@ 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 BeginBuxtonImport = new Message("Begin Buxton Import"); + export const BuxtonDocumentResult = new Message("Buxton Document Result"); + export const BuxtonImportComplete = new Message("Buxton Import Complete"); export const GetRefField = new Message("Get Ref Field"); export const GetRefFields = new Message("Get Ref Fields"); @@ -65,5 +69,4 @@ export namespace MessageStore { export const DeleteField = new Message("Delete field"); export const DeleteFields = new Message("Delete fields"); - } diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index ba7ca8f35..724221be1 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -12,6 +12,7 @@ import { timeMap } from "../ApiManagers/UserManager"; import { green } from "colors"; import { networkInterfaces, type } from "os"; import { object } from "serializr"; +import executeImport from "../../scraping/buxton/final/BuxtonImporter"; export namespace WebSocket { @@ -106,6 +107,12 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); + Utils.AddServerHandler(socket, MessageStore.BeginBuxtonImport, () => { + executeImport( + deviceOrError => Utils.Emit(socket, MessageStore.BuxtonDocumentResult, deviceOrError), + results => Utils.Emit(socket, MessageStore.BuxtonImportComplete, results) + ); + }); disconnect = () => { socket.broadcast.emit("connection_terminated", Date.now()); -- cgit v1.2.3-70-g09d2 From 44808c5ffc4ed9d3859dfd28cd59f7f3b3621cfc Mon Sep 17 00:00:00 2001 From: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> Date: Wed, 12 Feb 2020 04:37:44 -0500 Subject: one line cleanup --- src/server/Websocket/Websocket.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'src/server/Websocket/Websocket.ts') diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 724221be1..6ee571395 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -11,7 +11,6 @@ import { logPort } from "../ActionUtilities"; import { timeMap } from "../ApiManagers/UserManager"; import { green } from "colors"; import { networkInterfaces, type } from "os"; -import { object } from "serializr"; import executeImport from "../../scraping/buxton/final/BuxtonImporter"; export namespace WebSocket { -- cgit v1.2.3-70-g09d2 From 4859d4e3f5827c3341d43d19a5309d2ec25ab67b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Apr 2020 17:15:14 -0400 Subject: fixed warnings and some compile errors. Made a key value layout and extened DocumentBox to have a childLayoutKey field --- src/client/ClientRecommender.tsx | 54 +++++----- src/client/cognitive_services/CognitiveServices.ts | 36 +++---- src/client/documents/Documents.ts | 3 +- src/client/util/RichTextSchema.tsx | 18 ++-- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/EditableView.tsx | 22 ++-- src/client/views/GestureOverlay.tsx | 8 +- src/client/views/GlobalKeyHandler.ts | 4 +- src/client/views/MainView.tsx | 2 + src/client/views/RecommendationsBox.tsx | 16 +-- src/client/views/TemplateMenu.tsx | 8 +- src/client/views/TouchScrollableMenu.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 2 +- .../views/collections/CollectionSchemaCells.tsx | 2 + .../views/collections/CollectionSchemaView.tsx | 8 +- .../views/collections/CollectionStackingView.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 3 +- .../views/collections/CollectionStaffView.tsx | 12 +-- src/client/views/collections/CollectionSubView.tsx | 3 +- .../views/collections/CollectionTimeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../views/collections/CollectionViewChromes.tsx | 10 +- .../CollectionFreeFormLinksView.tsx | 116 ++++++++++----------- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- src/client/views/nodes/ButtonBox.tsx | 2 +- .../views/nodes/ContentFittingDocumentView.tsx | 2 + src/client/views/nodes/DocumentBox.tsx | 3 +- src/client/views/nodes/DocumentContentsView.tsx | 24 +++-- src/client/views/nodes/KeyValuePair.tsx | 2 + src/client/views/nodes/RadialMenuItem.tsx | 30 +++--- src/client/views/nodes/ScreenshotBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 2 +- src/client/views/webcam/DashWebRTCVideo.tsx | 8 +- src/mobile/MobileInterface.tsx | 2 + src/new_fields/Doc.ts | 4 +- src/new_fields/util.ts | 2 +- .../Session/agents/applied_session_agent.ts | 2 +- src/server/DashSession/Session/agents/monitor.ts | 2 +- .../Session/agents/promisified_ipc_manager.ts | 6 +- src/server/Websocket/Websocket.ts | 8 +- src/server/database.ts | 2 +- src/server/server_Initialization.ts | 2 +- 43 files changed, 232 insertions(+), 218 deletions(-) (limited to 'src/server/Websocket/Websocket.ts') diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx index 0e67a6e57..537e331ab 100644 --- a/src/client/ClientRecommender.tsx +++ b/src/client/ClientRecommender.tsx @@ -69,7 +69,7 @@ export class ClientRecommender extends React.Component { */ private distance(vector1: number[], vector2: number[], metric: string = "cosine") { - assert(vector1.length === vector2.length, "Vectors are not the same length"); + // assert(vector1.length === vector2.length, "Vectors are not the same length"); let similarity: number; switch (metric) { case "cosine": @@ -113,7 +113,7 @@ export class ClientRecommender extends React.Component { } } ); - let doclist = Array.from(ClientRecommender.Instance.docVectors); + const doclist = Array.from(ClientRecommender.Instance.docVectors); if (distance_metric === "euclidian") { doclist.sort((a: RecommenderDocument, b: RecommenderDocument) => a.score - b.score); } @@ -169,12 +169,12 @@ export class ClientRecommender extends React.Component { */ generateMetadata = async (dataDoc: Doc, extDoc: Doc, threshold: Confidence = Confidence.Excellent) => { - let converter = (results: any) => { - let tagDoc = new Doc; - let tagsList = new List(); + const converter = (results: any) => { + const tagDoc = new Doc; + const tagsList = new List(); results.tags.map((tag: Tag) => { tagsList.push(tag.name); - let sanitized = tag.name.replace(" ", "_"); + const sanitized = tag.name.replace(" ", "_"); tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`); }); extDoc.generatedTags = tagsList; @@ -193,7 +193,7 @@ export class ClientRecommender extends React.Component { */ private url(dataDoc: Doc) { - let data = Cast(Doc.GetProto(dataDoc)[fieldkey], ImageField); + const data = Cast(Doc.GetProto(dataDoc)[fieldkey], ImageField); return data ? data.url.href : undefined; } @@ -215,14 +215,14 @@ export class ClientRecommender extends React.Component { } } else { - let fielddata = Cast(dataDoc.data, RichTextField); - fielddata ? data = fielddata[ToPlainText]() : data = ""; + const fielddata = Cast(dataDoc.data, RichTextField, null); + data = fielddata?.Text || ""; } // STEP 2. Upon receiving response from Text Cognitive Services, do additional processing on keywords. // Currently we are still using Cognitive Services for internal recommendations, but in the future this might not be necessary. - let converter = async (results: any, data: string, isImage: boolean = false) => { + const converter = async (results: any, data: string, isImage: boolean = false) => { let keyterms = new List(); // raw keywords let kp_string: string = ""; // keywords*frequency concatenated into a string. input into TF let highKP: string[] = [""]; // most frequent keyphrase @@ -237,7 +237,7 @@ export class ClientRecommender extends React.Component { } else { // text processing results.documents.forEach((doc: any) => { - let keyPhrases = doc.keyPhrases; // returned by Cognitive Services + const keyPhrases = doc.keyPhrases; // returned by Cognitive Services keyPhrases.map((kp: string) => { keyterms.push(kp); const frequency = this.countFrequencies(kp, data); // frequency of keyphrase in paragraph @@ -308,10 +308,10 @@ export class ClientRecommender extends React.Component { */ private countFrequencies(keyphrase: string, paragraph: string) { - let data = paragraph.split(/ |\n/); // splits by new lines and spaces - let kp_array = keyphrase.split(" "); - let num_keywords = kp_array.length; - let par_length = data.length; + const data = paragraph.split(/ |\n/); // splits by new lines and spaces + const kp_array = keyphrase.split(" "); + const num_keywords = kp_array.length; + const par_length = data.length; let frequency = 0; // slides keyphrase windows across paragraph and checks if it matches with corresponding paragraph slice for (let i = 0; i <= par_length - num_keywords; i++) { @@ -353,8 +353,8 @@ export class ClientRecommender extends React.Component { bingWebSearch = async (query: string) => { const converter = async (results: any) => { - let title_vals: string[] = []; - let url_vals: string[] = []; + const title_vals: string[] = []; + const url_vals: string[] = []; results.webPages.value.forEach((doc: any) => { title_vals.push(doc.name); url_vals.push(doc.url); @@ -369,23 +369,23 @@ export class ClientRecommender extends React.Component { */ arxivrequest = async (query: string) => { - let xhttp = new XMLHttpRequest(); - let serveraddress = "http://export.arxiv.org/api"; + const xhttp = new XMLHttpRequest(); + const serveraddress = "http://export.arxiv.org/api"; const maxresults = 5; - let endpoint = serveraddress + "/query?search_query=all:" + query + "&start=0&max_results=" + maxresults.toString(); - let promisified = (resolve: any, reject: any) => { + const endpoint = serveraddress + "/query?search_query=all:" + query + "&start=0&max_results=" + maxresults.toString(); + const promisified = (resolve: any, reject: any) => { xhttp.onreadystatechange = function () { if (this.readyState === 4) { - let result = xhttp.response; - let xml = xhttp.responseXML; + const result = xhttp.response; + const xml = xhttp.responseXML; console.log("arXiv Result: ", xml); switch (this.status) { case 200: - let title_vals: string[] = []; - let url_vals: string[] = []; + const title_vals: string[] = []; + const url_vals: string[] = []; //console.log(result); if (xml) { - let titles = xml.getElementsByTagName("title"); + const titles = xml.getElementsByTagName("title"); let counter = 1; if (titles && titles.length > 1) { while (counter <= maxresults) { @@ -394,7 +394,7 @@ export class ClientRecommender extends React.Component { counter++; } } - let ids = xml.getElementsByTagName("id"); + const ids = xml.getElementsByTagName("id"); counter = 1; if (ids && ids.length > 1) { while (counter <= maxresults) { diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 542ccf04d..3f3726621 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -208,7 +208,7 @@ export namespace CognitiveServices { results.recognitionUnits && (results = results.recognitionUnits); } return results; - } + }; } export interface AzureStrokeData { @@ -232,13 +232,13 @@ export namespace CognitiveServices { return data; }, requester: async (apiKey: string, query: string) => { - let xhttp = new XMLHttpRequest(); - let serverAddress = "https://api.cognitive.microsoft.com"; - let endpoint = serverAddress + '/bing/v5.0/search?q=' + encodeURIComponent(query); - let promisified = (resolve: any, reject: any) => { + const xhttp = new XMLHttpRequest(); + const serverAddress = "https://api.cognitive.microsoft.com"; + const endpoint = serverAddress + '/bing/v5.0/search?q=' + encodeURIComponent(query); + const promisified = (resolve: any, reject: any) => { xhttp.onreadystatechange = function () { if (this.readyState === 4) { - let result = xhttp.responseText; + const result = xhttp.responseText; switch (this.status) { case 200: return resolve(result); @@ -266,7 +266,7 @@ export namespace CognitiveServices { export namespace Appliers { export const analyzer = async (query: string, converter: BingConverter) => { - let results = await ExecuteQuery(Service.Bing, Manager, query); + const results = await ExecuteQuery(Service.Bing, Manager, query); console.log("Bing results: ", results); const { title_vals, url_vals } = await converter(results); return { title_vals, url_vals }; @@ -281,13 +281,13 @@ export namespace CognitiveServices { return data; }, requester: async (apiKey: string, query: string) => { - let xhttp = new XMLHttpRequest(); - let serverAddress = "https://babel.hathitrust.org/cgi/htd/​"; - let endpoint = serverAddress + '/bing/v5.0/search?q=' + encodeURIComponent(query); - let promisified = (resolve: any, reject: any) => { + const xhttp = new XMLHttpRequest(); + const serverAddress = "https://babel.hathitrust.org/cgi/htd/​"; + const endpoint = serverAddress + '/bing/v5.0/search?q=' + encodeURIComponent(query); + const promisified = (resolve: any, reject: any) => { xhttp.onreadystatechange = function () { if (this.readyState === 4) { - let result = xhttp.responseText; + const result = xhttp.responseText; switch (this.status) { case 200: return resolve(result); @@ -315,7 +315,7 @@ export namespace CognitiveServices { export namespace Appliers { export const analyzer = async (query: string, converter: BingConverter) => { - let results = await ExecuteQuery(Service.Bing, Manager, query); + const results = await ExecuteQuery(Service.Bing, Manager, query); console.log("Bing results: ", results); const { title_vals, url_vals } = await converter(results); return { title_vals, url_vals }; @@ -337,9 +337,9 @@ export namespace CognitiveServices { }); }, requester: async (apiKey: string, body: string, service: Service) => { - let serverAddress = "https://eastus.api.cognitive.microsoft.com"; - let endpoint = serverAddress + "/text/analytics/v2.1/keyPhrases"; - let sampleBody = { + const serverAddress = "https://eastus.api.cognitive.microsoft.com"; + const endpoint = serverAddress + "/text/analytics/v2.1/keyPhrases"; + const sampleBody = { "documents": [ { "language": "en", @@ -348,7 +348,7 @@ export namespace CognitiveServices { } ] }; - let actualBody = body; + const actualBody = body; const options = { uri: endpoint, body: actualBody, @@ -368,7 +368,7 @@ export namespace CognitiveServices { console.log("vectorizing..."); //keyterms = ["father", "king"]; - let args = { method: 'POST', uri: Utils.prepend("/recommender"), body: { keyphrases: keyterms }, json: true }; + const args = { method: 'POST', uri: Utils.prepend("/recommender"), body: { keyphrases: keyterms }, json: true }; await requestPromise.post(args).then(async (wordvecs) => { if (wordvecs) { const indices = Object.keys(wordvecs); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dbea8062e..96425ba30 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -266,7 +266,7 @@ export namespace Docs { options: { _width: 40, _height: 40, borderRounding: "100%" }, }], [DocumentType.RECOMMENDATION, { - layout: { view: RecommendationsBox }, + layout: { view: RecommendationsBox, dataField: data }, options: { width: 200, height: 200 }, }], [DocumentType.WEBCAM, { @@ -365,6 +365,7 @@ export namespace Docs { const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; options.layout = layout.view.LayoutString(layout.dataField); const doc = Doc.assign(new Doc(prototypeId, true), { layoutKey: "layout", ...options }); + doc.layout_keyValue = KeyValueBox.LayoutString(""); return doc; } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a2fb7c11b..81ab95ff5 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -810,7 +810,7 @@ export class DashDocView { this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px"; this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray")); }, { fireImmediately: true }); - let doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => { + const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => { ReactDOM.unmountComponentAtNode(this._dashSpan); ReactDOM.render( { if (!Doc.AreProtosEqual(finalLayout, dashDoc)) { @@ -939,7 +939,7 @@ export class DashFieldView { this._fieldCheck.style.backgroundColor = "rgba(155, 155, 155, 0.24)"; this._fieldCheck.onchange = function (e: any) { self._dashDoc![self._fieldKey] = e.target.checked; - } + }; this._fieldSpan = document.createElement("div"); this._fieldSpan.id = Utils.GenerateGuid(); @@ -1095,12 +1095,12 @@ export class FootnoteView { "Mod-y": () => redo(this.outerView.state, this.outerView.dispatch), "Mod-b": toggleMark(schema.marks.strong) }), - new Plugin({ - view(newView) { - // TODO -- make this work with RichTextMenu - // return FormattedTextBox.getToolTip(newView); - } - }) + // new Plugin({ + // view(newView) { + // // TODO -- make this work with RichTextMenu + // // return FormattedTextBox.getToolTip(newView); + // } + // }) ], }), diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index e673189f9..c6fb9ad0b 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -39,7 +39,7 @@ library.add(faCheckCircle); library.add(faCloudUploadAlt); library.add(faSyncAlt); library.add(faShare); -library.add(faPhotoVideo) +library.add(faPhotoVideo); const cloud: IconProp = "cloud-upload-alt"; const fetch: IconProp = "sync-alt"; diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 4a27425e8..2219966e5 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -4,10 +4,7 @@ import { observer } from 'mobx-react'; import * as Autosuggest from 'react-autosuggest'; import { ObjectField } from '../../new_fields/ObjectField'; import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField'; -import { ContextMenu } from './ContextMenu'; -import { ContextMenuProps } from './ContextMenuItem'; import "./EditableView.scss"; -import { CollectionTreeView } from './collections/CollectionTreeView'; export interface EditableProps { /** @@ -88,12 +85,12 @@ export class EditableView extends React.Component { onKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Tab") { e.stopPropagation(); - this.finalizeEdit(e.currentTarget.value, e.shiftKey); + this.finalizeEdit(e.currentTarget.value, e.shiftKey, false); this.props.OnTab && this.props.OnTab(e.shiftKey); } else if (e.key === "Enter") { e.stopPropagation(); if (!e.ctrlKey) { - this.finalizeEdit(e.currentTarget.value, e.shiftKey); + this.finalizeEdit(e.currentTarget.value, e.shiftKey, false); } else if (this.props.OnFillDown) { this.props.OnFillDown(e.currentTarget.value); this._editing = false; @@ -123,10 +120,17 @@ export class EditableView extends React.Component { } @action - private finalizeEdit(value: string, shiftDown: boolean) { - this._editing = false; + private finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean) { if (this.props.SetValue(value, shiftDown)) { + this._editing = false; + this.props.isEditingCallback?.(false); + } else { + this._editing = false; this.props.isEditingCallback?.(false); + !lostFocus && setTimeout(action(() => { + this._editing = true; + this.props.isEditingCallback?.(true); + }), 0); } } @@ -151,7 +155,7 @@ export class EditableView extends React.Component { className: "editableView-input", onKeyDown: this.onKeyDown, autoFocus: true, - onBlur: e => this.finalizeEdit(e.currentTarget.value, false), + onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true), onPointerDown: this.stopPropagation, onClick: this.stopPropagation, onPointerUp: this.stopPropagation, @@ -163,7 +167,7 @@ export class EditableView extends React.Component { defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus={true} - onBlur={e => this.finalizeEdit(e.currentTarget.value, false)} + onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} style={{ display: this.props.display, fontSize: this.props.fontSize }} />; diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 1eff58948..ea60907f6 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -165,7 +165,7 @@ export default class GestureOverlay extends Touchable { this._holdTimer = setTimeout(() => { console.log("hold"); const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); - let pt: any = te.touches[te.touches.length - 1]; + const pt: any = te.touches[te.touches.length - 1]; if (nts.nt.length === 1 && pt.radiusX > 1 && pt.radiusY > 1) { target?.dispatchEvent( new CustomEvent>("dashOnTouchHoldStart", @@ -589,7 +589,7 @@ export default class GestureOverlay extends Touchable { for (const wR of wordResults) { console.log(wR); if (wR?.recognizedText) { - possibilities.push(wR?.recognizedText) + possibilities.push(wR?.recognizedText); } possibilities.push(...wR?.alternates?.map((a: any) => a.recognizedString)); } @@ -743,16 +743,16 @@ export default class GestureOverlay extends Touchable { {this.elements}
{this._clipboardDoc}
{ - let stopPropagation = false; - let preventDefault = false; + const stopPropagation = false; + const preventDefault = false; switch (keyname) { // case "~": diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a81e0cc2c..8d9be5980 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -521,7 +521,9 @@ export class MainView extends React.Component { DataDoc={undefined} LibraryPath={emptyPath} fieldKey={"data"} + dropAction={"alias"} annotationsKey={""} + bringToFront={emptyFunction} select={emptyFunction} active={returnFalse} isSelected={returnFalse} diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx index 262226bac..5ebba0abb 100644 --- a/src/client/views/RecommendationsBox.tsx +++ b/src/client/views/RecommendationsBox.tsx @@ -6,7 +6,7 @@ import "./RecommendationsBox.scss"; import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc"; import { DocumentIcon } from "./nodes/DocumentIcon"; import { StrCast, NumCast } from "../../new_fields/Types"; -import { returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../Utils"; +import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath } from "../../Utils"; import { Transform } from "../util/Transform"; import { ObjectField } from "../../new_fields/ObjectField"; import { DocumentView } from "./nodes/DocumentView"; @@ -31,7 +31,7 @@ library.add(faBullseye, faLink); @observer export class RecommendationsBox extends React.Component { - public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(RecommendationsBox, fieldKey); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(RecommendationsBox, fieldKey); } // @observable private _display: boolean = false; @observable private _pageX: number = 0; @@ -48,17 +48,17 @@ export class RecommendationsBox extends React.Component { @action private DocumentIcon(doc: Doc) { - let layoutresult = StrCast(doc.type); + const layoutresult = StrCast(doc.type); let renderDoc = doc; //let box: number[] = []; if (layoutresult.indexOf(DocumentType.COL) !== -1) { renderDoc = Doc.MakeDelegate(renderDoc); } - let returnXDimension = () => 150; - let returnYDimension = () => 150; - let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension()); + const returnXDimension = () => 150; + const returnYDimension = () => 150; + const scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension()); //let scale = () => 1; - let newRenderDoc = Doc.MakeAlias(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt + const newRenderDoc = Doc.MakeAlias(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt newRenderDoc.height = NumCast(this.props.Document.documentIconHeight); newRenderDoc.autoHeight = false; const docview =
@@ -66,8 +66,8 @@ export class RecommendationsBox extends React.Component { fitToBox={StrCast(doc.type).indexOf(DocumentType.COL) !== -1} Document={newRenderDoc} addDocument={returnFalse} + LibraryPath={emptyPath} removeDocument={returnFalse} - ruleProvider={undefined} ScreenToLocalTransform={Transform.Identity} addDocTab={returnFalse} pinToPres={returnFalse} diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 83dbd3db3..996928cca 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -114,7 +114,7 @@ export class TemplateMenu extends React.Component { const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", ""); const noteTypesDoc = Cast(Doc.UserDoc().noteTypes, Doc, null); const noteTypes = DocListCast(noteTypesDoc?.data); - const addedTypes = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data) + const addedTypes = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data); const layout = Doc.Layout(firstDoc); const templateMenu: Array = []; this.props.templates.forEach((checked, template) => @@ -170,8 +170,8 @@ Scripting.addGlobal(function switchView(doc: Doc, template: Doc) { if (template.dragFactory) { template = Cast(template.dragFactory, Doc, null); } - let templateTitle = StrCast(template?.title); - return templateTitle && DocumentView.makeCustomViewClicked(doc, undefined, Docs.Create.FreeformDocument, templateTitle, template) + const templateTitle = StrCast(template?.title); + return templateTitle && DocumentView.makeCustomViewClicked(doc, undefined, Docs.Create.FreeformDocument, templateTitle, template); }); Scripting.addGlobal(function templateIsUsed(templateDoc: Doc, firstDocTitlte: string) { @@ -180,4 +180,4 @@ Scripting.addGlobal(function templateIsUsed(templateDoc: Doc, firstDocTitlte: st const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title); return StrCast(firstDoc.layoutKey) === "layout_" + template ? 'check' : 'unchecked'; // return SelectionManager.SelectedDocuments().some(view => StrCast(view.props.Document.layoutKey) === "layout_" + template) ? 'check' : 'unchecked' -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/src/client/views/TouchScrollableMenu.tsx b/src/client/views/TouchScrollableMenu.tsx index 4bda0818e..969605be9 100644 --- a/src/client/views/TouchScrollableMenu.tsx +++ b/src/client/views/TouchScrollableMenu.tsx @@ -44,7 +44,7 @@ export default class TouchScrollableMenu extends React.Component
- ) + ); } } @@ -54,6 +54,6 @@ export class TouchScrollableMenuItem extends React.Component {this.props.text}
- ) + ); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 2ee39bc0d..4e1e76f39 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -513,7 +513,7 @@ export class CollectionDockingView extends React.Component ((view: Opt) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)), (views) => { diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 79b5d7bb7..f124fe21b 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -157,6 +157,8 @@ export class CollectionSchemaCell extends React.Component { Document: this.props.rowProps.original, DataDoc: this.props.rowProps.original, LibraryPath: [], + dropAction: "alias", + bringToFront: emptyFunction, fieldKey: this.props.rowProps.column.id as string, ContainingCollectionView: this.props.CollectionView, ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document, diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index a4502cced..981438513 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -51,8 +51,7 @@ const columnTypes: Map = new Map([ @observer export class CollectionSchemaView extends CollectionSubView(doc => doc) { - private _mainCont?: HTMLDivElement; - private _startPreviewWidth = 0; + private _previewCont?: HTMLDivElement; private DIVIDER_WIDTH = 4; @observable previewDoc: Doc | undefined = undefined; @@ -64,7 +63,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } private createTarget = (ele: HTMLDivElement) => { - this._mainCont = ele; + this._previewCont = ele; super.CreateDropTarget(ele); } @@ -81,12 +80,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } onDividerDown = (e: React.PointerEvent) => { - this._startPreviewWidth = this.previewWidth(); setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, action(() => this.toggleExpander())); } @action onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => { - const nativeWidth = this._mainCont!.getBoundingClientRect(); + const nativeWidth = this._previewCont!.getBoundingClientRect(); const minWidth = 40; const maxWidth = 1000; const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 086e0842e..076dd3629 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -106,7 +106,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { fields.delete(header); sectionHeaders.splice(sectionHeaders.indexOf(header), 1); changed = true; - }) + }); } changed && setTimeout(action(() => { if (this.sectionHeaders) { this.sectionHeaders.length = 0; this.sectionHeaders.push(...sectionHeaders); } }), 0); return fields; diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 646b433bf..0a48c95e4 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -131,7 +131,8 @@ export class CollectionStackingViewFieldColumn extends React.Component NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0); const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; newDoc.heading = heading; - return this.props.parent.props.addDocument(newDoc); + this.props.parent.props.addDocument(newDoc); + return false; } @action diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx index 8c7e113b2..5b9a69bf7 100644 --- a/src/client/views/collections/CollectionStaffView.tsx +++ b/src/client/views/collections/CollectionStaffView.tsx @@ -1,22 +1,20 @@ import { CollectionSubView } from "./CollectionSubView"; -import { Transform } from "../../util/Transform"; import React = require("react"); import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx"; -import { Doc } from "../../../new_fields/Doc"; import { NumCast } from "../../../new_fields/Types"; import "./CollectionStaffView.scss"; import { observer } from "mobx-react"; @observer export class CollectionStaffView extends CollectionSubView(doc => doc) { - private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(0, -this._mainCont.current!.scrollTop); - private _mainCont = React.createRef(); private _reactionDisposer: IReactionDisposer | undefined; @observable private _staves = NumCast(this.props.Document.staves); + componentWillUnmount() { + this._reactionDisposer?.(); + } componentDidMount = () => { - this._reactionDisposer = reaction( - () => NumCast(this.props.Document.staves), + this._reactionDisposer = reaction(() => NumCast(this.props.Document.staves), (staves) => runInAction(() => this._staves = staves) ); @@ -47,7 +45,7 @@ export class CollectionStaffView extends CollectionSubView(doc => doc) { } render() { - return
+ return
{this.staves} {this.addStaffButton}
; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 88cfde0b6..70927cf22 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,5 +1,4 @@ import { action, computed, IReactionDisposer, reaction } from "mobx"; -import * as rp from 'request-promise'; import CursorField from "../../../new_fields/CursorField"; import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; @@ -107,7 +106,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: get childLayoutPairs(): { layout: Doc; data: Doc; }[] { const { Document, DataDoc } = this.props; const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.annotationsKey ? DataDoc : undefined, doc)).filter(pair => pair.layout); - return validPairs.map(({ data, layout }) => ({ data, layout: layout! })); // this mapping is a bit of a hack to coerce types + return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types } get childDocList() { return Cast(this.dataField, listSpec(Doc)); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 64832506b..0d2207b27 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -146,7 +146,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { back -
+
; } render() { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index edb9fd930..b6ce2f3a9 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -299,7 +299,7 @@ export class CollectionView extends Touchable { get childLayoutPairs(): { layout: Doc; data: Doc; }[] { const { Document, DataDoc } = this.props; const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, DataDoc, doc)).filter(pair => pair.layout); - return validPairs.map(({ data, layout }) => ({ data, layout: layout! })); // this mapping is a bit of a hack to coerce types + return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types } get childDocList() { return Cast(this.dataField, listSpec(Doc)); diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 960c6554e..2d565d9db 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -267,10 +267,10 @@ export class CollectionViewBaseChrome extends React.Component); - case CollectionViewType.Schema: return (); - case CollectionViewType.Tree: return (); - case CollectionViewType.Masonry: return (); + case CollectionViewType.Stacking: return (); + case CollectionViewType.Schema: return (); + case CollectionViewType.Tree: return (); + case CollectionViewType.Masonry: return (); default: return null; } } @@ -355,7 +355,7 @@ export class CollectionViewBaseChrome extends React.Component Doc.setChildLayout(this.target, source?.[0]), initialize: emptyFunction, }; - DragManager.StartButtonDrag([this._viewRef.current!], c.script, c.title, + DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title), { target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY); return true; }, emptyFunction, emptyFunction); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index a1ec0daef..49ca024a2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,4 +1,4 @@ -import { computed, IReactionDisposer } from "mobx"; +import { computed } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -10,67 +10,9 @@ import React = require("react"); import { Utils, emptyFunction } from "../../../../Utils"; import { SelectionManager } from "../../../util/SelectionManager"; import { DocumentType } from "../../../documents/DocumentTypes"; -import { StrCast } from "../../../../new_fields/Types"; @observer export class CollectionFreeFormLinksView extends React.Component { - - _brushReactionDisposer?: IReactionDisposer; - componentDidMount() { - // this._brushReactionDisposer = reaction( - // () => { - // let doclist = DocListCast(this.props.Document[this.props.fieldKey]); - // return { doclist: doclist ? doclist : [], xs: doclist.map(d => d.x) }; - // }, - // () => { - // let doclist = DocListCast(this.props.Document[this.props.fieldKey]); - // let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : []; - // views.forEach((dstDoc, i) => { - // views.forEach((srcDoc, j) => { - // let dstTarg = dstDoc; - // let srcTarg = srcDoc; - // let x1 = NumCast(srcDoc.x); - // let x2 = NumCast(dstDoc.x); - // let x1w = NumCast(srcDoc.width, -1); - // let x2w = NumCast(dstDoc.width, -1); - // if (x1w < 0 || x2w < 0 || i === j) { } - // else { - // let findBrush = (field: (Doc | Promise)[]) => field.findIndex(brush => { - // let bdocs = brush instanceof Doc ? Cast(brush.brushingDocs, listSpec(Doc), []) : undefined; - // return bdocs && bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false; - // }); - // let brushAction = (field: (Doc | Promise)[]) => { - // let found = findBrush(field); - // if (found !== -1) { - // field.splice(found, 1); - // } - // }; - // if (Math.abs(x1 + x1w - x2) < 20) { - // let linkDoc: Doc = new Doc(); - // linkDoc.title = "Histogram Brush"; - // linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title); - // linkDoc.brushingDocs = new List([dstTarg, srcTarg]); - - // brushAction = (field: (Doc | Promise)[]) => { - // if (findBrush(field) === -1) { - // field.push(linkDoc); - // } - // }; - // } - // if (dstTarg.brushingDocs === undefined) dstTarg.brushingDocs = new List(); - // if (srcTarg.brushingDocs === undefined) srcTarg.brushingDocs = new List(); - // let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc), []); - // let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc), []); - // brushAction(dstBrushDocs); - // brushAction(srcBrushDocs); - // } - // }); - // }); - // }); - } - componentWillUnmount() { - this._brushReactionDisposer && this._brushReactionDisposer(); - } @computed get uniqueConnections() { const connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => { @@ -101,4 +43,60 @@ export class CollectionFreeFormLinksView extends React.Component { {this.props.children}
; } + // _brushReactionDisposer?: IReactionDisposer; + // componentDidMount() { + // this._brushReactionDisposer = reaction( + // () => { + // let doclist = DocListCast(this.props.Document[this.props.fieldKey]); + // return { doclist: doclist ? doclist : [], xs: doclist.map(d => d.x) }; + // }, + // () => { + // let doclist = DocListCast(this.props.Document[this.props.fieldKey]); + // let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : []; + // views.forEach((dstDoc, i) => { + // views.forEach((srcDoc, j) => { + // let dstTarg = dstDoc; + // let srcTarg = srcDoc; + // let x1 = NumCast(srcDoc.x); + // let x2 = NumCast(dstDoc.x); + // let x1w = NumCast(srcDoc.width, -1); + // let x2w = NumCast(dstDoc.width, -1); + // if (x1w < 0 || x2w < 0 || i === j) { } + // else { + // let findBrush = (field: (Doc | Promise)[]) => field.findIndex(brush => { + // let bdocs = brush instanceof Doc ? Cast(brush.brushingDocs, listSpec(Doc), []) : undefined; + // return bdocs && bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false; + // }); + // let brushAction = (field: (Doc | Promise)[]) => { + // let found = findBrush(field); + // if (found !== -1) { + // field.splice(found, 1); + // } + // }; + // if (Math.abs(x1 + x1w - x2) < 20) { + // let linkDoc: Doc = new Doc(); + // linkDoc.title = "Histogram Brush"; + // linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title); + // linkDoc.brushingDocs = new List([dstTarg, srcTarg]); + + // brushAction = (field: (Doc | Promise)[]) => { + // if (findBrush(field) === -1) { + // field.push(linkDoc); + // } + // }; + // } + // if (dstTarg.brushingDocs === undefined) dstTarg.brushingDocs = new List(); + // if (srcTarg.brushingDocs === undefined) srcTarg.brushingDocs = new List(); + // let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc), []); + // let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc), []); + // brushAction(dstBrushDocs); + // brushAction(srcBrushDocs); + // } + // }); + // }); + // }); + // } + // componentWillUnmount() { + // this._brushReactionDisposer?.(); + // } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 0f94bffd6..276a49570 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -4,7 +4,7 @@ import { Doc, DocListCast, DataSym, WidthSym, HeightSym } from "../../../../new_ import { InkField, InkData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField"; -import { Cast, NumCast, FieldValue } from "../../../../new_fields/Types"; +import { Cast, NumCast, FieldValue, StrCast } from "../../../../new_fields/Types"; import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; import { Utils } from "../../../../Utils"; import { Docs, DocUtils } from "../../../documents/Documents"; @@ -107,7 +107,7 @@ export class MarqueeView extends React.Component(Butt style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}>
{(this.Document.text || this.Document.title)} diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index 8632f9c9a..9494a4bc4 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -22,6 +22,7 @@ interface ContentFittingDocumentViewProps { childDocs?: Doc[]; renderDepth: number; fitToBox?: boolean; + layoutKey?: string; dropAction?: dropActionType; PanelWidth: () => number; PanelHeight: () => number; @@ -88,6 +89,7 @@ export class ContentFittingDocumentView extends React.Componentawaiting layout

"; const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string"); - if (layout === undefined) { - return this.props.Document.data ? - "" : - KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : ""); - } else if (typeof layout === "string") { - return layout; - } else { - return "

Loading layout

"; - } + if (this.props.layoutKey === "layout_keyValue") { + return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data")); + } else + if (layout === undefined) { + return this.props.Document.data ? + "" : + KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : ""); + } else if (typeof layout === "string") { + return layout; + } else { + return "

Loading layout

"; + } } get dataDoc() { @@ -81,7 +84,8 @@ export class DocumentContentsView extends React.Component { fieldKey: this.props.keyName, isSelected: returnFalse, select: emptyFunction, + dropAction:"alias", + bringToFront:emptyFunction, renderDepth: 1, active: returnFalse, whenActiveChanged: emptyFunction, diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx index fdc732d3f..bd5b3bff4 100644 --- a/src/client/views/nodes/RadialMenuItem.tsx +++ b/src/client/views/nodes/RadialMenuItem.tsx @@ -44,12 +44,12 @@ export class RadialMenuItem extends React.Component { setcircle() { let circlemin = 0; - let circlemax = 1 + let circlemax = 1; this.props.min ? circlemin = this.props.min : null; this.props.max ? circlemax = this.props.max : null; if (document.getElementById("myCanvas") !== null) { - var c: any = document.getElementById("myCanvas"); - let color = "white" + const c: any = document.getElementById("myCanvas"); + let color = "white"; switch (circlemin % 3) { case 1: color = "#c2c2c5"; @@ -70,38 +70,38 @@ export class RadialMenuItem extends React.Component { } if (c.getContext) { - var ctx = c.getContext("2d"); + const ctx = c.getContext("2d"); ctx.beginPath(); ctx.arc(150, 150, 150, (circlemin / circlemax) * 2 * Math.PI, ((circlemin + 1) / circlemax) * 2 * Math.PI); ctx.arc(150, 150, 50, ((circlemin + 1) / circlemax) * 2 * Math.PI, (circlemin / circlemax) * 2 * Math.PI, true); ctx.fillStyle = color; - ctx.fill() + ctx.fill(); } } } calculatorx() { let circlemin = 0; - let circlemax = 1 + let circlemax = 1; this.props.min ? circlemin = this.props.min : null; this.props.max ? circlemax = this.props.max : null; - let avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2; - let degrees = 360 * avg; - let x = 100 * Math.cos(degrees * Math.PI / 180); - let y = -125 * Math.sin(degrees * Math.PI / 180); + const avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2; + const degrees = 360 * avg; + const x = 100 * Math.cos(degrees * Math.PI / 180); + const y = -125 * Math.sin(degrees * Math.PI / 180); return x; } calculatory() { let circlemin = 0; - let circlemax = 1 + let circlemax = 1; this.props.min ? circlemin = this.props.min : null; this.props.max ? circlemax = this.props.max : null; - let avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2; - let degrees = 360 * avg; - let x = 125 * Math.cos(degrees * Math.PI / 180); - let y = -100 * Math.sin(degrees * Math.PI / 180); + const avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2; + const degrees = 360 * avg; + const x = 125 * Math.cos(degrees * Math.PI / 180); + const y = -100 * Math.sin(degrees * Math.PI / 180); return y; } diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 548066f1c..7c58a5148 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -142,7 +142,7 @@ export class ScreenshotBox extends DocAnnotatableComponent { this._screenCapture = !this._screenCapture; this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); - }) + }); private get uIButtons() { return (
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 591864f2c..838fbefb1 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -63,7 +63,7 @@ export class WebBox extends DocAnnotatableComponent this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect; } } else if (field?.url) { - var result = await WebRequest.get(Utils.CorsProxy(field.url.href)); + const result = await WebRequest.get(Utils.CorsProxy(field.url.href)); this.dataDoc.text = htmlToText.fromString(result.content); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index a1e7d5c2a..cc187cd67 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -238,7 +238,7 @@ export class PDFViewer extends DocAnnotatableComponent { if (e.keyCode === 13) { - let submittedTitle = this.roomText!.value; + const submittedTitle = this.roomText!.value; this.roomText!.value = ""; this.roomText!.blur(); initialize(submittedTitle, this.changeUILook); @@ -56,7 +56,7 @@ export class DashWebRTCVideo extends React.Component
DashWebRTC
this.roomText = e!} onKeyDown={this.onEnterKeyDown} /> @@ -72,8 +72,8 @@ export class DashWebRTCVideo extends React.Component
; - let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); + const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; + const classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); return ( diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 5d3a517ae..1d2d57b96 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -199,6 +199,8 @@ export default class MobileInterface extends React.Component { DataDoc={undefined} LibraryPath={emptyPath} fieldKey={""} + dropAction={"alias"} + bringToFront={emptyFunction } addDocTab={returnFalse} pinToPres={emptyFunction} PanelHeight={() => window.innerHeight} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 25b526168..440f13d6b 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -353,7 +353,7 @@ export namespace Doc { // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype'). export function GetProto(doc: Doc): Doc { if (doc instanceof Promise) { - console.log("GetProto: error: got Promise insead of Doc") + console.log("GetProto: error: got Promise insead of Doc"); } const proto = doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc)); return proto === doc ? proto : Doc.GetProto(proto); @@ -697,7 +697,7 @@ export namespace Doc { // the document containing the view layout information - will be the Document itself unless the Document has // a layout field or 'layout' is given. export function Layout(doc: Doc, layout?: Doc): Doc { - const overrideLayout = layout && Cast(doc["data-layout[" + layout[Id] + "]"], Doc, null); + const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, "data")}-layout[` + layout[Id] + "]"], Doc, null); return overrideLayout || doc[LayoutSym] || doc; } export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; } diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 3a1fd41f8..8c719ccd8 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -12,7 +12,7 @@ function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); } -let tracing = false; +const tracing = false; export function TraceMobx() { tracing && trace(); } diff --git a/src/server/DashSession/Session/agents/applied_session_agent.ts b/src/server/DashSession/Session/agents/applied_session_agent.ts index 46c9e22ed..12064668b 100644 --- a/src/server/DashSession/Session/agents/applied_session_agent.ts +++ b/src/server/DashSession/Session/agents/applied_session_agent.ts @@ -44,7 +44,7 @@ export abstract class AppliedSessionAgent { if (!this.launched) { this.launched = true; if (isMaster) { - this.sessionMonitorRef = Monitor.Create() + this.sessionMonitorRef = Monitor.Create(); const sessionKey = await this.initializeMonitor(this.sessionMonitorRef); this.sessionMonitorRef.finalize(sessionKey); } else { diff --git a/src/server/DashSession/Session/agents/monitor.ts b/src/server/DashSession/Session/agents/monitor.ts index 6f8d25614..ee8afee65 100644 --- a/src/server/DashSession/Session/agents/monitor.ts +++ b/src/server/DashSession/Session/agents/monitor.ts @@ -167,7 +167,7 @@ export class Monitor extends IPCMessageReceiver { * and pass down any variables the pertinent to the child processes as environment variables. */ private loadAndValidateConfiguration = (): Configuration => { - let config: Configuration; + let config: Configuration | undefined; try { console.log(this.timestamp(), cyan("validating configuration...")); config = JSON.parse(readFileSync('./session.config.json', 'utf8')); diff --git a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts index 9f0db8330..feff568e1 100644 --- a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts +++ b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts @@ -35,8 +35,8 @@ export type MessageHandler = (args: T) => (any | Promise); * When a message is emitted, it is embedded with private metadata * to facilitate the resolution of promises, etc. */ -interface InternalMessage extends Message { metadata: Metadata } -interface Metadata { isResponse: boolean; id: string } +interface InternalMessage extends Message { metadata: Metadata; } +interface Metadata { isResponse: boolean; id: string; } type InternalMessageHandler = (message: InternalMessage) => (any | Promise); /** @@ -133,7 +133,7 @@ export class PromisifiedIPCManager { Object.keys(pendingMessages).forEach(id => { const error: ErrorLike = { name: "ManagerDestroyed", message: "The IPC manager was destroyed before the response could be returned." }; const message: InternalMessage = { name: pendingMessages[id], args: { error }, metadata: { id, isResponse: true } }; - this.target.send?.(message) + this.target.send?.(message); }); this.pendingMessages = {}; } diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index c5dc22912..9f9fc9619 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -55,8 +55,8 @@ export namespace WebSocket { socket.on('create or join', function (room) { console.log('Received request to create or join room ' + room); - var clientsInRoom = socket.adapter.rooms[room]; - var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; + const clientsInRoom = socket.adapter.rooms[room]; + const numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; console.log('Room ' + room + ' now has ' + numClients + ' client(s)'); if (numClients === 0) { @@ -76,8 +76,8 @@ export namespace WebSocket { }); socket.on('ipaddr', function () { - var ifaces = networkInterfaces(); - for (var dev in ifaces) { + const ifaces = networkInterfaces(); + for (const dev in ifaces) { ifaces[dev].forEach(function (details) { if (details.family === 'IPv4' && details.address !== '127.0.0.1') { socket.emit('ipaddr', details.address); diff --git a/src/server/database.ts b/src/server/database.ts index 055f04c49..fc91ff3a2 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -328,7 +328,7 @@ export namespace Database { export const LogUpload = async (information: Upload.ImageInformation) => { const bundle = { - _id: Utils.GenerateDeterministicGuid(String(information.contentSize!)), + _id: Utils.GenerateDeterministicGuid(String(information.contentSize)), ...information }; return Instance.insert(bundle, AuxiliaryCollections.GooglePhotosUploadHistory); diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 7b2228831..1150118f7 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -123,7 +123,7 @@ function registerCorsProxy(server: express.Express) { const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; server.use("/corsProxy", (req, res) => { - let requrl = decodeURIComponent(req.url.substring(1)); + const requrl = decodeURIComponent(req.url.substring(1)); const referer = req.headers.referer ? decodeURIComponent(req.headers.referer) : ""; // cors weirdness here... // if the referer is a cors page and the cors() route (I think) redirected to /corsProxy/ and the requested url path was relative, -- cgit v1.2.3-70-g09d2