From 5e646f861cf592e72ae7eb18e64bd65f15de3245 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 27 Apr 2021 18:59:10 -0600 Subject: [PATCH] Wire up the send button for voice messages This fixes a bug where we couldn't upload voice messages because the audio buffer was being read, therefore changing the position of the cursor. When this happened, the upload function would claim that the buffer was empty and could not be read. --- src/components/views/rooms/MessageComposer.tsx | 12 +++++++++++- src/voice/VoiceRecording.ts | 14 ++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index d126a7b161..3671069903 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -198,6 +198,7 @@ interface IState { export default class MessageComposer extends React.Component { private dispatcherRef: string; private messageComposerInput: SendMessageComposer; + private voiceRecordingButton: VoiceRecordComposerTile; constructor(props) { super(props); @@ -322,7 +323,15 @@ export default class MessageComposer extends React.Component { }); } - sendMessage = () => { + sendMessage = async () => { + if (this.state.haveRecording && this.voiceRecordingButton) { + // There shouldn't be any text message to send when a voice recording is active, so + // just send out the voice recording. + await this.voiceRecordingButton.send(); + return; + } + + // XXX: Private function access this.messageComposerInput._sendMessage(); } @@ -387,6 +396,7 @@ export default class MessageComposer extends React.Component { if (SettingsStore.getValue("feature_voice_messages")) { controls.push( this.voiceRecordingButton = c} room={this.props.room} />); } diff --git a/src/voice/VoiceRecording.ts b/src/voice/VoiceRecording.ts index 692e317333..0c76ac406f 100644 --- a/src/voice/VoiceRecording.ts +++ b/src/voice/VoiceRecording.ts @@ -54,7 +54,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { private recorderStream: MediaStream; private recorderFFT: AnalyserNode; private recorderWorklet: AudioWorkletNode; - private buffer = new Uint8Array(0); + private buffer = new Uint8Array(0); // use this.audioBuffer to access private mxc: string; private recording = false; private observable: SimpleObservable; @@ -166,6 +166,12 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { }; } + private get audioBuffer(): Uint8Array { + // We need a clone of the buffer to avoid accidentally changing the position + // on the real thing. + return this.buffer.slice(0); + } + public get liveData(): SimpleObservable { if (!this.recording) throw new Error("No observable when not recording"); return this.observable; @@ -267,13 +273,13 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { await this.recorder.close(); this.emit(RecordingState.Ended); - return this.buffer; + return this.audioBuffer; }); } public getPlayback(): Promise { return Singleflight.for(this, "playback").do(async () => { - const playback = new Playback(this.buffer.buffer); // cast to ArrayBuffer proper + const playback = new Playback(this.audioBuffer.buffer); // cast to ArrayBuffer proper await playback.prepare(); return playback; }); @@ -294,7 +300,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { if (this.mxc) return this.mxc; this.emit(RecordingState.Uploading); - this.mxc = await this.client.uploadContent(new Blob([this.buffer], { + this.mxc = await this.client.uploadContent(new Blob([this.audioBuffer], { type: this.contentType, }), { onlyContentUri: false, // to stop the warnings in the console