From ee03fa6e04dd9dba3099f75154de6ffab566ff5c Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Fri, 13 Sep 2019 16:43:09 -0400 Subject: Trial 5(Most succesful yet) --- src/client/documents/Documents.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 602a7f9ad..036cc75a0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -22,7 +22,7 @@ import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { Field, Doc, Opt } from "../../new_fields/Doc"; import { OmitKeys, JSONUtils } from "../../Utils"; -import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField"; +import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField, WebCamField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; import { Cast, NumCast } from "../../new_fields/Types"; @@ -46,6 +46,7 @@ import { ComputedField } from "../../new_fields/ScriptField"; import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; +import { DashWebCam } from "../views/webcam/DashWebCam"; //import { PresBox } from "../views/nodes/PresBox"; //import { PresField } from "../../new_fields/PresField"; var requestImageSize = require('../util/request-image-size'); @@ -176,6 +177,9 @@ export namespace Docs { }], [DocumentType.LINKFOLLOW, { layout: { view: LinkFollowBox } + }], + [DocumentType.WEBCAM, { + layout: { view: DashWebCam } }] ]); @@ -357,6 +361,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(new URL(url)), options); } + export function WebCamDocument(url: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), "", options); + } + export function AudioDocument(url: string, options: DocumentOptions = {}) { return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)), options); } -- cgit v1.2.3-70-g09d2 From 45b9f489033cd323614463ca9c36f41900bf1965 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 1 Oct 2019 18:14:04 -0400 Subject: trials 2 --- package.json | 1 + src/client/documents/Documents.ts | 3 ++- src/client/views/nodes/DocumentContentsView.tsx | 3 ++- src/client/views/webcam/DashWebRTC.tsx | 9 +++++++-- 4 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/package.json b/package.json index d0718345f..7ae8c3399 100644 --- a/package.json +++ b/package.json @@ -216,6 +216,7 @@ "typescript-collections": "^1.3.2", "url-loader": "^1.1.2", "uuid": "^3.3.2", + "webrtc-adapter": "^7.3.0", "words-to-numbers": "^1.5.1", "xoauth2": "^1.2.0", "youtube": "^0.1.0" diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 036cc75a0..1f9b575a4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -47,6 +47,7 @@ import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; import { DashWebCam } from "../views/webcam/DashWebCam"; +import { DashWebRTC } from "../views/webcam/DashWebRTC"; //import { PresBox } from "../views/nodes/PresBox"; //import { PresField } from "../../new_fields/PresField"; var requestImageSize = require('../util/request-image-size'); @@ -179,7 +180,7 @@ export namespace Docs { layout: { view: LinkFollowBox } }], [DocumentType.WEBCAM, { - layout: { view: DashWebCam } + layout: { view: DashWebRTC } }] ]); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index df5ff04dd..2b797eeca 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -33,6 +33,7 @@ import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; import { ScriptField } from "../../../new_fields/ScriptField"; import { fromPromise } from "mobx-utils"; import { DashWebCam } from "../../views/webcam/DashWebCam"; +import { DashWebRTC } from "../webcam/DashWebRTC"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -112,7 +113,7 @@ export class DocumentContentsView extends React.Component - + + + + + ; let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; @@ -77,7 +83,6 @@ export class DashWebRTC extends React.Component {!frozen ? (null) :
} ); - ); } -- cgit v1.2.3-70-g09d2 From a8f14c501cf676f6a2697b73d7f2a162d4100a9e Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Wed, 2 Oct 2019 16:52:57 -0400 Subject: Merge conflicts fixed --- src/client/documents/Documents.ts | 16 +- src/client/views/MainView.tsx | 4 - src/client/views/nodes/DocumentContentsView.tsx | 16 +- src/client/views/webcam/DashWebRTC.tsx | 225 ++++++++++++++++++++++-- 4 files changed, 214 insertions(+), 47 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3482033d5..6bd91c3b9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -20,12 +20,8 @@ import { AggregateFunction } from "../northstar/model/idea/idea"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { OmitKeys, JSONUtils } from "../../Utils"; -<<<<<<< HEAD -import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField, WebCamField } from "../../new_fields/URLField"; -======= import { Field, Doc, Opt, DocListCastAsync } from "../../new_fields/Doc"; -import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField"; ->>>>>>> 69e4a936c4eb0cc2e35e4e7f3258aed1f72b8da7 +import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField, WebCamField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; import { Cast, NumCast } from "../../new_fields/Types"; @@ -49,14 +45,9 @@ import { ComputedField } from "../../new_fields/ScriptField"; import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; -<<<<<<< HEAD import { DashWebCam } from "../views/webcam/DashWebCam"; import { DashWebRTC } from "../views/webcam/DashWebRTC"; -//import { PresBox } from "../views/nodes/PresBox"; -//import { PresField } from "../../new_fields/PresField"; -======= import { PresElementBox } from "../views/presentationview/PresElementBox"; ->>>>>>> 69e4a936c4eb0cc2e35e4e7f3258aed1f72b8da7 var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -186,15 +177,12 @@ export namespace Docs { [DocumentType.LINKFOLLOW, { layout: { view: LinkFollowBox } }], -<<<<<<< HEAD [DocumentType.WEBCAM, { layout: { view: DashWebRTC } - }] -======= + }], [DocumentType.PRESELEMENT, { layout: { view: PresElementBox } }], ->>>>>>> 69e4a936c4eb0cc2e35e4e7f3258aed1f72b8da7 ]); // All document prototypes are initialized with at least these values diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 22d8171c0..7e2b4fa3a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,9 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -<<<<<<< HEAD import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faVideo } from '@fortawesome/free-solid-svg-icons'; -======= -import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; ->>>>>>> 69e4a936c4eb0cc2e35e4e7f3258aed1f72b8da7 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index f1c10f2f2..f1678d4f9 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -30,20 +30,12 @@ import { PresElementBox } from "../presentationview/PresElementBox"; import { VideoBox } from "./VideoBox"; import { WebBox } from "./WebBox"; import React = require("react"); -<<<<<<< HEAD -import { FieldViewProps } from "./FieldView"; -import { Without, OmitKeys } from "../../../Utils"; -import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; +import { StrCast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; -import { Doc } from "../../../new_fields/Doc"; -import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; -import { ScriptField } from "../../../new_fields/ScriptField"; import { fromPromise } from "mobx-utils"; import { DashWebCam } from "../../views/webcam/DashWebCam"; import { DashWebRTC } from "../webcam/DashWebRTC"; -======= ->>>>>>> 69e4a936c4eb0cc2e35e4e7f3258aed1f72b8da7 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; @@ -115,11 +107,7 @@ export class DocumentContentsView extends React.Component>>>>>> 69e4a936c4eb0cc2e35e4e7f3258aed1f72b8da7 + components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, DashWebCam, DashWebRTC }} bindings={this.CreateBindings()} jsx={this.finalLayout} showWarnings={true} diff --git a/src/client/views/webcam/DashWebRTC.tsx b/src/client/views/webcam/DashWebRTC.tsx index 9c289b40f..64ddb318f 100644 --- a/src/client/views/webcam/DashWebRTC.tsx +++ b/src/client/views/webcam/DashWebRTC.tsx @@ -2,7 +2,7 @@ import { observer } from "mobx-react"; import React = require("react"); import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView"; import { FieldViewProps, FieldView } from "../nodes/FieldView"; -import { observable, trace } from "mobx"; +import { observable } from "mobx"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import "../../views/nodes/WebBox.scss"; @@ -11,7 +11,7 @@ import adapter from 'webrtc-adapter'; -const mediaStreamConstaints = { +const mediaStreamConstraints = { video: true, }; @@ -26,7 +26,7 @@ export class DashWebRTC extends React.Component { //console.log("navigator.getUserMedia error: ", error); - trace(`navigator.getUserMedia error: ${error.toString()}.`); + this.trace(`navigator.getUserMedia error: ${error.toString()}.`); } - logVideoLoaded(event: any) { + logVideoLoaded = (event: any) => { let video = event.target; - trace(`${video!.id} videoWidth: ${video!.videoWidth}px, ` + - `videoHeight: ${video!.videoHeight}px.`); + this.trace(`${video.id} videoWidth: ${video.videoWidth}px, ` + + `videoHeight: ${video.videoHeight}px.`); } - logResizedVideo(event: any) { + logResizedVideo = (event: any) => { this.logVideoLoaded(event); if (this.startTime) { - let elapsedTime = window.performance.now() - this.startTime!; + let elapsedTime = window.performance.now() - this.startTime; this.startTime = null; - trace(`Setup time: ${elapsedTime.toFixed(3)}ms.`); + this.trace(`Setup time: ${elapsedTime.toFixed(3)}ms.`); } } + handleConnection = (event: any) => { + let peerConnection = event.target; + let iceCandidate = event.candidate; + + if (iceCandidate) { + let newIceCandidate: RTCIceCandidate = new RTCIceCandidate(iceCandidate); + let otherPeer: any = this.getOtherPeer(peerConnection); + + otherPeer.addIceCandidate(newIceCandidate).then(() => { + this.handleConnectionSuccess(peerConnection); + }).catch((error: any) => { + this.handleConnectionFailure(peerConnection, error); + }); + + this.trace(`${this.getPeerName(peerConnection)} ICE candidate:\n` + + `${event.candidate.candidate}.`); + + } + } + + // Logs that the connection succeeded. + handleConnectionSuccess = (peerConnection: any) => { + this.trace(`${this.getPeerName(peerConnection)} addIceCandidate success.`); + } + + handleConnectionFailure = (peerConnection: any, error: any) => { + this.trace(`${this.getPeerName(peerConnection)} failed to add ICE Candidate:\n` + + `${error.toString()}.`); + } + + // Logs changes to the connection state. + handleConnectionChange = (event: any) => { + let peerConnection = event.target; + console.log('ICE state change event: ', event); + this.trace(`${this.getPeerName(peerConnection)} ICE state: ` + + `${peerConnection.iceConnectionState}.`); + } + + // Logs error when setting session description fails. + setSessionDescriptionError = (error: any) => { + this.trace(`Failed to create session description: ${error.toString()}.`); + } + + // Logs success when setting session description. + setDescriptionSuccess = (peerConnection: any, functionName: any) => { + let peerName = this.getPeerName(peerConnection); + this.trace(`${peerName} ${functionName} complete.`); + } + + + // Logs success when localDescription is set. + setLocalDescriptionSuccess = (peerConnection: any) => { + this.setDescriptionSuccess(peerConnection, 'setLocalDescription'); + } + + // Logs success when remoteDescription is set. + setRemoteDescriptionSuccess = (peerConnection: any) => { + this.setDescriptionSuccess(peerConnection, 'setRemoteDescription'); + } + + createdOffer = (description: any) => { + this.trace(`Offer from localPeerConnection:\n${description.sdp}`); + this.trace('localPeerConnection setLocalDescription start.'); + + this.localPeerConnection.setLocalDescription(description).then(() => { + this.setLocalDescriptionSuccess(this.localPeerConnection); + }).catch(this.setSessionDescriptionError); + + + this.trace('remotePeerConnection setRemoteDescription start.'); + this.remotePeerConnection.setRemoteDescription(description) + .then(() => { + this.setRemoteDescriptionSuccess(this.remotePeerConnection); + }).catch(this.setSessionDescriptionError); + + this.trace('remotePeerConnection createAnswer start.'); + this.remotePeerConnection.createAnswer() + .then(this.createdAnswer) + .catch(this.setSessionDescriptionError); + + } + + createdAnswer = (description: any) => { + this.trace(`Answer from remotePeerConnection:\n${description.sdp}.`); + + this.trace('remotePeerConnection setLocalDescription start.'); + this.remotePeerConnection.setLocalDescription(description) + .then(() => { + this.setLocalDescriptionSuccess(this.remotePeerConnection); + }).catch(this.setSessionDescriptionError); + + this.trace('localPeerConnection setRemoteDescription start.'); + this.localPeerConnection.setRemoteDescription(description) + .then(() => { + this.setRemoteDescriptionSuccess(this.localPeerConnection); + }).catch(this.setSessionDescriptionError); + } + + + startAction = () => { + this.startButton!.disabled = true; + navigator.mediaDevices.getUserMedia(mediaStreamConstraints) + .then(this.gotLocalMediaStream).catch(this.handleLocalMediaStreamError); + this.trace('Requesting local stream.'); + } + + + // Handles call button action: creates peer connection. + callAction = () => { + this.callButton!.disabled = true; + this.hangupButton!.disabled = false; + + this.trace('Starting call.'); + this.startTime = window.performance.now(); + + // Get local media stream tracks. + const videoTracks = this.localStream!.getVideoTracks(); + const audioTracks = this.localStream!.getAudioTracks(); + if (videoTracks.length > 0) { + this.trace(`Using video device: ${videoTracks[0].label}.`); + } + if (audioTracks.length > 0) { + this.trace(`Using audio device: ${audioTracks[0].label}.`); + } + + let servers: RTCConfiguration | undefined = undefined; // Allows for RTC server configuration. + + // Create peer connections and add behavior. + this.localPeerConnection = new RTCPeerConnection(servers); + this.trace('Created local peer connection object localPeerConnection.'); + + this.localPeerConnection.addEventListener('icecandidate', this.handleConnection); + this.localPeerConnection.addEventListener( + 'iceconnectionstatechange', this.handleConnectionChange); + + this.remotePeerConnection = new RTCPeerConnection(servers); + this.trace('Created remote peer connection object remotePeerConnection.'); + + this.remotePeerConnection.addEventListener('icecandidate', this.handleConnection); + this.remotePeerConnection.addEventListener( + 'iceconnectionstatechange', this.handleConnectionChange); + this.remotePeerConnection.addEventListener('addstream', this.gotRemoteMediaStream); + + // Add local stream to connection and create offer to connect. + this.localPeerConnection.addStream(this.localStream); + this.trace('Added local stream to localPeerConnection.'); + + this.trace('localPeerConnection createOffer start.'); + this.localPeerConnection.createOffer(offerOptions) + .then(this.createdOffer).catch(this.setSessionDescriptionError); + } + + + // Handles hangup action: ends up call, closes connections and resets peers. + hangupAction = () => { + this.localPeerConnection.close(); + this.remotePeerConnection.close(); + this.localPeerConnection = null; + this.remotePeerConnection = null; + this.hangupButton!.disabled = true; + this.callButton!.disabled = false; + this.trace('Ending call.'); + } + + // Gets the "other" peer connection. + getOtherPeer = (peerConnection: any) => { + return (peerConnection === this.localPeerConnection) ? + this.remotePeerConnection : this.localPeerConnection; + } + + // Gets the name of a certain peer connection. + getPeerName = (peerConnection: any) => { + return (peerConnection === this.localPeerConnection) ? + 'localPeerConnection' : 'remotePeerConnection'; + } + + // Logs an action (text) and the time when it happened on the console. + trace = (text: string) => { + text = text.trim(); + const now = (window.performance.now() / 1000).toFixed(3); + + console.log(now, text); + } + + + + + + + + + + + + @@ -116,9 +311,9 @@ export class DashWebRTC extends React.Component - - - + + +
; let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; -- cgit v1.2.3-70-g09d2 From 6dab9e4067b2763d23e41085b263fed811d71a86 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Wed, 2 Oct 2019 17:31:25 -0400 Subject: Unused field removed --- src/client/documents/Documents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 90028fc43..8b3c03866 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -21,7 +21,7 @@ import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { OmitKeys, JSONUtils } from "../../Utils"; import { Field, Doc, Opt, DocListCastAsync } from "../../new_fields/Doc"; -import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField, WebCamField } from "../../new_fields/URLField"; +import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; import { Cast, NumCast } from "../../new_fields/Types"; -- cgit v1.2.3-70-g09d2 From 9c7521a36f67b3a69ac40f8a55e66638ed96bf04 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 29 Oct 2019 18:29:57 -0400 Subject: Client Side Namespace --- src/client/DocServer.ts | 2 +- src/client/documents/Documents.ts | 4 +- src/client/views/webcam/DashWebRTC.ts | 306 +++++++++++++++++++++++++ src/client/views/webcam/DashWebRTC.tsx | 334 --------------------------- src/client/views/webcam/DashWebRTCVideo.tsx | 339 ++++++++++++++++++++++++++++ 5 files changed, 648 insertions(+), 337 deletions(-) create mode 100644 src/client/views/webcam/DashWebRTC.ts delete mode 100644 src/client/views/webcam/DashWebRTC.tsx create mode 100644 src/client/views/webcam/DashWebRTCVideo.tsx (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 2cec1046b..7ffb43684 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -21,7 +21,7 @@ import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; */ export namespace DocServer { let _cache: { [id: string]: RefField | Promise> } = {}; - let _socket: SocketIOClient.Socket; + export let _socket: SocketIOClient.Socket; // this client's distinct GUID created at initialization let GUID: string; // indicates whether or not a document is currently being udpated, and, if so, its id diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8b3c03866..3df1e0e9a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -46,8 +46,8 @@ import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; import { DashWebCam } from "../views/webcam/DashWebCam"; -import { DashWebRTC } from "../views/webcam/DashWebRTC"; import { PresElementBox } from "../views/presentationview/PresElementBox"; +import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -178,7 +178,7 @@ export namespace Docs { layout: { view: LinkFollowBox } }], [DocumentType.WEBCAM, { - layout: { view: DashWebRTC } + layout: { view: DashWebRTCVideo } }], [DocumentType.PRESELEMENT, { layout: { view: PresElementBox } diff --git a/src/client/views/webcam/DashWebRTC.ts b/src/client/views/webcam/DashWebRTC.ts new file mode 100644 index 000000000..4472f5ba5 --- /dev/null +++ b/src/client/views/webcam/DashWebRTC.ts @@ -0,0 +1,306 @@ +import { DocServer } from '../../DocServer'; + + + +export namespace DashWebRTC { + + + let isChannelReady = false; + let isInitiator = false; + let isStarted = false; + let localStream: MediaStream | undefined; + let pc: any; + let remoteStream: MediaStream | undefined; + let turnReady; + + let pcConfig = { + 'iceServers': [{ + 'urls': 'stun:stun.l.google.com:19302' + }] + }; + + // Set up audio and video regardless of what devices are present. + let sdpConstraints = { + offerToReceiveAudio: true, + offerToReceiveVideo: true + }; + + + let room = 'test'; + + //let socket = io.connect(); + + 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); + 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 + '!'); + isChannelReady = true; + }); + + + DocServer._socket.on('joined', function (room: string) { + console.log('joined: ' + room); + isChannelReady = true; + }); + + + DocServer._socket.on('log', function (array: any) { + console.log.apply(console, array); + }); + + + function sendMessage(message: any) { + console.log('Client sending message: ', message); + DocServer._socket.emit('message', message); + } + + + // This client receives a message + DocServer._socket.on('message', function (message: any) { + console.log('Client received message:', message); + if (message === 'got user media') { + maybeStart(); + } else if (message.type === 'offer') { + if (!isInitiator && !isStarted) { + maybeStart(); + } + pc.setRemoteDescription(new RTCSessionDescription(message)); + doAnswer(); + } else if (message.type === 'answer' && isStarted) { + pc.setRemoteDescription(new RTCSessionDescription(message)); + } else if (message.type === 'candidate' && isStarted) { + var candidate = new RTCIceCandidate({ + sdpMLineIndex: message.label, + candidate: message.candidate + }); + pc.addIceCandidate(candidate); + } else if (message === 'bye' && isStarted) { + handleRemoteHangup(); + } + }); + + let localVideo: HTMLVideoElement; + let remoteVideo: HTMLVideoElement; + + export function setVideoObjects(localVideo: HTMLVideoElement, remoteVideo: HTMLVideoElement) { + localVideo = localVideo; + remoteVideo = remoteVideo; + } + + navigator.mediaDevices.getUserMedia({ + audio: false, + video: true + }) + .then(gotStream) + .catch(function (e) { + alert('getUserMedia() error: ' + e.name); + }); + + + function gotStream(stream: any) { + console.log('Adding local stream.'); + localStream = stream; + localVideo.srcObject = stream; + sendMessage('got user media'); + if (isInitiator) { + maybeStart(); + } + } + + let constraints = { + video: true, + audio: true + }; + + + //Trying this one out!!! + console.log('Getting user media with constraints', constraints); + + if (location.hostname !== 'localhost') { + requestTurn( + 'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913' + ); + } + + + function maybeStart() { + console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady); + if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) { + console.log('>>>>>> creating peer connection'); + createPeerConnection(); + pc.addStream(localStream); + isStarted = true; + console.log('isInitiator', isInitiator); + if (isInitiator) { + doCall(); + } + } + } + + + //this will need to be changed to our version of hangUp + window.onbeforeunload = function () { + sendMessage('bye'); + }; + + function createPeerConnection() { + try { + pc = new RTCPeerConnection(undefined); + pc.onicecandidate = handleIceCandidate; + pc.onaddstream = handleRemoteStreamAdded; + pc.onremovestream = handleRemoteStreamRemoved; + console.log('Created RTCPeerConnnection'); + } catch (e) { + console.log('Failed to create PeerConnection, exception: ' + e.message); + alert('Cannot create RTCPeerConnection object.'); + return; + } + } + + function handleIceCandidate(event: any) { + console.log('icecandidate event: ', event); + if (event.candidate) { + sendMessage({ + type: 'candidate', + label: event.candidate.sdpMLineIndex, + id: event.candidate.sdpMid, + candidate: event.candidate.candidate + }); + } else { + console.log('End of candidates.'); + } + } + + function handleCreateOfferError(event: any) { + console.log('createOffer() error: ', event); + } + + function doCall() { + console.log('Sending offer to peer'); + pc.createOffer(setLocalAndSendMessage, handleCreateOfferError); + } + + function doAnswer() { + console.log('Sending answer to peer.'); + pc.createAnswer().then( + setLocalAndSendMessage, + onCreateSessionDescriptionError + ); + } + + function setLocalAndSendMessage(sessionDescription: any) { + pc.setLocalDescription(sessionDescription); + console.log('setLocalAndSendMessage sending message', sessionDescription); + sendMessage(sessionDescription); + } + + function onCreateSessionDescriptionError(error: any) { + console.log('Failed to create session description: ' + error.toString()); + } + + + function requestTurn(turnURL: any) { + var turnExists = false; + for (var i in pcConfig.iceServers) { + if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') { + turnExists = true; + 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); + pcConfig.iceServers.push({ + 'urls': 'turn:' + turnServer.username + '@' + turnServer.turn, + //'credential': turnServer.password + }); + turnReady = true; + } + }; + xhr.open('GET', turnURL, true); + xhr.send(); + } + } + + function handleRemoteStreamAdded(event: MediaStreamEvent) { + console.log('Remote stream added.'); + remoteStream = event.stream!; + remoteVideo.srcObject = remoteStream; + } + + function handleRemoteStreamRemoved(event: MediaStreamEvent) { + console.log('Remote stream removed. Event: ', event); + } + + function hangup() { + console.log('Hanging up.'); + stop(); + sendMessage('bye'); + } + + function handleRemoteHangup() { + console.log('Session terminated.'); + stop(); + isInitiator = false; + } + + function stop() { + isStarted = false; + pc.close(); + pc = null; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/src/client/views/webcam/DashWebRTC.tsx b/src/client/views/webcam/DashWebRTC.tsx deleted file mode 100644 index 9c93fb4cf..000000000 --- a/src/client/views/webcam/DashWebRTC.tsx +++ /dev/null @@ -1,334 +0,0 @@ -import { observer } from "mobx-react"; -import React = require("react"); -import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView"; -import { FieldViewProps, FieldView } from "../nodes/FieldView"; -import { observable } from "mobx"; -import { DocumentDecorations } from "../DocumentDecorations"; -import { InkingControl } from "../InkingControl"; -import "../../views/nodes/WebBox.scss"; -import "./DashWebRTC.scss" -import adapter from 'webrtc-adapter'; - - - - -const mediaStreamConstraints = { - video: true, -}; - -const offerOptions = { - offerToReceiveVideo: 1, -}; - - -@observer -export class DashWebRTC extends React.Component { - - @observable private localVideoEl: HTMLVideoElement | undefined; - @observable private peerVideoEl: HTMLVideoElement | undefined; - @observable private localStream: MediaStream | undefined; - @observable private startTime: any = null; - @observable private remoteStream: MediaStream | undefined; - @observable private localPeerConnection: any; - @observable private remotePeerConnection: any; - private callButton: HTMLButtonElement | undefined; - private startButton: HTMLButtonElement | undefined; - private hangupButton: HTMLButtonElement | undefined; - - - componentDidMount() { - this.callButton!.disabled = true; - this.hangupButton!.disabled = true; - // navigator.mediaDevices.getUserMedia(mediaStreamConstraints).then(this.gotLocalMediaStream).catch(this.handleLocalMediaStreamError); - this.localVideoEl!.addEventListener('loadedmetadata', this.logVideoLoaded); - this.peerVideoEl!.addEventListener('loadedmetadata', this.logVideoLoaded); - this.peerVideoEl!.addEventListener('onresize', this.logResizedVideo); - } - - - gotLocalMediaStream = (mediaStream: MediaStream) => { - this.localStream = mediaStream; - if (this.localVideoEl) { - this.localVideoEl.srcObject = mediaStream; - } - this.trace('Received local stream.'); - this.callButton!.disabled = false; - - } - - gotRemoteMediaStream = (event: MediaStreamEvent) => { - let mediaStream = event.stream; - this.peerVideoEl!.srcObject = mediaStream; - this.remoteStream = mediaStream!; - - } - - handleLocalMediaStreamError = (error: string) => { - //console.log("navigator.getUserMedia error: ", error); - this.trace(`navigator.getUserMedia error: ${error.toString()}.`); - - } - - logVideoLoaded = (event: any) => { - let video = event.target; - this.trace(`${video.id} videoWidth: ${video.videoWidth}px, ` + - `videoHeight: ${video.videoHeight}px.`); - } - - logResizedVideo = (event: any) => { - this.logVideoLoaded(event); - - if (this.startTime) { - let elapsedTime = window.performance.now() - this.startTime; - this.startTime = null; - this.trace(`Setup time: ${elapsedTime.toFixed(3)}ms.`); - } - - } - - handleConnection = (event: any) => { - let peerConnection = event.target; - let iceCandidate = event.candidate; - - if (iceCandidate) { - let newIceCandidate: RTCIceCandidate = new RTCIceCandidate(iceCandidate); - let otherPeer: any = this.getOtherPeer(peerConnection); - - otherPeer.addIceCandidate(newIceCandidate).then(() => { - this.handleConnectionSuccess(peerConnection); - }).catch((error: any) => { - this.handleConnectionFailure(peerConnection, error); - }); - - this.trace(`${this.getPeerName(peerConnection)} ICE candidate:\n` + - `${event.candidate.candidate}.`); - - } - } - - // Logs that the connection succeeded. - handleConnectionSuccess = (peerConnection: any) => { - this.trace(`${this.getPeerName(peerConnection)} addIceCandidate success.`); - } - - handleConnectionFailure = (peerConnection: any, error: any) => { - this.trace(`${this.getPeerName(peerConnection)} failed to add ICE Candidate:\n` + - `${error.toString()}.`); - } - - // Logs changes to the connection state. - handleConnectionChange = (event: any) => { - let peerConnection = event.target; - console.log('ICE state change event: ', event); - this.trace(`${this.getPeerName(peerConnection)} ICE state: ` + - `${peerConnection.iceConnectionState}.`); - } - - // Logs error when setting session description fails. - setSessionDescriptionError = (error: any) => { - this.trace(`Failed to create session description: ${error.toString()}.`); - } - - // Logs success when setting session description. - setDescriptionSuccess = (peerConnection: any, functionName: any) => { - let peerName = this.getPeerName(peerConnection); - this.trace(`${peerName} ${functionName} complete.`); - } - - - // Logs success when localDescription is set. - setLocalDescriptionSuccess = (peerConnection: any) => { - this.setDescriptionSuccess(peerConnection, 'setLocalDescription'); - } - - // Logs success when remoteDescription is set. - setRemoteDescriptionSuccess = (peerConnection: any) => { - this.setDescriptionSuccess(peerConnection, 'setRemoteDescription'); - } - - createdOffer = (description: any) => { - this.trace(`Offer from localPeerConnection:\n${description.sdp}`); - this.trace('localPeerConnection setLocalDescription start.'); - - this.localPeerConnection.setLocalDescription(description).then(() => { - this.setLocalDescriptionSuccess(this.localPeerConnection); - }).catch(this.setSessionDescriptionError); - - - this.trace('remotePeerConnection setRemoteDescription start.'); - this.remotePeerConnection.setRemoteDescription(description) - .then(() => { - this.setRemoteDescriptionSuccess(this.remotePeerConnection); - }).catch(this.setSessionDescriptionError); - - this.trace('remotePeerConnection createAnswer start.'); - this.remotePeerConnection.createAnswer() - .then(this.createdAnswer) - .catch(this.setSessionDescriptionError); - - } - - createdAnswer = (description: any) => { - this.trace(`Answer from remotePeerConnection:\n${description.sdp}.`); - - this.trace('remotePeerConnection setLocalDescription start.'); - this.remotePeerConnection.setLocalDescription(description) - .then(() => { - this.setLocalDescriptionSuccess(this.remotePeerConnection); - }).catch(this.setSessionDescriptionError); - - this.trace('localPeerConnection setRemoteDescription start.'); - this.localPeerConnection.setRemoteDescription(description) - .then(() => { - this.setRemoteDescriptionSuccess(this.localPeerConnection); - }).catch(this.setSessionDescriptionError); - } - - - startAction = () => { - this.startButton!.disabled = true; - navigator.mediaDevices.getUserMedia(mediaStreamConstraints) - .then(this.gotLocalMediaStream).catch(this.handleLocalMediaStreamError); - this.trace('Requesting local stream.'); - } - - - // Handles call button action: creates peer connection. - callAction = () => { - this.callButton!.disabled = true; - this.hangupButton!.disabled = false; - - this.trace('Starting call.'); - this.startTime = window.performance.now(); - - // Get local media stream tracks. - const videoTracks = this.localStream!.getVideoTracks(); - const audioTracks = this.localStream!.getAudioTracks(); - if (videoTracks.length > 0) { - this.trace(`Using video device: ${videoTracks[0].label}.`); - } - if (audioTracks.length > 0) { - this.trace(`Using audio device: ${audioTracks[0].label}.`); - } - - let servers: RTCConfiguration | undefined = undefined; // Allows for RTC server configuration. - - // Create peer connections and add behavior. - this.localPeerConnection = new RTCPeerConnection(servers); - this.trace('Created local peer connection object localPeerConnection.'); - - this.localPeerConnection.addEventListener('icecandidate', this.handleConnection); - this.localPeerConnection.addEventListener( - 'iceconnectionstatechange', this.handleConnectionChange); - - this.remotePeerConnection = new RTCPeerConnection(servers); - this.trace('Created remote peer connection object remotePeerConnection.'); - - this.remotePeerConnection.addEventListener('icecandidate', this.handleConnection); - this.remotePeerConnection.addEventListener( - 'iceconnectionstatechange', this.handleConnectionChange); - this.remotePeerConnection.addEventListener('addstream', this.gotRemoteMediaStream); - - // Add local stream to connection and create offer to connect. - this.localPeerConnection.addStream(this.localStream); - this.trace('Added local stream to localPeerConnection.'); - - this.trace('localPeerConnection createOffer start.'); - this.localPeerConnection.createOffer(offerOptions) - .then(this.createdOffer).catch(this.setSessionDescriptionError); - } - - - // Handles hangup action: ends up call, closes connections and resets peers. - hangupAction = () => { - this.localPeerConnection.close(); - this.remotePeerConnection.close(); - this.localPeerConnection = null; - this.remotePeerConnection = null; - this.hangupButton!.disabled = true; - this.callButton!.disabled = false; - this.trace('Ending call.'); - } - - // Gets the "other" peer connection. - getOtherPeer = (peerConnection: any) => { - return (peerConnection === this.localPeerConnection) ? - this.remotePeerConnection : this.localPeerConnection; - } - - // Gets the name of a certain peer connection. - getPeerName = (peerConnection: any) => { - return (peerConnection === this.localPeerConnection) ? - 'localPeerConnection' : 'remotePeerConnection'; - } - - // Logs an action (text) and the time when it happened on the console. - trace = (text: string) => { - text = text.trim(); - const now = (window.performance.now() / 1000).toFixed(3); - - console.log(now, text); - } - - - - - - - - - - - - - - - - public static LayoutString() { return FieldView.LayoutString(DashWebRTC); } - - - _ignore = 0; - onPreWheel = (e: React.WheelEvent) => { - this._ignore = e.timeStamp; - } - onPrePointer = (e: React.PointerEvent) => { - this._ignore = e.timeStamp; - } - onPostPointer = (e: React.PointerEvent) => { - if (this._ignore !== e.timeStamp) { - e.stopPropagation(); - } - } - onPostWheel = (e: React.WheelEvent) => { - if (this._ignore !== e.timeStamp) { - e.stopPropagation(); - } - } - - - - render() { - let content = -
- - - - - -
; - - let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); - - - return ( - <> -
- {content} -
- {!frozen ? (null) :
} - ); - } - - -} \ No newline at end of file diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx new file mode 100644 index 000000000..db9c922fc --- /dev/null +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -0,0 +1,339 @@ +import { observer } from "mobx-react"; +import React = require("react"); +import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView"; +import { FieldViewProps, FieldView } from "../nodes/FieldView"; +import { observable } from "mobx"; +import { DocumentDecorations } from "../DocumentDecorations"; +import { InkingControl } from "../InkingControl"; +import "../../views/nodes/WebBox.scss"; +import "./DashWebRTC.scss"; +import adapter from 'webrtc-adapter'; +import { DashWebRTC } from "./DashWebRTC"; + + + + +const mediaStreamConstraints = { + video: true, +}; + +const offerOptions = { + offerToReceiveVideo: 1, +}; + + +@observer +export class DashWebRTCVideo extends React.Component { + + @observable private localVideoEl: HTMLVideoElement | undefined; + @observable private peerVideoEl: HTMLVideoElement | undefined; + @observable private localStream: MediaStream | undefined; + @observable private startTime: any = null; + @observable private remoteStream: MediaStream | undefined; + @observable private localPeerConnection: any; + @observable private remotePeerConnection: any; + private callButton: HTMLButtonElement | undefined; + private startButton: HTMLButtonElement | undefined; + private hangupButton: HTMLButtonElement | undefined; + + componentDidMount() { + DashWebRTC.setVideoObjects(this.localVideoEl!, this.peerVideoEl!); + } + + + // componentDidMount() { + // this.callButton!.disabled = true; + // this.hangupButton!.disabled = true; + // // navigator.mediaDevices.getUserMedia(mediaStreamConstraints).then(this.gotLocalMediaStream).catch(this.handleLocalMediaStreamError); + // this.localVideoEl!.addEventListener('loadedmetadata', this.logVideoLoaded); + // this.peerVideoEl!.addEventListener('loadedmetadata', this.logVideoLoaded); + // this.peerVideoEl!.addEventListener('onresize', this.logResizedVideo); + // } + + + // gotLocalMediaStream = (mediaStream: MediaStream) => { + // this.localStream = mediaStream; + // if (this.localVideoEl) { + // this.localVideoEl.srcObject = mediaStream; + // } + // this.trace('Received local stream.'); + // this.callButton!.disabled = false; + + // } + + // gotRemoteMediaStream = (event: MediaStreamEvent) => { + // let mediaStream = event.stream; + // this.peerVideoEl!.srcObject = mediaStream; + // this.remoteStream = mediaStream!; + + // } + + // handleLocalMediaStreamError = (error: string) => { + // //console.log("navigator.getUserMedia error: ", error); + // this.trace(`navigator.getUserMedia error: ${error.toString()}.`); + + // } + + // logVideoLoaded = (event: any) => { + // let video = event.target; + // this.trace(`${video.id} videoWidth: ${video.videoWidth}px, ` + + // `videoHeight: ${video.videoHeight}px.`); + // } + + // logResizedVideo = (event: any) => { + // this.logVideoLoaded(event); + + // if (this.startTime) { + // let elapsedTime = window.performance.now() - this.startTime; + // this.startTime = null; + // this.trace(`Setup time: ${elapsedTime.toFixed(3)}ms.`); + // } + + // } + + // handleConnection = (event: any) => { + // let peerConnection = event.target; + // let iceCandidate = event.candidate; + + // if (iceCandidate) { + // let newIceCandidate: RTCIceCandidate = new RTCIceCandidate(iceCandidate); + // let otherPeer: any = this.getOtherPeer(peerConnection); + + // otherPeer.addIceCandidate(newIceCandidate).then(() => { + // this.handleConnectionSuccess(peerConnection); + // }).catch((error: any) => { + // this.handleConnectionFailure(peerConnection, error); + // }); + + // this.trace(`${this.getPeerName(peerConnection)} ICE candidate:\n` + + // `${event.candidate.candidate}.`); + + // } + // } + + // // Logs that the connection succeeded. + // handleConnectionSuccess = (peerConnection: any) => { + // this.trace(`${this.getPeerName(peerConnection)} addIceCandidate success.`); + // } + + // handleConnectionFailure = (peerConnection: any, error: any) => { + // this.trace(`${this.getPeerName(peerConnection)} failed to add ICE Candidate:\n` + + // `${error.toString()}.`); + // } + + // // Logs changes to the connection state. + // handleConnectionChange = (event: any) => { + // let peerConnection = event.target; + // console.log('ICE state change event: ', event); + // this.trace(`${this.getPeerName(peerConnection)} ICE state: ` + + // `${peerConnection.iceConnectionState}.`); + // } + + // // Logs error when setting session description fails. + // setSessionDescriptionError = (error: any) => { + // this.trace(`Failed to create session description: ${error.toString()}.`); + // } + + // // Logs success when setting session description. + // setDescriptionSuccess = (peerConnection: any, functionName: any) => { + // let peerName = this.getPeerName(peerConnection); + // this.trace(`${peerName} ${functionName} complete.`); + // } + + + // // Logs success when localDescription is set. + // setLocalDescriptionSuccess = (peerConnection: any) => { + // this.setDescriptionSuccess(peerConnection, 'setLocalDescription'); + // } + + // // Logs success when remoteDescription is set. + // setRemoteDescriptionSuccess = (peerConnection: any) => { + // this.setDescriptionSuccess(peerConnection, 'setRemoteDescription'); + // } + + // createdOffer = (description: any) => { + // this.trace(`Offer from localPeerConnection:\n${description.sdp}`); + // this.trace('localPeerConnection setLocalDescription start.'); + + // this.localPeerConnection.setLocalDescription(description).then(() => { + // this.setLocalDescriptionSuccess(this.localPeerConnection); + // }).catch(this.setSessionDescriptionError); + + + // this.trace('remotePeerConnection setRemoteDescription start.'); + // this.remotePeerConnection.setRemoteDescription(description) + // .then(() => { + // this.setRemoteDescriptionSuccess(this.remotePeerConnection); + // }).catch(this.setSessionDescriptionError); + + // this.trace('remotePeerConnection createAnswer start.'); + // this.remotePeerConnection.createAnswer() + // .then(this.createdAnswer) + // .catch(this.setSessionDescriptionError); + + // } + + // createdAnswer = (description: any) => { + // this.trace(`Answer from remotePeerConnection:\n${description.sdp}.`); + + // this.trace('remotePeerConnection setLocalDescription start.'); + // this.remotePeerConnection.setLocalDescription(description) + // .then(() => { + // this.setLocalDescriptionSuccess(this.remotePeerConnection); + // }).catch(this.setSessionDescriptionError); + + // this.trace('localPeerConnection setRemoteDescription start.'); + // this.localPeerConnection.setRemoteDescription(description) + // .then(() => { + // this.setRemoteDescriptionSuccess(this.localPeerConnection); + // }).catch(this.setSessionDescriptionError); + // } + + + // startAction = () => { + // this.startButton!.disabled = true; + // navigator.mediaDevices.getUserMedia(mediaStreamConstraints) + // .then(this.gotLocalMediaStream).catch(this.handleLocalMediaStreamError); + // this.trace('Requesting local stream.'); + // } + + + // // Handles call button action: creates peer connection. + // callAction = () => { + // this.callButton!.disabled = true; + // this.hangupButton!.disabled = false; + + // this.trace('Starting call.'); + // this.startTime = window.performance.now(); + + // // Get local media stream tracks. + // const videoTracks = this.localStream!.getVideoTracks(); + // const audioTracks = this.localStream!.getAudioTracks(); + // if (videoTracks.length > 0) { + // this.trace(`Using video device: ${videoTracks[0].label}.`); + // } + // if (audioTracks.length > 0) { + // this.trace(`Using audio device: ${audioTracks[0].label}.`); + // } + + // let servers: RTCConfiguration | undefined = undefined; // Allows for RTC server configuration. + + // // Create peer connections and add behavior. + // this.localPeerConnection = new RTCPeerConnection(servers); + // this.trace('Created local peer connection object localPeerConnection.'); + + // this.localPeerConnection.addEventListener('icecandidate', this.handleConnection); + // this.localPeerConnection.addEventListener( + // 'iceconnectionstatechange', this.handleConnectionChange); + + // this.remotePeerConnection = new RTCPeerConnection(servers); + // this.trace('Created remote peer connection object remotePeerConnection.'); + + // this.remotePeerConnection.addEventListener('icecandidate', this.handleConnection); + // this.remotePeerConnection.addEventListener( + // 'iceconnectionstatechange', this.handleConnectionChange); + // this.remotePeerConnection.addEventListener('addstream', this.gotRemoteMediaStream); + + // // Add local stream to connection and create offer to connect. + // this.localPeerConnection.addStream(this.localStream); + // this.trace('Added local stream to localPeerConnection.'); + + // this.trace('localPeerConnection createOffer start.'); + // this.localPeerConnection.createOffer(offerOptions) + // .then(this.createdOffer).catch(this.setSessionDescriptionError); + // } + + + // // Handles hangup action: ends up call, closes connections and resets peers. + // hangupAction = () => { + // this.localPeerConnection.close(); + // this.remotePeerConnection.close(); + // this.localPeerConnection = null; + // this.remotePeerConnection = null; + // this.hangupButton!.disabled = true; + // this.callButton!.disabled = false; + // this.trace('Ending call.'); + // } + + // // Gets the "other" peer connection. + // getOtherPeer = (peerConnection: any) => { + // return (peerConnection === this.localPeerConnection) ? + // this.remotePeerConnection : this.localPeerConnection; + // } + + // // Gets the name of a certain peer connection. + // getPeerName = (peerConnection: any) => { + // return (peerConnection === this.localPeerConnection) ? + // 'localPeerConnection' : 'remotePeerConnection'; + // } + + // // Logs an action (text) and the time when it happened on the console. + // trace = (text: string) => { + // text = text.trim(); + // const now = (window.performance.now() / 1000).toFixed(3); + + // console.log(now, text); + // } + + + + + + + + + + + + + + + + public static LayoutString() { return FieldView.LayoutString(DashWebRTCVideo); } + + + _ignore = 0; + onPreWheel = (e: React.WheelEvent) => { + this._ignore = e.timeStamp; + } + onPrePointer = (e: React.PointerEvent) => { + this._ignore = e.timeStamp; + } + onPostPointer = (e: React.PointerEvent) => { + if (this._ignore !== e.timeStamp) { + e.stopPropagation(); + } + } + onPostWheel = (e: React.WheelEvent) => { + if (this._ignore !== e.timeStamp) { + e.stopPropagation(); + } + } + + + + render() { + let content = +
+ + + {/* + + */} +
; + + let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; + let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); + + + return ( + <> +
+ {content} +
+ {!frozen ? (null) :
} + ); + } + + +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From dac1b07c4b5ed42d50531e66ea3a1561f62ed11e Mon Sep 17 00:00:00 2001 From: Mohammad Amoush <47069173+mamoush34@users.noreply.github.com> Date: Sun, 9 Feb 2020 14:33:51 -0500 Subject: CleanUp --- src/client/documents/Documents.ts | 1 - src/client/views/webcam/DashWebCam.tsx | 396 --------------------------- src/client/views/webcam/DashWebRTC.scss | 23 -- src/client/views/webcam/DashWebRTC.ts | 314 --------------------- src/client/views/webcam/DashWebRTCVideo.scss | 18 -- src/client/views/webcam/DashWebRTCVideo.tsx | 312 +-------------------- src/client/views/webcam/WebCamLogic.js | 6 - src/server/Message.ts | 2 - src/server/Websocket/Websocket.ts | 47 ---- 9 files changed, 1 insertion(+), 1118 deletions(-) delete mode 100644 src/client/views/webcam/DashWebCam.tsx delete mode 100644 src/client/views/webcam/DashWebRTC.scss delete mode 100644 src/client/views/webcam/DashWebRTC.ts (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ba0f69846..7233fbea5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -44,7 +44,6 @@ import { ComputedField, ScriptField } from "../../new_fields/ScriptField"; import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; -import { DashWebCam } from "../views/webcam/DashWebCam"; import { PresElementBox } from "../views/presentationview/PresElementBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { QueryBox } from "../views/nodes/QueryBox"; diff --git a/src/client/views/webcam/DashWebCam.tsx b/src/client/views/webcam/DashWebCam.tsx deleted file mode 100644 index a9669750f..000000000 --- a/src/client/views/webcam/DashWebCam.tsx +++ /dev/null @@ -1,396 +0,0 @@ -import React = require("react"); -import { observer } from "mobx-react"; -import { FieldViewProps, FieldView } from "../nodes/FieldView"; -import { observable, action } from "mobx"; -import { DocumentDecorations } from "../DocumentDecorations"; -import { InkingControl } from "../InkingControl"; -import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView"; -import "../../views/nodes/WebBox.scss"; - - -//https://github.com/mozmorris/react-webcam is the source code used for the bigger part of this implementation. It's only modified to fit our current system. - - -function hasGetUserMedia() { - return !!( - (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)); -} - -interface WebcamProps { - audio: boolean; - audioConstraints?: MediaStreamConstraints["audio"]; - imageSmoothing: boolean; - minScreenshotHeight?: number; - minScreenshotWidth?: number; - onUserMedia: () => void; - onUserMediaError: (error: string) => void; - screenshotFormat: "image/webp" | "image/png" | "image/jpeg"; - screenshotQuality: number; - videoConstraints?: MediaStreamConstraints["video"]; -} - -interface WebcamState { - hasUserMedia: boolean; - src?: string; -} - -@observer -export class DashWebCam extends React.Component & { - layoutKey: string, -}, WebcamState> { - static defaultProps = { - audio: true, - imageSmoothing: true, - onUserMedia: () => { }, - onUserMediaError: () => { }, - screenshotFormat: "image/webp", - screenshotQuality: 0.92 - }; - - private static mountedInstances: DashWebCam[] = []; - private static userMediaRequested = false; - private canvas: HTMLCanvasElement | undefined; - private ctx: CanvasRenderingContext2D | null = null; - private stream: MediaStream | undefined; - private video: HTMLVideoElement | null | undefined; - - // @observable private hasUserMedia: boolean | undefined; - // @observable private src: string | undefined; - - constructor(props: any) { - super(props); - this.state = { - hasUserMedia: false - }; - } - - componentDidMount() { - if (!hasGetUserMedia()) return; - - const { state } = this; - - DashWebCam.mountedInstances.push(this); - - if (!state.hasUserMedia && !DashWebCam.userMediaRequested) { - this.requestUserMedia(); - } - } - - componentDidUpdate(nextProps: WebcamProps) { - const { props } = this; - if ( - JSON.stringify(nextProps.audioConstraints) !== - JSON.stringify(props.audioConstraints) || - JSON.stringify(nextProps.videoConstraints) !== - JSON.stringify(props.videoConstraints) - ) { - this.requestUserMedia(); - } - } - - componentWillUnmount() { - const { state } = this; - const index = DashWebCam.mountedInstances.indexOf(this); - DashWebCam.mountedInstances.splice(index, 1); - - DashWebCam.userMediaRequested = false; - if (DashWebCam.mountedInstances.length === 0 && state.hasUserMedia) { - if (this.stream!.getVideoTracks && this.stream!.getAudioTracks) { - this.stream!.getVideoTracks().map(track => track.stop()); - this.stream!.getAudioTracks().map(track => track.stop()); - } else { - ((this.stream as unknown) as MediaStreamTrack).stop(); - } - - if (state.src) { - window.URL.revokeObjectURL(state.src); - } - } - } - - //These are for screenshot if wanted. - - // getScreenshot() { - // const { state, props } = this; - - // if (!state.hasUserMedia) return null; - - // const canvas = this.getCanvas(); - // return ( - // canvas && - // canvas.toDataURL(props.screenshotFormat, props.screenshotQuality) - // ); - // } - - // getCanvas() { - // const { state, props } = this; - - // if (!this.video) { - // return null; - // } - - // if (!state.hasUserMedia || !this.video.videoHeight) return null; - - // if (!this.ctx) { - // const canvas = document.createElement("canvas"); - // const aspectRatio = this.video.videoWidth / this.video.videoHeight; - - // let canvasWidth = props.minScreenshotWidth || this.video.clientWidth; - // let canvasHeight = canvasWidth / aspectRatio; - - // if ( - // props.minScreenshotHeight && - // canvasHeight < props.minScreenshotHeight - // ) { - // canvasHeight = props.minScreenshotHeight; - // canvasWidth = canvasHeight * aspectRatio; - // } - - // canvas.width = canvasWidth; - // canvas.height = canvasHeight; - - // this.canvas = canvas; - // this.ctx = canvas.getContext("2d"); - // } - - // const { ctx, canvas } = this; - - // if (ctx) { - // ctx.imageSmoothingEnabled = props.imageSmoothing; - // ctx.drawImage(this.video, 0, 0, canvas!.width, canvas!.height); - // } - - // return canvas; - // } - - requestUserMedia() { - const { props } = this; - - navigator.getUserMedia = - navigator.mediaDevices.getUserMedia; - - const sourceSelected = (audioConstraints: any, videoConstraints: any) => { - const constraints: MediaStreamConstraints = { - video: typeof videoConstraints !== "undefined" ? videoConstraints : true - }; - - if (props.audio) { - constraints.audio = - typeof audioConstraints !== "undefined" ? audioConstraints : true; - } - - navigator.mediaDevices - .getUserMedia(constraints) - .then(stream => { - DashWebCam.mountedInstances.forEach(instance => - instance.handleUserMedia(null, stream) - ); - }) - .catch(e => { - DashWebCam.mountedInstances.forEach(instance => - instance.handleUserMedia(e) - ); - }); - }; - - if ("mediaDevices" in navigator) { - sourceSelected(props.audioConstraints, props.videoConstraints); - } else { - const optionalSource = (id: any) => ({ optional: [{ sourceId: id }] }); - - const constraintToSourceId = (constraint: any) => { - const { deviceId } = constraint; - - if (typeof deviceId === "string") { - return deviceId; - } - - if (Array.isArray(deviceId) && deviceId.length > 0) { - return deviceId[0]; - } - - if (typeof deviceId === "object" && deviceId.ideal) { - return deviceId.ideal; - } - - return null; - }; - - // @ts-ignore: deprecated api - MediaStreamTrack.getSources(sources => { - let audioSource = null; - let videoSource = null; - - sources.forEach((source: { kind: string; id: any; }) => { - if (source.kind === "audio") { - audioSource = source.id; - } else if (source.kind === "video") { - videoSource = source.id; - } - }); - - const audioSourceId = constraintToSourceId(props.audioConstraints); - if (audioSourceId) { - audioSource = audioSourceId; - } - - const videoSourceId = constraintToSourceId(props.videoConstraints); - if (videoSourceId) { - videoSource = videoSourceId; - } - - sourceSelected( - optionalSource(audioSource), - optionalSource(videoSource) - ); - }); - } - - DashWebCam.userMediaRequested = true; - } - - handleUserMedia(err: string | null, stream?: MediaStream) { - const { props } = this; - - if (err || !stream) { - this.setState({ hasUserMedia: false }); - // action(() => this.hasUserMedia = false); - props.onUserMediaError(err!); - - return; - } - - this.stream = stream; - - console.log("Stream done: ", stream); - - try { - if (this.video) { - this.video.srcObject = stream; - console.log("Source object: ", stream); - - } - this.setState({ hasUserMedia: true }); - // action(() => this.hasUserMedia = true); - - } catch (error) { - this.setState({ - hasUserMedia: true, - src: window.URL.createObjectURL(stream) - }); - console.log("State src set: ", this.state.src); - - // action(() => this.hasUserMedia = true); - // action(() => this.src = window.URL.createObjectURL(stream)); - } - - props.onUserMedia(); - } - - _ignore = 0; - onPreWheel = (e: React.WheelEvent) => { - this._ignore = e.timeStamp; - } - onPrePointer = (e: React.PointerEvent) => { - this._ignore = e.timeStamp; - } - onPostPointer = (e: React.PointerEvent) => { - if (this._ignore !== e.timeStamp) { - e.stopPropagation(); - } - } - onPostWheel = (e: React.WheelEvent) => { - if (this._ignore !== e.timeStamp) { - e.stopPropagation(); - } - } - - - - - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DashWebCam, fieldKey); } - - render() { - const { state, props } = this; - - - - const { - audio, - onUserMedia, - onUserMediaError, - screenshotFormat, - screenshotQuality, - minScreenshotWidth, - minScreenshotHeight, - audioConstraints, - videoConstraints, - imageSmoothing, - fieldKey, - fitToBox, - ContainingCollectionView, - Document, - DataDoc, - onClick, - isSelected, - select, - renderDepth, - addDocument, - addDocTab, - pinToPres, - removeDocument, - moveDocument, - ScreenToLocalTransform, - active, - whenActiveChanged, - focus, - PanelWidth, - PanelHeight, - setVideoBox, - ContentScaling, - ChromeHeight, - jitterRotation, - backgroundColor, - bringToFront, - zoomToScale, - getScale, - animateBetweenIcon, - layoutKey, - ...rest - } = props; - - console.log("Source produced: ", state.src); - - - - let content = -
-
; - - - let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); - - return ( - <> -
- {content} -
- {!frozen ? (null) :
} - ); - - } -} \ No newline at end of file diff --git a/src/client/views/webcam/DashWebRTC.scss b/src/client/views/webcam/DashWebRTC.scss deleted file mode 100644 index ddf4777a8..000000000 --- a/src/client/views/webcam/DashWebRTC.scss +++ /dev/null @@ -1,23 +0,0 @@ -.webcam-cont { - button { - margin: 10px; - position: relative; - top: 20%; - left: -60%; - } - - #localVideo { - margin: 10px; - position: relative; - width: 300px; - max-height: 300px; - } - - #remoteVideo { - margin: 10px; - position: relative; - width: 300px; - max-height: 300px; - - } -} \ No newline at end of file diff --git a/src/client/views/webcam/DashWebRTC.ts b/src/client/views/webcam/DashWebRTC.ts deleted file mode 100644 index ef5ecf0fc..000000000 --- a/src/client/views/webcam/DashWebRTC.ts +++ /dev/null @@ -1,314 +0,0 @@ -// import { DocServer } from '../../DocServer'; -// import { Utils } from '../../../Utils'; -// import { MessageStore } from '../../../server/Message'; - - - -// /** -// * This namespace will have the code required to have functionality code for the usage of webRTC. -// */ -// export class DashWebRTC { - - -// private isChannelReady = false; -// private isInitiator = false; -// private isStarted = false; -// localStream: MediaStream | undefined; -// private pc: any; -// remoteStream: MediaStream | undefined; -// private turnReady: boolean | undefined; -// localVideo: HTMLVideoElement | undefined; -// remoteVideo: HTMLVideoElement | undefined; -// curRoom: string = ""; - - -// private pcConfig: any; -// private sdpConstraints: any; - -// constructor() { -// this.pcConfig = { -// 'iceServers': [{ -// 'urls': 'stun:stun.l.google.com:19302' -// }] -// }; - -// // Set up audio and video regardless of what devices are present. -// this.sdpConstraints = { -// offerToReceiveAudio: true, -// offerToReceiveVideo: true -// }; -// } - - - -// 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', 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 === 'bye' && self.isStarted) { -// self.handleRemoteHangup(); -// } -// }); - -// navigator.mediaDevices.getUserMedia({ -// audio: false, -// 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' -// ); -// } - - -// } - - -// 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); -// } - - - - - -// setVideoObjects(localVideo: HTMLVideoElement, remoteVideo: HTMLVideoElement) { -// this.localVideo = localVideo; -// this.remoteVideo = remoteVideo; -// } - -// setLocalVideoObject(localVideoRef: HTMLVideoElement) { -// this.localVideo = localVideoRef; -// } - -// setRemoteVideoObject(remoteVideoRef: HTMLVideoElement) { -// this.remoteVideo = remoteVideoRef; -// } - - - - -// gotStream(stream: any) { -// console.log('Adding local stream.'); -// this.localStream = stream; -// this.localVideo!.srcObject = stream; -// this.sendMessage('got user media'); -// if (this.isInitiator) { -// this.maybeStart(); -// } -// } - -// constraints = { -// video: true, -// audio: true -// }; - - - - - -// 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'); -// // }; - -// 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; -// } -// } - -// 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.'); -// } -// } - -// handleCreateOfferError(event: any) { -// console.log('createOffer() error: ', event); -// } - -// doCall() { -// console.log('Sending offer to peer'); -// this.pc.createOffer(this.setLocalAndSendMessage, this.handleCreateOfferError); -// } - -// doAnswer() { -// console.log('Sending answer to peer.'); -// this.pc.createAnswer().then( -// this.setLocalAndSendMessage, -// this.onCreateSessionDescriptionError -// ); -// } - -// setLocalAndSendMessage(sessionDescription: any) { -// this.pc.setLocalDescription(sessionDescription); -// console.log('setLocalAndSendMessage sending message', sessionDescription); -// this.sendMessage(sessionDescription); -// } - -// onCreateSessionDescriptionError(error: any) { -// console.log('Failed to create session description: ' + error.toString()); -// } - - -// 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(); -// } -// } - -// handleRemoteStreamAdded(event: MediaStreamEvent) { -// console.log('Remote stream added.'); -// this.remoteStream = event.stream!; -// this.remoteVideo!.srcObject = this.remoteStream; -// } - -// handleRemoteStreamRemoved(event: MediaStreamEvent) { -// console.log('Remote stream removed. Event: ', event); -// } - -// hangup() { -// console.log('Hanging up.'); -// if (this.pc) { -// stop(); -// this.sendMessage('bye'); -// } - -// if (this.localStream) { -// this.localStream.getTracks().forEach(track => track.stop()); -// } - -// } - -// handleRemoteHangup() { -// console.log('Session terminated.'); -// stop(); -// this.isInitiator = false; -// } - -// stop() { -// this.isStarted = false; -// this.pc.close(); -// this.pc = null; -// } - - -// } \ No newline at end of file diff --git a/src/client/views/webcam/DashWebRTCVideo.scss b/src/client/views/webcam/DashWebRTCVideo.scss index 052832db5..2f35eeca2 100644 --- a/src/client/views/webcam/DashWebRTCVideo.scss +++ b/src/client/views/webcam/DashWebRTCVideo.scss @@ -1,7 +1,6 @@ @import "../globalCssVariables"; .webcam-cont { - // position: absolute; background: whitesmoke; color: grey; border-radius: 15px; @@ -27,15 +26,6 @@ border: 1px solid #BBBBBBBB; } - // #localVideo { - // width: 50%; - // height: 50%; - // position: relative; - // // top: 65%; - // // z-index: 2; - // // right: 5%; - // } - .side { width: 25%; height: 20%; @@ -53,12 +43,4 @@ 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 f93d4a662..0eefbbc91 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -8,60 +8,19 @@ import { InkingControl } from "../InkingControl"; import "../../views/nodes/WebBox.scss"; import "./DashWebRTCVideo.scss"; import adapter from 'webrtc-adapter'; -import { DocServer } from "../../DocServer"; -import { DocumentView } from "../nodes/DocumentView"; -import { Utils } from "../../../Utils"; -import { MessageStore } from "../../../server/Message"; import { initialize, hangup } from "./WebCamLogic"; -const mediaStreamConstraints = { - video: true, -}; - -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 - // }; componentDidMount() { DocumentDecorations.Instance.addCloseCall(this.closeConnection); - // setTimeout(() => initialize(), 10000); - // let self = this; - // window.onbeforeunload = function () { - // self.sendMessage('bye'); - // }; } closeConnection: CloseCall = () => { @@ -73,273 +32,6 @@ export class DashWebRTCVideo extends React.Component { - // 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(); - // } - // } - - // 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 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 handleRemoteStreamRemoved = (event: MediaStreamEvent) => { - // console.log('Remote stream removed. Event: ', event); - // } - - // private hangup = () => { - // console.log('Hanging up.'); - // if (this.pc) { - // stop(); - // this.sendMessage('bye'); - // } - - // if (this.localStream) { - // this.localStream.getTracks().forEach(track => track.stop()); - // } - - // } - - // private handleRemoteHangup = () => { - // console.log('Session terminated.'); - // this.stop(); - // this.isInitiator = false; - - // if (this.localStream) { - // this.localStream.getTracks().forEach(track => track.stop()); - // } - - - // } - - // private stop = () => { - // this.isStarted = false; - // this.pc.close(); - // this.pc = null; - // } - - - - - - /** * Function that submits the title entered by user on enter press. */ @@ -379,10 +71,8 @@ export class DashWebRTCVideo extends React.ComponentDashWebRTC
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 96b2f5f5334fb475180a095905e19e45a0414233 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 9 Feb 2020 17:02:43 -0500 Subject: from added range sliders --- package-lock.json | 78 ++++--- package.json | 1 + src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 8 + .../views/collections/CollectionTimeView.tsx | 57 ++++- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 +- src/client/views/nodes/DocumentContentsView.tsx | 4 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/KeyValueBox.scss | 4 +- src/client/views/nodes/SliderBox-components.tsx | 256 +++++++++++++++++++++ src/client/views/nodes/SliderBox-tooltip.css | 33 +++ src/client/views/nodes/SliderBox.scss | 8 + src/client/views/nodes/SliderBox.tsx | 128 +++++++++++ src/new_fields/Doc.ts | 22 +- 14 files changed, 564 insertions(+), 53 deletions(-) create mode 100644 src/client/views/nodes/SliderBox-components.tsx create mode 100644 src/client/views/nodes/SliderBox-tooltip.css create mode 100644 src/client/views/nodes/SliderBox.scss create mode 100644 src/client/views/nodes/SliderBox.tsx (limited to 'src/client/documents/Documents.ts') diff --git a/package-lock.json b/package-lock.json index 379cd3337..d1c169bed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -647,7 +647,7 @@ }, "@types/passport": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.0.tgz", "integrity": "sha512-Pf39AYKf8q+YoONym3150cEwfUD66dtwHJWvbeOzKxnA0GZZ/vAXhNWv9vMhKyRQBQZiQyWQnhYBEBlKW6G8wg==", "requires": { "@types/express": "*" @@ -3776,6 +3776,11 @@ "type": "^1.0.1" } }, + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, "d3-format": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.3.tgz", @@ -5391,8 +5396,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -5410,13 +5414,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5429,18 +5431,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -5543,8 +5542,7 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -5554,7 +5552,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5567,20 +5564,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.9.0", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5597,7 +5591,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5678,8 +5671,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -5689,7 +5681,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5765,8 +5756,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -5796,7 +5786,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5814,7 +5803,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5853,13 +5841,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.1.1", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -13518,6 +13504,27 @@ "tinycolor2": "^1.4.1" } }, + "react-compound-slider": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/react-compound-slider/-/react-compound-slider-2.5.0.tgz", + "integrity": "sha512-T84FtSI0bkQPmH5GaaHbL+2McOyIR6M5sqS80dqw/bHc5r2UKLYY64BWTbsL+XO0jlx7REuJJnZUBqo4eSRl7g==", + "requires": { + "@babel/runtime": "^7.7.7", + "d3-array": "^1.2.4", + "prop-types": "^15.7.2", + "warning": "^3.0.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", + "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + } + } + }, "react-dimensions": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/react-dimensions/-/react-dimensions-1.3.1.tgz", @@ -13804,7 +13811,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -14244,7 +14251,8 @@ "fsevents": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==" + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "optional": true }, "get-caller-file": { "version": "2.0.5", @@ -15722,7 +15730,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -17949,7 +17957,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", diff --git a/package.json b/package.json index 3b87a9a47..c71d321f4 100644 --- a/package.json +++ b/package.json @@ -205,6 +205,7 @@ "react-bootstrap": "^1.0.0-beta.16", "react-bootstrap-dropdown-menu": "^1.1.15", "react-color": "^2.18.0", + "react-compound-slider": "^2.5.0", "react-dimensions": "^1.3.1", "react-dom": "^16.12.0", "react-golden-layout": "^1.0.6", diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 8f96b2fa6..6ef23ef08 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -14,6 +14,7 @@ export enum DocumentType { LINK = "link", LINKDOC = "linkdoc", BUTTON = "button", + SLIDER = "slider", TEMPLATE = "template", EXTENSION = "extension", YOUTUBE = "youtube", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d647b34e6..3d43c6eb2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -37,6 +37,7 @@ import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; +import { SliderBox } from "../views/nodes/SliderBox"; import { FontIconBox } from "../views/nodes/FontIconBox"; import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; import { PresBox } from "../views/nodes/PresBox"; @@ -230,6 +231,9 @@ export namespace Docs { [DocumentType.BUTTON, { layout: { view: ButtonBox, dataField: data }, }], + [DocumentType.SLIDER, { + layout: { view: SliderBox, dataField: data }, + }], [DocumentType.PRES, { layout: { view: PresBox, dataField: data }, options: {} @@ -559,6 +563,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}) }); } + export function SliderDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.SLIDER), undefined, { ...(options || {}) }); + } + export function FontIconDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) }); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 4983acbc2..2a45c91dd 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -2,7 +2,6 @@ import { faEdit } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, trace, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Set } from "typescript-collections"; import { Doc, DocListCast, Field } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { RichTextField } from "../../../new_fields/RichTextField"; @@ -54,7 +53,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { const facets = new Set(); this.childDocs.forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.forEach(child => Object.keys(child).forEach(key => facets.add(key))); - return facets.toArray(); + return Array.from(facets); } /** @@ -73,12 +72,52 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { docFilter.splice(index, 3); } } + const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string")); + if (docRangeFilters) { + let index: number; + while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) { + docRangeFilters.splice(index, 3); + } + } } else { - const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); - const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; - const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; - newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); - Doc.AddDocToList(facetCollection, "data", newFacet); + const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]); + const facetValues = Array.from(allCollectionDocs.reduce((set, child) => + set.add(Field.toString(child[facetHeader] as Field)), new Set())); + + let nonNumbers = 0; + let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; + facetValues.map(val => { + const num = Number(val); + if (Number.isNaN(num)) { + nonNumbers++; + } else { + minVal = Math.min(num, minVal); + maxVal = Math.max(num, maxVal); + } + }); + if (nonNumbers / allCollectionDocs.length < .1) { + const newFacet = Docs.Create.SliderDocument({ title: facetHeader }); + newFacet.treeViewExpandedView = "layout"; + newFacet.treeViewOpen = true; + newFacet._sliderMin = minVal; + newFacet._sliderMax = maxVal; + newFacet._sliderMinThumb = minVal; + newFacet._sliderMaxThumb = maxVal; + newFacet.target = this.props.Document; + const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`; + newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); + + // const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; + // const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; + // newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); + Doc.AddDocToList(facetCollection, "data", newFacet); + } else { + const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); + const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; + const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; + newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); + Doc.AddDocToList(facetCollection, "data", newFacet); + } } } } @@ -120,7 +159,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { pair.layout[fieldKey] instanceof RichTextField || typeof (pair.layout[fieldKey]) === "number" || typeof (pair.layout[fieldKey]) === "string").map(fieldKey => keySet.add(fieldKey))); - keySet.toArray().map(fieldKey => + Array.from(keySet).map(fieldKey => docItems.push({ description: ":" + fieldKey, event: () => this.props.Document._pivotField = fieldKey, icon: "compress-arrows-alt" })); docItems.push({ description: ":(null)", event: () => this.props.Document._pivotField = undefined, icon: "compress-arrows-alt" }) ContextMenu.Instance.addItem({ description: "Pivot Fields ...", subitems: docItems, icon: "eye" }); @@ -227,7 +266,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
- + this._facetWidth} Document={facetCollection} />
; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2518a4a55..799627969 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -828,6 +828,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get filterDocs() { const docFilters = Cast(this.props.Document._docFilter, listSpec("string"), []); + const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"), []); const clusters: { [key: string]: { [value: string]: string } } = {}; for (let i = 0; i < docFilters.length; i += 3) { const [key, value, modifiers] = docFilters.slice(i, i + 3); @@ -853,7 +854,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } return true; }) : this.childDocs; - return filteredDocs; + const rangeFilteredDocs = docRangeFilters.length ? filteredDocs.filter(d => { + for (let i = 0; i < docRangeFilters.length; i += 3) { + const key = docRangeFilters[i]; + const min = Number(docRangeFilters[i + 1]); + const max = Number(docRangeFilters[i + 2]); + const val = Cast(d[key], "number", null); + if (val !== undefined && (val < min || val > max)) { + return false; + } + } + return true; + }) : this.childDocs; + return rangeFilteredDocs; } get doLayoutComputation() { const { newPool, computedElementData } = this.doInternalLayoutComputation; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 3b9015994..284600bb4 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -1,7 +1,6 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../new_fields/Doc"; -import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, StrCast } from "../../../new_fields/Types"; import { OmitKeys, Without } from "../../../Utils"; import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; @@ -14,6 +13,7 @@ import { LinkFollowBox } from "../linking/LinkFollowBox"; import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; import { AudioBox } from "./AudioBox"; import { ButtonBox } from "./ButtonBox"; +import { SliderBox } from "./SliderBox"; import { DocumentBox } from "./DocumentBox"; import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; @@ -104,7 +104,7 @@ export class DocumentContentsView extends React.Component 2 && ret.start >= 0) { let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start if (ret.frag.firstChild) { - selection = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected + selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected } editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView()); const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight); diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss index 6e8a36c6a..a26880c9e 100644 --- a/src/client/views/nodes/KeyValueBox.scss +++ b/src/client/views/nodes/KeyValueBox.scss @@ -74,7 +74,7 @@ $header-height: 30px; .keyValueBox-evenRow { position: relative; - display: inline-block; + display: flex; width:100%; height:$header-height; background: $light-color; @@ -114,7 +114,7 @@ $header-height: 30px; .keyValueBox-oddRow { position: relative; - display: inline-block; + display: flex; width:100%; height:30px; background: $light-color-secondary; diff --git a/src/client/views/nodes/SliderBox-components.tsx b/src/client/views/nodes/SliderBox-components.tsx new file mode 100644 index 000000000..a38cad459 --- /dev/null +++ b/src/client/views/nodes/SliderBox-components.tsx @@ -0,0 +1,256 @@ +import * as React from "react"; +import { SliderItem } from "react-compound-slider"; +import "./SliderBox-tooltip.css"; + +const { Component, Fragment } = React; + +// ******************************************************* +// TOOLTIP RAIL +// ******************************************************* +const railStyle: React.CSSProperties = { + position: "absolute", + width: "100%", + height: 40, + top: -13, + borderRadius: 7, + cursor: "pointer", + opacity: 0.3, + zIndex: 300, + border: "1px solid grey" +}; + +const railCenterStyle: React.CSSProperties = { + position: "absolute", + width: "100%", + height: 14, + borderRadius: 7, + cursor: "pointer", + pointerEvents: "none", + backgroundColor: "rgb(155,155,155)" +}; + +interface TooltipRailProps { + activeHandleID: string; + getRailProps: (props: object) => object; + getEventData: (e: Event) => object; +} + +export class TooltipRail extends Component { + state = { + value: null, + percent: null + }; + + static defaultProps = { + disabled: false + }; + + onMouseEnter = () => { + document.addEventListener("mousemove", this.onMouseMove); + }; + + onMouseLeave = () => { + this.setState({ value: null, percent: null }); + document.removeEventListener("mousemove", this.onMouseMove); + }; + + onMouseMove = (e: Event) => { + const { activeHandleID, getEventData } = this.props; + + if (activeHandleID) { + this.setState({ value: null, percent: null }); + } else { + this.setState(getEventData(e)); + } + }; + + render() { + const { value, percent } = this.state; + const { activeHandleID, getRailProps } = this.props; + + return ( + + {!activeHandleID && value ? ( +
+
+ Value: {value} +
+
+ ) : null} +
+
+ + ); + } +} + +// ******************************************************* +// HANDLE COMPONENT +// ******************************************************* +interface HandleProps { + key: string; + handle: SliderItem; + isActive: Boolean; + disabled?: Boolean; + domain: number[]; + getHandleProps: (id: string, config: object) => object; +} + +export class Handle extends Component { + static defaultProps = { + disabled: false + }; + + state = { + mouseOver: false + }; + + onMouseEnter = () => { + this.setState({ mouseOver: true }); + }; + + onMouseLeave = () => { + this.setState({ mouseOver: false }); + }; + + render() { + const { + domain: [min, max], + handle: { id, value, percent }, + isActive, + disabled, + getHandleProps + } = this.props; + const { mouseOver } = this.state; + + return ( + + {(mouseOver || isActive) && !disabled ? ( +
+
+ Value: {value} +
+
+ ) : null} +
+ + ); + } +} + +// ******************************************************* +// TRACK COMPONENT +// ******************************************************* +interface TrackProps { + source: SliderItem; + target: SliderItem; + disabled: Boolean; + getTrackProps: () => object; +} + +export function Track({ + source, + target, + getTrackProps, + disabled = false +}: TrackProps) { + return ( +
+ ); +} + +// ******************************************************* +// TICK COMPONENT +// ******************************************************* +interface TickProps { + tick: SliderItem; + count: number; + format: (val: number) => string; +} + +const defaultFormat = (d: number) => `d`; + +export function Tick({ tick, count, format = defaultFormat }: TickProps) { + return ( +
+
+
+ {format(tick.value)} +
+
+ ); +} diff --git a/src/client/views/nodes/SliderBox-tooltip.css b/src/client/views/nodes/SliderBox-tooltip.css new file mode 100644 index 000000000..8afde8eb5 --- /dev/null +++ b/src/client/views/nodes/SliderBox-tooltip.css @@ -0,0 +1,33 @@ +.tooltip { + position: relative; + display: inline-block; + border-bottom: 1px dotted #222; + margin-left: 22px; + } + + .tooltip .tooltiptext { + width: 100px; + background-color: #222; + color: #fff; + opacity: 0.8; + text-align: center; + border-radius: 6px; + padding: 5px 0; + position: absolute; + z-index: 1; + bottom: 150%; + left: 50%; + margin-left: -60px; + } + + .tooltip .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #222 transparent transparent transparent; + } + \ No newline at end of file diff --git a/src/client/views/nodes/SliderBox.scss b/src/client/views/nodes/SliderBox.scss new file mode 100644 index 000000000..4ef277d8c --- /dev/null +++ b/src/client/views/nodes/SliderBox.scss @@ -0,0 +1,8 @@ +.sliderBox-outerDiv { + width: 100%; + height: 100%; + pointer-events: all; + border-radius: inherit; + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx new file mode 100644 index 000000000..00d3baf7c --- /dev/null +++ b/src/client/views/nodes/SliderBox.tsx @@ -0,0 +1,128 @@ +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faEdit } from '@fortawesome/free-regular-svg-icons'; +import { computed } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Handles, Rail, Slider, Tracks, Ticks } from 'react-compound-slider'; +import { Doc } from '../../../new_fields/Doc'; +import { documentSchema } from '../../../new_fields/documentSchemas'; +import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; +import { ScriptField } from '../../../new_fields/ScriptField'; +import { BoolCast, FieldValue, StrCast, NumCast, Cast } from '../../../new_fields/Types'; +import { DragManager } from '../../util/DragManager'; +import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; +import { DocComponent } from '../DocComponent'; +import './SliderBox.scss'; +import { Handle, TooltipRail, Track, Tick } from './SliderBox-components'; +import { FieldView, FieldViewProps } from './FieldView'; + + +library.add(faEdit as any); + +const ButtonSchema = createSchema({ + onClick: ScriptField, + buttonParams: listSpec("string"), + text: "string" +}); + +type SliderDocument = makeInterface<[typeof ButtonSchema, typeof documentSchema]>; +const SliderDocument = makeInterface(ButtonSchema, documentSchema); + +@observer +export class SliderBox extends DocComponent(SliderDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); } + private dropDisposer?: DragManager.DragDropDisposer; + + @computed get dataDoc() { + return this.props.DataDoc && + (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) || + this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); + } + + specificContextMenu = (e: React.MouseEvent): void => { + const funcs: ContextMenuProps[] = []; + funcs.push({ + description: "Clear Script Params", event: () => { + const params = FieldValue(this.Document.buttonParams); + params && params.map(p => this.props.Document[p] = undefined); + }, icon: "trash" + }); + + ContextMenu.Instance.addItem({ description: "OnClick...", subitems: funcs, icon: "asterisk" }); + } + onChange = (values: readonly number[]) => { + Cast(this.props.Document.onThumbChanged, ScriptField, null)?.script.run({ range: values, this: this.props.Document }) + } + + render() { + const domain = [NumCast(this.props.Document._sliderMin), NumCast(this.props.Document._sliderMax)] + const defaultValues = [NumCast(this.props.Document._sliderMinThumb), NumCast(this.props.Document._sliderMaxThumb)]; + return ( +
e.stopPropagation()} + style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}> +
+ + + {railProps => } + + {({ handles, activeHandleID, getHandleProps }) => ( +
+ {handles.map(handle => ( + + ))} +
+ )} +
+ + {({ tracks, getTrackProps }) => ( +
+ {tracks.map(({ id, source, target }) => ( + + ))} +
+ )} +
+ + {({ ticks }) => ( +
+ {ticks.map((tick) => ( + val.toString()} + /> + ))} +
+ )} +
+
+
+
+ ); + } +} \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index b1c1fda05..3ccf4c49b 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -804,7 +804,22 @@ export namespace Doc { if (StrCast(doc.title).endsWith("_" + prevLayout)) doc.title = StrCast(doc.title).replace("_" + prevLayout, ""); doc.layoutKey = deiconify || "layout"; } - export function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { + export function setDocFilterRange(container: Doc, key: string, range?: number[]) { + const docFilters = Cast(container._docRangeFilters, listSpec("string"), []); + for (let i = 0; i < docFilters.length; i += 3) { + if (docFilters[i] === key) { + docFilters.splice(i, 3); + break; + } + } + if (range !== undefined) { + docFilters.push(key); + docFilters.push(range[0].toString()); + docFilters.push(range[1].toString()); + container._docRangeFilters = new List(docFilters); + } + } + export function setDocFilter(container: Doc, key: string, value: any, modifiers?: string | number) { const docFilters = Cast(container._docFilter, listSpec("string"), []); for (let i = 0; i < docFilters.length; i += 3) { if (docFilters[i] === key && docFilters[i + 1] === value) { @@ -812,7 +827,7 @@ export namespace Doc { break; } } - if (modifiers !== undefined) { + if (typeof modifiers === "string") { docFilters.push(key); docFilters.push(value); docFilters.push(modifiers); @@ -843,4 +858,5 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo const docs = DocListCast(Doc.UserDoc().SelectedDocs).filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCUMENT && d.type !== DocumentType.KVP && (!excludeCollections || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; }); -Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { Doc.setDocFilter(container, key, value, modifiers); }); \ No newline at end of file +Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { Doc.setDocFilter(container, key, value, modifiers); }); +Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, min: number, max: number) { Doc.setDocFilterRange(container, key, min, max); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 33556b484c54337427a81e8ac50d05996ec876b7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 10 Feb 2020 01:11:56 -0500 Subject: fixed filtering bug with pivot view. fixed flyout dismissing for facets. --- src/client/documents/Documents.ts | 1 + src/client/views/TemplateMenu.tsx | 10 +++------- src/client/views/collections/CollectionTimeView.tsx | 16 +++++++++------- src/client/views/collections/CollectionTreeView.tsx | 16 ++++++++-------- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- 5 files changed, 23 insertions(+), 24 deletions(-) (limited to 'src/client/documents/Documents.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 82112836b..2f12b50d0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -132,6 +132,7 @@ export interface DocumentOptions { strokeWidth?: number; color?: string; treeViewHideTitle?: boolean; // whether to hide the title of a tree view + treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. treeViewOpen?: boolean; // whether this document is expanded in a tree view treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked isFacetFilter?: boolean; // whether document functions as a facet filter in a tree view diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index f61eb9cd0..e03e4aa04 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -4,14 +4,11 @@ import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; import './TemplateMenu.scss'; import { DocumentView } from "./nodes/DocumentView"; -import { Template, Templates } from "./Templates"; +import { Template } from "./Templates"; import React = require("react"); import { Doc, DocListCast } from "../../new_fields/Doc"; import { StrCast, Cast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; -const higflyout = require("@hig/flyout"); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; @observer class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent, template: Template) => void }> { @@ -48,6 +45,8 @@ export interface TemplateMenuProps { @observer export class TemplateMenu extends React.Component { + _addedKeys = new ObservableSet(); + _customRef = React.createRef(); @observable private _hidden: boolean = true; toggleLayout = (e: React.ChangeEvent, layout: string): void => { @@ -62,7 +61,6 @@ export class TemplateMenu extends React.Component { DocumentView.FloatDoc(topDocView, ex, ey); } - @undoBatch @action toggleTemplate = (event: React.ChangeEvent, template: Template): void => { @@ -105,8 +103,6 @@ export class TemplateMenu extends React.Component { }); } - _addedKeys = new ObservableSet(); - _customRef = React.createRef(); render() { const layout = Doc.Layout(this.props.docViews[0].Document); const templateMenu: Array = []; diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 487e98b33..4306a07a1 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -1,27 +1,29 @@ import { faEdit } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, trace, runInAction } from "mobx"; +import { action, computed, observable, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, Field } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; +import { ObjectField } from "../../../new_fields/ObjectField"; import { RichTextField } from "../../../new_fields/RichTextField"; import { listSpec } from "../../../new_fields/Schema"; import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; +import { DocumentType } from "../../documents/DocumentTypes"; import { Scripting } from "../../util/Scripting"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { EditableView } from "../EditableView"; -import { anchorPoints, Flyout } from "../TemplateMenu"; import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTimeView.scss"; -import React = require("react"); import { CollectionTreeView } from "./CollectionTreeView"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import { DocumentType } from "../../documents/DocumentTypes"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; +import React = require("react"); @observer export class CollectionTimeView extends CollectionSubView(doc => doc) { @@ -31,7 +33,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { componentDidMount() { const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked; if (!this.props.Document._facetCollection) { - const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true }); + const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true, treeViewHideHeaderFields: true }); facetCollection.target = this.props.Document; this.props.Document.excludeFields = new List(["_facetCollection", "_docFilter"]); @@ -248,7 +250,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { trace(); const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null); const flyout = ( -
+
e.stopPropagation()}> {this._allFacets.map(facet =>
- {this.props.hideHeaderFields() ? (null) : headerElements} + {this.props.treeViewHideHeaderFields() ? (null) : headerElements} {openRight} ; } @@ -464,7 +464,7 @@ class TreeView extends React.Component { panelWidth: () => number, ChromeHeight: undefined | (() => number), renderDepth: number, - hideHeaderFields: () => boolean, + treeViewHideHeaderFields: () => boolean, preventTreeViewOpen: boolean, renderedIds: string[], libraryPath: Doc[] | undefined, @@ -574,7 +574,7 @@ class TreeView extends React.Component { outerXf={outerXf} parentKey={key} active={active} - hideHeaderFields={hideHeaderFields} + treeViewHideHeaderFields={treeViewHideHeaderFields} preventTreeViewOpen={preventTreeViewOpen} renderedIds={renderedIds} />; }); @@ -625,7 +625,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { } else { const layoutItems: ContextMenuProps[] = []; layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" }); - layoutItems.push({ description: (this.props.Document.hideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.hideHeaderFields = !this.props.Document.hideHeaderFields, icon: "paint-brush" }); + layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" }); layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" }); ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" }); } @@ -732,7 +732,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { { TreeView.GetChildElements(this.childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, - this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.hideHeaderFields), + this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.treeViewHideHeaderFields), BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick)) } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 799627969..17d06f50c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -854,7 +854,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } return true; }) : this.childDocs; - const rangeFilteredDocs = docRangeFilters.length ? filteredDocs.filter(d => { + const rangeFilteredDocs = filteredDocs.filter(d => { for (let i = 0; i < docRangeFilters.length; i += 3) { const key = docRangeFilters[i]; const min = Number(docRangeFilters[i + 1]); @@ -865,7 +865,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } return true; - }) : this.childDocs; + }); return rangeFilteredDocs; } get doLayoutComputation() { -- cgit v1.2.3-70-g09d2