From 26c973ecb47de86d94c39d71ad00ece38a85d623 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 4 Mar 2020 18:20:46 -0500
Subject: changed  text sampling to marking every second to get audio to
 synchronize better

---
 src/client/views/nodes/FormattedTextBox.tsx | 85 ++++++++++++++++++++---------
 1 file changed, 58 insertions(+), 27 deletions(-)

(limited to 'src/client/views/nodes/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 8f4cefbf4..f18183600 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -20,7 +20,7 @@ import { InkTool } from '../../../new_fields/InkField';
 import { RichTextField } from "../../../new_fields/RichTextField";
 import { RichTextUtils } from '../../../new_fields/RichTextUtils';
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
 import { TraceMobx } from '../../../new_fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, Utils } from '../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils';
@@ -82,6 +82,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     private _lastY = 0;
     private _undoTyping?: UndoManager.Batch;
     private _searchReactionDisposer?: Lambda;
+    private _recordReactionDisposer: Opt<IReactionDisposer>;
     private _scrollToRegionReactionDisposer: Opt<IReactionDisposer>;
     private _reactionDisposer: Opt<IReactionDisposer>;
     private _heightReactionDisposer: Opt<IReactionDisposer>;
@@ -92,6 +93,9 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     private _scrollDisposer: Opt<IReactionDisposer>;
     private dropDisposer?: DragManager.DragDropDisposer;
 
+    @computed get _recording() { return this.dataDoc.audioState === "recording"; }
+    set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; }
+
     @observable private _entered = false;
 
     public static FocusedBox: FormattedTextBox | undefined;
@@ -186,7 +190,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks);
 
             const tsel = this._editorView.state.selection.$from;
