Skip to content

Commit

Permalink
Add audible notifcation on broadcast error (#10654)
Browse files Browse the repository at this point in the history
* Add audible notifcation on broadcast error

* Add error audio file

* Add error ogg

* Catch play broadcast error

* Await play error sound

* Add promise error handling

* Add comment about audio elements
  • Loading branch information
weeman1337 committed May 11, 2023
1 parent 9aade5a commit 0a6fe83
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 0 deletions.
Binary file added res/media/error.mp3
Binary file not shown.
Binary file added res/media/error.ogg
Binary file not shown.
20 changes: 20 additions & 0 deletions src/voice-broadcast/models/VoiceBroadcastRecording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { ActionPayload } from "../../dispatcher/payloads";
import { VoiceBroadcastChunkEvents } from "../utils/VoiceBroadcastChunkEvents";
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
import { createReconnectedListener } from "../../utils/connection";
import { localNotificationsAreSilenced } from "../../utils/notifications";

export enum VoiceBroadcastRecordingEvent {
StateChanged = "liveness_changed",
Expand Down Expand Up @@ -333,10 +334,29 @@ export class VoiceBroadcastRecording
* It sets the connection error state and stops the recorder.
*/
private async onConnectionError(): Promise<void> {
this.playConnectionErrorAudioNotification().catch(() => {
// Error logged in playConnectionErrorAudioNotification().
});
await this.stopRecorder(false);
this.setState("connection_error");
}

private async playConnectionErrorAudioNotification(): Promise<void> {
if (localNotificationsAreSilenced(this.client)) {
return;
}

// Audio files are added to the document in Element Web.
// See <audio> elements in https://github.com/vector-im/element-web/blob/develop/src/vector/index.html
const audioElement = document.querySelector<HTMLAudioElement>("audio#errorAudio");

try {
await audioElement?.play();
} catch (e) {
logger.warn("error playing 'errorAudio'", e);
}
}

private async uploadFile(chunk: ChunkRecordedPayload): ReturnType<typeof uploadFile> {
return uploadFile(
this.client,
Expand Down
45 changes: 45 additions & 0 deletions test/voice-broadcast/models/VoiceBroadcastRecording-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ClientEvent,
EventTimelineSet,
EventType,
LOCAL_NOTIFICATION_SETTINGS_PREFIX,
MatrixClient,
MatrixEvent,
MatrixEventEvent,
Expand Down Expand Up @@ -89,6 +90,7 @@ describe("VoiceBroadcastRecording", () => {
let voiceBroadcastRecording: VoiceBroadcastRecording;
let onStateChanged: (state: VoiceBroadcastRecordingState) => void;
let voiceBroadcastRecorder: VoiceBroadcastRecorder;
let audioElement: HTMLAudioElement;

const mkVoiceBroadcastInfoEvent = (content: VoiceBroadcastInfoEventContent) => {
return mkEvent({
Expand Down Expand Up @@ -251,6 +253,18 @@ describe("VoiceBroadcastRecording", () => {
};
},
);

audioElement = {
play: jest.fn(),
} as any as HTMLAudioElement;

jest.spyOn(document, "querySelector").mockImplementation((selector: string) => {
if (selector === "audio#errorAudio") {
return audioElement;
}

return null;
});
});

afterEach(() => {
Expand Down Expand Up @@ -501,6 +515,33 @@ describe("VoiceBroadcastRecording", () => {
});
});

describe("and audible notifications are disabled", () => {
beforeEach(() => {
const notificationSettings = mkEvent({
event: true,
type: `${LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${client.getDeviceId()}`,
user: client.getSafeUserId(),
content: {
is_silenced: true,
},
});
mocked(client.getAccountData).mockReturnValue(notificationSettings);
});

describe("and a chunk has been recorded and sending the voice message fails", () => {
beforeEach(() => {
mocked(client.sendMessage).mockRejectedValue("Error");
emitFirsChunkRecorded();
});

itShouldBeInState("connection_error");

it("should not play a notification", () => {
expect(audioElement.play).not.toHaveBeenCalled();
});
});
});

describe("and a chunk has been recorded and sending the voice message fails", () => {
beforeEach(() => {
mocked(client.sendMessage).mockRejectedValue("Error");
Expand All @@ -509,6 +550,10 @@ describe("VoiceBroadcastRecording", () => {

itShouldBeInState("connection_error");

it("should play a notification", () => {
expect(audioElement.play).toHaveBeenCalled();
});

describe("and the connection is back", () => {
beforeEach(() => {
mocked(client.sendMessage).mockClear();
Expand Down

0 comments on commit 0a6fe83

Please sign in to comment.