diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index a5a2c4c963..f0df64fcb4 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -68,37 +68,49 @@ export default class VoiceRecordComposerTile extends React.PureComponent Math.round(v * 1024)), - }, - "org.matrix.msc3245.voice": {}, // No content, this is a rendering hint - }); + // noinspection ES6MissingAwait - we don't care if it fails, it'll get queued. + MatrixClientPeg.get().sendMessage(this.props.room.roomId, { + "body": "Voice message", + //"msgtype": "org.matrix.msc2516.voice", + "msgtype": MsgType.Audio, + "url": upload.mxc, + "file": upload.encrypted, + "info": { + duration: Math.round(this.state.recorder.durationSeconds * 1000), + mimetype: this.state.recorder.contentType, + size: this.state.recorder.contentLength, + }, + + // MSC1767 + Ideals of MSC2516 as MSC3245 + // https://github.com/matrix-org/matrix-doc/pull/3245 + "org.matrix.msc1767.text": "Voice message", + "org.matrix.msc1767.file": { + url: upload.mxc, + file: upload.encrypted, + name: "Voice message.ogg", + mimetype: this.state.recorder.contentType, + size: this.state.recorder.contentLength, + }, + "org.matrix.msc1767.audio": { + duration: Math.round(this.state.recorder.durationSeconds * 1000), + + // https://github.com/matrix-org/matrix-doc/pull/3246 + waveform: this.state.recorder.getPlayback().thumbnailWaveform.map(v => Math.round(v * 1024)), + }, + "org.matrix.msc3245.voice": {}, // No content, this is a rendering hint + }); + } catch (e) { + console.error("Error sending/uploading voice message:", e); + Modal.createTrackedDialog('Upload failed', '', ErrorDialog, { + title: _t('Upload Failed'), + description: _t("The voice message failed to upload."), + }); + return; // don't dispose the recording so the user can retry, maybe + } await this.disposeRecording(); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1116e4cdc1..c63e97b145 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1678,6 +1678,7 @@ "Invited by %(sender)s": "Invited by %(sender)s", "Jump to first unread message.": "Jump to first unread message.", "Mark all as read": "Mark all as read", + "The voice message failed to upload.": "The voice message failed to upload.", "Unable to access your microphone": "Unable to access your microphone", "We were unable to access your microphone. Please check your browser settings and try again.": "We were unable to access your microphone. Please check your browser settings and try again.", "No microphone found": "No microphone found", diff --git a/src/voice/VoiceRecording.ts b/src/voice/VoiceRecording.ts index 536283689a..efd616e5ae 100644 --- a/src/voice/VoiceRecording.ts +++ b/src/voice/VoiceRecording.ts @@ -333,12 +333,17 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { if (this.lastUpload) return this.lastUpload; - this.emit(RecordingState.Uploading); - const { url: mxc, file: encrypted } = await uploadFile(this.client, inRoomId, new Blob([this.audioBuffer], { - type: this.contentType, - })); - this.lastUpload = { mxc, encrypted }; - this.emit(RecordingState.Uploaded); + try { + this.emit(RecordingState.Uploading); + const { url: mxc, file: encrypted } = await uploadFile(this.client, inRoomId, new Blob([this.audioBuffer], { + type: this.contentType, + })); + this.lastUpload = { mxc, encrypted }; + this.emit(RecordingState.Uploaded); + } catch (e) { + this.emit(RecordingState.Ended); + throw e; + } return this.lastUpload; } }