aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ScreenshotBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/ScreenshotBox.tsx')
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx181
1 files changed, 149 insertions, 32 deletions
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index c4f8d1143..252c029e4 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,7 +1,9 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
+// import { Canvas } from '@react-three/fiber';
+import { action, computed, observable, reaction } from "mobx";
import { observer } from "mobx-react";
+// import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
import { DateField } from "../../../fields/DateField";
import { Doc, WidthSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
@@ -12,7 +14,7 @@ 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, Utils } from "../../../Utils";
+import { emptyFunction, numberRange, OmitKeys, returnFalse, returnOne, Utils } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { Networking } from "../../Network";
@@ -33,18 +35,101 @@ declare class MediaRecorder {
type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
const ScreenshotDocument = makeInterface(documentSchema);
+// interface VideoTileProps {
+// raised: { coord: Vector2, off: Vector3 }[];
+// setRaised: (r: { coord: Vector2, off: Vector3 }[]) => void;
+// x: number;
+// y: number;
+// rootDoc: Doc;
+// color: string;
+// }
+
+// @observer
+// export class VideoTile extends React.Component<VideoTileProps> {
+// @observable _videoRef: HTMLVideoElement | undefined;
+// _mesh: any = undefined;
+
+// render() {
+// const topLeft = [this.props.x, this.props.y];
+// const raised = this.props.raised;
+// const find = (raised: { coord: Vector2, off: Vector3 }[], what: Vector2) => raised.find(r => r.coord.x === what.x && r.coord.y === what.y);
+// const tl1 = find(raised, new Vector2(topLeft[0], topLeft[1] + 1));
+// const tl2 = find(raised, new Vector2(topLeft[0] + 1, topLeft[1] + 1));
+// const tl3 = find(raised, new Vector2(topLeft[0] + 1, topLeft[1]));
+// const tl4 = find(raised, new Vector2(topLeft[0], topLeft[1]));
+// const quad_indices = [0, 2, 1, 0, 3, 2];
+// const quad_uvs = [0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0];
+// const quad_normals = [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,];
+// const quad_vertices =
+// [
+// topLeft[0] - 0.0 + (tl1?.off.x || 0), topLeft[1] + 1.0 + (tl1?.off.y || 0), 0.0 + (tl1?.off.z || 0),
+// topLeft[0] + 1.0 + (tl2?.off.x || 0), topLeft[1] + 1.0 + (tl2?.off.y || 0), 0.0 + (tl2?.off.z || 0),
+// topLeft[0] + 1.0 + (tl3?.off.x || 0), topLeft[1] - 0.0 + (tl3?.off.y || 0), 0.0 + (tl3?.off.z || 0),
+// topLeft[0] - 0.0 + (tl4?.off.x || 0), topLeft[1] - 0.0 + (tl4?.off.y || 0), 0.0 + (tl4?.off.z || 0)
+// ];
+
+// const vertices = new Float32Array(quad_vertices);
+// const normals = new Float32Array(quad_normals);
+// const uvs = new Float32Array(quad_uvs); // Each vertex has one uv coordinate for texture mapping
+// const indices = new Uint32Array(quad_indices); // Use the four vertices to draw the two triangles that make up the square.
+// const popOut = () => NumCast(this.props.rootDoc.popOut);
+// const popOff = () => NumCast(this.props.rootDoc.popOff);
+// return (
+// <mesh key={`mesh${topLeft[0]}${topLeft[1]}`} onClick={action(async e => {
+// this.props.setRaised([
+// { coord: new Vector2(topLeft[0], topLeft[1]), off: new Vector3(-popOff(), -popOff(), popOut()) },
+// { coord: new Vector2(topLeft[0] + 1, topLeft[1]), off: new Vector3(popOff(), -popOff(), popOut()) },
+// { coord: new Vector2(topLeft[0], topLeft[1] + 1), off: new Vector3(-popOff(), popOff(), popOut()) },
+// { coord: new Vector2(topLeft[0] + 1, topLeft[1] + 1), off: new Vector3(popOff(), popOff(), popOut()) }
+// ]);
+// if (!this._videoRef) {
+// (navigator.mediaDevices as any).getDisplayMedia({ video: true }).then(action((stream: any) => {
+// //const videoSettings = stream.getVideoTracks()[0].getSettings();
+// this._videoRef = document.createElement("video");
+// Object.assign(this._videoRef, {
+// srcObject: stream,
+// //height: videoSettings.height,
+// //width: videoSettings.width,
+// autoplay: true
+// });
+// }));
+// }
+// })} ref={(r: any) => this._mesh = r}>
+// <bufferGeometry attach="geometry" ref={(r: any) => {
+// // itemSize = 3 because there are 3 values (components) per vertex
+// r?.setAttribute('position', new BufferAttribute(vertices, 3));
+// r?.setAttribute('normal', new BufferAttribute(normals, 3));
+// r?.setAttribute('uv', new BufferAttribute(uvs, 2));
+// r?.setIndex(new BufferAttribute(indices, 1));
+// }} />
+// {!this._videoRef ? <meshStandardMaterial color={this.props.color} /> :
+// <meshBasicMaterial >
+// <videoTexture attach="map" args={[this._videoRef]} />
+// </meshBasicMaterial>}
+// </mesh>
+// );
+// }
+// }
+
@observer
export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
- private _videoRef: HTMLVideoElement | null = null;
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(); }
constructor(props: any) {
super(props);
- this.setupDictation();
+ if (this.rootDoc.videoWall) {
+ this.rootDoc.nativeWidth = undefined;
+ this.rootDoc.nativeHeight = undefined;
+ this.layoutDoc.popOff = 0;
+ this.layoutDoc.popOut = 1;
+ } else {
+ this.setupDictation();
+ }
}
getAnchor = () => {
const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
@@ -67,6 +152,16 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
componentDidMount() {
this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+ // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+ // ({ width, height }) => {
+ // if (this._camera) {
+ // const angle = -Math.abs(1 - width / height);
+ // const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
+ // this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
+ // this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+ // (this._camera as any).updateProjectionMatrix();
+ // }
+ // });
}
componentWillUnmount() {
const ind = DocUtils.ActiveRecordings.indexOf(this);
@@ -79,26 +174,50 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
}
@computed get content() {
+ if (this.rootDoc.videoWall) return (null);
const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
return <video className={"videoBox-content" + interactive} key="video"
ref={r => {
this._videoRef = r;
setTimeout(() => {
- if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) { // TODO glr: use mediaState
+ if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
this.toggleRecording();
}
- }, 1000);
+ }, 100);
}}
- autoPlay={true}
- style={{ width: "100%", height: "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>;
+ </video>;
}
+ // _numScreens = 5;
+ // _camera: Camera | undefined;
+ // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
+ // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
+ @computed get threed() {
+ // if (this.rootDoc.videoWall) {
+ // const screens: any[] = [];
+ // const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
+ // let count = 0;
+ // numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
+ // <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
+ // return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
+ // this._camera = props.camera;
+ // props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
+ // props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+ // }}>
+ // {/* <ambientLight />*/}
+ // <pointLight position={[10, 10, 10]} intensity={1} />
+ // {screens}
+ // </ Canvas>;
+ // }
+ return (null);
+ }
toggleRecording = action(async () => {
this._screenCapture = !this._screenCapture;
if (this._screenCapture) {
@@ -155,7 +274,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
}
- contentFunc = () => [this.content];
+ contentFunc = () => [this.threed, this.content];
videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], 1) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], 1) * this.props.PanelWidth();
formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
render() {
@@ -183,28 +302,26 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
{this.contentFunc}
</CollectionFreeFormView></div>
<div style={{ position: "relative", height: this.formattedPanelHeight() }}>
- <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- Document={this.dataDoc[this.fieldKey + "-dictation"]}
- fieldKey={"text"}
- PanelHeight={this.formattedPanelHeight}
- PanelWidth={this.props.PanelWidth}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- isAnnotationOverlay={true}
- select={emptyFunction}
- isContentActive={returnFalse}
- scaling={returnOne}
- xMargin={25}
- yMargin={10}
- whenChildContentsActiveChanged={emptyFunction}
- removeDocument={returnFalse}
- moveDocument={returnFalse}
- addDocument={returnFalse}
- CollectionView={undefined}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
- </FormattedTextBox></div>
+ {!(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={returnFalse}
+ scaling={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} >