diff options
Diffstat (limited to 'src/client/views/nodes/ScreenshotBox.tsx')
| -rw-r--r-- | src/client/views/nodes/ScreenshotBox.tsx | 238 |
1 files changed, 125 insertions, 113 deletions
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index dbb567d3a..76a24d831 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -1,32 +1,31 @@ -import React = require("react"); -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React = require('react'); +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; // import { Canvas } from '@react-three/fiber'; -import { computed, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; +import { computed, observable, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three'; -import { DateField } from "../../../fields/DateField"; -import { Doc, HeightSym, WidthSym } from "../../../fields/Doc"; -import { Id } from "../../../fields/FieldSymbols"; -import { ComputedField } from "../../../fields/ScriptField"; -import { Cast, NumCast } from "../../../fields/Types"; -import { AudioField, VideoField } from "../../../fields/URLField"; -import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, OmitKeys, returnFalse, returnOne } from "../../../Utils"; -import { DocUtils } from "../../documents/Documents"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { Networking } from "../../Network"; -import { CaptureManager } from "../../util/CaptureManager"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; -import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; -import { ContextMenu } from "../ContextMenu"; -import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; +import { DateField } from '../../../fields/DateField'; +import { Doc, HeightSym, WidthSym } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; +import { ComputedField } from '../../../fields/ScriptField'; +import { Cast, NumCast } from '../../../fields/Types'; +import { AudioField, VideoField } from '../../../fields/URLField'; +import { TraceMobx } from '../../../fields/util'; +import { emptyFunction, OmitKeys, returnFalse, returnOne } from '../../../Utils'; +import { DocUtils } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Networking } from '../../Network'; +import { CaptureManager } from '../../util/CaptureManager'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; +import { ContextMenu } from '../ContextMenu'; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; -import { FormattedTextBox } from "./formattedText/FormattedTextBox"; -import "./ScreenshotBox.scss"; -import { VideoBox } from "./VideoBox"; +import { FormattedTextBox } from './formattedText/FormattedTextBox'; +import './ScreenshotBox.scss'; +import { VideoBox } from './VideoBox'; declare class MediaRecorder { - constructor(e: any, options?: any); // whatever MediaRecorder has + constructor(e: any, options?: any); // whatever MediaRecorder has } // interface VideoTileProps { @@ -107,12 +106,16 @@ declare class MediaRecorder { @observer export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); } + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(ScreenshotBox, fieldKey); + } private _audioRec: any; private _videoRec: any; @observable private _videoRef: HTMLVideoElement | null = null; @observable _screenCapture = false; - @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); } + @computed get recordingStart() { + return Cast(this.dataDoc[this.props.fieldKey + '-recordingStart'], DateField)?.date.getTime(); + } constructor(props: any) { super(props); @@ -126,11 +129,9 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl } } getAnchor = () => { - 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; - } + 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; @@ -141,7 +142,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect); this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect; } - } + }; componentDidMount() { this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0; @@ -159,33 +160,37 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl } componentWillUnmount() { const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1)); + ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); } specificContextMenu = (e: React.MouseEvent): void => { - const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }]; - ContextMenu.Instance.addItem({ description: "Options...", 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() { - if (this.rootDoc.videoWall) return (null); - return <video className={"videoBox-content"} key="video" - ref={r => { - this._videoRef = r; - setTimeout(() => { - if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) { - this.toggleRecording(); - } - }, 100); - }} - autoPlay={this._screenCapture} - style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }} - onCanPlay={this.videoLoad} - controls={true} - onClick={e => e.preventDefault()}> - <source type="video/mp4" /> - Not supported. - </video>; + if (this.rootDoc.videoWall) return null; + return ( + <video + className={'videoBox-content'} + key="video" + ref={r => { + this._videoRef = r; + setTimeout(() => { + if (this.rootDoc.mediaState === 'pendingRecording' && this._videoRef) { + this.toggleRecording(); + } + }, 100); + }} + autoPlay={this._screenCapture} + style={{ width: this._screenCapture ? '100%' : undefined, height: this._screenCapture ? '100%' : undefined }} + onCanPlay={this.videoLoad} + controls={true} + onClick={e => e.preventDefault()}> + <source type="video/mp4" /> + Not supported. + </video> + ); } // _numScreens = 5; @@ -209,7 +214,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl // {screens} // </ Canvas>; // } - return (null); + return null; } toggleRecording = async () => { if (!this._screenCapture) { @@ -219,31 +224,33 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl 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(result.accessPaths.agnostic.client); + this.dataDoc[this.props.fieldKey + '-audio'] = new AudioField(result.accessPaths.agnostic.client); } }; this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); this._videoRec = new MediaRecorder(this._videoRef!.srcObject); const vid_chunks: any = []; - this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date()); + 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) => { + console.log('screenshotbox: upload'); 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)) { // convert this screenshotBox into normal videoBox + this.dataDoc[this.fieldKey + '-duration'] = (new Date().getTime() - this.recordingStart!) / 1000; + if (!(result instanceof Error)) { + // convert this screenshotBox into normal videoBox this.dataDoc.type = DocumentType.VID; this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey); this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined; this.layoutDoc._fitWidth = undefined; this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client); - } else alert("video conversion failed"); + } else alert('video conversion failed'); }; this._audioRec.start(); this._videoRec.start(); runInAction(() => { this._screenCapture = true; - this.dataDoc.mediaState = "recording"; + this.dataDoc.mediaState = 'recording'; }); DocUtils.ActiveRecordings.push(this); } else { @@ -251,81 +258,86 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl this._videoRec?.stop(); runInAction(() => { this._screenCapture = false; - this.dataDoc.mediaState = "paused"; + this.dataDoc.mediaState = 'paused'; }); const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1)); + ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); CaptureManager.Instance.open(this.rootDoc); } - } + }; setupDictation = () => { - if (this.dataDoc[this.fieldKey + "-dictation"]) return; - const dictationText = CurrentUserUtils.GetNewTextDoc("dictation", - NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, - NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height)); + if (this.dataDoc[this.fieldKey + '-dictation']) return; + const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height)); dictationText._autoHeight = false; const dictationTextProto = Doc.GetProto(dictationText); dictationTextProto.recordingSource = this.dataDoc; dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`); - dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState"); - this.dataDoc[this.fieldKey + "-dictation"] = dictationText; - } + dictationTextProto.mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState'); + this.dataDoc[this.fieldKey + '-dictation'] = dictationText; + }; contentFunc = () => [this.threed, this.content]; - videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], this.layoutDoc[WidthSym]()) * this.props.PanelWidth(); + videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth(); formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight()); render() { TraceMobx(); - return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} > - <div className="videoBox-viewer" > - <div style={{ position: "relative", height: this.videoPanelHeight() }}> - <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} - PanelHeight={this.videoPanelHeight} - PanelWidth={this.props.PanelWidth} - focus={this.props.focus} - isSelected={this.props.isSelected} - isAnnotationOverlay={true} - select={emptyFunction} - isContentActive={returnFalse} - scaling={returnOne} - whenChildContentsActiveChanged={emptyFunction} - removeDocument={returnFalse} - moveDocument={returnFalse} - addDocument={returnFalse} - CollectionView={undefined} - ScreenToLocalTransform={this.props.ScreenToLocalTransform} - renderDepth={this.props.renderDepth + 1} - ContainingCollectionDoc={this.props.ContainingCollectionDoc}> - {this.contentFunc} - </CollectionFreeFormView></div> - <div style={{ position: "relative", height: this.formattedPanelHeight() }}> - {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) : - <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} - Document={this.dataDoc[this.fieldKey + "-dictation"]} - fieldKey={"text"} - PanelHeight={this.formattedPanelHeight} + return ( + <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: '100%', height: '100%' }}> + <div className="videoBox-viewer"> + <div style={{ position: 'relative', height: this.videoPanelHeight() }}> + <CollectionFreeFormView + {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit} + PanelHeight={this.videoPanelHeight} + PanelWidth={this.props.PanelWidth} + focus={this.props.focus} + isSelected={this.props.isSelected} isAnnotationOverlay={true} select={emptyFunction} - isContentActive={emptyFunction} - scaling={returnOne} - xPadding={25} - yPadding={10} + isContentActive={returnFalse} + NativeDimScaling={returnOne} whenChildContentsActiveChanged={emptyFunction} removeDocument={returnFalse} moveDocument={returnFalse} addDocument={returnFalse} CollectionView={undefined} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} renderDepth={this.props.renderDepth + 1} ContainingCollectionDoc={this.props.ContainingCollectionDoc}> - </FormattedTextBox>} + {this.contentFunc} + </CollectionFreeFormView> + </div> + <div style={{ position: 'relative', height: this.formattedPanelHeight() }}> + {!(this.dataDoc[this.fieldKey + '-dictation'] instanceof Doc) ? null : ( + <FormattedTextBox + {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit} + Document={this.dataDoc[this.fieldKey + '-dictation']} + fieldKey={'text'} + PanelHeight={this.formattedPanelHeight} + isAnnotationOverlay={true} + select={emptyFunction} + isContentActive={emptyFunction} + NativeDimScaling={returnOne} + xPadding={25} + yPadding={10} + whenChildContentsActiveChanged={emptyFunction} + removeDocument={returnFalse} + moveDocument={returnFalse} + addDocument={returnFalse} + CollectionView={undefined} + renderDepth={this.props.renderDepth + 1} + ContainingCollectionDoc={this.props.ContainingCollectionDoc}></FormattedTextBox> + )} + </div> </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> - {!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 +} |
