diff options
Diffstat (limited to 'src/client/views/nodes/VideoBox.tsx')
| -rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 123 |
1 files changed, 67 insertions, 56 deletions
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 04780155d..2dad115e1 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, override, reaction, runInAction, untracked } from 'mobx'; import { observer } from 'mobx-react'; import { basename } from 'path'; import { Doc, StrListCast } from '../../../fields/Doc'; @@ -9,7 +9,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { AudioField, ImageField, VideoField } from '../../../fields/URLField'; -import { emptyFunction, formatTime, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; +import { copyProps, emptyFunction, formatTime, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; @@ -33,7 +33,6 @@ import { FieldView, FieldViewProps } from './FieldView'; import { RecordingBox } from './RecordingBox'; import { PinProps, PresBox } from './trails'; import './VideoBox.scss'; -const path = require('path'); /** * VideoBox @@ -52,7 +51,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); } - static _youtubeIframeCounter: number = 0; static heightPercent = 80; // height of video relative to videoBox when timeline is open static numThumbnails = 20; @@ -70,6 +68,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp private _playRegionTimer: any = null; // timeout for playback private _controlsFadeTimer: any = null; // timeout for controls fade + _prevProps: ViewBoxAnnotatableProps & FieldViewProps; + @override _props: ViewBoxAnnotatableProps & FieldViewProps; + constructor(props: ViewBoxAnnotatableProps & FieldViewProps) { + super(props); + this._props = this._prevProps = props; + makeObservable(this); + this._props.setContentView?.(this); + } + + componentDidUpdate() { + copyProps(this); + } + @observable _stackedTimeline: any; // CollectionStackedTimeline ref @observable static _nativeControls: boolean; // default html controls @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); @@ -96,13 +107,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp @observable rawDuration: number = 0; @computed get youtubeVideoId() { - const field = Cast(this.dataDoc[this.props.fieldKey], VideoField); + const field = Cast(this.dataDoc[this._props.fieldKey], VideoField); return field && field.url.href.indexOf('youtube') !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split('/')) : ''; } // returns the path of the audio file @computed get audiopath() { - const field = Cast(this.props.Document[this.props.fieldKey + '_audio'], AudioField, null); + const field = Cast(this.Document[this._props.fieldKey + '_audio'], AudioField, null); const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null); return field?.url.href ?? vfield?.url.href ?? ''; } @@ -125,7 +136,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp componentDidMount() { this.unmounting = false; - this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link. + this._props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link. if (this.youtubeVideoId) { const youtubeaspect = 400 / 315; const nativeWidth = Doc.NativeWidth(this.layoutDoc); @@ -162,7 +173,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp if ( // need to include range inputs because after dragging time slider it becomes target element !(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) && - this.props.isSelected() + this._props.isSelected() ) { switch (e.key) { case 'ArrowLeft': @@ -267,7 +278,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this.player && this._contentRef && this._contentRef.requestFullscreen(); } try { - this._youtubePlayer && this.props.addDocTab(this.Document, OpenWhere.add); + this._youtubePlayer && this._props.addDocTab(this.Document, OpenWhere.add); } catch (e) { console.log('Video FullScreen Exception:', e); } @@ -328,7 +339,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp title: (this.layoutDoc._layout_currentTimecode || 0).toString(), onClick: FollowLinkScript(), }); - this.props.addDocument?.(b); + this._props.addDocument?.(b); DocUtils.MakeLink(b, this.Document, { link_relationship: 'video snapshot' }); Networking.PostToServer('/youtubeScreenshot', { id: this.youtubeVideoId, @@ -336,7 +347,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp }).then(response => { const resolved = response?.accessPaths?.agnostic?.client; if (resolved) { - this.props.removeDocument?.(b); + this._props.removeDocument?.(b); this.createSnapshotLink(resolved); } }); @@ -377,7 +388,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp }); Doc.SetNativeWidth(Doc.GetProto(imageSnapshot), Doc.NativeWidth(this.layoutDoc)); Doc.SetNativeHeight(Doc.GetProto(imageSnapshot), Doc.NativeHeight(this.layoutDoc)); - this.props.addDocument?.(imageSnapshot); + this._props.addDocument?.(imageSnapshot); const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' }); link && (Doc.GetProto(link.link_anchor_2 as Doc).timecodeToHide = NumCast((link.link_anchor_2 as Doc).timecodeToShow) + 3); setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true)); @@ -492,7 +503,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // context menu specificContextMenu = (e: React.MouseEvent): void => { - const field = Cast(this.dataDoc[this.props.fieldKey], VideoField); + const field = Cast(this.dataDoc[this._props.fieldKey], VideoField); if (field) { const url = field.url.href; const subitems: ContextMenuProps[] = []; @@ -532,7 +543,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp event: () => { this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey); // delete assoicated video data - this.dataDoc[this.props.fieldKey] = ''; + this.dataDoc[this._props.fieldKey] = ''; this.dataDoc[this.fieldKey + '_duration'] = ''; // delete assoicated presentation data this.dataDoc[this.fieldKey + '_presentation'] = ''; @@ -550,7 +561,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // renders the video and audio @computed get content() { const field = Cast(this.dataDoc[this.fieldKey], VideoField); - const interactive = Doc.ActiveTool !== InkTool.None || !this.props.isSelected() ? '' : '-interactive'; + const interactive = Doc.ActiveTool !== InkTool.None || !this._props.isSelected() ? '' : '-interactive'; const classname = 'videoBox-content' + (this._fullScreen ? '-fullScreen' : '') + interactive; const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0; return !field ? ( @@ -586,7 +597,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp Not supported. </video> {!this.audiopath || this.audiopath === field.url.href ? null : ( - <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}> + <audio ref={this.setAudioRef} className={`audiobox-control${this._props.isContentActive() ? '-interactive' : ''}`}> <source src={this.audiopath} type="audio/mpeg" /> Not supported. </audio> @@ -626,7 +637,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp () => !this._playing && this.Seek(NumCast(this.layoutDoc._layout_currentTimecode)) ); this._disposers.youtubeReactionDisposer = reaction( - () => Doc.ActiveTool === InkTool.None && this.props.isSelected() && !SnappingManager.GetIsDragging() && !DocumentView.Interacting, + () => Doc.ActiveTool === InkTool.None && this._props.isSelected() && !SnappingManager.GetIsDragging() && !DocumentView.Interacting, interactive => (iframe.style.pointerEvents = interactive ? 'all' : 'none'), { fireImmediately: true } ); @@ -636,8 +647,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp (YT as any)?.ready(() => { this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, { events: { - onReady: this.props.dontRegisterView ? undefined : onYoutubePlayerReady, - onStateChange: this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange, + onReady: this._props.dontRegisterView ? undefined : onYoutubePlayerReady, + onStateChange: this._props.dontRegisterView ? undefined : onYoutubePlayerStateChange, }, }); }); @@ -677,9 +688,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp e, action(encodeURIComponent => { this._clicking = false; - if (this.props.isContentActive()) { - // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY); - // this.layoutDoc._layout_timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100)); + if (this._props.isContentActive()) { + // const local = this._props.ScreenToLocalTransform().scale(this._props.scaling?.() || 1).transformPoint(e.clientX, e.clientY); + // this.layoutDoc._layout_timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this._props.PanelHeight() * 100)); this.layoutDoc._layout_timelineHeightPercent = 80; } @@ -693,15 +704,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp 500 ); }, - this.props.isContentActive(), - this.props.isContentActive() + this._props.isContentActive(), + this._props.isContentActive() ); }; // removes from currently playing display @action removeCurrentlyPlaying = () => { - const docView = this.props.DocumentView?.(); + const docView = this._props.DocumentView?.(); if (CollectionStackedTimeline.CurrentlyPlaying && docView) { const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView); index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); @@ -710,7 +721,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // adds doc to currently playing display @action addCurrentlyPlaying = () => { - const docView = this.props.DocumentView?.(); + const docView = this._props.DocumentView?.(); if (!CollectionStackedTimeline.CurrentlyPlaying) { CollectionStackedTimeline.CurrentlyPlaying = []; } @@ -861,7 +872,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // starts marquee selection marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this._props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) { setupMoveUpEvents( this, e, @@ -882,31 +893,31 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp @action finishMarquee = () => { this._marqueeref.current?.onTerminateSelection(); - this.props.select(true); + this._props.select(true); }; - timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive))); + timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive))); timelineScreenToLocal = () => - this.props + this._props .ScreenToLocalTransform() .scale(this.scaling()) - .translate(0, (-this.heightPercent / 100) * this.props.PanelHeight()); + .translate(0, (-this.heightPercent / 100) * this._props.PanelHeight()); setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._layout_currentTimecode = time); - timelineHeight = () => (this.props.PanelHeight() * (100 - this.heightPercent)) / 100; + timelineHeight = () => (this._props.PanelHeight() * (100 - this.heightPercent)) / 100; playing = () => this._playing; - scaling = () => this.props.NativeDimScaling?.() || 1; + scaling = () => this._props.NativeDimScaling?.() || 1; - panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100; - panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.dataDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100); + panelWidth = () => (this._props.PanelWidth() * this.heightPercent) / 100; + panelHeight = () => (this.layoutDoc._layout_fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.dataDoc) || 1) : (this._props.PanelHeight() * this.heightPercent) / 100); screenToLocalTransform = () => { - const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling(); - return this.props + const offset = (this._props.PanelWidth() - this.panelWidth()) / 2 / this.scaling(); + return this._props .ScreenToLocalTransform() .translate(-offset, 0) .scale(100 / this.heightPercent); @@ -918,10 +929,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // renders video controls componentUI = (boundsLeft: number, boundsTop: number) => { - const xf = this.props.ScreenToLocalTransform().inverse(); - const height = this.props.PanelHeight(); + const xf = this._props.ScreenToLocalTransform().inverse(); + const height = this._props.PanelHeight(); const vidHeight = (height * this.heightPercent) / 100 / this.scaling(); - const vidWidth = this.props.PanelWidth() / this.scaling(); + const vidWidth = this._props.PanelWidth() / this.scaling(); const uiHeight = 25; const uiMargin = 10; const yBot = xf.transformPoint(0, vidHeight)[1]; @@ -936,7 +947,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp <div className="videoBox-ui" style={{ - transform: `rotate(${this.props.ScreenToLocalTransform().inverse().RotateDeg}deg) translate(${-(xRight - xPos) + 10}px, ${yBot - yMid - uiHeight - uiMargin}px)`, + transform: `rotate(${this._props.ScreenToLocalTransform().inverse().RotateDeg}deg) translate(${-(xRight - xPos) + 10}px, ${yBot - yMid - uiHeight - uiMargin}px)`, left: xPos, top: yMid, height: uiHeight, @@ -957,13 +968,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%`, display: this.heightPercent === 100 ? 'none' : '' }}> <CollectionStackedTimeline ref={action((r: any) => (this._stackedTimeline = r))} - {...this.props} + {...this._props} dataFieldKey={this.fieldKey} fieldKey={this.annotationKey} dictationKey={this.fieldKey + '_dictation'} mediaPath={this.audiopath} thumbnails={this.thumbnails} - renderDepth={this.props.renderDepth + 1} + renderDepth={this._props.renderDepth + 1} startTag={'_timecodeToShow' /* videoStart */} endTag={'_timecodeToHide' /* videoEnd */} bringToFront={emptyFunction} @@ -1011,8 +1022,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp cropping.title = 'crop: ' + this.Document.title; cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width); cropping.y = NumCast(this.Document.y); - cropping._width = anchw * (this.props.NativeDimScaling?.() || 1); - cropping._height = anchh * (this.props.NativeDimScaling?.() || 1); + cropping._width = anchw * (this._props.NativeDimScaling?.() || 1); + cropping._height = anchh * (this._props.NativeDimScaling?.() || 1); cropping.timecodeToHide = undefined; cropping.timecodeToShow = undefined; cropping.onClick = undefined; @@ -1038,12 +1049,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp if (addCrop) { DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' }); } - this.props.bringToFront(cropping); + this._props.bringToFront(cropping); return cropping; }; savedAnnotations = () => this._savedAnnotations; render() { - const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); + const borderRad = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding); const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / this.scaling()}px` : borderRad; return ( <div @@ -1053,7 +1064,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp style={{ pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined, borderRadius, - overflow: this.props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined, + overflow: this._props.docViewPath?.().slice(-1)[0].layout_fitWidth ? 'auto' : undefined, }}> <div className="videoBox-viewer" onPointerDown={this.marqueeDown}> <div @@ -1063,19 +1074,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp width: this.panelWidth(), height: this.panelHeight(), top: 0, - left: (this.props.PanelWidth() - this.panelWidth()) / 2, + left: (this._props.PanelWidth() - this.panelWidth()) / 2, }}> <CollectionFreeFormView - {...this.props} + {...this._props} setContentView={emptyFunction} NativeWidth={returnZero} NativeHeight={returnZero} - renderDepth={this.props.renderDepth + 1} + renderDepth={this._props.renderDepth + 1} fieldKey={this.annotationKey} isAnnotationOverlay={true} annotationLayerHostsContent={true} - PanelWidth={this.props.PanelWidth} - PanelHeight={this.props.PanelHeight} + PanelWidth={this._props.PanelWidth} + PanelHeight={this._props.PanelHeight} isAnyChildContentActive={returnFalse} ScreenToLocalTransform={this.screenToLocalTransform} childFilters={this.timelineDocFilter} @@ -1097,8 +1108,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp scrollTop={0} annotationLayerScrollTop={0} scaling={returnOne} - annotationLayerScaling={this.props.NativeDimScaling} - docView={this.props.DocumentView!} + annotationLayerScaling={this._props.NativeDimScaling} + docView={this._props.DocumentView!} containerOffset={this.marqueeOffset} addDocument={this.addDocWithTimecode} finishMarquee={this.finishMarquee} @@ -1116,7 +1127,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp } @computed get UIButtons() { - const bounds = this.props.docViewPath().lastElement().getBounds(); + const bounds = this._props.docViewPath().lastElement().getBounds(); const width = (bounds?.right || 0) - (bounds?.left || 0); const curTime = NumCast(this.layoutDoc._layout_currentTimecode); return ( |
