UNPKG

@azure/communication-react

Version:

React library for building modern communication user experiences utilizing Azure Communication Services

196 lines • 11.7 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 { deviceManagerDeclaratify } from './DeviceManagerDeclarative'; import { CallClient, Features } from '@azure/communication-calling'; import { CallContext } from './CallContext'; import { callAgentDeclaratify } from './CallAgentDeclarative'; import { InternalCallContext } from './InternalCallContext'; import { createView, disposeView } from './StreamUtils'; import { getIdentifierKind } from '@azure/communication-common'; import { toFlatCommunicationIdentifier, _getApplicationId } from "../../acs-ui-common/src"; import { callingStatefulLogger } from './Logger'; import { teamsCallAgentDeclaratify } from './TeamsCallAgentDeclarative'; import { videoStreamRendererViewDeclaratify } from './VideoStreamRendererViewDeclarative'; import { createView as createCallFeatureView, disposeView as disposeCallFeatureView } from './CallFeatureStreamUtils'; /** * ProxyCallClient proxies CallClient {@link @azure/communication-calling#CallClient} and subscribes to all events that * affect state. ProxyCallClient keeps its own copy of the call state and when state is updated, ProxyCallClient emits * the event 'stateChanged'. */ class ProxyCallClient { constructor(context, internalContext) { this._context = context; this._internalContext = internalContext; } // eslint-disable-next-line @typescript-eslint/no-explicit-any get(target, prop) { switch (prop) { case 'createCallAgent': { return this._context.withAsyncErrorTeedToState((...args) => __awaiter(this, void 0, void 0, function* () { // createCallAgent will throw an exception if the previous callAgent was not disposed. If the previous // callAgent was disposed then it would have unsubscribed to events so we can just create a new declarative // callAgent if the createCallAgent succeeds. const callAgent = yield target.createCallAgent(...args); this._callAgent = callAgentDeclaratify(callAgent, this._context, this._internalContext); this._context.setCallAgent({ displayName: this._callAgent.displayName }); return this._callAgent; }), 'CallClient.createCallAgent'); } case 'createTeamsCallAgent': { return this._context.withAsyncErrorTeedToState((...args) => __awaiter(this, void 0, void 0, function* () { // createCallAgent will throw an exception if the previous callAgent was not disposed. If the previous // callAgent was disposed then it would have unsubscribed to events so we can just create a new declarative // callAgent if the createCallAgent succeeds. const callAgent = yield target.createTeamsCallAgent(...args); this._callAgent = teamsCallAgentDeclaratify(callAgent, this._context, this._internalContext); this._context.setCallAgent({ displayName: undefined }); return this._callAgent; }), 'CallClient.createTeamsCallAgent'); } case 'getDeviceManager': { return this._context.withAsyncErrorTeedToState(() => __awaiter(this, void 0, void 0, function* () { // As of writing, the SDK always returns the same instance of DeviceManager so we keep a reference of // DeviceManager and if it does not change we return the cached DeclarativeDeviceManager. If it does not we'll // throw an error that indicate we need to fix this issue as our implementation has diverged from the SDK. const deviceManager = yield target.getDeviceManager(); if (this._sdkDeviceManager) { if (this._sdkDeviceManager === deviceManager) { return this._deviceManager; } else { throw new Error('Multiple DeviceManager not supported. This means a incompatible version of communication-calling is ' + 'used OR calling declarative was not properly updated to communication-calling version.'); } } else { this._sdkDeviceManager = deviceManager; } this._deviceManager = deviceManagerDeclaratify(deviceManager, this._context, this._internalContext); return this._deviceManager; }), 'CallClient.getDeviceManager'); } case 'feature': { return this._context.withErrorTeedToState((...args) => { if (args[0] === Features.DebugInfo) { const feature = target.feature(Features.DebugInfo); /** * add to this object if we want to proxy anything else off the DebugInfo feature object. */ return Object.assign(Object.assign({}, feature), { getEnvironmentInfo: () => __awaiter(this, void 0, void 0, function* () { const environmentInfo = yield feature.getEnvironmentInfo(); this._context.setEnvironmentInfo(environmentInfo); return environmentInfo; }) }); } return Reflect.get(target, prop); }, 'CallClient.feature'); } case 'dispose': { return this._context.withAsyncErrorTeedToState(() => __awaiter(this, void 0, void 0, function* () { yield target.dispose(); }), 'CallClient.dispose'); } default: return Reflect.get(target, prop); } } } /** * Creates a StatefulCallClient {@link StatefulCallClient} by proxying CallClient * {@link @azure/communication-calling#CallClient} with ProxyCallClient {@link ProxyCallClient} which then allows access * to state in a declarative way. * * It is important to use the {@link @azure/communication-calling#DeviceManager} and * {@link @azure/communication-calling#CallAgent} and {@link @azure/communication-calling#Call} (and etc.) that are * obtained from the StatefulCallClient in order for their state changes to be proxied properly. * * @param args - {@link StatefulCallClientArgs} * @param options - {@link StatefulCallClientOptions} * * @public */ export const createStatefulCallClient = (args, options) => { return _createStatefulCallClientInner(args, options); }; /** * This inner function is used to allow injection of TelemetryImplementationHint without changing the public API. * * @internal */ export const _createStatefulCallClientInner = (args, options, telemetryImplementationHint = 'StatefulComponents') => { callingStatefulLogger.info(`Creating calling stateful client using library version: ${_getApplicationId(telemetryImplementationHint)}`); return createStatefulCallClientWithDeps(new CallClient(withTelemetryTag(telemetryImplementationHint, options === null || options === void 0 ? void 0 : options.callClientOptions)), new CallContext(getIdentifierKind(args.userId), options === null || options === void 0 ? void 0 : options.maxStateChangeListeners), new InternalCallContext()); }; /** * Package-internal version of createStatefulCallClient that allows dependency injection. * * This function should not be exported from the package. */ export const createStatefulCallClientWithDeps = (callClient, context, internalContext) => { Object.defineProperty(callClient, 'getState', { configurable: false, value: () => context.getState() }); Object.defineProperty(callClient, 'onStateChange', { configurable: false, value: (handler) => context.onStateChange(handler) }); Object.defineProperty(callClient, 'offStateChange', { configurable: false, value: (handler) => context.offStateChange(handler) }); Object.defineProperty(callClient, 'createView', { configurable: false, value: (callId, participantId, stream, options) => __awaiter(void 0, void 0, void 0, function* () { if ('feature' in stream) { return yield createCallFeatureView(context, internalContext, callId, stream, options); } const participantIdKind = participantId ? getIdentifierKind(participantId) : undefined; const result = yield createView(context, internalContext, callId, participantIdKind, stream, options); // We only need to declaratify the VideoStreamRendererView object for remote participants. Because the updateScalingMode only needs to be called on remote participant stream views. if ('id' in stream && callId && participantId && result) { const participantKey = toFlatCommunicationIdentifier(participantId); result.view = videoStreamRendererViewDeclaratify(result.view, context, callId, participantKey, stream.id); } return result; }) }); Object.defineProperty(callClient, 'disposeView', { configurable: false, value: (callId, participantId, stream) => { if ('feature' in stream) { disposeCallFeatureView(context, internalContext, callId, stream); } const participantIdKind = participantId ? getIdentifierKind(participantId) : undefined; disposeView(context, internalContext, callId, participantIdKind, stream); } }); const newStatefulCallClient = new Proxy(callClient, new ProxyCallClient(context, internalContext)); // Populate initial state newStatefulCallClient.feature(Features.DebugInfo).getEnvironmentInfo(); return newStatefulCallClient; }; const withTelemetryTag = (telemetryImplementationHint, options) => { var _a, _b; const tags = (_b = (_a = options === null || options === void 0 ? void 0 : options.diagnostics) === null || _a === void 0 ? void 0 : _a.tags) !== null && _b !== void 0 ? _b : []; tags.push(_getApplicationId(telemetryImplementationHint)); return Object.assign(Object.assign({}, options), { diagnostics: Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.diagnostics), { tags }) }); }; //# sourceMappingURL=StatefulCallClient.js.map