Surface userId and displayName in Calling Declarative state (#200)
* Add ability to retrieve userId and displayName in Calling Declarative * Change files * Fix build break * Fix build break * Change files * Address code review comments
This commit is contained in:
Родитель
e476866319
Коммит
b8b5439dd3
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "prerelease",
|
||||
"comment": "Surface userId and displayName in Calling Declarative state",
|
||||
"packageName": "@azure/acs-calling-declarative",
|
||||
"email": "allenhwang@microsoft.com",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "prerelease",
|
||||
"comment": "Change DeviceManagerState to DeviceManager to be consistent with other objects",
|
||||
"packageName": "@azure/acs-calling-selector",
|
||||
"email": "allenhwang@microsoft.com",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "prerelease",
|
||||
"comment": "Add userId to callClientDeclaratify to add userId to Declarative state",
|
||||
"packageName": "@azure/communication-ui",
|
||||
"email": "allenhwang@microsoft.com",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -40,15 +40,22 @@ export interface Call {
|
|||
}
|
||||
|
||||
// @public
|
||||
export const callClientDeclaratify: (callClient: CallClient) => DeclarativeCallClient;
|
||||
export interface CallAgent {
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const callClientDeclaratify: (callClient: CallClient, userId: string) => DeclarativeCallClient;
|
||||
|
||||
// @public
|
||||
export interface CallClientState {
|
||||
callAgent: CallAgent | undefined;
|
||||
calls: Map<string, Call>;
|
||||
callsEnded: Call[];
|
||||
deviceManagerState: DeviceManagerState;
|
||||
deviceManager: DeviceManager;
|
||||
incomingCalls: Map<string, IncomingCall>;
|
||||
incomingCallsEnded: IncomingCall[];
|
||||
userId: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
@ -61,7 +68,7 @@ export interface DeclarativeCallClient extends CallClient {
|
|||
}
|
||||
|
||||
// @public
|
||||
export type DeviceManagerState = {
|
||||
export type DeviceManager = {
|
||||
isSpeakerSelectionAvailable: boolean;
|
||||
selectedMicrophone?: AudioDeviceInfo;
|
||||
selectedSpeaker?: AudioDeviceInfo;
|
||||
|
|
|
@ -101,7 +101,7 @@ class MockCallAgent implements CallAgent {
|
|||
describe('declarative call agent', () => {
|
||||
test('should subscribe to callsUpdated and incomingCall events when created', () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
callAgentDeclaratify(mockCallAgent, context, internalContext);
|
||||
expect(mockCallAgent.emitter.eventNames().includes('incomingCall')).toBe(true);
|
||||
|
@ -110,7 +110,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should subscribe to any existing calls and add to state when created', () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const mockCall = createMockCall(mockCallId);
|
||||
mockCallAgent.calls = [mockCall];
|
||||
const internalContext = new InternalCallContext();
|
||||
|
@ -121,7 +121,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should unsubscribe but not clear data when disposed is invoked', async () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
const mockCall = createMockCall(mockCallId);
|
||||
mockCallAgent.calls = [mockCall];
|
||||
|
@ -134,7 +134,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should clear state if newly created agent and if there is old existing state', async () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
const mockCall = createMockCall(mockCallId);
|
||||
mockCallAgent.calls = [mockCall];
|
||||
|
@ -151,7 +151,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should update state with new call when startCall is invoked', () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
expect(context.getState().calls.size).toBe(0);
|
||||
const declarativeCallAgent = callAgentDeclaratify(mockCallAgent, context, internalContext);
|
||||
|
@ -161,7 +161,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should update state with new call when join is invoked', () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
expect(context.getState().calls.size).toBe(0);
|
||||
const declarativeCallAgent = callAgentDeclaratify(mockCallAgent, context, internalContext);
|
||||
|
@ -171,7 +171,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should move call to callEnded when call is removed and add endTime', async () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
expect(context.getState().calls.size).toBe(0);
|
||||
callAgentDeclaratify(mockCallAgent, context, internalContext);
|
||||
|
@ -198,7 +198,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should move incoming call to incomingCallEnded when incoming call is ended and add endTime', async () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
expect(context.getState().calls.size).toBe(0);
|
||||
callAgentDeclaratify(mockCallAgent, context, internalContext);
|
||||
|
@ -222,7 +222,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should make sure that callsEnded not exceed max length', async () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
callAgentDeclaratify(mockCallAgent, context, internalContext);
|
||||
|
||||
|
@ -246,7 +246,7 @@ describe('declarative call agent', () => {
|
|||
|
||||
test('should make sure that incomingCallsEnded not exceed max length', async () => {
|
||||
const mockCallAgent = new MockCallAgent();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
callAgentDeclaratify(mockCallAgent, context, internalContext);
|
||||
|
||||
|
|
|
@ -67,6 +67,8 @@ jest.mock('@azure/communication-calling', () => {
|
|||
const mockCallId = 'a';
|
||||
const mockCallId2 = 'b';
|
||||
const mockParticipantCommunicationUserId = 'c';
|
||||
const mockDisplayName = 'd';
|
||||
const mockUserId = 'e';
|
||||
|
||||
interface TestData {
|
||||
mockCallClient: any;
|
||||
|
@ -78,7 +80,7 @@ interface TestData {
|
|||
|
||||
function createClientAndAgentMocks(testData: TestData): void {
|
||||
const mockCallClient = new CallClient();
|
||||
const mockCallAgent = { calls: [] as ReadonlyArray<Call> } as MockCallAgent;
|
||||
const mockCallAgent = { calls: [] as ReadonlyArray<Call>, displayName: mockDisplayName } as MockCallAgent;
|
||||
addMockEmitter(mockCallAgent);
|
||||
mockCallClient.createCallAgent = (): Promise<CallAgent> => {
|
||||
return Promise.resolve(mockCallAgent);
|
||||
|
@ -88,7 +90,7 @@ function createClientAndAgentMocks(testData: TestData): void {
|
|||
}
|
||||
|
||||
function createDeclarativeClient(testData: TestData): void {
|
||||
testData.declarativeCallClient = callClientDeclaratify(testData.mockCallClient);
|
||||
testData.declarativeCallClient = callClientDeclaratify(testData.mockCallClient, '');
|
||||
}
|
||||
|
||||
async function createMockCallAndEmitCallsUpdated(
|
||||
|
@ -155,6 +157,22 @@ async function waitWithBreakCondition(breakCondition: () => boolean): Promise<vo
|
|||
}
|
||||
|
||||
describe('declarative call client', () => {
|
||||
test('should allow developer to specify userId and provide access to it in state', async () => {
|
||||
const callClient = new CallClient();
|
||||
const declarativeCallClient = callClientDeclaratify(callClient, mockUserId);
|
||||
expect(declarativeCallClient.state.userId).toBe(mockUserId);
|
||||
});
|
||||
|
||||
test('should update callAgent state and have displayName when callAgent is created', async () => {
|
||||
const testData = {} as TestData;
|
||||
createClientAndAgentMocks(testData);
|
||||
createDeclarativeClient(testData);
|
||||
await createMockCallAndEmitCallsUpdated(testData);
|
||||
|
||||
expect(testData.declarativeCallClient.state.callAgent).toBeDefined();
|
||||
expect(testData.declarativeCallClient.state.callAgent?.displayName).toBe(mockDisplayName);
|
||||
});
|
||||
|
||||
test('should update state when call added in `callUpdated` event and subscribe to call', async () => {
|
||||
const testData = {} as TestData;
|
||||
createClientAndAgentMocks(testData);
|
||||
|
|
|
@ -80,6 +80,7 @@ class ProxyCallClient implements ProxyHandler<CallClient> {
|
|||
// callAgent if the createCallAgent succeeds.
|
||||
const callAgent = await target.createCallAgent(...args);
|
||||
this._callAgent = callAgentDeclaratify(callAgent, this._context, this._internalContext);
|
||||
this._context.setCallAgent({ displayName: this._callAgent.displayName });
|
||||
return this._callAgent;
|
||||
};
|
||||
}
|
||||
|
@ -117,9 +118,12 @@ class ProxyCallClient implements ProxyHandler<CallClient> {
|
|||
* to state in a declarative way.
|
||||
*
|
||||
* @param callClient - CallClient from SDK to declaratify
|
||||
* @param userId - UserId from SDK. This is provided for developer convenience to easily access the userId from the
|
||||
* state. It is not used by DeclarativeCallClient so if you do not have this value or do not want to use this value,
|
||||
* you could pass any dummy value like empty string.
|
||||
*/
|
||||
export const callClientDeclaratify = (callClient: CallClient): DeclarativeCallClient => {
|
||||
const context: CallContext = new CallContext();
|
||||
export const callClientDeclaratify = (callClient: CallClient, userId: string): DeclarativeCallClient => {
|
||||
const context: CallContext = new CallContext(userId);
|
||||
const internalContext: InternalCallContext = new InternalCallContext();
|
||||
|
||||
Object.defineProperty(callClient, 'state', {
|
||||
|
|
|
@ -19,6 +19,19 @@ import {
|
|||
UnknownIdentifierKind
|
||||
} from '@azure/communication-common';
|
||||
|
||||
/**
|
||||
* State only version of {@Link @azure/communication-calling#CallAgent} except calls is moved to be a child directly of
|
||||
* {@Link CallClientState}. The reason to have CallAgent's state proxied is to provide access to displayName. We don't
|
||||
* flatten CallAgent.displayName and put it in CallClientState because it would be ambiguious that displayName is
|
||||
* actually reliant on the creation/existence of CallAgent to be available.
|
||||
*/
|
||||
export interface CallAgent {
|
||||
/**
|
||||
* Proxy of {@Link @azure/communication-calling#CallAgent.displayName}.
|
||||
*/
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* State only version of {@Link @azure/communication-calling#TranscriptionCallFeature}.
|
||||
*/
|
||||
|
@ -235,7 +248,7 @@ export interface IncomingCall {
|
|||
* This type is meant to encapsulate all the state inside {@Link @azure/communication-calling#DeviceManager}. For
|
||||
* optional parameters they may not be available until permission is granted by the user.
|
||||
*/
|
||||
export type DeviceManagerState = {
|
||||
export type DeviceManager = {
|
||||
/**
|
||||
* Proxy of {@Link @azure/communication-calling#DeviceManager.isSpeakerSelectionAvailable}.
|
||||
*/
|
||||
|
@ -297,5 +310,15 @@ export interface CallClientState {
|
|||
/**
|
||||
* Proxy of {@Link @azure/communication-calling#DeviceManager} and its events.
|
||||
*/
|
||||
deviceManagerState: DeviceManagerState;
|
||||
deviceManager: DeviceManager;
|
||||
/**
|
||||
* Proxy of {@Link @azure/communication-calling#CallAgent} without the calls property. Provides access to displayName
|
||||
* but only available if CallAgent has been created.
|
||||
*/
|
||||
callAgent: CallAgent | undefined;
|
||||
/**
|
||||
* Stores a userId string. This is not used by the stateful client and is provided here as a convenience for the
|
||||
* developer for easier access to userId. Must be passed in at initialization of the stateful client.
|
||||
*/
|
||||
userId: string;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ import {
|
|||
RemoteParticipant,
|
||||
RemoteVideoStream,
|
||||
IncomingCall,
|
||||
VideoStreamRendererView
|
||||
VideoStreamRendererView,
|
||||
CallAgent
|
||||
} from './CallClientState';
|
||||
import { getRemoteParticipantKey } from './Converter';
|
||||
|
||||
|
@ -20,19 +21,26 @@ enableMapSet();
|
|||
export const MAX_CALL_HISTORY_LENGTH = 10;
|
||||
|
||||
export class CallContext {
|
||||
private _state: CallClientState = {
|
||||
calls: new Map<string, Call>(),
|
||||
callsEnded: [],
|
||||
incomingCalls: new Map<string, IncomingCall>(),
|
||||
incomingCallsEnded: [],
|
||||
deviceManagerState: {
|
||||
isSpeakerSelectionAvailable: false,
|
||||
cameras: [],
|
||||
microphones: [],
|
||||
speakers: []
|
||||
}
|
||||
};
|
||||
private _emitter: EventEmitter = new EventEmitter();
|
||||
private _state: CallClientState;
|
||||
private _emitter: EventEmitter;
|
||||
|
||||
constructor(userId: string) {
|
||||
this._state = {
|
||||
calls: new Map<string, Call>(),
|
||||
callsEnded: [],
|
||||
incomingCalls: new Map<string, IncomingCall>(),
|
||||
incomingCallsEnded: [],
|
||||
deviceManager: {
|
||||
isSpeakerSelectionAvailable: false,
|
||||
cameras: [],
|
||||
microphones: [],
|
||||
speakers: []
|
||||
},
|
||||
callAgent: undefined,
|
||||
userId: userId
|
||||
};
|
||||
this._emitter = new EventEmitter();
|
||||
}
|
||||
|
||||
public setState(state: CallClientState): void {
|
||||
this._state = state;
|
||||
|
@ -65,6 +73,14 @@ export class CallContext {
|
|||
);
|
||||
}
|
||||
|
||||
public setCallAgent(callAgent: CallAgent): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.callAgent = callAgent;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public setCall(call: Call): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
|
@ -440,7 +456,7 @@ export class CallContext {
|
|||
public setDeviceManagerIsSpeakerSelectionAvailable(isSpeakerSelectionAvailable: boolean): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.deviceManagerState.isSpeakerSelectionAvailable = isSpeakerSelectionAvailable;
|
||||
draft.deviceManager.isSpeakerSelectionAvailable = isSpeakerSelectionAvailable;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -448,7 +464,7 @@ export class CallContext {
|
|||
public setDeviceManagerSelectedMicrophone(selectedMicrophone?: AudioDeviceInfo): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.deviceManagerState.selectedMicrophone = selectedMicrophone;
|
||||
draft.deviceManager.selectedMicrophone = selectedMicrophone;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -456,7 +472,7 @@ export class CallContext {
|
|||
public setDeviceManagerSelectedSpeaker(selectedSpeaker?: AudioDeviceInfo): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.deviceManagerState.selectedSpeaker = selectedSpeaker;
|
||||
draft.deviceManager.selectedSpeaker = selectedSpeaker;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -464,7 +480,7 @@ export class CallContext {
|
|||
public setDeviceManagerCameras(cameras: VideoDeviceInfo[]): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.deviceManagerState.cameras = cameras;
|
||||
draft.deviceManager.cameras = cameras;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -472,7 +488,7 @@ export class CallContext {
|
|||
public setDeviceManagerMicrophones(microphones: AudioDeviceInfo[]): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.deviceManagerState.microphones = microphones;
|
||||
draft.deviceManager.microphones = microphones;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -480,7 +496,7 @@ export class CallContext {
|
|||
public setDeviceManagerSpeakers(speakers: AudioDeviceInfo[]): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.deviceManagerState.speakers = speakers;
|
||||
draft.deviceManager.speakers = speakers;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -488,7 +504,7 @@ export class CallContext {
|
|||
public setDeviceManagerDeviceAccess(deviceAccess: DeviceAccess): void {
|
||||
this.setState(
|
||||
produce(this._state, (draft: CallClientState) => {
|
||||
draft.deviceManagerState.deviceAccess = deviceAccess;
|
||||
draft.deviceManager.deviceAccess = deviceAccess;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('declarative call', () => {
|
|||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
context.setCall(convertSdkCallToDeclarativeCall(mockCall));
|
||||
|
||||
const declarativeCall = callDeclaratify(mockCall, context);
|
||||
|
|
|
@ -116,7 +116,7 @@ function createDeclarativeDeviceManager(): {
|
|||
callContext: CallContext;
|
||||
} {
|
||||
const mockDeviceManager = new MockDeviceManager();
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
return {
|
||||
declarativeDeviceManager: deviceManagerDeclaratify(mockDeviceManager, context),
|
||||
mockDeviceManager: mockDeviceManager,
|
||||
|
@ -130,8 +130,8 @@ describe('declarative device manager', () => {
|
|||
const cameras = await declarativeDeviceManager.getCameras();
|
||||
expect(cameras.length).toBe(1);
|
||||
expect(cameras[0].name).toBe('camera');
|
||||
expect(callContext.getState().deviceManagerState.cameras.length).toBe(1);
|
||||
expect(callContext.getState().deviceManagerState.cameras[0].name).toBe('camera');
|
||||
expect(callContext.getState().deviceManager.cameras.length).toBe(1);
|
||||
expect(callContext.getState().deviceManager.cameras[0].name).toBe('camera');
|
||||
});
|
||||
|
||||
test('should proxy getMicrophones and update microphones in state', async () => {
|
||||
|
@ -139,8 +139,8 @@ describe('declarative device manager', () => {
|
|||
const microphones = await declarativeDeviceManager.getMicrophones();
|
||||
expect(microphones.length).toBe(1);
|
||||
expect(microphones[0].name).toBe('microphone');
|
||||
expect(callContext.getState().deviceManagerState.microphones.length).toBe(1);
|
||||
expect(callContext.getState().deviceManagerState.microphones[0].name).toBe('microphone');
|
||||
expect(callContext.getState().deviceManager.microphones.length).toBe(1);
|
||||
expect(callContext.getState().deviceManager.microphones[0].name).toBe('microphone');
|
||||
});
|
||||
|
||||
test('should proxy getSpeakers and update speakers in state', async () => {
|
||||
|
@ -148,8 +148,8 @@ describe('declarative device manager', () => {
|
|||
const speakers = await declarativeDeviceManager.getSpeakers();
|
||||
expect(speakers.length).toBe(1);
|
||||
expect(speakers[0].name).toBe('speaker');
|
||||
expect(callContext.getState().deviceManagerState.speakers.length).toBe(1);
|
||||
expect(callContext.getState().deviceManagerState.speakers[0].name).toBe('speaker');
|
||||
expect(callContext.getState().deviceManager.speakers.length).toBe(1);
|
||||
expect(callContext.getState().deviceManager.speakers[0].name).toBe('speaker');
|
||||
});
|
||||
|
||||
test('should proxy selectMicrophone and update selectedMicrophone in state', async () => {
|
||||
|
@ -160,8 +160,8 @@ describe('declarative device manager', () => {
|
|||
isSystemDefault: false,
|
||||
deviceType: 'Microphone' as AudioDeviceType
|
||||
});
|
||||
expect(callContext.getState().deviceManagerState.selectedMicrophone).toBeDefined();
|
||||
expect(callContext.getState().deviceManagerState.selectedMicrophone?.name).toBe('new_selected_microphone');
|
||||
expect(callContext.getState().deviceManager.selectedMicrophone).toBeDefined();
|
||||
expect(callContext.getState().deviceManager.selectedMicrophone?.name).toBe('new_selected_microphone');
|
||||
});
|
||||
|
||||
test('should proxy selectSpeaker and update selectedSpeaker in state', async () => {
|
||||
|
@ -172,8 +172,8 @@ describe('declarative device manager', () => {
|
|||
isSystemDefault: false,
|
||||
deviceType: 'Speaker' as AudioDeviceType
|
||||
});
|
||||
expect(callContext.getState().deviceManagerState.selectedSpeaker).toBeDefined();
|
||||
expect(callContext.getState().deviceManagerState.selectedSpeaker?.name).toBe('new_selected_speaker');
|
||||
expect(callContext.getState().deviceManager.selectedSpeaker).toBeDefined();
|
||||
expect(callContext.getState().deviceManager.selectedSpeaker?.name).toBe('new_selected_speaker');
|
||||
});
|
||||
|
||||
test('should proxy askDevicePermission and update deviceAccess in state', async () => {
|
||||
|
@ -181,17 +181,17 @@ describe('declarative device manager', () => {
|
|||
const deviceAccess = await declarativeDeviceManager.askDevicePermission({ audio: false, video: false });
|
||||
expect(deviceAccess.audio).toBe(true);
|
||||
expect(deviceAccess.video).toBe(true);
|
||||
expect(callContext.getState().deviceManagerState.deviceAccess).toBeDefined();
|
||||
expect(callContext.getState().deviceManagerState.deviceAccess?.audio).toBe(true);
|
||||
expect(callContext.getState().deviceManagerState.deviceAccess?.video).toBe(true);
|
||||
expect(callContext.getState().deviceManager.deviceAccess).toBeDefined();
|
||||
expect(callContext.getState().deviceManager.deviceAccess?.audio).toBe(true);
|
||||
expect(callContext.getState().deviceManager.deviceAccess?.video).toBe(true);
|
||||
});
|
||||
|
||||
test('should detect videoDevicesUpdated and update cameras in state using deviceManager', async () => {
|
||||
const { mockDeviceManager, callContext } = createDeclarativeDeviceManager();
|
||||
mockDeviceManager.emit('videoDevicesUpdated', {});
|
||||
await waitWithBreakCondition(() => callContext.getState().deviceManagerState.cameras.length !== 0);
|
||||
expect(callContext.getState().deviceManagerState.cameras.length).toBe(1);
|
||||
expect(callContext.getState().deviceManagerState.cameras[0].name).toBe('camera');
|
||||
await waitWithBreakCondition(() => callContext.getState().deviceManager.cameras.length !== 0);
|
||||
expect(callContext.getState().deviceManager.cameras.length).toBe(1);
|
||||
expect(callContext.getState().deviceManager.cameras[0].name).toBe('camera');
|
||||
});
|
||||
|
||||
test('should detect audioDevicesUpdated update microphones/speakers in state using deviceManager', async () => {
|
||||
|
@ -199,28 +199,28 @@ describe('declarative device manager', () => {
|
|||
mockDeviceManager.emit('audioDevicesUpdated', {});
|
||||
await waitWithBreakCondition(
|
||||
() =>
|
||||
callContext.getState().deviceManagerState.microphones.length !== 0 &&
|
||||
callContext.getState().deviceManagerState.speakers.length !== 0
|
||||
callContext.getState().deviceManager.microphones.length !== 0 &&
|
||||
callContext.getState().deviceManager.speakers.length !== 0
|
||||
);
|
||||
expect(callContext.getState().deviceManagerState.speakers.length).toBe(1);
|
||||
expect(callContext.getState().deviceManagerState.speakers[0].name).toBe('speaker');
|
||||
expect(callContext.getState().deviceManagerState.microphones.length).toBe(1);
|
||||
expect(callContext.getState().deviceManagerState.microphones[0].name).toBe('microphone');
|
||||
expect(callContext.getState().deviceManager.speakers.length).toBe(1);
|
||||
expect(callContext.getState().deviceManager.speakers[0].name).toBe('speaker');
|
||||
expect(callContext.getState().deviceManager.microphones.length).toBe(1);
|
||||
expect(callContext.getState().deviceManager.microphones[0].name).toBe('microphone');
|
||||
});
|
||||
|
||||
test('should detect selectedMicrophoneChanged update selectedMicrophone in state using deviceManager', async () => {
|
||||
const { mockDeviceManager, callContext } = createDeclarativeDeviceManager();
|
||||
mockDeviceManager.emit('selectedMicrophoneChanged', {});
|
||||
await waitWithBreakCondition(() => callContext.getState().deviceManagerState.selectedMicrophone !== undefined);
|
||||
expect(callContext.getState().deviceManagerState.selectedMicrophone).toBeDefined();
|
||||
expect(callContext.getState().deviceManagerState.selectedMicrophone?.name).toBe('selected_microphone');
|
||||
await waitWithBreakCondition(() => callContext.getState().deviceManager.selectedMicrophone !== undefined);
|
||||
expect(callContext.getState().deviceManager.selectedMicrophone).toBeDefined();
|
||||
expect(callContext.getState().deviceManager.selectedMicrophone?.name).toBe('selected_microphone');
|
||||
});
|
||||
|
||||
test('should detect selectedSpeakerChanged update selectedSpeaker in state using deviceManager', async () => {
|
||||
const { mockDeviceManager, callContext } = createDeclarativeDeviceManager();
|
||||
mockDeviceManager.emit('selectedSpeakerChanged', {});
|
||||
await waitWithBreakCondition(() => callContext.getState().deviceManagerState.selectedSpeaker !== undefined);
|
||||
expect(callContext.getState().deviceManagerState.selectedSpeaker).toBeDefined();
|
||||
expect(callContext.getState().deviceManagerState.selectedSpeaker?.name).toBe('selected_speaker');
|
||||
await waitWithBreakCondition(() => callContext.getState().deviceManager.selectedSpeaker !== undefined);
|
||||
expect(callContext.getState().deviceManager.selectedSpeaker).toBeDefined();
|
||||
expect(callContext.getState().deviceManager.selectedSpeaker?.name).toBe('selected_speaker');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,11 +17,11 @@ class ProxyDeviceManager implements ProxyHandler<DeviceManager> {
|
|||
this._deviceManager = deviceManager;
|
||||
this._context = context;
|
||||
|
||||
this.setDeviceManagerState();
|
||||
this.setDeviceManager();
|
||||
this.subscribe();
|
||||
}
|
||||
|
||||
private setDeviceManagerState = (): void => {
|
||||
private setDeviceManager = (): void => {
|
||||
// isSpeakerSelectionAvailable, selectedMicrophone, and selectedSpeaker are properties on DeviceManager. Since they
|
||||
// are not functions we can't proxy them so we'll update whenever we think they may need updating such as at
|
||||
// construction time or when certain events happen.
|
||||
|
@ -108,7 +108,7 @@ class ProxyDeviceManager implements ProxyHandler<DeviceManager> {
|
|||
return (...args: Parameters<DeviceManager['askDevicePermission']>): Promise<DeviceAccess> => {
|
||||
return target.askDevicePermission(...args).then((deviceAccess: DeviceAccess) => {
|
||||
this._context.setDeviceManagerDeviceAccess(deviceAccess);
|
||||
this.setDeviceManagerState();
|
||||
this.setDeviceManager();
|
||||
return deviceAccess;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -100,7 +100,7 @@ function addMockRemoteStreamAndParticipant(call: Call, key: string, id: number):
|
|||
}
|
||||
|
||||
function createContexts(): TestData {
|
||||
const context = new CallContext();
|
||||
const context = new CallContext('');
|
||||
const internalContext = new InternalCallContext();
|
||||
|
||||
return {
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
export { callClientDeclaratify } from './CallClientDeclarative';
|
||||
export type { DeclarativeCallClient } from './CallClientDeclarative';
|
||||
export type {
|
||||
DeviceManagerState,
|
||||
CallClientState,
|
||||
Call,
|
||||
CallAgent,
|
||||
CallClientState,
|
||||
DeviceManager,
|
||||
IncomingCall,
|
||||
LocalVideoStream,
|
||||
RemoteVideoStream,
|
||||
RemoteParticipant,
|
||||
RemoteVideoStream,
|
||||
VideoStreamRendererView,
|
||||
RecordingCallFeature,
|
||||
TranscriptionCallFeature
|
||||
|
|
|
@ -85,7 +85,7 @@ export const optionsButtonSelector: reselect.OutputParametricSelector<callingDec
|
|||
selectedMicrophone: AudioDeviceInfo | undefined;
|
||||
selectedSpeaker: AudioDeviceInfo | undefined;
|
||||
selectedCamera: VideoDeviceInfo | undefined;
|
||||
}, (res1: callingDeclarative.DeviceManagerState, res2: callingDeclarative.Call | undefined) => {
|
||||
}, (res1: callingDeclarative.DeviceManager, res2: callingDeclarative.Call | undefined) => {
|
||||
microphones: AudioDeviceInfo[];
|
||||
speakers: AudioDeviceInfo[];
|
||||
cameras: VideoDeviceInfo[];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// © Microsoft Corporation. All rights reserved.
|
||||
|
||||
import { Call, CallClientState, DeviceManagerState, IncomingCall } from '@azure/acs-calling-declarative';
|
||||
import { Call, CallClientState, DeviceManager, IncomingCall } from '@azure/acs-calling-declarative';
|
||||
|
||||
/**
|
||||
* Common props used to reference declarative client state.
|
||||
|
@ -17,7 +17,7 @@ export const getIncomingCalls = (state: CallClientState): Map<string, IncomingCa
|
|||
|
||||
export const getIncomingCallsEnded = (state: CallClientState): IncomingCall[] => state.incomingCallsEnded;
|
||||
|
||||
export const getDeviceManager = (state: CallClientState): DeviceManagerState => state.deviceManagerState;
|
||||
export const getDeviceManager = (state: CallClientState): DeviceManager => state.deviceManager;
|
||||
|
||||
export const getCall = (state: CallClientState, props: BaseSelectorProps): Call | undefined =>
|
||||
state.calls.get(props.callId);
|
||||
|
|
|
@ -57,7 +57,9 @@ const CallingProviderBase = (props: CallingProviderProps & ErrorHandlingProps):
|
|||
|
||||
// if there is no valid token then there is no valid userId
|
||||
const userIdFromToken = token ? getIdFromToken(token) : '';
|
||||
const [callClient, setCallClient] = useState<CallClient>(callClientDeclaratify(new CallClient(callClientOptions)));
|
||||
const [callClient, setCallClient] = useState<CallClient>(
|
||||
callClientDeclaratify(new CallClient(callClientOptions), userIdFromToken)
|
||||
);
|
||||
const [callAgent, setCallAgent] = useState<CallAgent | undefined>(undefined);
|
||||
const [deviceManager, setDeviceManager] = useState<DeviceManager | undefined>(undefined);
|
||||
const [userId, setUserId] = useState<string>(userIdFromToken);
|
||||
|
|
Загрузка…
Ссылка в новой задаче