diff options
author | Bob Zeleznik <zzzman@gmail.com> | 2019-10-22 14:20:16 -0400 |
---|---|---|
committer | Bob Zeleznik <zzzman@gmail.com> | 2019-10-22 14:20:16 -0400 |
commit | 0206517bbf09c10b8f3926c7ea133fa4c0263a3a (patch) | |
tree | 278a2cdee0ce8da1369cca0ff668940291bcafc3 /src | |
parent | 79476255d05e0bd7c24e46267507769800c547f8 (diff) |
using links to trigger audio playback.
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 4 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 80 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 2 |
4 files changed, 59 insertions, 29 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b1406d5e1..fd2009dd6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -334,6 +334,10 @@ export namespace Docs { let dataDoc = MakeDataDelegate(proto, protoProps, data); let viewDoc = Doc.MakeDelegate(dataDoc, delegId); + 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/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d7350fe1a..f6518a01d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -263,7 +263,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onPointerDown = (e: React.PointerEvent): void => { if (e.nativeEvent.cancelBubble) return; this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false; - if (e.button === 0 && !e.shiftKey && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) { + if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 5f5202f65..85607c6b8 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -9,12 +9,13 @@ import { makeInterface, createSchema } from "../../../new_fields/Schema"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Utils } from "../../../Utils"; import { RouteStore } from "../../../server/RouteStore"; -import { runInAction, observable, reaction, IReactionDisposer, computed } from "mobx"; +import { runInAction, observable, reaction, IReactionDisposer, computed, action } from "mobx"; import { DateField } from "../../../new_fields/DateField"; import { SelectionManager } from "../../util/SelectionManager"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; +import { Id } from "../../../new_fields/FieldSymbols"; interface Window { MediaRecorder: MediaRecorder; @@ -33,41 +34,60 @@ const AudioDocument = makeInterface(documentSchema, audioSchema); @observer export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocument>(AudioDocument) { - _reactionDisposer: IReactionDisposer | undefined; - @observable private _audioState = 0; - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); } + + _linkPlayDisposer: IReactionDisposer | undefined; + _reactionDisposer: IReactionDisposer | undefined; _ref = React.createRef<HTMLAudioElement>(); + _recorder: any; + + @observable private _audioState = 0; + public static ActiveRecordings: Doc[] = []; componentDidMount() { runInAction(() => this._audioState = this.path ? 2 : 0); + 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) { + setTimeout(() => this.playFrom(Doc.AreProtosEqual(la1, this.dataDoc) ? la2 : la1), 250); + } + }); + scrollLinkId && (this.layoutDoc.scrollLinkID = undefined); + }, { fireImmediately: true }); this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(), selected => { let sel = selected.length ? selected[0].props.Document : undefined; - const extensionDoc = this.extensionDoc; - let start = extensionDoc && DateCast(extensionDoc.recordingStart); - let seek = sel && DateCast(sel.creationDate) - if (this._ref.current && start && seek) { - if (this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document)) { - 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(); - } else { - this._ref.current.pause(); - } - } else { - this._ref.current.pause(); - } - } + this.Document.playOnSelect && sel && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFrom(sel); }); } + 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) { + 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(); + } else { + this._ref.current.pause(); + } + } else { + this._ref.current.pause(); + } + } + } + componentWillUnmount() { this._reactionDisposer && this._reactionDisposer(); + this._linkPlayDisposer && this._linkPlayDisposer(); } - _recorder: any; recordAudioAnnotation = () => { let gumStream: any; let self = this; @@ -78,6 +98,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume gumStream = stream; self._recorder = new MediaRecorder(stream); extensionDoc.recordingStart = new DateField(new Date()); + AudioBox.ActiveRecordings.push(self.props.Document); self._recorder.ondataavailable = async function (e: any) { const formData = new FormData(); formData.append("file", e.data); @@ -93,8 +114,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume runInAction(() => self._audioState = 1); self._recorder.start(); setTimeout(() => { - self._recorder.stop(); - runInAction(() => self._audioState = 2); + self.stopRecording(); gumStream.getAudioTracks()[0].stop(); }, 60 * 60 * 1000); // stop after an hour? }); @@ -107,11 +127,17 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" }); } + stopRecording = action(() => { + this._recorder.stop(); + 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) { + if (e.button === 0 && !e.ctrlKey) { if (this._recorder) { - this._recorder.stop(); - runInAction(() => this._audioState = 2); + this.stopRecording(); } else { this.recordAudioAnnotation(); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 05080b824..67c85e158 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -218,7 +218,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu this._hitTemplateDrag = true; } } - if ((this.active || this.Document.onDragStart || this.Document.onClick) && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); + if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); |