-            tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 1000)));
+            tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000)));
             const curText = state.doc.textBetween(0, state.doc.content.size, "\n\n");
             const curTemp = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField);
             if (!this._applyingChange) {
@@ -387,7 +391,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         !this.props.Document.expandedTemplate && funcs.push({ description: "Make Template", event: () => { this.props.Document.isTemplateDoc = true; Doc.AddDocToList(Cast(Doc.UserDoc().noteTypes, Doc, null), "data", this.props.Document); }, icon: "eye" });
         funcs.push({ description: "Toggle Single Line", event: () => this.props.Document._singleLine = !this.props.Document._singleLine, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Sidebar", event: () => this.props.Document._showSidebar = !this.props.Document._showSidebar, icon: "expand-arrows-alt" });
-        funcs.push({ description: "Record Bullet", event: () => this.recordBullet(), icon: "expand-arrows-alt" });
+        funcs.push({ description: "Toggle Audio", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" });
         funcs.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" });
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
             funcs.push({
@@ -405,12 +409,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: funcs, icon: "asterisk" });
     }
 
-    @observable _recording = false;
-
     recordDictation = () => {
-        //this._editorView!.focus();
-        if (this._recording) return;
-        runInAction(() => this._recording = true);
         DictationManager.Controls.listen({
             interimHandler: this.setCurrentBulletContent,
             continuous: { indefinite: false },
@@ -418,13 +417,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             if (results && [DictationManager.Controls.Infringed].includes(results)) {
                 DictationManager.Controls.stop();
             }
-            this._editorView!.focus();
+            //this._editorView!.focus();
         });
     }
-    stopDictation = (abort: boolean) => {
-        runInAction(() => this._recording = false);
-        DictationManager.Controls.stop(!abort);
-    }
+    stopDictation = (abort: boolean) => { DictationManager.Controls.stop(!abort); }
 
     @action
     toggleMenubar = () => {
@@ -449,12 +445,28 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     setCurrentBulletContent = (value: string) => {
         if (this._editorView) {
             let state = this._editorView.state;
+            let now = Date.now();
+            if (NumCast(this.props.Document.recordingStart, -1) === 0) {
+                this.props.Document.recordingStart = now = AudioBox.START;
+            }
+            console.log("NOW = " + (now - AudioBox.START) / 1000);
+            let mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(now / 1000) });
+            if (!this._break && state.selection.to !== state.selection.from) {
+                for (let i = state.selection.from; i <= state.selection.to; i++) {
+                    const pos = state.doc.resolve(i);
+                    const um = Array.from(pos.marks()).find(m => m.type === schema.marks.user_mark);
+                    if (um) {
+                        mark = um;
+                        break;
+                    }
+                }
+            }
+            this._break = false;
+            console.log("start = " + (mark.attrs.modified * 1000 - AudioBox.START) / 1000);
+            value = "" + (mark.attrs.modified * 1000 - AudioBox.START) / 1000 + value;
             const from = state.selection.from;
-            const to = state.selection.to;
-            this._editorView.dispatch(state.tr.insertText(value, from, to));
-            state = this._editorView.state;
-            const updated = TextSelection.create(state.doc, from, from + value.length);
-            this._editorView.dispatch(state.tr.setSelection(updated));
+            const inserted = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
+            this._editorView.dispatch(inserted.setSelection(TextSelection.create(inserted.doc, from, from + value.length + 1)));
         }
     }
 
@@ -558,6 +570,17 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             search => search ? this.highlightSearchTerms([Doc.SearchQuery()]) : this.unhighlightSearchTerms(),
             { fireImmediately: true });
 
+        this._recordReactionDisposer = reaction(() => this._recording,
+            () => {
+                if (this._recording) {
+                    setTimeout(action(() => {
+                        this.stopDictation(true);
+                        setTimeout(() => this.recordDictation(), 500);
+                    }), 500);
+                } else setTimeout(() => this.stopDictation(true), 0);
+            }
+        );
+
         this._scrollToRegionReactionDisposer = reaction(
             () => StrCast(this.layoutDoc.scrollToLinkID),
             async (scrollToLinkID) => {
@@ -807,7 +830,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         }
         (selectOnLoad /* || !rtfField?.Text*/) && this._editorView!.focus();
         // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
-        this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) })];
+        this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
     }
     getFont(font: string) {
         switch (font) {
@@ -831,6 +854,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         this._pullReactionDisposer?.();
         this._heightReactionDisposer?.();
         this._searchReactionDisposer?.();
+        this._recordReactionDisposer?.();
         this._buttonBarReactionDisposer?.();
         this._editorView?.destroy();
     }
@@ -838,7 +862,19 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     static _downEvent: any;
     _downX = 0;
     _downY = 0;
+    _break = false;
     onPointerDown = (e: React.PointerEvent): void => {
+        if (this._recording && !e.ctrlKey && e.button === 0) {
+            this.stopDictation(true);
+            this._break = true;
+            let state = this._editorView!.state;
+            const to = state.selection.to;
+            const updated = TextSelection.create(state.doc, to, to);
+            this._editorView!.dispatch(this._editorView!.state.tr.setSelection(updated).insertText("\n", to));
+            e.preventDefault();
+            e.stopPropagation();
+            if (this._recording) setTimeout(() => this.recordDictation(), 500);
+        }
         this._downX = e.clientX;
         this._downY = e.clientY;
         this.doLinkOnDeselect();
@@ -955,7 +991,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
             this.props.select(e.ctrlKey);
             this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, false);
         }
-        if (this._recording) setTimeout(() => { this.stopDictation(true); setTimeout(() => this.recordDictation(), 500); }, 500);
     }
 
     // this hackiness handles clicking on the list item bullets to do expand/collapse.  the bullets are ::before pseudo elements so there's no real way to hit test against them.
@@ -1062,17 +1097,13 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (e.key === "Tab" || e.key === "Enter") {
             e.preventDefault();
         }
-        const mark = e.key !== " " && this._lastTimedMark ? this._lastTimedMark : schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.round(Date.now() / 1000 / 5) });
+        const mark = e.key !== " " && this._lastTimedMark ? this._lastTimedMark : schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
         this._lastTimedMark = mark;
         this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
 
         if (!this._undoTyping) {
             this._undoTyping = UndoManager.StartBatch("undoTyping");
         }
-        if (this._recording) {
-            this.stopDictation(true);
-            setTimeout(() => this.recordDictation(), 250);
-        }
     }
 
     onscrolled = (ev: React.UIEvent) => {
@@ -1169,8 +1200,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                     </div>}
                 {!this.props.Document._showAudio ? (null) :
                     <div className="formattedTextBox-dictation"
-                        onClick={e => {
-                            this._recording ? this.stopDictation(true) : this.recordDictation();
+                        onPointerDown={e => {
+                            runInAction(() => this._recording = !this._recording);
                             setTimeout(() => this._editorView!.focus(), 500);
                             e.stopPropagation();
                         }} >
-- 
cgit v1.2.3-70-g09d2

