diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/ScreenshotBox.scss | 2 | ||||
-rw-r--r-- | src/client/views/nodes/ScreenshotBox.tsx | 101 |
2 files changed, 38 insertions, 65 deletions
diff --git a/src/client/views/nodes/ScreenshotBox.scss b/src/client/views/nodes/ScreenshotBox.scss index 141960f60..ab54cf526 100644 --- a/src/client/views/nodes/ScreenshotBox.scss +++ b/src/client/views/nodes/ScreenshotBox.scss @@ -26,7 +26,7 @@ position: absolute; right: 25; top: 0; - width:50; + width:25; height: 25; .screenshotBox-snapshot{ color : white; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index bed6566a6..999ccf5f6 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -1,6 +1,6 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, observable } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { DateField } from "../../../fields/DateField"; import { Doc, WidthSym } from "../../../fields/Doc"; @@ -16,17 +16,13 @@ import { DocUtils } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { Networking } from "../../Network"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { DocumentManager } from "../../util/DocumentManager"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; import { ContextMenu } from "../ContextMenu"; -import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; -import { DocumentView } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import "./ScreenshotBox.scss"; import { VideoBox } from "./VideoBox"; -const path = require('path'); declare class MediaRecorder { constructor(e: any, options?: any); // whatever MediaRecorder has } @@ -36,27 +32,22 @@ const ScreenshotDocument = makeInterface(documentSchema); @observer export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) { - private _reactionDisposer?: IReactionDisposer; - private _videoRef: HTMLVideoElement | null = null; - private _vchunks: any; - private _achunks: any; - private _vrecorder: any; - private _arecorder: any; - private _dictation: Doc | undefined; - private _dictationView: DocumentView | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); } + private _videoRef = React.createRef<HTMLVideoElement>(); + private _audioRec: any; + private _videoRec: any; + @observable _screenCapture = false; @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); } - getAnchor = () => { - const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._vrecorder ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined); - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow" /* audioStart */, "_timecodeToHide" /* audioEnd */, + const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined); + return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow", "_timecodeToHide", startTime, startTime === undefined ? undefined : startTime + 3) || this.rootDoc; } videoLoad = () => { - const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight; + const aspect = this._videoRef.current!.videoWidth / this._videoRef.current!.videoHeight; const nativeWidth = Doc.NativeWidth(this.layoutDoc); const nativeHeight = Doc.NativeHeight(this.layoutDoc); if (!nativeWidth || !nativeHeight) { @@ -67,28 +58,19 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, S } componentWillUnmount() { - this._reactionDisposer?.(); const ind = DocUtils.ActiveRecordings.indexOf(this); ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1)); } - @action - setVideoRef = (vref: HTMLVideoElement | null) => this._videoRef = vref; - - @observable _screenCapture = false; specificContextMenu = (e: React.MouseEvent): void => { - const field = Cast(this.dataDoc[this.fieldKey], VideoField); - if (field) { - const subitems: ContextMenuProps[] = []; - subitems.push({ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" }); - ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" }); - } + const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }]; + ContextMenu.Instance.addItem({ description: "Options...", subitems, icon: "video" }); } @computed get content() { const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive"; - const style = "videoBox-content" + interactive; - return <video className={`${style}`} key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} + return <video className={"videoBox-content" + interactive} key="video" ref={this._videoRef} + autoPlay={this._screenCapture} style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }} onCanPlay={this.videoLoad} controls={true} @@ -101,40 +83,38 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, S toggleRecording = action(async () => { this._screenCapture = !this._screenCapture; if (this._screenCapture) { - this._arecorder = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true })); - this._achunks = []; - this._arecorder.ondataavailable = (e: any) => this._achunks.push(e.data); - this._arecorder.onstop = async (e: any) => { - const [{ result }] = await Networking.UploadFilesToServer(this._achunks); + this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true })); + const aud_chunks: any = []; + this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data); + this._audioRec.onstop = async (e: any) => { + const [{ result }] = await Networking.UploadFilesToServer(aud_chunks); if (!(result instanceof Error)) { this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); } }; - const vstream = await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); - this._videoRef!.srcObject = vstream; - this._vrecorder = new MediaRecorder(vstream); - this._vchunks = []; - this._vrecorder.onstart = action(() => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date())); - this._vrecorder.ondataavailable = (e: any) => this._vchunks.push(e.data); - this._vrecorder.onstop = async (e: any) => { - const file = new File(this._vchunks, `${this.rootDoc[Id]}.mkv`, { type: this._vchunks[0].type, lastModified: Date.now() }); + this._videoRef.current!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); + this._videoRec = new MediaRecorder(this._videoRef.current!.srcObject); + const vid_chunks: any = []; + this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date()); + this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data); + this._videoRec.onstop = async (e: any) => { + const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() }); const [{ result }] = await Networking.UploadFilesToServer(file); this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000; - if (!(result instanceof Error)) { + if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox this.dataDoc.type = DocumentType.VID; this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey); this.dataDoc[this.props.fieldKey] = new VideoField(Utils.prepend(result.accessPaths.agnostic.client)); } else alert("video conversion failed"); }; - this._dictation = this.setupDictation(); - setTimeout(() => this._dictationView = DocumentManager.Instance.getDocumentView(this._dictation!)); - this._arecorder.start(); - this._vrecorder.start(); + this.setupDictation(); + this._audioRec.start(); + this._videoRec.start(); this.dataDoc.mediaState = "recording"; DocUtils.ActiveRecordings.push(this); } else { - this._arecorder.stop(); - this._vrecorder.stop(); + this._audioRec.stop(); + this._videoRec.stop(); this.dataDoc.mediaState = "paused"; const ind = DocUtils.ActiveRecordings.indexOf(this); ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1)); @@ -150,21 +130,10 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, S dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`); dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState"); this.props.addDocument?.(dictationText) || this.props.addDocTab(dictationText, "add:bottom"); - return dictationText; } - - private get uIButtons() { - return (<div className="screenshotBox-uiButtons"> - <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} > - <FontAwesomeIcon icon="file" size="lg" /> - </div> - </div>); - } - contentFunc = () => [this.content]; render() { - return (<div className="videoBox" onContextMenu={this.specificContextMenu} - style={{ width: `${100}%`, height: `${100}%` }} > + return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} > <div className="videoBox-viewer" > <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} PanelHeight={this.props.PanelHeight} @@ -186,7 +155,11 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps, S {this.contentFunc} </CollectionFreeFormView> </div> - {this.props.isSelected() ? this.uIButtons : (null)} - </div >); + {!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons"> + <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} > + <FontAwesomeIcon icon="file" size="lg" /> + </div> + </div>} + </div >; } }
\ No newline at end of file |