diff options
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.scss | 12 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 58 |
4 files changed, 54 insertions, 20 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fd2009dd6..316efe44c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -336,7 +336,7 @@ export namespace Docs { AudioBox.ActiveRecordings.map(d => { DocUtils.MakeLink({ doc: viewDoc }, { doc: d }, "audio link", "link to audio: " + d.title); - }) + }); return Doc.assign(viewDoc, delegateProps); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c14739aa2..78b8ac0b7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -449,7 +449,7 @@ export class MainView extends React.Component { public static expandFlyout = action(() => { MainView.Instance._flyoutTranslate = true; MainView.Instance.flyoutWidth = 250; - }) + }); @computed get expandButton() { return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={MainView.expandFlyout}><FontAwesomeIcon icon="chevron-right" color="grey" size="lg" /></div>) : (null); diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 5c43c3c00..9bda5b2a7 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -1,14 +1,21 @@ .audiobox-container, .audiobox-container-interactive { width: 100%; height: 100%; + min-height: 32px; position: inherit; - display:inline-block; + display:flex; pointer-events: all; + .audiobox-handle { + width:20px; + height:100%; + display:inline-block; + background: gray; + } .audiobox-control, .audiobox-control-interactive { top:0; max-height: 32px; - position: absolute; width: 100%; + display:inline-block; pointer-events: none; } .audiobox-control-interactive { @@ -23,6 +30,5 @@ } .audiobox-record-interactive { pointer-events: all; - } }
\ No newline at end of file diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 85607c6b8..55b472726 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -38,8 +38,9 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume _linkPlayDisposer: IReactionDisposer | undefined; _reactionDisposer: IReactionDisposer | undefined; - _ref = React.createRef<HTMLAudioElement>(); + _ele: HTMLAudioElement | null = null; _recorder: any; + _lastUpdate = 0; @observable private _audioState = 0; public static ActiveRecordings: Doc[] = []; @@ -64,21 +65,44 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume }); } + updateHighlights = () => { + const extensionDoc = this.extensionDoc; + const htmlEle = this._ele; + const start = extensionDoc && DateCast(extensionDoc.recordingStart); + if (htmlEle && !htmlEle.paused && start) { + setTimeout(this.updateHighlights, 30); + DocListCast(this.dataDoc.links).map(l => { + let la1 = l.anchor1 as Doc; + if (Doc.AreProtosEqual(la1, this.dataDoc)) { + la1 = l.anchor2 as Doc; + } + let date = DateCast(la1.creationDate); + let offset = (date!.date.getTime() - start.date.getTime()) / 1000; + if (offset > this._lastUpdate && offset < htmlEle.currentTime) { + Doc.linkFollowHighlight(la1); + } + }); + this._lastUpdate = htmlEle.currentTime; + } + } + playFrom = (sel: Doc) => { const extensionDoc = this.extensionDoc; let start = extensionDoc && DateCast(extensionDoc.recordingStart); - let seek = sel && DateCast(sel.creationDate) - if (this._ref.current && start && seek) { + let seek = sel && DateCast(sel.creationDate); + if (this._ele && start && seek) { if (sel) { let delta = (seek.date.getTime() - start.date.getTime()) / 1000; - if (start && seek && delta > 0 && delta < this._ref.current.duration) { - this._ref.current.currentTime = delta; - this._ref.current.play(); + if (start && seek && delta > 0 && delta < this._ele.duration) { + this._ele.currentTime = delta; + this._ele.play(); + this._lastUpdate = delta; + setTimeout(this.updateHighlights, 0); } else { - this._ref.current.pause(); + this._ele.pause(); } } else { - this._ref.current.pause(); + this._ele.pause(); } } } @@ -132,19 +156,22 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume this._audioState = 2; let ind = AudioBox.ActiveRecordings.indexOf(this.props.Document); ind !== -1 && (AudioBox.ActiveRecordings.splice(ind, 1)); - }) + }); recordClick = (e: React.MouseEvent) => { if (e.button === 0 && !e.ctrlKey) { - if (this._recorder) { - this.stopRecording(); - } else { - this.recordAudioAnnotation(); - } + this._recorder ? this.stopRecording() : this.recordAudioAnnotation(); e.stopPropagation(); } } + playClick = (e: any) => setTimeout(this.updateHighlights, 30); + + setRef = (e: HTMLAudioElement | null) => { + e && e.addEventListener("play", this.playClick as any); + this._ele = e; + } + @computed get path() { let field = Cast(this.props.Document[this.props.fieldKey], AudioField); let path = (field instanceof AudioField) ? field.url.href : ""; @@ -153,7 +180,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume @computed get audio() { let interactive = this.active() ? "-interactive" : ""; - return <audio controls ref={this._ref} className={`audiobox-control${interactive}`}> + return <audio controls ref={this.setRef} className={`audiobox-control${interactive}`}> <source src={this.path} type="audio/mpeg" /> Not supported. </audio>; @@ -163,6 +190,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume let interactive = this.active() ? "-interactive" : ""; return (!this.extensionDoc ? (null) : <div className={`audiobox-container`} onContextMenu={this.specificContextMenu} onClick={!this.path ? this.recordClick : undefined}> + <div className="audiobox-handle"></div> {!this.path ? <button className={`audiobox-record${interactive}`} style={{ backgroundColor: ["black", "red", "blue"][this._audioState] }}> {this._audioState === 1 ? "STOP" : "RECORD"} |