Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Start voice broadcast recording while listening #9630

Merged
merged 9 commits into from
Nov 30, 2022
1 change: 1 addition & 0 deletions src/components/views/rooms/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
setUpVoiceBroadcastPreRecording(
this.props.room,
MatrixClientPeg.get(),
SdkContextClass.instance.voiceBroadcastPlaybacksStore,
VoiceBroadcastRecordingsStore.instance(),
SdkContextClass.instance.voiceBroadcastPreRecordingStore,
);
Expand Down
8 changes: 4 additions & 4 deletions src/components/views/voip/PipView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -367,14 +367,14 @@ class PipView extends React.Component<IProps, IState> {
const pipMode = true;
let pipContent: CreatePipChildren | null = null;

if (this.props.voiceBroadcastPreRecording) {
pipContent = this.createVoiceBroadcastPreRecordingPipContent(this.props.voiceBroadcastPreRecording);
}

if (this.props.voiceBroadcastPlayback) {
pipContent = this.createVoiceBroadcastPlaybackPipContent(this.props.voiceBroadcastPlayback);
}

if (this.props.voiceBroadcastPreRecording) {
pipContent = this.createVoiceBroadcastPreRecordingPipContent(this.props.voiceBroadcastPreRecording);
}

if (this.props.voiceBroadcastRecording) {
pipContent = this.createVoiceBroadcastRecordingPipContent(this.props.voiceBroadcastRecording);
}
Expand Down
3 changes: 3 additions & 0 deletions src/voice-broadcast/models/VoiceBroadcastPreRecording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";

import { IDestroyable } from "../../utils/IDestroyable";
import { VoiceBroadcastPlaybacksStore } from "../stores/VoiceBroadcastPlaybacksStore";
import { VoiceBroadcastRecordingsStore } from "../stores/VoiceBroadcastRecordingsStore";
import { startNewVoiceBroadcastRecording } from "../utils/startNewVoiceBroadcastRecording";

Expand All @@ -34,6 +35,7 @@ export class VoiceBroadcastPreRecording
public room: Room,
public sender: RoomMember,
private client: MatrixClient,
private playbacksStore: VoiceBroadcastPlaybacksStore,
private recordingsStore: VoiceBroadcastRecordingsStore,
) {
super();
Expand All @@ -43,6 +45,7 @@ export class VoiceBroadcastPreRecording
await startNewVoiceBroadcastRecording(
this.room,
this.client,
this.playbacksStore,
this.recordingsStore,
);
this.emit("dismiss", this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { MatrixClient, Room } from "matrix-js-sdk/src/matrix";

import {
checkVoiceBroadcastPreConditions,
VoiceBroadcastPlaybacksStore,
VoiceBroadcastPreRecording,
VoiceBroadcastPreRecordingStore,
VoiceBroadcastRecordingsStore,
Expand All @@ -26,6 +27,7 @@ import {
export const setUpVoiceBroadcastPreRecording = (
room: Room,
client: MatrixClient,
playbacksStore: VoiceBroadcastPlaybacksStore,
recordingsStore: VoiceBroadcastRecordingsStore,
preRecordingStore: VoiceBroadcastPreRecordingStore,
): VoiceBroadcastPreRecording | null => {
Expand All @@ -39,7 +41,11 @@ export const setUpVoiceBroadcastPreRecording = (
const sender = room.getMember(userId);
if (!sender) return null;

const preRecording = new VoiceBroadcastPreRecording(room, sender, client, recordingsStore);
// pause and clear current playback (if any)
playbacksStore.getCurrent()?.pause();
playbacksStore.clearCurrent();

const preRecording = new VoiceBroadcastPreRecording(room, sender, client, playbacksStore, recordingsStore);
preRecordingStore.setCurrent(preRecording);
return preRecording;
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
VoiceBroadcastRecordingsStore,
VoiceBroadcastRecording,
getChunkLength,
VoiceBroadcastPlaybacksStore,
} from "..";
import { checkVoiceBroadcastPreConditions } from "./checkVoiceBroadcastPreConditions";

Expand Down Expand Up @@ -80,17 +81,23 @@ const startBroadcast = async (
/**
* Starts a new Voice Broadcast Recording, if
* - the user has the permissions to do so in the room
* - the user is not already recording a voice broadcast
* - there is no other broadcast being recorded in the room, yet
* Sends a voice_broadcast_info state event and waits for the event to actually appear in the room state.
*/
export const startNewVoiceBroadcastRecording = async (
room: Room,
client: MatrixClient,
playbacksStore: VoiceBroadcastPlaybacksStore,
recordingsStore: VoiceBroadcastRecordingsStore,
): Promise<VoiceBroadcastRecording | null> => {
if (!checkVoiceBroadcastPreConditions(room, client, recordingsStore)) {
return null;
}

// pause and clear current playback (if any)
playbacksStore.getCurrent()?.pause();
playbacksStore.clearCurrent();

return startBroadcast(room, client, recordingsStore);
};
14 changes: 14 additions & 0 deletions test/components/views/voip/PipView-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ describe("PipView", () => {
room,
alice,
client,
voiceBroadcastPlaybacksStore,
voiceBroadcastRecordingsStore,
);
voiceBroadcastPreRecordingStore.setCurrent(voiceBroadcastPreRecording);
Expand Down Expand Up @@ -271,6 +272,19 @@ describe("PipView", () => {
});
});

describe("when there is a voice broadcast playback and pre-recording", () => {
beforeEach(() => {
startVoiceBroadcastPlayback(room);
setUpVoiceBroadcastPreRecording();
renderPip();
});

it("should render the voice broadcast pre-recording PiP", () => {
// check for the „Go live“ button
expect(screen.queryByText("Go live")).toBeInTheDocument();
});
});

describe("when there is a voice broadcast pre-recording", () => {
beforeEach(() => {
setUpVoiceBroadcastPreRecording();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { act, render, RenderResult, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import {
VoiceBroadcastPlaybacksStore,
VoiceBroadcastPreRecording,
VoiceBroadcastPreRecordingPip,
VoiceBroadcastRecordingsStore,
Expand All @@ -42,6 +43,7 @@ jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
describe("VoiceBroadcastPreRecordingPip", () => {
let renderResult: RenderResult;
let preRecording: VoiceBroadcastPreRecording;
let playbacksStore: VoiceBroadcastPlaybacksStore;
let recordingsStore: VoiceBroadcastRecordingsStore;
let client: MatrixClient;
let room: Room;
Expand All @@ -51,6 +53,7 @@ describe("VoiceBroadcastPreRecordingPip", () => {
client = stubClient();
room = new Room("[email protected]", client, client.getUserId() || "");
sender = new RoomMember(room.roomId, client.getUserId() || "");
playbacksStore = new VoiceBroadcastPlaybacksStore();
recordingsStore = new VoiceBroadcastRecordingsStore();
mocked(requestMediaPermissions).mockReturnValue(new Promise<MediaStream>((r) => {
r({
Expand All @@ -76,6 +79,7 @@ describe("VoiceBroadcastPreRecordingPip", () => {
room,
sender,
client,
playbacksStore,
recordingsStore,
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";

import {
startNewVoiceBroadcastRecording,
VoiceBroadcastPlaybacksStore,
VoiceBroadcastPreRecording,
VoiceBroadcastRecordingsStore,
} from "../../../src/voice-broadcast";
Expand All @@ -30,6 +31,7 @@ describe("VoiceBroadcastPreRecording", () => {
let client: MatrixClient;
let room: Room;
let sender: RoomMember;
let playbacksStore: VoiceBroadcastPlaybacksStore;
let recordingsStore: VoiceBroadcastRecordingsStore;
let preRecording: VoiceBroadcastPreRecording;
let onDismiss: (voiceBroadcastPreRecording: VoiceBroadcastPreRecording) => void;
Expand All @@ -38,12 +40,13 @@ describe("VoiceBroadcastPreRecording", () => {
client = stubClient();
room = new Room(roomId, client, client.getUserId() || "");
sender = new RoomMember(roomId, client.getUserId() || "");
playbacksStore = new VoiceBroadcastPlaybacksStore();
recordingsStore = new VoiceBroadcastRecordingsStore();
});

beforeEach(() => {
onDismiss = jest.fn();
preRecording = new VoiceBroadcastPreRecording(room, sender, client, recordingsStore);
preRecording = new VoiceBroadcastPreRecording(room, sender, client, playbacksStore, recordingsStore);
preRecording.on("dismiss", onDismiss);
});

Expand All @@ -56,6 +59,7 @@ describe("VoiceBroadcastPreRecording", () => {
expect(startNewVoiceBroadcastRecording).toHaveBeenCalledWith(
room,
client,
playbacksStore,
recordingsStore,
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { mocked } from "jest-mock";
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";

import {
VoiceBroadcastPlaybacksStore,
VoiceBroadcastPreRecording,
VoiceBroadcastPreRecordingStore,
VoiceBroadcastRecordingsStore,
Expand All @@ -31,6 +32,7 @@ describe("VoiceBroadcastPreRecordingStore", () => {
let client: MatrixClient;
let room: Room;
let sender: RoomMember;
let playbacksStore: VoiceBroadcastPlaybacksStore;
let recordingsStore: VoiceBroadcastRecordingsStore;
let store: VoiceBroadcastPreRecordingStore;
let preRecording1: VoiceBroadcastPreRecording;
Expand All @@ -39,14 +41,15 @@ describe("VoiceBroadcastPreRecordingStore", () => {
client = stubClient();
room = new Room(roomId, client, client.getUserId() || "");
sender = new RoomMember(roomId, client.getUserId() || "");
playbacksStore = new VoiceBroadcastPlaybacksStore();
recordingsStore = new VoiceBroadcastRecordingsStore();
});

beforeEach(() => {
store = new VoiceBroadcastPreRecordingStore();
jest.spyOn(store, "emit");
jest.spyOn(store, "removeAllListeners");
preRecording1 = new VoiceBroadcastPreRecording(room, sender, client, recordingsStore);
preRecording1 = new VoiceBroadcastPreRecording(room, sender, client, playbacksStore, recordingsStore);
jest.spyOn(preRecording1, "off");
});

Expand Down Expand Up @@ -117,7 +120,7 @@ describe("VoiceBroadcastPreRecordingStore", () => {
beforeEach(() => {
mocked(store.emit).mockClear();
mocked(preRecording1.off).mockClear();
preRecording2 = new VoiceBroadcastPreRecording(room, sender, client, recordingsStore);
preRecording2 = new VoiceBroadcastPreRecording(room, sender, client, playbacksStore, recordingsStore);
store.setCurrent(preRecording2);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ limitations under the License.
*/

import { mocked } from "jest-mock";
import { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";

import {
checkVoiceBroadcastPreConditions,
VoiceBroadcastInfoState,
VoiceBroadcastPlayback,
VoiceBroadcastPlaybacksStore,
VoiceBroadcastPreRecording,
VoiceBroadcastPreRecordingStore,
VoiceBroadcastRecordingsStore,
} from "../../../src/voice-broadcast";
import { setUpVoiceBroadcastPreRecording } from "../../../src/voice-broadcast/utils/setUpVoiceBroadcastPreRecording";
import { mkRoomMemberJoinEvent, stubClient } from "../../test-utils";
import { mkVoiceBroadcastInfoStateEvent } from "./test-utils";

jest.mock("../../../src/voice-broadcast/utils/checkVoiceBroadcastPreConditions");

Expand All @@ -34,11 +38,20 @@ describe("setUpVoiceBroadcastPreRecording", () => {
let userId: string;
let room: Room;
let preRecordingStore: VoiceBroadcastPreRecordingStore;
let infoEvent: MatrixEvent;
let playback: VoiceBroadcastPlayback;
let playbacksStore: VoiceBroadcastPlaybacksStore;
let recordingsStore: VoiceBroadcastRecordingsStore;

const itShouldReturnNull = () => {
it("should return null", () => {
expect(setUpVoiceBroadcastPreRecording(room, client, recordingsStore, preRecordingStore)).toBeNull();
expect(setUpVoiceBroadcastPreRecording(
room,
client,
playbacksStore,
recordingsStore,
preRecordingStore,
)).toBeNull();
expect(checkVoiceBroadcastPreConditions).toHaveBeenCalledWith(room, client, recordingsStore);
});
};
Expand All @@ -51,7 +64,16 @@ describe("setUpVoiceBroadcastPreRecording", () => {
userId = clientUserId;

room = new Room(roomId, client, userId);
infoEvent = mkVoiceBroadcastInfoStateEvent(
roomId,
VoiceBroadcastInfoState.Started,
client.getUserId()!,
client.getDeviceId()!,
);
preRecordingStore = new VoiceBroadcastPreRecordingStore();
playback = new VoiceBroadcastPlayback(infoEvent, client);
jest.spyOn(playback, "pause");
playbacksStore = new VoiceBroadcastPlaybacksStore();
recordingsStore = new VoiceBroadcastRecordingsStore();
});

Expand Down Expand Up @@ -85,15 +107,25 @@ describe("setUpVoiceBroadcastPreRecording", () => {
itShouldReturnNull();
});

describe("and there is a room member", () => {
describe("and there is a room member and listening to another broadcast", () => {
beforeEach(() => {
playbacksStore.setCurrent(playback);
room.currentState.setStateEvents([
mkRoomMemberJoinEvent(userId, roomId),
]);
});

it("should create a voice broadcast pre-recording", () => {
const result = setUpVoiceBroadcastPreRecording(room, client, recordingsStore, preRecordingStore);
it("should pause the current playback and create a voice broadcast pre-recording", () => {
const result = setUpVoiceBroadcastPreRecording(
room,
client,
playbacksStore,
recordingsStore,
preRecordingStore,
);
expect(playback.pause).toHaveBeenCalled();
expect(playbacksStore.getCurrent()).toBeNull();

expect(checkVoiceBroadcastPreConditions).toHaveBeenCalledWith(room, client, recordingsStore);
expect(result).toBeInstanceOf(VoiceBroadcastPreRecording);
});
Expand Down
Loading