UNPKG

communication-react-19

Version:

React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)

1,007 lines 79.1 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { compositeLogger } from '../../../Logger'; import { _isInCall, _isInLobbyOrConnecting } from "../../../../../calling-component-bindings/src"; import { _createStatefulCallClientInner } from "../../../../../calling-stateful-client/src"; import { _isTeamsCallAgent } from "../../../../../calling-stateful-client/src"; import { LocalVideoStream as SDKLocalVideoStream } from '@azure/communication-calling'; import { Features } from '@azure/communication-calling'; import { EventEmitter } from 'events'; import { getCallCompositePage, getLocatorOrTargetCallees, IsCallEndedPage, isCameraOn } from '../utils'; import { toFlatCommunicationIdentifier, _toCommunicationIdentifier, _isValidIdentifier } from "../../../../../acs-ui-common/src"; import { isMicrosoftTeamsUserIdentifier, isMicrosoftTeamsAppIdentifier } from '@azure/communication-common'; import { isCommunicationUserIdentifier } from '@azure/communication-common'; import { isPhoneNumberIdentifier } from '@azure/communication-common'; import { ParticipantSubscriber } from './ParticipantSubcriber'; import { DiagnosticsForwarder } from './DiagnosticsForwarder'; import { useEffect, useRef, useState } from 'react'; import { createHandlers } from './createHandlers'; import { createProfileStateModifier } from './OnFetchProfileCallback'; import { getBackgroundEffectFromSelectedEffect } from '../utils'; import { getSelectedCameraFromAdapterState } from '../utils'; import { CallingSoundSubscriber } from './CallingSoundSubscriber'; /** * For each time that we use the hook {@link useSelector} in the {@link CallComposite} we add another listener * to the `stateChanged` event on the this adapter. This number is set in relation to the number of * times that we are using the hook useSelector in the CallComposite. * * We will need to update this as the threshold is reached with more usages of useSelector. */ const EVENT_LISTENER_WARNING_WARNING_LIMIT = 125; /** Context of call, which is a centralized context for all state updates */ class CallContext { constructor(clientState, isTeamsCall, isTeamsMeeting, isRoomsCall, options, targetCallees) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; this.emitter = new EventEmitter(); this.isReturningFromBreakoutRoom = false; this.state = { isLocalPreviewMicrophoneEnabled: false, userId: clientState.userId, displayName: (_a = clientState.callAgent) === null || _a === void 0 ? void 0 : _a.displayName, devices: clientState.deviceManager, call: undefined, targetCallees: targetCallees, page: 'configuration', latestErrors: clientState.latestErrors, latestNotifications: clientState.latestNotifications, isTeamsCall, isTeamsMeeting, isRoomsCall, alternateCallerId: options === null || options === void 0 ? void 0 : options.alternateCallerId, environmentInfo: clientState.environmentInfo, /* @conditional-compile-remove(unsupported-browser) */ unsupportedBrowserVersionsAllowed: false, videoBackgroundImages: (_b = options === null || options === void 0 ? void 0 : options.videoBackgroundOptions) === null || _b === void 0 ? void 0 : _b.videoBackgroundImages, onResolveVideoEffectDependency: (_c = options === null || options === void 0 ? void 0 : options.videoBackgroundOptions) === null || _c === void 0 ? void 0 : _c.onResolveDependency, onResolveDeepNoiseSuppressionDependency: (_d = options === null || options === void 0 ? void 0 : options.deepNoiseSuppressionOptions) === null || _d === void 0 ? void 0 : _d.onResolveDependency, deepNoiseSuppressionOnByDefault: (_f = (_e = options === null || options === void 0 ? void 0 : options.deepNoiseSuppressionOptions) === null || _e === void 0 ? void 0 : _e.deepNoiseSuppressionOnByDefault) !== null && _f !== void 0 ? _f : true, hideDeepNoiseSuppressionButton: (_h = (_g = options === null || options === void 0 ? void 0 : options.deepNoiseSuppressionOptions) === null || _g === void 0 ? void 0 : _g.hideDeepNoiseSuppressionButton) !== null && _h !== void 0 ? _h : false, selectedVideoBackgroundEffect: undefined, cameraStatus: undefined, sounds: options === null || options === void 0 ? void 0 : options.callingSounds, reactions: options === null || options === void 0 ? void 0 : options.reactionResources }; this.emitter.setMaxListeners((_j = options === null || options === void 0 ? void 0 : options.maxListeners) !== null && _j !== void 0 ? _j : EVENT_LISTENER_WARNING_WARNING_LIMIT); this.bindPublicMethods(); this.displayNameModifier = (options === null || options === void 0 ? void 0 : options.onFetchProfile) ? createProfileStateModifier(options.onFetchProfile, () => { this.setState(this.getState()); }) : undefined; } bindPublicMethods() { /* @conditional-compile-remove(unsupported-browser) */ this.setAllowedUnsupportedBrowser.bind(this); } onStateChange(handler) { this.emitter.on('stateChanged', handler); } offStateChange(handler) { this.emitter.off('stateChanged', handler); } setState(state) { this.state = this.displayNameModifier ? this.displayNameModifier(state) : state; // This context privately tracks how captions was started to determine if captions is running only in the background. // If so we should not show the UI. this.state = captionsUIVisibilityModifier(this.state); this.emitter.emit('stateChanged', this.state); } updateCaptionsStartedInBackground(captionsStartedInBackground) { var _a; const captionsTriggers = (_a = this.state.captionsTriggers) !== null && _a !== void 0 ? _a : {}; captionsTriggers.captionsStartedInBackground = captionsStartedInBackground; this.setState(Object.assign(Object.assign({}, this.state), { captionsTriggers })); } updateCaptionsStartedWithUI(captionsStartedWithUI) { var _a; const captionsTriggers = (_a = this.state.captionsTriggers) !== null && _a !== void 0 ? _a : {}; captionsTriggers.captionsStartedWithUI = captionsStartedWithUI; this.setState(Object.assign(Object.assign({}, this.state), { captionsTriggers })); } getState() { return this.state; } setIsLocalMicrophoneEnabled(isLocalPreviewMicrophoneEnabled) { this.setState(Object.assign(Object.assign({}, this.state), { isLocalPreviewMicrophoneEnabled })); } // This is the key to find current call object in client state setCurrentCallId(callId) { this.callId = callId; } setTargetCallee(targetCallees) { this.setState(Object.assign(Object.assign({}, this.state), { targetCallees })); } setIsReturningFromBreakoutRoom(isReturningFromBreakoutRoom) { this.isReturningFromBreakoutRoom = isReturningFromBreakoutRoom; } onCallEnded(handler) { this.emitter.on('callEnded', handler); } offCallEnded(handler) { this.emitter.off('callEnded', handler); } updateClientState(clientState) { var _a, _b, _c; let call = this.callId ? clientState.calls[this.callId] : undefined; const latestEndedCall = clientState.callsEnded ? findLatestEndedCall(clientState.callsEnded) : undefined; // As the state is transitioning to a new state, trigger appropriate callback events. const oldPage = this.state.page; /* @conditional-compile-remove(unsupported-browser) */ const environmentInfo = { environmentInfo: this.state.environmentInfo, unsupportedBrowserVersionOptedIn: this.state.unsupportedBrowserVersionsAllowed }; const latestAcceptedTransfer = (call === null || call === void 0 ? void 0 : call.transfer.acceptedTransfers) ? findLatestAcceptedTransfer(call.transfer.acceptedTransfers) : undefined; const transferCall = latestAcceptedTransfer ? clientState.calls[latestAcceptedTransfer.callId] : undefined; if ((call === null || call === void 0 ? void 0 : call.state) === 'Connected' || (call === null || call === void 0 ? void 0 : call.state) === 'Connecting') { this.setIsReturningFromBreakoutRoom(false); } let isReturningFromBreakoutRoom = false; isReturningFromBreakoutRoom = this.isReturningFromBreakoutRoom; const newPage = getCallCompositePage(call, latestEndedCall, transferCall, isReturningFromBreakoutRoom, /* @conditional-compile-remove(unsupported-browser) */ environmentInfo); if (!IsCallEndedPage(oldPage) && IsCallEndedPage(newPage)) { /** * We want to make sure that the id of the call that is ending * is the same as the call in the adapter as this is a scenario where * the call has ended and not been transferred and report the codes for this call. */ if (this.callId === (latestEndedCall === null || latestEndedCall === void 0 ? void 0 : latestEndedCall.id)) { this.emitter.emit('callEnded', { callId: latestEndedCall === null || latestEndedCall === void 0 ? void 0 : latestEndedCall.id, code: (_a = latestEndedCall === null || latestEndedCall === void 0 ? void 0 : latestEndedCall.callEndReason) === null || _a === void 0 ? void 0 : _a.code, subCode: (_b = latestEndedCall === null || latestEndedCall === void 0 ? void 0 : latestEndedCall.callEndReason) === null || _b === void 0 ? void 0 : _b.subCode }); } // Reset the callId to undefined as the call has ended. this.setCurrentCallId(undefined); // Make sure that the call is set to undefined in the state. call = undefined; } if (this.state.page) { this.setState(Object.assign(Object.assign({}, this.state), { userId: clientState.userId, displayName: (_c = clientState.callAgent) === null || _c === void 0 ? void 0 : _c.displayName, call, page: newPage, endedCall: latestEndedCall, devices: clientState.deviceManager, latestErrors: clientState.latestErrors, latestNotifications: clientState.latestNotifications, cameraStatus: (call === null || call === void 0 ? void 0 : call.localVideoStreams.find((s) => s.mediaStreamType === 'Video')) || clientState.deviceManager.unparentedViews.find((s) => s.mediaStreamType === 'Video') ? 'On' : 'Off', acceptedTransferCallState: transferCall })); } } /* @conditional-compile-remove(unsupported-browser) */ setAllowedUnsupportedBrowser() { this.setState(Object.assign(Object.assign({}, this.state), { unsupportedBrowserVersionsAllowed: true })); } setBackroundPickerImages(videoBackgroundImages) { this.setState(Object.assign(Object.assign({}, this.state), { videoBackgroundImages })); } setSelectedVideoBackgroundEffect(selectedVideoBackgroundEffect) { this.setState(Object.assign(Object.assign({}, this.state), { selectedVideoBackgroundEffect })); } setAcceptedTransferCall(call) { this.setState(Object.assign(Object.assign({}, this.state), { acceptedTransferCallState: call })); } } const findLatestEndedCall = (calls) => { var _a, _b, _c, _d; const callStates = Object.values(calls); if (callStates.length === 0) { return undefined; } let latestCall = callStates[0]; for (const call of callStates.slice(1)) { if (((_b = (_a = call.endTime) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) > ((_d = (_c = latestCall === null || latestCall === void 0 ? void 0 : latestCall.endTime) === null || _c === void 0 ? void 0 : _c.getTime()) !== null && _d !== void 0 ? _d : 0)) { latestCall = call; } } return latestCall; }; const findLatestAcceptedTransfer = (acceptedTransfers) => { var _a, _b, _c, _d; const acceptedTransferValues = Object.values(acceptedTransfers); if (acceptedTransferValues.length === 0) { return undefined; } let latestAcceptedTransfer = acceptedTransferValues[0]; for (const acceptedTransfer of acceptedTransferValues.slice(1)) { if (((_b = (_a = acceptedTransfer.timestamp) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) > ((_d = (_c = latestAcceptedTransfer === null || latestAcceptedTransfer === void 0 ? void 0 : latestAcceptedTransfer.timestamp) === null || _c === void 0 ? void 0 : _c.getTime()) !== null && _d !== void 0 ? _d : 0)) { latestAcceptedTransfer = acceptedTransfer; } } return latestAcceptedTransfer; }; /** * @private */ export class AzureCommunicationCallAdapter { get call() { return this._call; } set call(newCall) { this.resetDiagnosticsForwarder(newCall); this._call = newCall; } constructor(callClient, locatorOrTargetCalless, callAgent, deviceManager, options) { var _a, _b, _c; this.participantSubscribers = new Map(); this.emitter = new EventEmitter(); this.isMyMutedChanged = () => { var _a; this.emitter.emit('isMutedChanged', { participantId: this.getState().userId, isMuted: (_a = this.call) === null || _a === void 0 ? void 0 : _a.isMuted }); }; this.bindPublicMethods(); this.callClient = callClient; this.callAgent = callAgent; this.targetCallees = getLocatorOrTargetCallees(locatorOrTargetCalless) === true ? locatorOrTargetCalless : undefined; this.locator = getLocatorOrTargetCallees(locatorOrTargetCalless) === false ? locatorOrTargetCalless : undefined; this.deviceManager = deviceManager; const isTeamsMeeting = this.locator ? 'meetingLink' in this.locator || 'meetingId' in this.locator : false; let isTeamsCall; (_a = this.targetCallees) === null || _a === void 0 ? void 0 : _a.forEach((callee) => { if (isMicrosoftTeamsUserIdentifier(callee) || isMicrosoftTeamsAppIdentifier(callee)) { isTeamsCall = true; } }); const isRoomsCall = this.locator ? 'roomId' in this.locator : false; this.onResolveVideoBackgroundEffectsDependency = (_b = options === null || options === void 0 ? void 0 : options.videoBackgroundOptions) === null || _b === void 0 ? void 0 : _b.onResolveDependency; this.onResolveDeepNoiseSuppressionDependency = (_c = options === null || options === void 0 ? void 0 : options.deepNoiseSuppressionOptions) === null || _c === void 0 ? void 0 : _c.onResolveDependency; this.context = new CallContext(callClient.getState(), !!isTeamsCall, isTeamsMeeting, isRoomsCall, options, this.targetCallees); this.context.onCallEnded((endCallData) => this.emitter.emit('callEnded', endCallData)); const onStateChange = (clientState) => { var _a, _b, _c, _d, _e; // unsubscribe when the instance gets disposed if (!this) { callClient.offStateChange(onStateChange); return; } // `updateClientState` searches for the current call from all the calls in the state using a cached `call.id` // from the call object. `call.id` can change during a call. We must update the cached `call.id` before // calling `updateClientState` so that we find the correct state object for the call even when `call.id` // has changed. // https://github.com/Azure/communication-ui-library/pull/1820 if ((_a = this.call) === null || _a === void 0 ? void 0 : _a.id) { this.context.setCurrentCallId(this.call.id); } // if the call hits the connected state we want to pause all calling sounds if playing. if (((_b = this.call) === null || _b === void 0 ? void 0 : _b.state) === 'Connected' && ((_c = this.callingSoundSubscriber) === null || _c === void 0 ? void 0 : _c.playingSounds)) { this.callingSoundSubscriber.pauseSounds(); } // If the call connects we need to clean up any previous unparentedViews if ((((_d = this.call) === null || _d === void 0 ? void 0 : _d.state) === 'InLobby' || ((_e = this.call) === null || _e === void 0 ? void 0 : _e.state) === 'Connected') && this.callClient.getState().deviceManager.unparentedViews.length > 0) { this.callClient.getState().deviceManager.unparentedViews.forEach((view) => { this.callClient.disposeView(undefined, undefined, view); }); } this.context.updateClientState(clientState); }; this.handlers = createHandlers(callClient, callAgent, deviceManager, undefined, { onResolveVideoBackgroundEffectsDependency: this.onResolveVideoBackgroundEffectsDependency, onResolveDeepNoiseSuppressionDependency: this.onResolveDeepNoiseSuppressionDependency }); this.onClientStateChange = onStateChange; this.subscribeDeviceManagerEvents(); this.callClient.onStateChange(onStateChange); if (this.callAgent.kind === 'CallAgent') { const onCallsUpdated = (args) => { var _a; if (!((_a = this.call) === null || _a === void 0 ? void 0 : _a.id)) { return; } const removedCall = args.removed.find((call) => { var _a; return call.id === ((_a = this.call) === null || _a === void 0 ? void 0 : _a.id); }); if (!removedCall) { return; } const removedCallState = this.callClient.getState().callsEnded[removedCall.id]; if (!removedCallState) { return; } const latestAcceptedTransfer = findLatestAcceptedTransfer(removedCallState.transfer.acceptedTransfers); const _callAgent = callAgent; const transferCall = _callAgent.calls.find((call) => call.id === (latestAcceptedTransfer === null || latestAcceptedTransfer === void 0 ? void 0 : latestAcceptedTransfer.callId)); if (!transferCall) { return; } this.processNewCall(transferCall); }; this.callAgent.on('callsUpdated', onCallsUpdated); } if (this.callAgent.kind === 'TeamsCallAgent') { const onTeamsCallsUpdated = (args) => { var _a; if (!((_a = this.call) === null || _a === void 0 ? void 0 : _a.id)) { return; } const removedCall = args.removed.find((call) => { var _a; return call.id === ((_a = this.call) === null || _a === void 0 ? void 0 : _a.id); }); if (!removedCall) { return; } const removedCallState = this.callClient.getState().callsEnded[removedCall.id]; if (!removedCallState) { return; } const latestAcceptedTransfer = findLatestAcceptedTransfer(removedCallState.transfer.acceptedTransfers); const _callAgent = callAgent; const transferCall = _callAgent.calls.find((call) => call.id === (latestAcceptedTransfer === null || latestAcceptedTransfer === void 0 ? void 0 : latestAcceptedTransfer.callId)); if (!transferCall) { return; } this.processNewCall(transferCall); }; this.callAgent.on('callsUpdated', onTeamsCallsUpdated); } } // TODO: update this to include the 'selectedCameraChanged' when calling adds it to the device manager subscribeDeviceManagerEvents() { this.deviceManager.on('selectedMicrophoneChanged', () => { this.emitter.emit('selectedMicrophoneChanged'); }); this.deviceManager.on('selectedSpeakerChanged', () => { this.emitter.emit('selectedSpeakerChanged'); }); } bindPublicMethods() { this.onStateChange.bind(this); this.offStateChange.bind(this); this.getState.bind(this); this.dispose.bind(this); this.joinCall.bind(this); this.leaveCall.bind(this); this.setCamera.bind(this); this.setMicrophone.bind(this); this.setSpeaker.bind(this); this.askDevicePermission.bind(this); this.queryCameras.bind(this); this.queryMicrophones.bind(this); this.querySpeakers.bind(this); this.startCamera.bind(this); this.stopCamera.bind(this); this.mute.bind(this); this.unmute.bind(this); this.startCall.bind(this); this.startScreenShare.bind(this); this.stopScreenShare.bind(this); this.raiseHand.bind(this); this.onReactionClick.bind(this); this.lowerHand.bind(this); this.removeParticipant.bind(this); this.createStreamView.bind(this); this.disposeStreamView.bind(this); this.createTogetherModeStreamView.bind(this); this.startTogetherMode.bind(this); this.setTogetherModeSceneSize.bind(this); this.disposeTogetherModeStreamView.bind(this); this.disposeScreenShareStreamView.bind(this); this.disposeRemoteVideoStreamView.bind(this); this.disposeLocalVideoStreamView.bind(this); this.on.bind(this); this.off.bind(this); this.processNewCall.bind(this); this.addParticipant.bind(this); this.holdCall.bind(this); this.resumeCall.bind(this); this.sendDtmfTone.bind(this); /* @conditional-compile-remove(unsupported-browser) */ this.allowUnsupportedBrowserVersion.bind(this); this.startCaptions.bind(this); this.stopCaptions.bind(this); this.setSpokenLanguage.bind(this); this.setCaptionLanguage.bind(this); this.sendRealTimeText.bind(this); this.startVideoBackgroundEffect.bind(this); this.stopVideoBackgroundEffects.bind(this); this.updateBackgroundPickerImages.bind(this); this.startNoiseSuppressionEffect.bind(this); this.stopNoiseSuppressionEffect.bind(this); this.submitSurvey.bind(this); this.startSpotlight.bind(this); this.stopSpotlight.bind(this); this.stopAllSpotlight.bind(this); this.muteParticipant.bind(this); this.muteAllRemoteParticipants.bind(this); this.forbidOthersAudio.bind(this); this.permitOthersAudio.bind(this); this.forbidOthersAudio.bind(this); this.permitOthersAudio.bind(this); } dispose() { this.resetDiagnosticsForwarder(); this.callClient.offStateChange(this.onClientStateChange); this.callAgent.dispose(); /* @conditional-compile-remove(calling-beta-sdk) */ this.callClient.dispose(); } queryCameras() { return __awaiter(this, void 0, void 0, function* () { const startTime = new Date().getTime(); return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { const cameras = yield this.deviceManager.getCameras(); const endTime = new Date().getTime(); compositeLogger.info('time to query cameras', endTime - startTime, 'ms'); return cameras; })); }); } queryMicrophones() { return __awaiter(this, void 0, void 0, function* () { const startTime = new Date().getTime(); return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { const microphones = yield this.deviceManager.getMicrophones(); const endTime = new Date().getTime(); compositeLogger.info('time to query microphones', endTime - startTime, 'ms'); return microphones; })); }); } querySpeakers() { return __awaiter(this, void 0, void 0, function* () { const startTime = new Date().getTime(); return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { const speakers = (yield this.deviceManager.isSpeakerSelectionAvailable) ? this.deviceManager.getSpeakers() : []; const endTime = new Date().getTime(); compositeLogger.info('time to query speakers', endTime - startTime, 'ms'); return speakers; })); }); } askDevicePermission(constrain) { return __awaiter(this, void 0, void 0, function* () { const startTime = new Date().getTime(); return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { const result = yield this.deviceManager.askDevicePermission(constrain); const endTime = new Date().getTime(); compositeLogger.info('time to query askDevicePermissions', endTime - startTime, 'ms'); return result; })); }); } joinCall(options) { var _a, _b; if (_isInCall((_b = (_a = this.getState().call) === null || _a === void 0 ? void 0 : _a.state) !== null && _b !== void 0 ? _b : 'None')) { throw new Error('You are already in the call!'); } else if (this.locator === undefined) { throw new Error('Locator is not defined!'); } return this.teeErrorToEventEmitter(() => { // Default to keeping camera/mic on if no override argument specified let shouldCameraBeOnInCall = this.getState().cameraStatus === 'On'; let shouldMicrophoneBeOnInCall = this.getState().isLocalPreviewMicrophoneEnabled; // Apply override arguments if (typeof options === 'boolean') { // Deprecated joinCall API (boolen) shouldMicrophoneBeOnInCall = options; } else if (typeof options === 'object') { // Options bag API if (options.microphoneOn && options.microphoneOn !== 'keep') { shouldMicrophoneBeOnInCall = options.microphoneOn; } if (options.cameraOn && options.cameraOn !== 'keep') { shouldCameraBeOnInCall = options.cameraOn; } } const audioOptions = { muted: !shouldMicrophoneBeOnInCall }; const selectedCamera = getSelectedCameraFromAdapterState(this.getState()); const videoOptions = selectedCamera && shouldCameraBeOnInCall ? { localVideoStreams: [new SDKLocalVideoStream(selectedCamera)] } : {}; const call = this._joinCall(audioOptions, videoOptions); this.originCall = call; this.processNewCall(call); return call; }); } _joinCall(audioOptions, videoOptions) { const isTeamsMeeting = this.locator ? 'meetingLink' in this.locator : false; const isTeamsMeetingId = this.locator ? 'meetingId' in this.locator : false; const isRoomsCall = this.locator ? 'roomId' in this.locator : false; if (_isTeamsCallAgent(this.callAgent)) { if (isTeamsMeeting) { return this.callAgent.join(this.locator, { audioOptions, videoOptions }); } if (isTeamsMeetingId) { return this.callAgent.join(this.locator, { audioOptions, videoOptions }); } throw new Error('Locator not supported by TeamsCallAgent'); } if (isTeamsMeeting) { return this.callAgent.join(this.locator, { audioOptions, videoOptions }); } if (isTeamsMeetingId) { return this.callAgent.join(this.locator, { audioOptions, videoOptions }); } if (isRoomsCall) { return this.callAgent.join(this.locator, { audioOptions, videoOptions }); } return this.callAgent.join(this.locator, { audioOptions, videoOptions }); } createStreamView(remoteUserId, options) { return __awaiter(this, void 0, void 0, function* () { if (remoteUserId === undefined) { return yield this.handlers.onCreateLocalStreamView(options); } else { return yield this.handlers.onCreateRemoteStreamView(remoteUserId, options); } }); } disposeStreamView(remoteUserId) { return __awaiter(this, void 0, void 0, function* () { if (remoteUserId === undefined) { yield this.handlers.onDisposeLocalStreamView(); } else { yield this.handlers.onDisposeRemoteStreamView(remoteUserId); } }); } disposeScreenShareStreamView(remoteUserId) { return __awaiter(this, void 0, void 0, function* () { if (remoteUserId !== '') { yield this.handlers.onDisposeRemoteScreenShareStreamView(remoteUserId); } else { yield this.handlers.onDisposeLocalScreenShareStreamView(); } }); } disposeRemoteVideoStreamView(remoteUserId) { return __awaiter(this, void 0, void 0, function* () { yield this.handlers.onDisposeRemoteVideoStreamView(remoteUserId); }); } disposeLocalVideoStreamView() { return __awaiter(this, void 0, void 0, function* () { yield this.handlers.onDisposeLocalStreamView(); }); } createTogetherModeStreamView(options) { return __awaiter(this, void 0, void 0, function* () { return yield this.handlers.onCreateTogetherModeStreamView(options); }); } startTogetherMode() { return __awaiter(this, void 0, void 0, function* () { return yield this.handlers.onStartTogetherMode(); }); } setTogetherModeSceneSize(width, height) { return this.handlers.onSetTogetherModeSceneSize(width, height); } disposeTogetherModeStreamView() { return __awaiter(this, void 0, void 0, function* () { return yield this.handlers.onDisposeTogetherModeStreamView(); }); } leaveCall(forEveryone) { return __awaiter(this, void 0, void 0, function* () { if (this.getState().page === 'transferring') { const transferCall = this.callAgent.calls.filter((call) => { var _a; return call.id === ((_a = this.getState().acceptedTransferCallState) === null || _a === void 0 ? void 0 : _a.id); })[0]; transferCall === null || transferCall === void 0 ? void 0 : transferCall.hangUp(); } yield this.handlers.onHangUp(forEveryone); this.unsubscribeCallEvents(); this.handlers = createHandlers(this.callClient, this.callAgent, this.deviceManager, this.call, { onResolveVideoBackgroundEffectsDependency: this.onResolveVideoBackgroundEffectsDependency, onResolveDeepNoiseSuppressionDependency: this.onResolveDeepNoiseSuppressionDependency }); // We set the adapter.call object to undefined immediately when a call is ended. // We do not set the context.callId to undefined because it is a part of the immutable data flow loop. this.call = undefined; this.stopCamera(); this.mute(); }); } setCamera(device, options) { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { yield this.handlers.onSelectCamera(device, options); })); }); } setMicrophone(device) { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { yield this.handlers.onSelectMicrophone(device); })); }); } setSpeaker(device) { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { yield this.handlers.onSelectSpeaker(device); })); }); } startCamera(options) { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { if (!isCameraOn(this.getState())) { // First kick off the effect on the local device before starting the camera in the call. // This prevents the effect not being applied for a brief moment when the camera is started. { const selectedEffect = this.getState().selectedVideoBackgroundEffect; const selectedCamera = getSelectedCameraFromAdapterState(this.getState()); if (selectedEffect && selectedCamera && this.onResolveVideoBackgroundEffectsDependency) { const stream = new SDKLocalVideoStream(selectedCamera); const effect = getBackgroundEffectFromSelectedEffect(selectedEffect, yield this.onResolveVideoBackgroundEffectsDependency()); if (effect) { yield stream.feature(Features.VideoEffects).startEffects(effect); } else { yield stream.feature(Features.VideoEffects).stopEffects(); } } } yield this.handlers.onToggleCamera(options); } })); }); } stopCamera() { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { if (isCameraOn(this.getState())) { yield this.handlers.onToggleCamera(); } })); }); } mute() { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { var _a, _b; this.context.setIsLocalMicrophoneEnabled(false); if (_isInCall((_a = this.call) === null || _a === void 0 ? void 0 : _a.state) && !((_b = this.call) === null || _b === void 0 ? void 0 : _b.isMuted)) { yield this.handlers.onToggleMicrophone(); } })); }); } unmute() { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; this.context.setIsLocalMicrophoneEnabled(true); if ((_isInCall((_a = this.call) === null || _a === void 0 ? void 0 : _a.state) || _isInLobbyOrConnecting((_b = this.call) === null || _b === void 0 ? void 0 : _b.state)) && ((_c = this.call) === null || _c === void 0 ? void 0 : _c.isMuted)) { yield this.handlers.onToggleMicrophone(); } })); }); } startScreenShare() { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { var _a; if (!((_a = this.call) === null || _a === void 0 ? void 0 : _a.isScreenSharingOn)) { yield this.handlers.onToggleScreenShare(); } })); }); } stopScreenShare() { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { var _a; if ((_a = this.call) === null || _a === void 0 ? void 0 : _a.isScreenSharingOn) { yield this.handlers.onToggleScreenShare(); } })); }); } raiseHand() { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { yield this.handlers.onToggleRaiseHand(); })); }); } lowerHand() { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { yield this.handlers.onToggleRaiseHand(); })); }); } onReactionClick(reaction) { return __awaiter(this, void 0, void 0, function* () { return yield this.asyncTeeErrorToEventEmitter(() => __awaiter(this, void 0, void 0, function* () { yield this.handlers.onReactionClick(reaction); })); }); } /* @conditional-compile-remove(unsupported-browser) */ allowUnsupportedBrowserVersion() { this.context.setAllowedUnsupportedBrowser(); this.context.updateClientState(this.callClient.getState()); } startVideoBackgroundEffect(videoBackgroundEffect) { return __awaiter(this, void 0, void 0, function* () { if (this.isBlurEffect(videoBackgroundEffect)) { const blurConfig = videoBackgroundEffect; yield this.handlers.onBlurVideoBackground(blurConfig); } else if (this.isReplacementEffect(videoBackgroundEffect)) { const replaceConfig = videoBackgroundEffect; yield this.handlers.onReplaceVideoBackground(replaceConfig); } }); } stopVideoBackgroundEffects() { return __awaiter(this, void 0, void 0, function* () { yield this.handlers.onRemoveVideoBackgroundEffects(); }); } updateBackgroundPickerImages(backgroundImages) { this.context.setBackroundPickerImages(backgroundImages); } updateSelectedVideoBackgroundEffect(selectedVideoBackground) { this.context.setSelectedVideoBackgroundEffect(selectedVideoBackground); } startNoiseSuppressionEffect() { return __awaiter(this, void 0, void 0, function* () { yield this.handlers.onStartNoiseSuppressionEffect(); }); } stopNoiseSuppressionEffect() { return __awaiter(this, void 0, void 0, function* () { yield this.handlers.onStopNoiseSuppressionEffect(); }); } startCall(participants, options) { var _a, _b; if (_isInCall((_b = (_a = this.getState().call) === null || _a === void 0 ? void 0 : _a.state) !== null && _b !== void 0 ? _b : 'None')) { throw new Error('You are already in the call.'); } const isCameraOn = this.getState().cameraStatus === 'On'; const selectedCamera = getSelectedCameraFromAdapterState(this.getState()); /* we only configure the video options here since the Calling SDK always unmutes the participant when starting a call */ const startCallVideoOptions = selectedCamera ? { videoOptions: isCameraOn ? { localVideoStreams: [new SDKLocalVideoStream(selectedCamera)] } : undefined } : {}; const combinedCallOptions = Object.assign(Object.assign({}, startCallVideoOptions), options); const idsToAdd = participants.map((participant) => { const backendId = _toCommunicationIdentifier(participant); if ('phoneNumber' in backendId) { if ((options === null || options === void 0 ? void 0 : options.alternateCallerId) === undefined) { throw new Error('Unable to start call, PSTN user present with no alternateCallerId.'); } } return backendId; }); this.context.setTargetCallee(idsToAdd); const call = this.handlers.onStartCall(idsToAdd, combinedCallOptions); if (!call) { throw new Error('Unable to start call.'); } this.processNewCall(call); return call; } processNewCall(call) { this.call = call; this.context.setCurrentCallId(call.id); // Resync state after callId is set this.context.updateClientState(this.callClient.getState()); this.handlers = createHandlers(this.callClient, this.callAgent, this.deviceManager, this.call, { onResolveVideoBackgroundEffectsDependency: this.onResolveVideoBackgroundEffectsDependency, onResolveDeepNoiseSuppressionDependency: this.onResolveDeepNoiseSuppressionDependency }); this.subscribeCallEvents(); } isBlurEffect(effect) { return effect.effectName === 'blur'; } isReplacementEffect(effect) { return effect.effectName === 'replacement'; } removeParticipant(userId) { return __awaiter(this, void 0, void 0, function* () { let participant = userId; participant = _toCommunicationIdentifier(userId); this.handlers.onRemoveParticipant(participant); }); } addParticipant(participant, options) { return __awaiter(this, void 0, void 0, function* () { if (isPhoneNumberIdentifier(participant) && options) { this.handlers.onAddParticipant(participant, options); } else if (isCommunicationUserIdentifier(participant)) { this.handlers.onAddParticipant(participant); } }); } holdCall() { return __awaiter(this, void 0, void 0, function* () { var _a, _b; if (((_a = this.call) === null || _a === void 0 ? void 0 : _a.state) !== 'LocalHold') { if ((_b = this.call) === null || _b === void 0 ? void 0 : _b.isLocalVideoStarted) { this.stopCamera().then(() => { this.handlers.onToggleHold(); }); } else { this.handlers.onToggleHold(); } } }); } resumeCall() { return __awaiter(this, void 0, void 0, function* () { var _a; if (((_a = this.call) === null || _a === void 0 ? void 0 : _a.state) === 'LocalHold') { this.handlers.onToggleHold().then(() => { var _a; if (((_a = this.call) === null || _a === void 0 ? void 0 : _a.feature(Features.Capabilities).capabilities.turnVideoOn.isPresent) === false) { this.stopCamera(); } }); } }); } sendDtmfTone(dtmfTone) { return __awaiter(this, void 0, void 0, function* () { this.handlers.onSendDtmfTone(dtmfTone); }); } startCaptions(options) { return __awaiter(this, void 0, void 0, function* () { if (options === null || options === void 0 ? void 0 : options.startInBackground) { this.context.updateCaptionsStartedInBackground(true); } else { this.context.updateCaptionsStartedWithUI(true); } this.handlers.onStartCaptions(options); }); } stopCaptions(options) { return __awaiter(this, void 0, void 0, function* () { var _a; if (options === null || options === void 0 ? void 0 : options.stopInBackground) { this.context.updateCaptionsStartedInBackground(false); } this.context.updateCaptionsStartedWithUI(false); // Only stop captions if they are not still running in the background if (!((_a = this.context.getState().captionsTriggers) === null || _a === void 0 ? void 0 : _a.captionsStartedInBackground)) { this.handlers.onStopCaptions(); } }); } setCaptionLanguage(language) { return __awaiter(this, void 0, void 0, function* () { this.handlers.onSetCaptionLanguage(language); }); } setSpokenLanguage(language) { return __awaiter(this, void 0, void 0, function* () { this.handlers.onSetSpokenLanguage(language); }); } sendRealTimeText(text, isFinalized) { return __awaiter(this, void 0, void 0, function* () { this.handlers.onSendRealTimeText(text, isFinalized); }); } submitSurvey(survey) { return __awaiter(this, void 0, void 0, function* () { return this.handlers.onSubmitSurvey(survey); }); } muteParticipant(userId) { return __awaiter(this, void 0, void 0, function* () { this.handlers.onMuteParticipant(userId); }); } muteAllRemoteParticipants() { return __awaiter(this, void 0, void 0, function* () { this.handlers.onMuteAllRemoteParticipants(); }); } startSpotlight(userIds) { return __awaiter(this, void 0, void 0, function* () { this.handlers.onStartSpotlight(userIds); }); } stopSpotlight(userIds) { return __awaiter(this, void 0, void 0, function* () { this.handlers.onStopSpotlight(userIds); }); } stopAllSpotlight() { return __awaiter(this, void 0, void 0, function* () { this.handlers.onStopAllSpotlight(); }); } forbidAudio(userIds) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onForbidAudio) === null || _b === void 0 ? void 0 : _b.call(_a, userIds); }); } permitAudio(userIds) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onPermitAudio) === null || _b === void 0 ? void 0 : _b.call(_a, userIds); }); } forbidOthersAudio() { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onForbidOthersAudio) === null || _b === void 0 ? void 0 : _b.call(_a); }); } permitOthersAudio() { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onPermitOthersAudio) === null || _b === void 0 ? void 0 : _b.call(_a); }); } forbidVideo(userIds) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onForbidVideo) === null || _b === void 0 ? void 0 : _b.call(_a, userIds); }); } permitVideo(userIds) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onPermitVideo) === null || _b === void 0 ? void 0 : _b.call(_a, userIds); }); } forbidOthersVideo() { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onForbidOthersVideo) === null || _b === void 0 ? void 0 : _b.call(_a); }); } permitOthersVideo() { return __awaiter(this, void 0, void 0, function* () { var _a, _b; (_b = (_a = this.handlers).onPermitOthersVideo) === null || _b === void 0 ? void 0 : _b.call(_a); }); } returnFromBreakoutRoom() { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const callState = ((_a = this.call) === null || _a === void 0 ? void 0 : _a.id) ? this.callClient.getState().calls[this.call.id] : undefined; const assignedBreakoutRoom = (_b = callState === null || callState === void 0 ? void 0 : callState.breakoutRooms) === null || _b === void 0 ? void 0 : _b.assignedBreakoutRoom; if (!assignedBreakoutRoom) { throw new Error('Could not return from breakout room because assigned breakout room state could not be retr