From 35a187a23196353b354a8462ebd416655ade6361 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Mon, 19 Dec 2022 09:44:19 +0100 Subject: [PATCH] Handle starting a call while listen to a broadcast (#9764) --- src/LegacyCallHandler.tsx | 11 ++ src/i18n/strings/en_EN.json | 2 + src/stores/RoomViewStore.tsx | 11 ++ .../utils/showCantStartACallDialog.tsx | 36 +++++ test/LegacyCallHandler-test.ts | 140 ++++++++++++++---- .../LegacyCallHandler-test.ts.snap | 24 +++ test/stores/RoomViewStore-test.ts | 93 +++++++++++- .../__snapshots__/RoomViewStore-test.ts.snap | 24 +++ 8 files changed, 312 insertions(+), 29 deletions(-) create mode 100644 src/voice-broadcast/utils/showCantStartACallDialog.tsx create mode 100644 test/__snapshots__/LegacyCallHandler-test.ts.snap create mode 100644 test/stores/__snapshots__/RoomViewStore-test.ts.snap diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index 847cd38aef..d81bd73661 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -63,6 +63,8 @@ import { OpenInviteDialogPayload } from "./dispatcher/payloads/OpenInviteDialogP import { findDMForUser } from "./utils/dm/findDMForUser"; import { getJoinedNonFunctionalMembers } from "./utils/room/getJoinedNonFunctionalMembers"; import { localNotificationsAreSilenced } from "./utils/notifications"; +import { SdkContextClass } from "./contexts/SDKContext"; +import { showCantStartACallDialog } from "./voice-broadcast/utils/showCantStartACallDialog"; export const PROTOCOL_PSTN = "m.protocol.pstn"; export const PROTOCOL_PSTN_PREFIXED = "im.vector.protocol.pstn"; @@ -932,6 +934,15 @@ export default class LegacyCallHandler extends EventEmitter { } public async placeCall(roomId: string, type?: CallType, transferee?: MatrixCall): Promise { + // Pause current broadcast, if any + SdkContextClass.instance.voiceBroadcastPlaybacksStore.getCurrent()?.pause(); + + if (SdkContextClass.instance.voiceBroadcastRecordingsStore.getCurrent()) { + // Do not start a call, if recording a broadcast + showCantStartACallDialog(); + return; + } + // We might be using managed hybrid widgets if (isManagedHybridWidgetEnabled()) { await addManagedHybridWidget(roomId); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8321a925ef..7aacb9f976 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -648,6 +648,8 @@ "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.": "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.", "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.": "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.", "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.": "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.", + "Can’t start a call": "Can’t start a call", + "You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.": "You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.", "You ended a voice broadcast": "You ended a voice broadcast", "%(senderName)s ended a voice broadcast": "%(senderName)s ended a voice broadcast", "You ended a voice broadcast": "You ended a voice broadcast", diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 84312fbf26..0d04bd1597 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -56,6 +56,7 @@ import { doMaybeSetCurrentVoiceBroadcastPlayback, } from "../voice-broadcast"; import { IRoomStateEventsActionPayload } from "../actions/MatrixActionCreators"; +import { showCantStartACallDialog } from "../voice-broadcast/utils/showCantStartACallDialog"; const NUM_JOIN_RETRY = 5; @@ -180,6 +181,16 @@ export class RoomViewStore extends EventEmitter { return; } + if (newState.viewingCall) { + // Pause current broadcast, if any + this.stores.voiceBroadcastPlaybacksStore.getCurrent()?.pause(); + + if (this.stores.voiceBroadcastRecordingsStore.getCurrent()) { + showCantStartACallDialog(); + newState.viewingCall = false; + } + } + const lastRoomId = this.state.roomId; this.state = Object.assign(this.state, newState); if (lastRoomId !== this.state.roomId) { diff --git a/src/voice-broadcast/utils/showCantStartACallDialog.tsx b/src/voice-broadcast/utils/showCantStartACallDialog.tsx new file mode 100644 index 0000000000..d2c7865796 --- /dev/null +++ b/src/voice-broadcast/utils/showCantStartACallDialog.tsx @@ -0,0 +1,36 @@ +/* +Copyright 2022 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 React from "react"; + +import InfoDialog from "../../components/views/dialogs/InfoDialog"; +import { _t } from "../../languageHandler"; +import Modal from "../../Modal"; + +export const showCantStartACallDialog = () => { + Modal.createDialog(InfoDialog, { + title: _t("Can’t start a call"), + description: ( +

+ {_t( + "You can’t start a call as you are currently recording a live broadcast. " + + "Please end your live broadcast in order to start a call.", + )} +

+ ), + hasCloseButton: true, + }); +}; diff --git a/test/LegacyCallHandler-test.ts b/test/LegacyCallHandler-test.ts index 474fb7d070..91d8a602a8 100644 --- a/test/LegacyCallHandler-test.ts +++ b/test/LegacyCallHandler-test.ts @@ -19,6 +19,7 @@ import { LOCAL_NOTIFICATION_SETTINGS_PREFIX, MatrixEvent, PushRuleKind, + Room, RuleId, TweakName, } from "matrix-js-sdk/src/matrix"; @@ -43,6 +44,28 @@ import { Action } from "../src/dispatcher/actions"; import { getFunctionalMembers } from "../src/utils/room/getFunctionalMembers"; import SettingsStore from "../src/settings/SettingsStore"; import { UIFeature } from "../src/settings/UIFeature"; +import { VoiceBroadcastInfoState, VoiceBroadcastPlayback, VoiceBroadcastRecording } from "../src/voice-broadcast"; +import { mkVoiceBroadcastInfoStateEvent } from "./voice-broadcast/utils/test-utils"; +import { SdkContextClass } from "../src/contexts/SDKContext"; +import Modal from "../src/Modal"; + +jest.mock("../src/Modal"); + +// mock VoiceRecording because it contains all the audio APIs +jest.mock("../src/audio/VoiceRecording", () => ({ + VoiceRecording: jest.fn().mockReturnValue({ + disableMaxLength: jest.fn(), + liveData: { + onUpdate: jest.fn(), + }, + off: jest.fn(), + on: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + destroy: jest.fn(), + contentType: "audio/ogg", + }), +})); jest.mock("../src/utils/room/getFunctionalMembers", () => ({ getFunctionalMembers: jest.fn(), @@ -71,7 +94,7 @@ const VIRTUAL_ROOM_BOB = "$virtual_bob_room:example.org"; // Bob's phone number const BOB_PHONE_NUMBER = "01818118181"; -function mkStubDM(roomId, userId) { +function mkStubDM(roomId: string, userId: string) { const room = mkStubRoom(roomId, "room", MatrixClientPeg.get()); room.getJoinedMembers = jest.fn().mockReturnValue([ { @@ -134,23 +157,24 @@ function untilCallHandlerEvent(callHandler: LegacyCallHandler, event: LegacyCall describe("LegacyCallHandler", () => { let dmRoomMap; - let callHandler; + let callHandler: LegacyCallHandler; let audioElement: HTMLAudioElement; - let fakeCall; + let fakeCall: MatrixCall | null; // what addresses the app has looked up via pstn and native lookup - let pstnLookup: string; - let nativeLookup: string; + let pstnLookup: string | null; + let nativeLookup: string | null; const deviceId = "my-device"; beforeEach(async () => { stubClient(); - MatrixClientPeg.get().createCall = (roomId) => { + fakeCall = null; + MatrixClientPeg.get().createCall = (roomId: string): MatrixCall | null => { if (fakeCall && fakeCall.roomId !== roomId) { throw new Error("Only one call is supported!"); } - fakeCall = new FakeCall(roomId); - return fakeCall; + fakeCall = new FakeCall(roomId) as unknown as MatrixCall; + return fakeCall as unknown as MatrixCall; }; MatrixClientPeg.get().deviceId = deviceId; @@ -172,7 +196,7 @@ describe("LegacyCallHandler", () => { const nativeRoomCharie = mkStubDM(NATIVE_ROOM_CHARLIE, NATIVE_CHARLIE); const virtualBobRoom = mkStubDM(VIRTUAL_ROOM_BOB, VIRTUAL_BOB); - MatrixClientPeg.get().getRoom = (roomId) => { + MatrixClientPeg.get().getRoom = (roomId: string): Room | null => { switch (roomId) { case NATIVE_ROOM_ALICE: return nativeRoomAlice; @@ -183,6 +207,8 @@ describe("LegacyCallHandler", () => { case VIRTUAL_ROOM_BOB: return virtualBobRoom; } + + return null; }; dmRoomMap = { @@ -212,13 +238,13 @@ describe("LegacyCallHandler", () => { return []; } }, - }; + } as DMRoomMap; DMRoomMap.setShared(dmRoomMap); pstnLookup = null; nativeLookup = null; - MatrixClientPeg.get().getThirdpartyUser = (proto, params) => { + MatrixClientPeg.get().getThirdpartyUser = (proto: string, params: any) => { if ([PROTOCOL_PSTN, PROTOCOL_PSTN_PREFIXED].includes(proto)) { pstnLookup = params["m.id.phone"]; return Promise.resolve([ @@ -261,6 +287,8 @@ describe("LegacyCallHandler", () => { } return Promise.resolve([]); } + + return Promise.resolve([]); }; audioElement = document.createElement("audio"); @@ -270,10 +298,10 @@ describe("LegacyCallHandler", () => { afterEach(() => { callHandler.stop(); + // @ts-ignore DMRoomMap.setShared(null); // @ts-ignore window.mxLegacyCallHandler = null; - fakeCall = null; MatrixClientPeg.unset(); document.body.removeChild(audioElement); @@ -292,25 +320,27 @@ describe("LegacyCallHandler", () => { // Check that a call was started: its room on the protocol level // should be the virtual room - expect(fakeCall.roomId).toEqual(VIRTUAL_ROOM_BOB); + expect(fakeCall).not.toBeNull(); + expect(fakeCall?.roomId).toEqual(VIRTUAL_ROOM_BOB); // but it should appear to the user to be in thw native room for Bob - expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB); + expect(callHandler.roomIdForCall(fakeCall!)).toEqual(NATIVE_ROOM_BOB); }); it("should look up the correct user and start a call in the room when a call is transferred", async () => { // we can pass a very minimal object as as the call since we pass consultFirst=true: // we don't need to actually do any transferring - const mockTransferreeCall = { type: CallType.Voice }; + const mockTransferreeCall = { type: CallType.Voice } as unknown as MatrixCall; await callHandler.startTransferToPhoneNumber(mockTransferreeCall, BOB_PHONE_NUMBER, true); // same checks as above const viewRoomPayload = await untilDispatch(Action.ViewRoom); expect(viewRoomPayload.room_id).toEqual(NATIVE_ROOM_BOB); - expect(fakeCall.roomId).toEqual(VIRTUAL_ROOM_BOB); + expect(fakeCall).not.toBeNull(); + expect(fakeCall!.roomId).toEqual(VIRTUAL_ROOM_BOB); - expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB); + expect(callHandler.roomIdForCall(fakeCall!)).toEqual(NATIVE_ROOM_BOB); }); it("should move calls between rooms when remote asserted identity changes", async () => { @@ -331,10 +361,11 @@ describe("LegacyCallHandler", () => { // Now emit an asserted identity for Bob: this should be ignored // because we haven't set the config option to obey asserted identity - fakeCall.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ + expect(fakeCall).not.toBeNull(); + fakeCall!.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ id: NATIVE_BOB, }); - fakeCall.emit(CallEvent.AssertedIdentityChanged); + fakeCall!.emit(CallEvent.AssertedIdentityChanged); // Now set the config option SdkConfig.add({ @@ -344,10 +375,10 @@ describe("LegacyCallHandler", () => { }); // ...and send another asserted identity event for a different user - fakeCall.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ + fakeCall!.getRemoteAssertedIdentity = jest.fn().mockReturnValue({ id: NATIVE_CHARLIE, }); - fakeCall.emit(CallEvent.AssertedIdentityChanged); + fakeCall!.emit(CallEvent.AssertedIdentityChanged); await roomChangePromise; callHandler.removeAllListeners(); @@ -362,21 +393,68 @@ describe("LegacyCallHandler", () => { expect(callHandler.getCallForRoom(NATIVE_ROOM_BOB)).toBeNull(); expect(callHandler.getCallForRoom(NATIVE_ROOM_CHARLIE)).toBe(fakeCall); }); + + describe("when listening to a voice broadcast", () => { + let voiceBroadcastPlayback: VoiceBroadcastPlayback; + + beforeEach(() => { + voiceBroadcastPlayback = new VoiceBroadcastPlayback( + mkVoiceBroadcastInfoStateEvent( + "!room:example.com", + VoiceBroadcastInfoState.Started, + MatrixClientPeg.get().getSafeUserId(), + "d42", + ), + MatrixClientPeg.get(), + ); + SdkContextClass.instance.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback); + jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation(); + }); + + it("and placing a call should pause the broadcast", async () => { + callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice); + await untilCallHandlerEvent(callHandler, LegacyCallHandlerEvent.CallState); + + expect(voiceBroadcastPlayback.pause).toHaveBeenCalled(); + }); + }); + + describe("when recording a voice broadcast", () => { + beforeEach(() => { + SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent( + new VoiceBroadcastRecording( + mkVoiceBroadcastInfoStateEvent( + "!room:example.com", + VoiceBroadcastInfoState.Started, + MatrixClientPeg.get().getSafeUserId(), + "d42", + ), + MatrixClientPeg.get(), + ), + ); + }); + + it("and placing a call should show the info dialog", async () => { + callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice); + expect(Modal.createDialog).toMatchSnapshot(); + }); + }); }); describe("LegacyCallHandler without third party protocols", () => { let dmRoomMap; let callHandler: LegacyCallHandler; let audioElement: HTMLAudioElement; - let fakeCall; + let fakeCall: MatrixCall | null; beforeEach(() => { stubClient(); + fakeCall = null; MatrixClientPeg.get().createCall = (roomId) => { if (fakeCall && fakeCall.roomId !== roomId) { throw new Error("Only one call is supported!"); } - fakeCall = new FakeCall(roomId); + fakeCall = new FakeCall(roomId) as unknown as MatrixCall; return fakeCall; }; @@ -389,11 +467,13 @@ describe("LegacyCallHandler without third party protocols", () => { const nativeRoomAlice = mkStubDM(NATIVE_ROOM_ALICE, NATIVE_ALICE); - MatrixClientPeg.get().getRoom = (roomId) => { + MatrixClientPeg.get().getRoom = (roomId: string): Room | null => { switch (roomId) { case NATIVE_ROOM_ALICE: return nativeRoomAlice; } + + return null; }; dmRoomMap = { @@ -411,7 +491,7 @@ describe("LegacyCallHandler without third party protocols", () => { return []; } }, - }; + } as DMRoomMap; DMRoomMap.setShared(dmRoomMap); MatrixClientPeg.get().getThirdpartyUser = (_proto, _params) => { @@ -421,14 +501,17 @@ describe("LegacyCallHandler without third party protocols", () => { audioElement = document.createElement("audio"); audioElement.id = "remoteAudio"; document.body.appendChild(audioElement); + + SdkContextClass.instance.voiceBroadcastPlaybacksStore.clearCurrent(); + SdkContextClass.instance.voiceBroadcastRecordingsStore.clearCurrent(); }); afterEach(() => { callHandler.stop(); + // @ts-ignore DMRoomMap.setShared(null); // @ts-ignore window.mxLegacyCallHandler = null; - fakeCall = null; MatrixClientPeg.unset(); document.body.removeChild(audioElement); @@ -442,10 +525,11 @@ describe("LegacyCallHandler without third party protocols", () => { // Check that a call was started: its room on the protocol level // should be the virtual room - expect(fakeCall.roomId).toEqual(NATIVE_ROOM_ALICE); + expect(fakeCall).not.toBeNull(); + expect(fakeCall!.roomId).toEqual(NATIVE_ROOM_ALICE); // but it should appear to the user to be in thw native room for Bob - expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_ALICE); + expect(callHandler.roomIdForCall(fakeCall!)).toEqual(NATIVE_ROOM_ALICE); }); describe("incoming calls", () => { diff --git a/test/__snapshots__/LegacyCallHandler-test.ts.snap b/test/__snapshots__/LegacyCallHandler-test.ts.snap new file mode 100644 index 0000000000..aaf4d78758 --- /dev/null +++ b/test/__snapshots__/LegacyCallHandler-test.ts.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LegacyCallHandler when recording a voice broadcast and placing a call should show the info dialog 1`] = ` +[MockFunction] { + "calls": [ + [ + [Function], + { + "description":

