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
JavaScript
// 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