@azure/communication-react
Version:
React library for building modern communication user experiences utilizing Azure Communication Services
196 lines • 11.7 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 { 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