diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 22 | ||||
| -rw-r--r-- | src/client/views/nodes/PresBox.scss | 83 | ||||
| -rw-r--r-- | src/client/views/nodes/PresBox.tsx | 323 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 51 |
4 files changed, 362 insertions, 117 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 1bdba7f9e..2527a2db1 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -48,9 +48,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD static RangeScript: ScriptField; static LabelScript: ScriptField; - _linkPlayDisposer: IReactionDisposer | undefined; - _reactionDisposer: IReactionDisposer | undefined; - _scrubbingDisposer: IReactionDisposer | undefined; + // _linkPlayDisposer: IReactionDisposer | undefined; + // _reactionDisposer: IReactionDisposer | undefined; + // _scrubbingDisposer: IReactionDisposer | undefined; private _disposers: { [name: string]: IReactionDisposer } = {}; _ele: HTMLAudioElement | null = null; _recorder: any; @@ -151,7 +151,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD }); this._disposers.scrubbing = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime)); - this._disposers._audioStart = reaction( + this._disposers.audioStart = reaction( () => this.Document._audioStart, (audioStart) => { if (audioStart !== undefined) { @@ -165,6 +165,20 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD }, { fireImmediately: true } ); + + this._disposers.audioStop = reaction( + () => this.Document._audioStop, + (audioStop) => { + if (audioStop !== undefined) { + if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) { + const delay = this._audioRef.current ? 0 : 250; // wait for mainCont and try again to play + setTimeout(() => this._audioRef.current && this.pause(), delay); + setTimeout(() => { this.Document._audioStop = undefined; }, 10 + delay); + } + } + }, + { fireImmediately: true } + ); } playLink = (doc: Doc) => { diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss index 56b3b0593..6579dbf41 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/PresBox.scss @@ -127,6 +127,17 @@ $light-background: #ececec; opacity: 0.8; } +.presBox-radioButtons { + background-color: rgba(0, 0, 0, 0.1); + + .checkbox-container { + margin-left: 10px; + display: inline-flex; + height: 20px; + align-items: center; + } +} + .presBox-ribbon { position: relative; display: inline; @@ -209,6 +220,42 @@ $light-background: #ececec; } @media screen and (-webkit-min-device-pixel-ratio:0) { + + .multiThumb-slider { + display: grid; + background-color: $light-background; + height: 10px; + border-radius: 10px; + overflow: hidden; + + .toolbar-slider { + margin-top: 0px; + background: none; + -webkit-appearance: none; + pointer-events: none; + } + + .toolbar-slider.start::-webkit-slider-thumb { + width: 10px; + pointer-events: auto; + -webkit-appearance: none; + height: 10px; + cursor: ew-resize; + background: $dark-blue; + box-shadow: -100vw 0 0 100vw $light-background; + } + + .toolbar-slider.end::-webkit-slider-thumb { + width: 10px; + pointer-events: auto; + -webkit-appearance: none; + height: 10px; + cursor: ew-resize; + background: $dark-blue; + box-shadow: -100vw 0 0 100vw $light-blue; + } + } + .toolbar-slider { margin-top: 5px; position: relative; @@ -219,7 +266,7 @@ $light-background: #ececec; height: 10px; border-radius: 10px; -webkit-appearance: none; - background-color: #ececec; + background-color: $light-background; } .toolbar-slider:focus { @@ -234,14 +281,44 @@ $light-background: #ececec; .toolbar-slider::-webkit-slider-thumb { width: 10px; + pointer-events: auto; -webkit-appearance: none; height: 10px; cursor: ew-resize; - background: #5b9ddd; - box-shadow: -100vw 0 0 100vw #aedef8; + background: $dark-blue; + box-shadow: -100vw 0 0 100vw $light-blue; + } + + .presBox-checkbox { + -webkit-appearance: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + margin: 0; + margin-right: 3px; + border-radius: 100%; + height: 15px; + width: 15px; + cursor: pointer; + background: $light-background; + } + + .presBox-checkbox:focus { + outline: none; + } + + .presBox-checkbox:hover { + background: #c0c0c0; + } + + .presBox-checkbox:checked { + background: $light-blue; } } + + .slider-headers { position: relative; display: grid; diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 1405016ae..9fb07040d 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -24,7 +24,6 @@ import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from "../collections/CollectionView"; import { TabDocView } from "../collections/TabDocView"; import { ViewBoxBaseComponent } from "../DocComponent"; -import { AudioBox } from "./AudioBox"; import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import "./PresBox.scss"; @@ -193,6 +192,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> nextAudioVideo = (targetDoc: Doc, activeItem: Doc) => { if (targetDoc.type === DocumentType.AUDIO) { targetDoc._audioStart = NumCast(activeItem.presStartTime); + + } else if (targetDoc.type === DocumentType.VID) { + targetDoc._currentTimecode = NumCast(activeItem.presStartTime); + setTimeout(() => targetDoc._videoStart = true, 0); } // if (targetDoc.type === DocumentType.VID) { VideoBox.Instance.Play() }; activeItem.playNow = false; @@ -214,7 +217,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> // If next slide is audio / video 'Play automatically' then the next slide should be played if (activeNext && (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) && activeNext.playAuto) { if (targetNext.type === DocumentType.AUDIO) targetNext._audioStart = NumCast(activeNext.presStartTime); - // if (targetNext.type === DocumentType.VID) { VideoBox.Instance.Play() }; } else if (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) { activeNext.playNow = true; console.log('play next after it is navigated to'); } } @@ -1071,6 +1073,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @computed get transitionDropdown() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; + const type: DocumentType = targetDoc.type; const isPresCollection: boolean = (targetDoc === this.layoutDoc.presCollection); const isPinWithView: boolean = BoolCast(activeItem.presPinView); if (activeItem && targetDoc) { @@ -1137,7 +1140,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>} <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? PresColor.LightBlue : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Open</div></Tooltip> </div> - <div className="ribbon-doubleButton" > + {(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <div className="ribbon-doubleButton" > <div className="presBox-subheading">Slide Duration</div> <div className="ribbon-property"> <input className="presBox-input" @@ -1152,7 +1155,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <FontAwesomeIcon icon={"caret-down"} /> </div> </div> - </div> + </div>} <input type="range" step="0.1" min="0.1" max="20" value={duration} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }} className={"toolbar-slider"} id="duration-slider" @@ -1235,20 +1238,233 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } }); } - @computed get optionsDropdown() { + + @computed get presPinViewOptionsDropdown() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 16, filter: 'invert(1)' }} />; + return ( + <> + {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)} + <div className="ribbon-doubleButton"> + <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? PresColor.LightBlue : "" }} + onClick={() => { + activeItem.presPinView = !activeItem.presPinView; + targetDoc.presPinView = activeItem.presPinView; + if (activeItem.presPinView) { + if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) { + const scroll = targetDoc._scrollTop; + activeItem.presPinView = true; + activeItem.presPinViewScroll = scroll; + } else if (targetDoc.type === DocumentType.VID) { + activeItem.presPinTimecode = targetDoc._currentTimecode; + } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) { + const x = targetDoc._panX; + const y = targetDoc._panY; + const scale = targetDoc._viewScale; + activeItem.presPinView = true; + activeItem.presPinViewX = x; + activeItem.presPinViewY = y; + activeItem.presPinViewScale = scale; + } else if (targetDoc.type === DocumentType.COMPARISON) { + const width = targetDoc._clipWidth; + activeItem.presPinClipWidth = width; + activeItem.presPinView = true; + } + } + }}>{presPinWithViewIcon}</div></Tooltip> + {activeItem.presPinView ? <Tooltip title={<><div className="dash-tooltip">{"Update the pinned view with the view of the selected document"}</div></>}><div className="ribbon-button" + onClick={() => { + if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) { + const scroll = targetDoc._scrollTop; + activeItem.presPinViewScroll = scroll; + } else if (targetDoc.type === DocumentType.VID) { + activeItem.presPinTimecode = targetDoc._currentTimecode; + } else if (targetDoc.type === DocumentType.COMPARISON) { + const clipWidth = targetDoc._clipWidth; + activeItem.presPinClipWidth = clipWidth; + } else { + const x = targetDoc._panX; + const y = targetDoc._panY; + const scale = targetDoc._viewScale; + activeItem.presPinViewX = x; + activeItem.presPinViewY = y; + activeItem.presPinViewScale = scale; + } + }}>Update</div></Tooltip> : (null)} + </div> + </> + ); + } + + @computed get panOptionsDropdown() { + const activeItem: Doc = this.activeItem; + const targetDoc: Doc = this.targetDoc; + return ( + <> + {this.panable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}> + <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> + <div className="presBox-subheading">Pan X</div> + <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> + <input className="presBox-input" + style={{ textAlign: 'left', width: 50 }} + type="number" value={NumCast(activeItem.presPinViewX)} + onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} /> + </div> + </div> + <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> + <div className="presBox-subheading">Pan Y</div> + <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> + <input className="presBox-input" + style={{ textAlign: 'left', width: 50 }} + type="number" value={NumCast(activeItem.presPinViewY)} + onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} /> + </div> + </div> + <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> + <div className="presBox-subheading">Scale</div> + <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> + <input className="presBox-input" + style={{ textAlign: 'left', width: 50 }} + type="number" value={NumCast(activeItem.presPinViewScale)} + onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} /> + </div> + </div> + </div> : (null)} + </> + ); + } + + @computed get scrollOptionsDropdown() { + const activeItem: Doc = this.activeItem; + const targetDoc: Doc = this.targetDoc; + return ( + <> + {this.scrollable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}> + <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> + <div className="presBox-subheading">Scroll</div> + <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> + <input className="presBox-input" + style={{ textAlign: 'left', width: 50 }} + type="number" value={NumCast(activeItem.presPinViewScroll)} + onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScroll = Number(val); })} /> + </div> + </div> + </div> : (null)} + </> + ); + } + + @computed get mediaOptionsDropdown() { + const activeItem: Doc = this.activeItem; + const targetDoc: Doc = this.targetDoc; if (activeItem && targetDoc) { return ( <div> <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> - <div className="ribbon-box"> + <div> + <div className="presBox-subheading">Start playing:</div> + <div className="presBox-radioButtons"> + <div className="checkbox-container"> + <input className="presBox-checkbox" + type="checkbox" + onChange={() => activeItem.mediaPlay = "manual"} + checked={activeItem.mediaPlay === "manual"} + /> + <div>Start manually</div> + </div> + <div className="checkbox-container"> + <input className="presBox-checkbox" + type="checkbox" + onChange={() => activeItem.mediaPlay = "auto"} + checked={activeItem.mediaPlay === "auto"} + /> + <div>Play with slide</div> + </div> + <div className="checkbox-container"> + <input className="presBox-checkbox" + type="checkbox" + onChange={() => activeItem.mediaPlay = "onClick"} + checked={activeItem.mediaPlay === "onClick"} + /> + <div>Play on click</div> + </div> + </div> + <div className="presBox-subheading">Stop playing:</div> + <div className="presBox-radioButtons"> + <div className="checkbox-container"> + <input className="presBox-checkbox" + type="checkbox" + onChange={() => activeItem.mediaStop = "manual"} + checked={activeItem.mediaStop === "manual"} + /> + <div>Stop manually</div> + </div> + <div className="checkbox-container"> + <input className="presBox-checkbox" + type="checkbox" + onChange={() => activeItem.mediaStop = "afterCurrent"} + checked={activeItem.mediaStop === "afterCurrent"} + /> + <div>After current slide</div> + </div> + <div className="checkbox-container"> + <input className="presBox-checkbox" + type="checkbox" + onChange={() => activeItem.mediaStop = "afterSlide"} + checked={activeItem.mediaStop === "afterSlide"} + /> + <div>After slide + {activeItem.mediaStop !== "afterSlide" ? + (null) + : + <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}> + {this.setMovementName(activeItem.presMovement, activeItem)} + <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} /> + <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>Pan</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>Jump cut</div> + </div> + </div>} + </div> + </div> + </div> + <div className="presBox-subheading">Range:</div> + <div className="multiThumb-slider"> + <input type="range" step="0.1" min="0" max={NumCast(activeItem.duration)} value={NumCast(activeItem.presEndTime)} + style={{ gridColumn: 1, gridRow: 1 }} + className={`toolbar-slider ${"end"}`} + id="toolbar-slider" + onPointerDown={() => this._batch = UndoManager.StartBatch("presEndTime")} + onPointerUp={() => this._batch?.end()} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + e.stopPropagation(); + activeItem.presEndTime = Number(e.target.value); + }} /> + <input type="range" step="0.1" min="0" max={NumCast(activeItem.duration)} value={NumCast(activeItem.presStartTime)} + style={{ gridColumn: 1, gridRow: 1 }} + className={`toolbar-slider ${"start"}`} + id="toolbar-slider" + onPointerDown={() => this._batch = UndoManager.StartBatch("presEndTime")} + onPointerUp={() => this._batch?.end()} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + e.stopPropagation(); + activeItem.presStartTime = Number(e.target.value); + }} /> + </div> + <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}> + <div className="slider-text">0 s</div> + <div className="slider-text"></div> + <div className="slider-text">{Math.round(NumCast(activeItem.duration))} s</div> + </div> + </div> + {/* <div className="ribbon-box"> <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.AUDIO ? "inline-flex" : "none" }}> <div className="ribbon-toggle" style={{ backgroundColor: activeItem.playAuto ? PresColor.LightBlue : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div> <div className="ribbon-toggle" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : PresColor.LightBlue }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div> </div> - {/* {targetDoc.type === DocumentType.VID ? <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presVidFullScreen ? PresColor.LightBlue : "" }} onClick={() => activeItem.presVidFullScreen = !activeItem.presVidFullScreen}>Full screen</div> : (null)} */} {targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> <div className="presBox-subheading">Start time</div> <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> @@ -1267,98 +1483,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presEndTime = Number(val); })} /> </div> </div> : (null)} - {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)} - <div className="ribbon-doubleButton"> - <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? PresColor.LightBlue : "" }} - onClick={() => { - activeItem.presPinView = !activeItem.presPinView; - targetDoc.presPinView = activeItem.presPinView; - if (activeItem.presPinView) { - if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) { - const scroll = targetDoc._scrollTop; - activeItem.presPinView = true; - activeItem.presPinViewScroll = scroll; - } else if (targetDoc.type === DocumentType.VID) { - activeItem.presPinTimecode = targetDoc._currentTimecode; - } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) { - const x = targetDoc._panX; - const y = targetDoc._panY; - const scale = targetDoc._viewScale; - activeItem.presPinView = true; - activeItem.presPinViewX = x; - activeItem.presPinViewY = y; - activeItem.presPinViewScale = scale; - } else if (targetDoc.type === DocumentType.COMPARISON) { - const width = targetDoc._clipWidth; - activeItem.presPinClipWidth = width; - activeItem.presPinView = true; - } - } - }}>{presPinWithViewIcon}</div></Tooltip> - {activeItem.presPinView ? <Tooltip title={<><div className="dash-tooltip">{"Update the pinned view with the view of the selected document"}</div></>}><div className="ribbon-button" - onClick={() => { - if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) { - const scroll = targetDoc._scrollTop; - activeItem.presPinViewScroll = scroll; - } else if (targetDoc.type === DocumentType.VID) { - activeItem.presPinTimecode = targetDoc._currentTimecode; - } else if (targetDoc.type === DocumentType.COMPARISON) { - const clipWidth = targetDoc._clipWidth; - activeItem.presPinClipWidth = clipWidth; - } else { - const x = targetDoc._panX; - const y = targetDoc._panY; - const scale = targetDoc._viewScale; - activeItem.presPinViewX = x; - activeItem.presPinViewY = y; - activeItem.presPinViewScale = scale; - } - }}>Update</div></Tooltip> : (null)} - </div> - {this.panable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Pan X</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" value={NumCast(activeItem.presPinViewX)} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} /> - </div> - </div> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Pan Y</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" value={NumCast(activeItem.presPinViewY)} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} /> - </div> - </div> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Scale</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" value={NumCast(activeItem.presPinViewScale)} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} /> - </div> - </div> - </div> : (null)} - {this.scrollable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}> - <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> - <div className="presBox-subheading">Scroll</div> - <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> - <input className="presBox-input" - style={{ textAlign: 'left', width: 50 }} - type="number" value={NumCast(activeItem.presPinViewScroll)} - onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScroll = Number(val); })} /> - </div> - </div> - </div> : (null)} - {/* <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" onClick={this.progressivizeText}>Store original website</div> - </div> */} - </div> + </div> */} </div> </div > ); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 8ce76a347..998ca839d 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -21,6 +21,8 @@ import { documentSchema } from "../../../fields/documentSchemas"; import { Networking } from "../../Network"; import { SnappingManager } from "../../util/SnappingManager"; import { SelectionManager } from "../../util/SelectionManager"; +import { LinkDocPreview } from "./LinkDocPreview"; +import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment"; const path = require('path'); export const timeSchema = createSchema({ @@ -32,8 +34,9 @@ const VideoDocument = makeInterface(documentSchema, timeSchema); @observer export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) { static _youtubeIframeCounter: number = 0; - private _reactionDisposer?: IReactionDisposer; - private _youtubeReactionDisposer?: IReactionDisposer; + // private _reactionDisposer?: IReactionDisposer; + // private _youtubeReactionDisposer?: IReactionDisposer; + private _disposers: { [name: string]: IReactionDisposer } = {}; private _youtubePlayer: YT.Player | undefined = undefined; private _videoRef: HTMLVideoElement | null = null; private _youtubeIframeId: number = -1; @@ -181,7 +184,32 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD componentDidMount() { if (this.props.setVideoBox) this.props.setVideoBox(this); - + this._disposers.videoStart = reaction( + () => this.Document._videoStart, + (videoStart) => { + if (videoStart !== undefined) { + if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) { + const delay = this.player ? 0 : 250; // wait for mainCont and try again to play + setTimeout(() => this.player && this.Play(), delay); + setTimeout(() => { this.Document._videoStart = undefined; }, 10 + delay); + } + } + }, + { fireImmediately: true } + ); + this._disposers.videoStop = reaction( + () => this.Document._videoStop, + (videoStop) => { + if (videoStop !== undefined) { + if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) { + const delay = this.player ? 0 : 250; // wait for mainCont and try again to play + setTimeout(() => this.player && this.Pause(), delay); + setTimeout(() => { this.Document._videoStop = undefined; }, 10 + delay); + } + } + }, + { fireImmediately: true } + ); if (this.youtubeVideoId) { const youtubeaspect = 400 / 315; const nativeWidth = Doc.NativeWidth(this.layoutDoc); @@ -196,8 +224,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD componentWillUnmount() { this.Pause(); - this._reactionDisposer?.(); - this._youtubeReactionDisposer?.(); + this._disposers.reactionDisposer?.(); + this._disposers.youtubeReactionDisposer?.(); + this._disposers.videoStart?.(); } @action @@ -207,8 +236,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD this._videoRef!.ontimeupdate = this.updateTimecode; // @ts-ignore vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); - this._reactionDisposer?.(); - this._reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0), + this._disposers.reactionDisposer?.(); + this._disposers.reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0), time => !this._playing && (vref.currentTime = time), { fireImmediately: true }); } } @@ -293,10 +322,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false); }); const onYoutubePlayerReady = (event: any) => { - this._reactionDisposer?.(); - this._youtubeReactionDisposer?.(); - this._reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0))); - this._youtubeReactionDisposer = reaction( + this._disposers.reactionDisposer?.(); + this._disposers.youtubeReactionDisposer?.(); + this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0))); + this._disposers.youtubeReactionDisposer = reaction( () => !this.props.Document.isAnnotating && Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting, (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true }); }; |
