End voice broadcast recording on any call (#9425)
parent
81533b905f
commit
0630a9c448
|
@ -65,6 +65,7 @@ import { arrayFastClone } from "../../utils/arrays";
|
||||||
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
|
||||||
import Modal from "../../Modal";
|
import Modal from "../../Modal";
|
||||||
import ErrorDialog from "../../components/views/dialogs/ErrorDialog";
|
import ErrorDialog from "../../components/views/dialogs/ErrorDialog";
|
||||||
|
import { VoiceBroadcastRecordingsStore } from "../../voice-broadcast";
|
||||||
|
|
||||||
// TODO: Destroy all of this code
|
// TODO: Destroy all of this code
|
||||||
|
|
||||||
|
@ -280,6 +281,10 @@ export class StopGapWidget extends EventEmitter {
|
||||||
});
|
});
|
||||||
this.messaging.on("capabilitiesNotified", () => this.emit("capabilitiesNotified"));
|
this.messaging.on("capabilitiesNotified", () => this.emit("capabilitiesNotified"));
|
||||||
this.messaging.on(`action:${WidgetApiFromWidgetAction.OpenModalWidget}`, this.onOpenModal);
|
this.messaging.on(`action:${WidgetApiFromWidgetAction.OpenModalWidget}`, this.onOpenModal);
|
||||||
|
this.messaging.on(`action:${ElementWidgetActions.JoinCall}`, () => {
|
||||||
|
// stop voice broadcast recording when any widget sends a "join"
|
||||||
|
VoiceBroadcastRecordingsStore.instance().getCurrent()?.stop();
|
||||||
|
});
|
||||||
|
|
||||||
// Always attach a handler for ViewRoom, but permission check it internally
|
// Always attach a handler for ViewRoom, but permission check it internally
|
||||||
this.messaging.on(`action:${ElementWidgetActions.ViewRoom}`, (ev: CustomEvent<IViewRoomApiRequest>) => {
|
this.messaging.on(`action:${ElementWidgetActions.ViewRoom}`, (ev: CustomEvent<IViewRoomApiRequest>) => {
|
||||||
|
|
|
@ -30,6 +30,8 @@ import { uploadFile } from "../../ContentMessages";
|
||||||
import { IEncryptedFile } from "../../customisations/models/IMediaEventContent";
|
import { IEncryptedFile } from "../../customisations/models/IMediaEventContent";
|
||||||
import { createVoiceMessageContent } from "../../utils/createVoiceMessageContent";
|
import { createVoiceMessageContent } from "../../utils/createVoiceMessageContent";
|
||||||
import { IDestroyable } from "../../utils/IDestroyable";
|
import { IDestroyable } from "../../utils/IDestroyable";
|
||||||
|
import dis from "../../dispatcher/dispatcher";
|
||||||
|
import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
|
|
||||||
export enum VoiceBroadcastRecordingEvent {
|
export enum VoiceBroadcastRecordingEvent {
|
||||||
StateChanged = "liveness_changed",
|
StateChanged = "liveness_changed",
|
||||||
|
@ -45,6 +47,7 @@ export class VoiceBroadcastRecording
|
||||||
private state: VoiceBroadcastInfoState;
|
private state: VoiceBroadcastInfoState;
|
||||||
private recorder: VoiceBroadcastRecorder;
|
private recorder: VoiceBroadcastRecorder;
|
||||||
private sequence = 1;
|
private sequence = 1;
|
||||||
|
private dispatcherRef: string;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public readonly infoEvent: MatrixEvent,
|
public readonly infoEvent: MatrixEvent,
|
||||||
|
@ -62,8 +65,9 @@ export class VoiceBroadcastRecording
|
||||||
this.state = !relatedEvents?.find((event: MatrixEvent) => {
|
this.state = !relatedEvents?.find((event: MatrixEvent) => {
|
||||||
return event.getContent()?.state === VoiceBroadcastInfoState.Stopped;
|
return event.getContent()?.state === VoiceBroadcastInfoState.Stopped;
|
||||||
}) ? VoiceBroadcastInfoState.Started : VoiceBroadcastInfoState.Stopped;
|
}) ? VoiceBroadcastInfoState.Started : VoiceBroadcastInfoState.Stopped;
|
||||||
|
|
||||||
// TODO Michael W: add listening for updates
|
// TODO Michael W: add listening for updates
|
||||||
|
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
|
@ -96,8 +100,16 @@ export class VoiceBroadcastRecording
|
||||||
}
|
}
|
||||||
|
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
|
dis.unregister(this.dispatcherRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onAction = (payload: ActionPayload) => {
|
||||||
|
if (payload.action !== "call_state") return;
|
||||||
|
|
||||||
|
// stop on any call action
|
||||||
|
this.stop();
|
||||||
|
};
|
||||||
|
|
||||||
private setState(state: VoiceBroadcastInfoState): void {
|
private setState(state: VoiceBroadcastInfoState): void {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.emit(VoiceBroadcastRecordingEvent.StateChanged, this.state);
|
this.emit(VoiceBroadcastRecordingEvent.StateChanged, this.state);
|
||||||
|
|
|
@ -15,12 +15,19 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mocked, MockedObject } from "jest-mock";
|
import { mocked, MockedObject } from "jest-mock";
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client";
|
import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client";
|
||||||
import { ClientWidgetApi } from "matrix-widget-api";
|
import { ClientWidgetApi } from "matrix-widget-api";
|
||||||
|
|
||||||
import { stubClient, mkRoom, mkEvent } from "../../test-utils";
|
import { stubClient, mkRoom, mkEvent } from "../../test-utils";
|
||||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||||
import { StopGapWidget } from "../../../src/stores/widgets/StopGapWidget";
|
import { StopGapWidget } from "../../../src/stores/widgets/StopGapWidget";
|
||||||
|
import { ElementWidgetActions } from "../../../src/stores/widgets/ElementWidgetActions";
|
||||||
|
import {
|
||||||
|
VoiceBroadcastInfoEventType,
|
||||||
|
VoiceBroadcastRecording,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
|
} from "../../../src/voice-broadcast";
|
||||||
|
|
||||||
jest.mock("matrix-widget-api/lib/ClientWidgetApi");
|
jest.mock("matrix-widget-api/lib/ClientWidgetApi");
|
||||||
|
|
||||||
|
@ -68,4 +75,39 @@ describe("StopGapWidget", () => {
|
||||||
await Promise.resolve(); // flush promises
|
await Promise.resolve(); // flush promises
|
||||||
expect(messaging.feedToDevice).toHaveBeenCalledWith(event.getEffectiveEvent(), false);
|
expect(messaging.feedToDevice).toHaveBeenCalledWith(event.getEffectiveEvent(), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("when there is a voice broadcast recording", () => {
|
||||||
|
let voiceBroadcastInfoEvent: MatrixEvent;
|
||||||
|
let voiceBroadcastRecording: VoiceBroadcastRecording;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
voiceBroadcastInfoEvent = mkEvent({
|
||||||
|
event: true,
|
||||||
|
room: client.getRoom("x").roomId,
|
||||||
|
user: client.getUserId(),
|
||||||
|
type: VoiceBroadcastInfoEventType,
|
||||||
|
content: {},
|
||||||
|
});
|
||||||
|
voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client);
|
||||||
|
jest.spyOn(voiceBroadcastRecording, "stop");
|
||||||
|
jest.spyOn(VoiceBroadcastRecordingsStore.instance(), "getCurrent").mockReturnValue(voiceBroadcastRecording);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`and receiving a action:${ElementWidgetActions.JoinCall} message`, () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
messaging.on.mock.calls.find(
|
||||||
|
([event, listener]) => {
|
||||||
|
if (event === `action:${ElementWidgetActions.JoinCall}`) {
|
||||||
|
listener();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should stop the current voice broadcast recording", () => {
|
||||||
|
expect(voiceBroadcastRecording.stop).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,6 +41,7 @@ import {
|
||||||
VoiceBroadcastRecordingEvent,
|
VoiceBroadcastRecordingEvent,
|
||||||
} from "../../../src/voice-broadcast";
|
} from "../../../src/voice-broadcast";
|
||||||
import { mkEvent, mkStubRoom, stubClient } from "../../test-utils";
|
import { mkEvent, mkStubRoom, stubClient } from "../../test-utils";
|
||||||
|
import dis from "../../../src/dispatcher/dispatcher";
|
||||||
|
|
||||||
jest.mock("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder", () => ({
|
jest.mock("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder", () => ({
|
||||||
...jest.requireActual("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder") as object,
|
...jest.requireActual("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder") as object,
|
||||||
|
@ -83,6 +84,12 @@ describe("VoiceBroadcastRecording", () => {
|
||||||
jest.spyOn(voiceBroadcastRecording, "removeAllListeners");
|
jest.spyOn(voiceBroadcastRecording, "removeAllListeners");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const itShouldBeInState = (state: VoiceBroadcastInfoState) => {
|
||||||
|
it(`should be in state stopped ${state}`, () => {
|
||||||
|
expect(voiceBroadcastRecording.getState()).toBe(state);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
client = stubClient();
|
client = stubClient();
|
||||||
room = mkStubRoom(roomId, "Test Room", client);
|
room = mkStubRoom(roomId, "Test Room", client);
|
||||||
|
@ -191,9 +198,7 @@ describe("VoiceBroadcastRecording", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be in state stopped", () => {
|
itShouldBeInState(VoiceBroadcastInfoState.Stopped);
|
||||||
expect(voiceBroadcastRecording.getState()).toBe(VoiceBroadcastInfoState.Stopped);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should emit a stopped state changed event", () => {
|
it("should emit a stopped state changed event", () => {
|
||||||
expect(onStateChanged).toHaveBeenCalledWith(VoiceBroadcastInfoState.Stopped);
|
expect(onStateChanged).toHaveBeenCalledWith(VoiceBroadcastInfoState.Stopped);
|
||||||
|
@ -209,6 +214,16 @@ describe("VoiceBroadcastRecording", () => {
|
||||||
expect(voiceBroadcastRecorder.start).toHaveBeenCalled();
|
expect(voiceBroadcastRecorder.start).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("and receiving a call action", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
dis.dispatch({
|
||||||
|
action: "call_state",
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
itShouldBeInState(VoiceBroadcastInfoState.Stopped);
|
||||||
|
});
|
||||||
|
|
||||||
describe("and a chunk has been recorded", () => {
|
describe("and a chunk has been recorded", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await onChunkRecorded({
|
await onChunkRecorded({
|
||||||
|
|
Loading…
Reference in New Issue