aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-03-14 10:55:29 -0400
committerbobzel <zzzman@gmail.com>2024-03-14 10:55:29 -0400
commit409a72c8c2546851fc824877b27a71d101839e7e (patch)
treebf9e842c36164786a70603a40ffd782773268915
parent9abf07d57edad6441e29a57759d14cc9fbdfedb4 (diff)
cleaned up some audio recording and annotating code
-rw-r--r--src/client/views/MarqueeAnnotator.tsx2
-rw-r--r--src/client/views/StyleProvider.tsx11
-rw-r--r--src/client/views/nodes/DocumentView.tsx90
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx21
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx6
-rw-r--r--src/fields/Doc.ts4
-rw-r--r--src/fields/DocSymbols.ts35
7 files changed, 81 insertions, 88 deletions
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index f59042b04..3c35b441b 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -184,7 +184,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
};
AnchorMenu.Instance.OnClick = undoable((e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
AnchorMenu.Instance.OnAudio = unimplementedFunction;
- AnchorMenu.Instance.Highlight = this.highlight;
+ AnchorMenu.Instance.Highlight = (color: string) => this.highlight(color, false, undefined, true);
AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index ab811858a..1adb0d9e5 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -50,6 +50,11 @@ export enum StyleProp {
Highlighting = 'highlighting', // border highlighting
}
+export enum AudioAnnoState {
+ stopped = 'stopped',
+ playing = 'playing',
+}
+
function toggleLockedPosition(doc: Doc) {
UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
}
@@ -330,14 +335,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
);
};
const audio = () => {
- const audioAnnoState = (doc: Doc) => StrCast(doc.audioAnnoState, 'stopped');
+ const audioAnnoState = (doc: Doc) => StrCast(doc.audioAnnoState, AudioAnnoState.stopped);
const audioAnnosCount = (doc: Doc) => StrListCast(doc[fieldKey + 'audioAnnotations']).length;
if (!doc || props?.renderDepth === -1 || !audioAnnosCount(doc)) return null;
- const audioIconColors: { [key: string]: string } = { recording: 'red', playing: 'green', stopped: 'blue' };
+ const audioIconColors: { [key: string]: string } = { playing: 'green', stopped: 'blue' };
return (
<Tooltip title={<div>{StrListCast(doc[fieldKey + 'audioAnnotations_text']).lastElement()}</div>}>
<div className="styleProvider-audio" onPointerDown={() => DocumentManager.Instance.getFirstDocumentView(doc)?.playAnnotation()}>
- <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors[audioAnnoState(doc)] }} icon={'file-audio'} size="sm" />
+ <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors[audioAnnoState(doc)] }} icon='file-audio' size="sm" />
</div>
</Tooltip>
);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 40592c2cd..bbeacef88 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -38,7 +38,7 @@ import { DocComponent, ViewBoxInterface } from '../DocComponent';
import { EditableView } from '../EditableView';
import { GestureOverlay } from '../GestureOverlay';
import { LightboxView } from '../LightboxView';
-import { StyleProp } from '../StyleProvider';
+import { AudioAnnoState, StyleProp } from '../StyleProvider';
import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView';
import { DocumentLinksButton } from './DocumentLinksButton';
import './DocumentView.scss';
@@ -1004,49 +1004,41 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
public static recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
let gumStream: any;
let recorder: any;
- navigator.mediaDevices
- .getUserMedia({
- audio: true,
- })
- .then(function (stream) {
- let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null);
- if (audioTextAnnos) audioTextAnnos.push('');
- else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List<string>(['']);
- DictationManager.Controls.listen({
- interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value),
- continuous: { indefinite: false },
- }).then(results => {
- if (results && [DictationManager.Controls.Infringed].includes(results)) {
- DictationManager.Controls.stop();
- }
- onEnd?.();
- });
-
- gumStream = stream;
- recorder = new MediaRecorder(stream);
- recorder.ondataavailable = async (e: any) => {
- const [{ result }] = await Networking.UploadFilesToServer({ file: e.data });
- if (!(result instanceof Error)) {
- const audioField = new AudioField(result.accessPaths.agnostic.client);
- const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null);
- if (audioAnnos === undefined) {
- dataDoc[field + '_audioAnnotations'] = new List([audioField]);
- } else {
- audioAnnos.push(audioField);
- }
- }
- };
- //runInAction(() => (dataDoc.audioAnnoState = 'recording'));
- recorder.start();
- const stopFunc = () => {
- recorder.stop();
- DictationManager.Controls.stop(false);
- runInAction(() => (dataDoc.audioAnnoState = 'stopped'));
- gumStream.getAudioTracks()[0].stop();
- };
- if (onRecording) onRecording(stopFunc);
- else setTimeout(stopFunc, 5000);
+ navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) {
+ let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null);
+ if (audioTextAnnos) audioTextAnnos.push('');
+ else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List<string>(['']);
+ DictationManager.Controls.listen({
+ interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value),
+ continuous: { indefinite: false },
+ }).then(results => {
+ if (results && [DictationManager.Controls.Infringed].includes(results)) {
+ DictationManager.Controls.stop();
+ }
+ onEnd?.();
});
+
+ gumStream = stream;
+ recorder = new MediaRecorder(stream);
+ recorder.ondataavailable = async (e: any) => {
+ const [{ result }] = await Networking.UploadFilesToServer({ file: e.data });
+ if (!(result instanceof Error)) {
+ const audioField = new AudioField(result.accessPaths.agnostic.client);
+ const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null);
+ if (audioAnnos) audioAnnos.push(audioField);
+ else dataDoc[field + '_audioAnnotations'] = new List([audioField]);
+ }
+ };
+ recorder.start();
+ const stopFunc = () => {
+ recorder.stop();
+ DictationManager.Controls.stop(false);
+ dataDoc.audioAnnoState = AudioAnnoState.stopped;
+ gumStream.getAudioTracks()[0].stop();
+ };
+ if (onRecording) onRecording(stopFunc);
+ else setTimeout(stopFunc, 5000);
+ });
}
}
@@ -1231,25 +1223,25 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public playAnnotation = () => {
const self = this;
- const audioAnnoState = this.dataDoc.audioAnnoState ?? 'stopped';
+ const audioAnnoState = this.dataDoc.audioAnnoState ?? AudioAnnoState.stopped;
const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '_audioAnnotations'], listSpec(AudioField), null);
const anno = audioAnnos?.lastElement();
if (anno instanceof AudioField) {
switch (audioAnnoState) {
- case 'stopped':
+ case AudioAnnoState.stopped:
this.dataDoc[AudioPlay] = new Howl({
src: [anno.url.href],
format: ['mp3'],
autoplay: true,
loop: false,
volume: 0.5,
- onend: action(() => (self.dataDoc.audioAnnoState = 'stopped')),
+ onend: action(() => (self.dataDoc.audioAnnoState = AudioAnnoState.stopped)),
});
- this.dataDoc.audioAnnoState = 'playing';
+ this.dataDoc.audioAnnoState = AudioAnnoState.playing;
break;
- case 'playing':
+ case AudioAnnoState.playing:
this.dataDoc[AudioPlay]?.stop();
- this.dataDoc.audioAnnoState = 'stopped';
+ this.dataDoc.audioAnnoState = AudioAnnoState.stopped;
break;
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index e80c869d3..fb709818c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -51,7 +51,7 @@ import { SidebarAnnos } from '../../SidebarAnnos';
import { StyleProp } from '../../StyleProvider';
import { media_state } from '../AudioBox';
import { DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView';
-import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
+import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView';
import { LinkInfo } from '../LinkDocPreview';
import { PinProps, PresBox } from '../trails';
import { DashDocCommentView } from './DashDocCommentView';
@@ -271,29 +271,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let stopFunc: any;
const targetData = target[DocData];
targetData.mediaState = media_state.Recording;
- targetData.audioAnnoState = 'recording';
DocumentViewInternal.recordAudioAnnotation(targetData, Doc.LayoutFieldKey(target), stop => (stopFunc = stop));
- let reactionDisposer = reaction(
+ const reactionDisposer = reaction(
() => target.mediaState,
- action(dictation => {
+ dictation => {
if (!dictation) {
- targetData.audioAnnoState = 'stopped';
stopFunc();
reactionDisposer();
}
- })
+ }
);
target.title = ComputedField.MakeFunction(`self["text_audioAnnotations_text"].lastElement()`);
}
});
};
- AnchorMenu.Instance.Highlight = undoable(
- action((color: string, isLinkButton: boolean) => {
- this._editorView?.state && RichTextMenu.Instance.setHighlight(color);
- return undefined;
- }),
- 'highlght text'
- );
+ AnchorMenu.Instance.Highlight = undoable((color: string) => {
+ this._editorView?.state && RichTextMenu.Instance.setHighlight(color);
+ return undefined;
+ }, 'highlght text');
AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
/**
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index d0688c338..59f191af0 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -46,7 +46,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
- public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
+ public Highlight: (color: string) => Opt<Doc> = (color: string) => undefined;
public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
public Delete: () => void = unimplementedFunction;
public PinToPres: () => void = unimplementedFunction;
@@ -118,8 +118,8 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
};
@action
- highlightClicked = (e: React.MouseEvent) => {
- this.Highlight(this.highlightColor, false, undefined, true);
+ highlightClicked = () => {
+ this.Highlight(this.highlightColor);
AnchorMenu.Instance.fadeOut(true);
};
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ea5411740..921d7aa5d 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -15,7 +15,7 @@ import { incrementTitleCopy, Utils } from '../Utils';
import { DateField } from './DateField';
import {
AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, Animation, AudioPlay, Brushed, CachedUpdates, DirectLinks,
- DocAcl, DocCss, DocData, DocFields, DocLayout, DocViews, FieldKeys, FieldTuples, ForceServerWrite, Height, Highlight,
+ DocAcl, DocCss, DocData, DocLayout, DocViews, FieldKeys, FieldTuples, ForceServerWrite, Height, Highlight,
Initializing, Self, SelfProxy, TransitionTimer, UpdatingFromServer, Width
} from './DocSymbols'; // prettier-ignore
import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
@@ -227,7 +227,6 @@ export class Doc extends RefField {
DocAcl,
DocCss,
DocData,
- DocFields,
DocLayout,
DocViews,
FieldKeys,
@@ -308,7 +307,6 @@ export class Doc extends RefField {
DocServer.UpdateField(this[Id], serverOp);
}
};
- public [DocFields] = () => this[Self][FieldTuples]; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any);
public [Width] = () => NumCast(this[SelfProxy]._width);
public [Height] = () => NumCast(this[SelfProxy]._height);
public [TransitionTimer]: any = undefined;
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index 64d657e4f..837fcc90e 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -1,8 +1,26 @@
-export const DocUpdated = Symbol('DocUpdated');
+// Symbols for fundamental Doc operations such as: permissions, field and proxy access and server interactions
+export const AclPrivate = Symbol('DocAclOwnerOnly');
+export const AclReadonly = Symbol('DocAclReadOnly');
+export const AclAugment = Symbol('DocAclAugment');
+export const AclSelfEdit = Symbol('DocAclSelfEdit');
+export const AclEdit = Symbol('DocAclEdit');
+export const AclAdmin = Symbol('DocAclAdmin');
+export const DocAcl = Symbol('DocAcl');
+export const CachedUpdates = Symbol('DocCachedUpdates');
+export const UpdatingFromServer = Symbol('DocUpdatingFromServer');
+export const ForceServerWrite = Symbol('DocForceServerWrite');
export const Self = Symbol('DocSelf');
export const SelfProxy = Symbol('DocSelfProxy');
export const FieldKeys = Symbol('DocFieldKeys');
export const FieldTuples = Symbol('DocFieldTuples');
+export const Initializing = Symbol('DocInitializing');
+
+// Symbols for core Dash document model including data docs, layout docs, and links
+export const DocData = Symbol('DocData');
+export const DocLayout = Symbol('DocLayout');
+export const DirectLinks = Symbol('DocDirectLinks');
+
+// Symbols for view related operations for Documents
export const AudioPlay = Symbol('DocAudioPlay');
export const Width = Symbol('DocWidth');
export const Height = Symbol('DocHeight');
@@ -10,22 +28,7 @@ export const Animation = Symbol('DocAnimation');
export const Highlight = Symbol('DocHighlight');
export const DocViews = Symbol('DocViews');
export const Brushed = Symbol('DocBrushed');
-export const DocData = Symbol('DocData');
-export const DocLayout = Symbol('DocLayout');
-export const DocFields = Symbol('DocFields');
export const DocCss = Symbol('DocCss');
-export const DocAcl = Symbol('DocAcl');
export const TransitionTimer = Symbol('DocTransitionTimer');
-export const DirectLinks = Symbol('DocDirectLinks');
-export const AclPrivate = Symbol('DocAclOwnerOnly');
-export const AclReadonly = Symbol('DocAclReadOnly');
-export const AclAugment = Symbol('DocAclAugment');
-export const AclSelfEdit = Symbol('DocAclSelfEdit');
-export const AclEdit = Symbol('DocAclEdit');
-export const AclAdmin = Symbol('DocAclAdmin');
-export const UpdatingFromServer = Symbol('DocUpdatingFromServer');
-export const Initializing = Symbol('DocInitializing');
-export const ForceServerWrite = Symbol('DocForceServerWrite');
-export const CachedUpdates = Symbol('DocCachedUpdates');
export const DashVersion = 'v0.8.0';