From 3c8d5d0a53d03d570fd57789ecf43121eb814b0f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 23 Oct 2019 10:39:53 -0400 Subject: several fixes to audio to better support generic timecode linking --- src/client/views/nodes/AudioBox.tsx | 63 +++++++++++++++++------------ src/client/views/nodes/DocumentView.tsx | 18 ++++----- src/client/views/nodes/FormattedTextBox.tsx | 2 +- 3 files changed, 47 insertions(+), 36 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 62ec683da..135874400 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { FieldViewProps, FieldView } from './FieldView'; import { observer } from "mobx-react"; import "./AudioBox.scss"; -import { Cast, DateCast } from "../../../new_fields/Types"; +import { Cast, DateCast, NumCast } from "../../../new_fields/Types"; import { AudioField, nullAudio } from "../../../new_fields/URLField"; import { DocExtendableComponent } from "../DocComponent"; import { makeInterface, createSchema } from "../../../new_fields/Schema"; @@ -41,33 +41,33 @@ export class AudioBox extends DocExtendableComponent this._audioState = this.path ? 2 : 0); + runInAction(() => this._audioState = this.path ? "recorded" : "unrecorded"); this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID, scrollLinkId => { - scrollLinkId && DocListCast(this.dataDoc.links).map(l => { - const la1 = l.anchor1 as Doc; - const la2 = l.anchor2 as Doc; - if (l[Id] === scrollLinkId && la1 && la2) { - let doc = Doc.AreProtosEqual(la1, this.dataDoc) ? la2 : la1; - let seek = DateCast(la1.creationTime); - setTimeout(() => this.playFrom(seek.date.getTime()), 250); - } + scrollLinkId && DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => { + let la1 = l.anchor1 as Doc; + let linkTime = Doc.AreProtosEqual(la1, this.dataDoc) ? NumCast(l.anchor1Timecode) : NumCast(l.anchor2Timecode); + setTimeout(() => this.playFrom(linkTime), 250); }); - scrollLinkId && (this.layoutDoc.scrollLinkID = undefined); + scrollLinkId && Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false); }, { fireImmediately: true }); this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(), selected => { let sel = selected.length ? selected[0].props.Document : undefined; this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFrom(DateCast(sel.creationTime).date.getTime()); }); - this._scrubbingDisposer = reaction(() => AudioBox.ScrubTime, time => this.Document.playOnSelect && this.playFrom(time)); + this._scrubbingDisposer = reaction(() => AudioBox.ScrubTime, timeInMillisecondsFrom1970 => { + let start = this.extensionDoc && DateCast(this.extensionDoc.recordingStart); + start && this.playFrom((timeInMillisecondsFrom1970 - start.date.getTime()) / 1000); + }); } updateHighlights = () => { @@ -76,14 +76,15 @@ export class AudioBox extends DocExtendableComponent { let la1 = l.anchor1 as Doc; + let linkTime = NumCast(l.anchor2Timecode); if (Doc.AreProtosEqual(la1, this.dataDoc)) { la1 = l.anchor2 as Doc; + linkTime = NumCast(l.anchor1Timecode); } - let date = DateCast(la1.creationDate); - let offset = (date!.date.getTime() - start.date.getTime()) / 1000; - if (offset > this._lastUpdate && offset < htmlEle.currentTime) { + if (linkTime > this._lastUpdate && linkTime < htmlEle.currentTime) { Doc.linkFollowHighlight(la1); } }); @@ -91,16 +92,15 @@ export class AudioBox extends DocExtendableComponent { + playFrom = (seekTimeInSeconds: number) => { const extensionDoc = this.extensionDoc; let start = extensionDoc && DateCast(extensionDoc.recordingStart); if (this._ele && start) { - if (seek) { - let delta = (seek - start.date.getTime()) / 1000; - if (start && delta > 0 && delta < this._ele.duration) { - this._ele.currentTime = delta; + if (seekTimeInSeconds) { + if (seekTimeInSeconds >= 0 && seekTimeInSeconds <= this._ele.duration) { + this._ele.currentTime = seekTimeInSeconds; this._ele.play(); - this._lastUpdate = delta; + this._lastUpdate = seekTimeInSeconds; setTimeout(this.updateHighlights, 0); } else { this._ele.pause(); @@ -117,6 +117,14 @@ export class AudioBox extends DocExtendableComponent { + if (this._audioState === "recording") { + setTimeout(this.updateRecordTime, 30); + this.Document.currentTimecode = (new Date().getTime() - this._recordStart) / 1000; + } + } + recordAudioAnnotation = () => { let gumStream: any; let self = this; @@ -140,7 +148,9 @@ export class AudioBox extends DocExtendableComponent self._audioState = 1); + runInAction(() => self._audioState = "recording"); + self._recordStart = new Date().getTime(); + setTimeout(self.updateRecordTime, 0); self._recorder.start(); setTimeout(() => { self.stopRecording(); @@ -158,7 +168,7 @@ export class AudioBox extends DocExtendableComponent { this._recorder.stop(); - this._audioState = 2; + this._audioState = "recorded"; let ind = AudioBox.ActiveRecordings.indexOf(this.props.Document); ind !== -1 && (AudioBox.ActiveRecordings.splice(ind, 1)); }); @@ -174,6 +184,7 @@ export class AudioBox extends DocExtendableComponent { e && e.addEventListener("play", this.playClick as any); + e && e.addEventListener("timeupdate", () => this.props.Document.currentTimecode = e!.currentTime); this._ele = e; } @@ -197,8 +208,8 @@ export class AudioBox extends DocExtendableComponent
{!this.path ? - : this.audio } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 67c85e158..c82fd0f0f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -165,7 +165,7 @@ export class DocumentView extends DocComponent(Docu Doc.UnBrushDoc(this.props.Document); } else if (this.onClickHandler && this.onClickHandler.script) { this.onClickHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); - } else if (this.props.Document.type === DocumentType.BUTTON) { + } else if (this.Document.type === DocumentType.BUTTON) { ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY); } else if (this.Document.isButton) { SelectionManager.SelectDoc(this, e.ctrlKey); // don't think this should happen if a button action is actually triggered. @@ -179,8 +179,8 @@ export class DocumentView extends DocComponent(Docu } buttonClick = async (altKey: boolean, ctrlKey: boolean) => { - let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); - let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); + let maximizedDocs = await DocListCastAsync(this.Document.maximizedDocs); + let summarizedDocs = await DocListCastAsync(this.Document.summarizedDocs); let linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; @@ -333,9 +333,9 @@ export class DocumentView extends DocComponent(Docu onDrop = (e: React.DragEvent) => { let text = e.dataTransfer.getData("text/plain"); if (!e.isDefaultPrevented() && text && text.startsWith("(Docu @undoBatch @action makeIntoPortal = async () => { - let anchors = await Promise.all(DocListCast(this.props.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc))); + let anchors = await Promise.all(DocListCast(this.Document.links).map(async (d: Doc) => Cast(d.anchor2, Doc))); if (!anchors.find(anchor2 => anchor2 && anchor2.title === this.Document.title + ".portal" ? true : false)) { let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, ""); DocServer.GetRefField(portalID).then(existingPortal => { @@ -590,7 +590,7 @@ export class DocumentView extends DocComponent(Docu render() { if (!this.props.Document) return (null); - let animDims = this.props.Document.animateToDimensions ? Array.from(Cast(this.props.Document.animateToDimensions, listSpec("number"))!) : undefined; + const animDims = this.Document.animateToDimensions ? Array.from(this.Document.animateToDimensions) : undefined; const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined; const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; const colorSet = this.setsLayoutProp("backgroundColor"); @@ -644,7 +644,7 @@ export class DocumentView extends DocComponent(Docu
(Docu onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} onPointerEnter={() => Doc.BrushDoc(this.props.Document)} onPointerLeave={() => Doc.UnBrushDoc(this.props.Document)} > - {this.props.Document.links && DocListCast(this.props.Document.links).map((d, i) => + {this.Document.links && DocListCast(this.Document.links).map((d, i) => //this.linkEndpointDoc(d).type === DocumentType.PDFANNO ? (null) :
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 5c2d39d98..5588bb4c9 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -605,7 +605,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0); setTimeout(() => this.unhighlightSearchTerms(), 2000); } - this.layoutDoc.scrollToLinkID = undefined; + Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false); } }, -- cgit v1.2.3-70-g09d2