aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Zeleznik <zzzman@gmail.com>2019-10-22 19:15:29 -0400
committerBob Zeleznik <zzzman@gmail.com>2019-10-22 19:15:29 -0400
commit171e5a1716e4a2f981e647ae26f8ddd0e2332693 (patch)
treebc34b9a6404e64164b216aeb9d05d7b309a6abba
parentc40f8fdc33bac66e943bdc066520f942648f02e4 (diff)
playback highlights from audio
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/nodes/AudioBox.scss12
-rw-r--r--src/client/views/nodes/AudioBox.tsx58
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"}