+ You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call. +

, + "hasCloseButton": true, + "title": "Can’t start a call", + }, + ], + ], + "results": [ + { + "type": "return", + "value": undefined, + }, + ], +} +`; diff --git a/test/stores/RoomViewStore-test.ts b/test/stores/RoomViewStore-test.ts index 6103c6bb46..04f595a914 100644 --- a/test/stores/RoomViewStore-test.ts +++ b/test/stores/RoomViewStore-test.ts @@ -28,6 +28,17 @@ import { UPDATE_EVENT } from "../../src/stores/AsyncStore"; import { ActiveRoomChangedPayload } from "../../src/dispatcher/payloads/ActiveRoomChangedPayload"; import { SpaceStoreClass } from "../../src/stores/spaces/SpaceStore"; import { TestSdkContext } from "../TestSdkContext"; +import { ViewRoomPayload } from "../../src/dispatcher/payloads/ViewRoomPayload"; +import { + VoiceBroadcastInfoState, + VoiceBroadcastPlayback, + VoiceBroadcastPlaybacksStore, + VoiceBroadcastRecording, +} from "../../src/voice-broadcast"; +import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils"; +import Modal from "../../src/Modal"; + +jest.mock("../../src/Modal"); // mock out the injected classes jest.mock("../../src/PosthogAnalytics"); @@ -37,6 +48,22 @@ const MockSlidingSyncManager = >(SlidingS jest.mock("../../src/stores/spaces/SpaceStore"); const MockSpaceStore = >(SpaceStoreClass); +// mock VoiceRecording because it contains all the audio APIs +jest.mock("../../src/audio/VoiceRecording", () => ({ + VoiceRecording: jest.fn().mockReturnValue({ + disableMaxLength: jest.fn(), + liveData: { + onUpdate: jest.fn(), + }, + off: jest.fn(), + on: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + destroy: jest.fn(), + contentType: "audio/ogg", + }), +})); + jest.mock("../../src/utils/DMRoomMap", () => { const mock = { getUserIdForRoomId: jest.fn(), @@ -60,12 +87,24 @@ describe("RoomViewStore", function () { getRoom: jest.fn(), getRoomIdForAlias: jest.fn(), isGuest: jest.fn(), + getSafeUserId: jest.fn(), }); const room = new Room(roomId, mockClient, userId); + const viewCall = async (): Promise => { + dis.dispatch({ + action: Action.ViewRoom, + room_id: roomId, + view_call: true, + metricsTrigger: undefined, + }); + await untilDispatch(Action.ViewRoom, dis); + }; + let roomViewStore: RoomViewStore; let slidingSyncManager: SlidingSyncManager; let dis: MatrixDispatcher; + let stores: TestSdkContext; beforeEach(function () { jest.clearAllMocks(); @@ -73,14 +112,16 @@ describe("RoomViewStore", function () { mockClient.joinRoom.mockResolvedValue(room); mockClient.getRoom.mockReturnValue(room); mockClient.isGuest.mockReturnValue(false); + mockClient.getSafeUserId.mockReturnValue(userId); // Make the RVS to test dis = new MatrixDispatcher(); slidingSyncManager = new MockSlidingSyncManager(); - const stores = new TestSdkContext(); + stores = new TestSdkContext(); stores._SlidingSyncManager = slidingSyncManager; stores._PosthogAnalytics = new MockPosthogAnalytics(); stores._SpaceStore = new MockSpaceStore(); + stores._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(); roomViewStore = new RoomViewStore(dis, stores); stores._RoomViewStore = roomViewStore; }); @@ -206,6 +247,56 @@ describe("RoomViewStore", function () { expect(roomViewStore.getRoomId()).toBeNull(); }); + it("when viewing a call without a broadcast, it should not raise an error", async () => { + await viewCall(); + }); + + describe("when listening to a voice broadcast", () => { + let voiceBroadcastPlayback: VoiceBroadcastPlayback; + + beforeEach(() => { + voiceBroadcastPlayback = new VoiceBroadcastPlayback( + mkVoiceBroadcastInfoStateEvent( + roomId, + VoiceBroadcastInfoState.Started, + mockClient.getSafeUserId(), + "d42", + ), + mockClient, + ); + stores.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback); + jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation(); + }); + + it("and viewing a call it should pause the current broadcast", async () => { + await viewCall(); + expect(voiceBroadcastPlayback.pause).toHaveBeenCalled(); + expect(roomViewStore.isViewingCall()).toBe(true); + }); + }); + + describe("when recording a voice broadcast", () => { + beforeEach(() => { + stores.voiceBroadcastRecordingsStore.setCurrent( + new VoiceBroadcastRecording( + mkVoiceBroadcastInfoStateEvent( + roomId, + VoiceBroadcastInfoState.Started, + mockClient.getSafeUserId(), + "d42", + ), + mockClient, + ), + ); + }); + + it("and trying to view a call, it should not actually view it and show the info dialog", async () => { + await viewCall(); + expect(Modal.createDialog).toMatchSnapshot(); + expect(roomViewStore.isViewingCall()).toBe(false); + }); + }); + describe("Sliding Sync", function () { beforeEach(() => { jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName, roomId, value) => { diff --git a/test/stores/__snapshots__/RoomViewStore-test.ts.snap b/test/stores/__snapshots__/RoomViewStore-test.ts.snap new file mode 100644 index 0000000000..64221efac4 --- /dev/null +++ b/test/stores/__snapshots__/RoomViewStore-test.ts.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RoomViewStore when recording a voice broadcast and trying to view a call, it should not actually view it and show the info dialog 1`] = ` +[MockFunction] { + "calls": [ + [ + [Function], + { + "description":

+ You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call. +

, + "hasCloseButton": true, + "title": "Can’t start a call", + }, + ], + ], + "results": [ + { + "type": "return", + "value": undefined, + }, + ], +} +`;