Encrypt the voice message file if needed

Fixes https://github.com/vector-im/element-web/issues/17729

"oops, should have done that"
This commit is contained in:
Travis Ralston 2021-06-25 13:54:05 -06:00
parent 6d6995e6fb
commit cb0d2a2c4f
3 changed files with 21 additions and 20 deletions

View file

@ -307,7 +307,7 @@ function readFileAsArrayBuffer(file: File | Blob): Promise<ArrayBuffer> {
* If the file is unencrypted then the object will have a "url" key. * If the file is unencrypted then the object will have a "url" key.
* If the file is encrypted then the object will have a "file" key. * If the file is encrypted then the object will have a "file" key.
*/ */
function uploadFile( export function uploadFile(
matrixClient: MatrixClient, matrixClient: MatrixClient,
roomId: string, roomId: string,
file: File | Blob, file: File | Blob,

View file

@ -65,12 +65,13 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
} }
await this.state.recorder.stop(); await this.state.recorder.stop();
const mxc = await this.state.recorder.upload(); const upload = await this.state.recorder.upload(this.props.room.roomId);
MatrixClientPeg.get().sendMessage(this.props.room.roomId, { MatrixClientPeg.get().sendMessage(this.props.room.roomId, {
"body": "Voice message", "body": "Voice message",
//"msgtype": "org.matrix.msc2516.voice", //"msgtype": "org.matrix.msc2516.voice",
"msgtype": MsgType.Audio, "msgtype": MsgType.Audio,
"url": mxc, "url": upload.mxc,
"file": upload.encrypted,
"info": { "info": {
duration: Math.round(this.state.recorder.durationSeconds * 1000), duration: Math.round(this.state.recorder.durationSeconds * 1000),
mimetype: this.state.recorder.contentType, mimetype: this.state.recorder.contentType,
@ -81,7 +82,8 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
// https://github.com/matrix-org/matrix-doc/pull/3245 // https://github.com/matrix-org/matrix-doc/pull/3245
"org.matrix.msc1767.text": "Voice message", "org.matrix.msc1767.text": "Voice message",
"org.matrix.msc1767.file": { "org.matrix.msc1767.file": {
url: mxc, "url": upload.mxc,
"file": upload.encrypted,
name: "Voice message.ogg", name: "Voice message.ogg",
mimetype: this.state.recorder.contentType, mimetype: this.state.recorder.contentType,
size: this.state.recorder.contentLength, size: this.state.recorder.contentLength,

View file

@ -27,6 +27,8 @@ import {PayloadEvent, WORKLET_NAME} from "./consts";
import {UPDATE_EVENT} from "../stores/AsyncStore"; import {UPDATE_EVENT} from "../stores/AsyncStore";
import {Playback} from "./Playback"; import {Playback} from "./Playback";
import {createAudioContext} from "./compat"; import {createAudioContext} from "./compat";
import { IEncryptedFile } from "matrix-js-sdk/src/@types/event";
import { uploadFile } from "../ContentMessages";
const CHANNELS = 1; // stereo isn't important const CHANNELS = 1; // stereo isn't important
export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality. export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality.
@ -49,6 +51,11 @@ export enum RecordingState {
Uploaded = "uploaded", Uploaded = "uploaded",
} }
export interface IUpload {
mxc?: string; // for unencrypted uploads
encrypted?: IEncryptedFile;
}
export class VoiceRecording extends EventEmitter implements IDestroyable { export class VoiceRecording extends EventEmitter implements IDestroyable {
private recorder: Recorder; private recorder: Recorder;
private recorderContext: AudioContext; private recorderContext: AudioContext;
@ -58,7 +65,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
private recorderWorklet: AudioWorkletNode; private recorderWorklet: AudioWorkletNode;
private recorderProcessor: ScriptProcessorNode; private recorderProcessor: ScriptProcessorNode;
private buffer = new Uint8Array(0); // use this.audioBuffer to access private buffer = new Uint8Array(0); // use this.audioBuffer to access
private mxc: string; private lastUpload: IUpload;
private recording = false; private recording = false;
private observable: SimpleObservable<IRecordingUpdate>; private observable: SimpleObservable<IRecordingUpdate>;
private amplitudes: number[] = []; // at each second mark, generated private amplitudes: number[] = []; // at each second mark, generated
@ -214,13 +221,6 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
return this.buffer.length > 0; return this.buffer.length > 0;
} }
public get mxcUri(): string {
if (!this.mxc) {
throw new Error("Recording has not been uploaded yet");
}
return this.mxc;
}
private onAudioProcess = (ev: AudioProcessingEvent) => { private onAudioProcess = (ev: AudioProcessingEvent) => {
this.processAudioUpdate(ev.playbackTime); this.processAudioUpdate(ev.playbackTime);
@ -290,7 +290,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
}; };
public async start(): Promise<void> { public async start(): Promise<void> {
if (this.mxc || this.hasRecording) { if (this.lastUpload || this.hasRecording) {
throw new Error("Recording already prepared"); throw new Error("Recording already prepared");
} }
if (this.recording) { if (this.recording) {
@ -362,20 +362,19 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
this.observable.close(); this.observable.close();
} }
public async upload(): Promise<string> { public async upload(inRoomId: string): Promise<IUpload> {
if (!this.hasRecording) { if (!this.hasRecording) {
throw new Error("No recording available to upload"); throw new Error("No recording available to upload");
} }
if (this.mxc) return this.mxc; if (this.lastUpload) return this.lastUpload;
this.emit(RecordingState.Uploading); this.emit(RecordingState.Uploading);
this.mxc = await this.client.uploadContent(new Blob([this.audioBuffer], { const { url: mxc, file: encrypted } = await uploadFile(this.client, inRoomId, new Blob([this.audioBuffer], {
type: this.contentType, type: this.contentType,
}), { }));
onlyContentUri: false, // to stop the warnings in the console this.lastUpload = {mxc, encrypted};
}).then(r => r['content_uri']);
this.emit(RecordingState.Uploaded); this.emit(RecordingState.Uploaded);
return this.mxc; return this.lastUpload;
} }
} }