diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index dbbb40cfea..429adb6f50 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -134,7 +134,7 @@ import { ValidatedServerConfig } from "../../utils/ValidatedServerConfig"; import { isLocalRoom } from "../../utils/localRoom/isLocalRoom"; import { SdkContextClass, SDKContext } from "../../contexts/SDKContext"; import { viewUserDeviceSettings } from "../../actions/handlers/viewUserDeviceSettings"; -import { VoiceBroadcastResumer } from "../../voice-broadcast"; +import { cleanUpBroadcasts, VoiceBroadcastResumer } from "../../voice-broadcast"; import GenericToast from "../views/toasts/GenericToast"; import { Linkify } from "../views/elements/Linkify"; import RovingSpotlightDialog, { Filter } from "../views/dialogs/spotlight/SpotlightDialog"; @@ -591,9 +591,10 @@ export default class MatrixChat extends React.PureComponent { break; case "logout": LegacyCallHandler.instance.hangupAllCalls(); - Promise.all([...CallStore.instance.activeCalls].map((call) => call.disconnect())).finally(() => - Lifecycle.logout(), - ); + Promise.all([ + ...[...CallStore.instance.activeCalls].map((call) => call.disconnect()), + cleanUpBroadcasts(this.stores), + ]).finally(() => Lifecycle.logout()); break; case "require_registration": startAnyRegistrationFlow(payload as any); diff --git a/src/voice-broadcast/index.ts b/src/voice-broadcast/index.ts index 57b29dc8f4..4fb03bc485 100644 --- a/src/voice-broadcast/index.ts +++ b/src/voice-broadcast/index.ts @@ -46,6 +46,7 @@ export * from "./stores/VoiceBroadcastPlaybacksStore"; export * from "./stores/VoiceBroadcastPreRecordingStore"; export * from "./stores/VoiceBroadcastRecordingsStore"; export * from "./utils/checkVoiceBroadcastPreConditions"; +export * from "./utils/cleanUpBroadcasts"; export * from "./utils/doClearCurrentVoiceBroadcastPlaybackIfStopped"; export * from "./utils/doMaybeSetCurrentVoiceBroadcastPlayback"; export * from "./utils/getChunkLength"; diff --git a/src/voice-broadcast/utils/cleanUpBroadcasts.ts b/src/voice-broadcast/utils/cleanUpBroadcasts.ts new file mode 100644 index 0000000000..f04e9eecfa --- /dev/null +++ b/src/voice-broadcast/utils/cleanUpBroadcasts.ts @@ -0,0 +1,28 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { SdkContextClass } from "../../contexts/SDKContext"; + +export const cleanUpBroadcasts = async (stores: SdkContextClass): Promise => { + stores.voiceBroadcastPlaybacksStore.getCurrent()?.stop(); + stores.voiceBroadcastPlaybacksStore.clearCurrent(); + + await stores.voiceBroadcastRecordingsStore.getCurrent()?.stop(); + stores.voiceBroadcastRecordingsStore.clearCurrent(); + + stores.voiceBroadcastPreRecordingStore.getCurrent()?.cancel(); + stores.voiceBroadcastPreRecordingStore.clearCurrent(); +}; diff --git a/test/voice-broadcast/utils/cleanUpBroadcasts-test.ts b/test/voice-broadcast/utils/cleanUpBroadcasts-test.ts new file mode 100644 index 0000000000..b4dce60d74 --- /dev/null +++ b/test/voice-broadcast/utils/cleanUpBroadcasts-test.ts @@ -0,0 +1,59 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { + cleanUpBroadcasts, + VoiceBroadcastPlayback, + VoiceBroadcastPreRecording, + VoiceBroadcastRecording, +} from "../../../src/voice-broadcast"; +import { stubClient } from "../../test-utils"; +import { TestSdkContext } from "../../TestSdkContext"; +import { mkVoiceBroadcastPlayback, mkVoiceBroadcastPreRecording, mkVoiceBroadcastRecording } from "./test-utils"; + +describe("cleanUpBroadcasts", () => { + let playback: VoiceBroadcastPlayback; + let recording: VoiceBroadcastRecording; + let preRecording: VoiceBroadcastPreRecording; + let stores: TestSdkContext; + + beforeEach(() => { + stores = new TestSdkContext(); + stores.client = stubClient(); + + playback = mkVoiceBroadcastPlayback(stores); + jest.spyOn(playback, "stop").mockReturnValue(); + stores.voiceBroadcastPlaybacksStore.setCurrent(playback); + + recording = mkVoiceBroadcastRecording(stores); + jest.spyOn(recording, "stop").mockResolvedValue(); + stores.voiceBroadcastRecordingsStore.setCurrent(recording); + + preRecording = mkVoiceBroadcastPreRecording(stores); + jest.spyOn(preRecording, "cancel").mockReturnValue(); + stores.voiceBroadcastPreRecordingStore.setCurrent(preRecording); + }); + + it("should stop and clear all broadcast related stuff", async () => { + await cleanUpBroadcasts(stores); + expect(playback.stop).toHaveBeenCalled(); + expect(stores.voiceBroadcastPlaybacksStore.getCurrent()).toBeNull(); + expect(recording.stop).toHaveBeenCalled(); + expect(stores.voiceBroadcastRecordingsStore.getCurrent()).toBeNull(); + expect(preRecording.cancel).toHaveBeenCalled(); + expect(stores.voiceBroadcastPreRecordingStore.getCurrent()).toBeNull(); + }); +}); diff --git a/test/voice-broadcast/utils/test-utils.ts b/test/voice-broadcast/utils/test-utils.ts index fc1ffd4b15..dbb214d8f4 100644 --- a/test/voice-broadcast/utils/test-utils.ts +++ b/test/voice-broadcast/utils/test-utils.ts @@ -15,8 +15,14 @@ limitations under the License. */ import { Optional } from "matrix-events-sdk"; -import { EventType, MatrixEvent, MsgType, RelationType } from "matrix-js-sdk/src/matrix"; +import { EventType, MatrixEvent, MsgType, RelationType, Room, RoomMember } from "matrix-js-sdk/src/matrix"; +import { SdkContextClass } from "../../../src/contexts/SDKContext"; +import { + VoiceBroadcastPlayback, + VoiceBroadcastPreRecording, + VoiceBroadcastRecording, +} from "../../../src/voice-broadcast"; import { VoiceBroadcastChunkEventType, VoiceBroadcastInfoEventType, @@ -97,3 +103,37 @@ export const mkVoiceBroadcastChunkEvent = ( ts: timestamp, }); }; + +export const mkVoiceBroadcastPlayback = (stores: SdkContextClass): VoiceBroadcastPlayback => { + const infoEvent = mkVoiceBroadcastInfoStateEvent( + "!room:example.com", + VoiceBroadcastInfoState.Started, + "@user:example.com", + "ASD123", + ); + return new VoiceBroadcastPlayback(infoEvent, stores.client!, stores.voiceBroadcastRecordingsStore); +}; + +export const mkVoiceBroadcastRecording = (stores: SdkContextClass): VoiceBroadcastRecording => { + const infoEvent = mkVoiceBroadcastInfoStateEvent( + "!room:example.com", + VoiceBroadcastInfoState.Started, + "@user:example.com", + "ASD123", + ); + return new VoiceBroadcastRecording(infoEvent, stores.client!); +}; + +export const mkVoiceBroadcastPreRecording = (stores: SdkContextClass): VoiceBroadcastPreRecording => { + const roomId = "!room:example.com"; + const userId = "@user:example.com"; + const room = new Room(roomId, stores.client!, userId); + const roomMember = new RoomMember(roomId, userId); + return new VoiceBroadcastPreRecording( + room, + roomMember, + stores.client!, + stores.voiceBroadcastPlaybacksStore, + stores.voiceBroadcastRecordingsStore, + ); +};