зеркало из
1
0
Форкнуть 0

Task/2426782 2426783 call control handlers selectors (#203)

* [#2426782] Initial handlers for call controls

* [#2426782][#2426783] Handlers and Selectors for Call Controls

* [#2426782][#2426783] Memoizing createDefaultHandlersForComponent

* [#2426782][#2426783] Renaming videoButtonSelector to cameraButtonSelector

* Change files

* Update rush changelog

* [#2426782][#2426783] Updating API signature

* [#2426782][#2426783] Updating handlers

* [#2426782][#2426783] Restructuring handlers into one unified memoized function
This commit is contained in:
Anjul Garg 2021-05-04 14:06:04 -07:00 коммит произвёл GitHub
Родитель 4a99ce8b5a
Коммит 6168a8007a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 231 добавлений и 104 удалений

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Add handler and selector for call controls",
"packageName": "@azure/acs-calling-declarative",
"email": "anjulgarg@live.com",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Add handler and selector for call controls",
"packageName": "@azure/acs-calling-selector",
"email": "anjulgarg@live.com",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -83,7 +83,7 @@ export interface IncomingCall {
export interface LocalVideoStream {
mediaStreamType: MediaStreamType;
source: VideoDeviceInfo;
videoStreamRendererView: VideoStreamRendererView | undefined;
videoStreamRendererView?: VideoStreamRendererView | undefined;
}
// @public

Просмотреть файл

@ -45,7 +45,7 @@ export interface LocalVideoStream {
* {@Link VideoStreamRendererView} is added/removed from state by startRenderVideo/stopRenderVideo in
* {@Link DeclarativeCallClient} API.
*/
videoStreamRendererView: VideoStreamRendererView | undefined;
videoStreamRendererView?: VideoStreamRendererView | undefined;
}
/**

Просмотреть файл

@ -55,7 +55,7 @@ module.exports = {
{
// remove ban on files affected by https://github.com/microsoft/rushstack/pull/1916.
// This should be removed once this issue is fixed.
files: ['TEMPORARY-REMOVE-THIS'],
files: ['callControlSelectors.ts'],
rules: {
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-unused-vars': 'off'

Просмотреть файл

@ -4,14 +4,18 @@
```ts
import { AudioDeviceInfo } from '@azure/communication-calling';
import { Call } from '@azure/communication-calling';
import { CallAgent } from '@azure/communication-calling';
import * as callingDeclarative from '@azure/acs-calling-declarative';
import { CommunicationUserIdentifier } from '@azure/communication-common';
import { CreateViewOptions } from '@azure/communication-calling';
import { DeclarativeCallClient } from '@azure/acs-calling-declarative';
import { DeviceManager } from '@azure/communication-calling';
import { HangUpOptions } from '@azure/communication-calling';
import { PhoneNumberIdentifier } from '@azure/communication-common';
import { ReactElement } from 'react';
import * as reselect from 'reselect';
import { StartCallOptions } from '@azure/communication-calling';
import { UnknownIdentifier } from '@azure/communication-common';
import { VideoDeviceInfo } from '@azure/communication-calling';
@ -21,33 +25,81 @@ export type BaseSelectorProps = {
callId: string;
};
// @public
export type CallAgentHandlers = {
onStartCall(participants: (CommunicationUserIdentifier | PhoneNumberIdentifier | UnknownIdentifier)[], options?: StartCallOptions): Call;
};
// @public
export type CallClientHandlers = {
getDeviceManager: () => Promise<DeviceManager>;
};
// @public
export type CallHandlers = {
onHangUp(options?: HangUpOptions): Promise<void>;
};
// @public (undocumented)
export const cameraButtonSelector: reselect.OutputParametricSelector<callingDeclarative.CallClientState, BaseSelectorProps, {
checked: boolean;
}, (res: callingDeclarative.Call | undefined) => {
checked: boolean;
}>;
// @public
export type CommonProperties<A, B> = {
[P in keyof A & keyof B]: A[P] extends B[P] ? (A[P] extends B[P] ? P : never) : never;
[P in keyof A & keyof B]: A[P] extends B[P] ? P : never;
}[keyof A & keyof B];
// @public
export const createDefaultHandlersForComponent: <Props>(declarativeCallClient: DeclarativeCallClient, callAgent: CallAgent | undefined, deviceManager: DeviceManager | undefined, call: Call | undefined, _: (props: Props) => ReactElement | null) => Pick<CallClientHandlers & CallAgentHandlers & DeviceManagerHandlers & CallHandlers, CommonProperties<CallClientHandlers & CallAgentHandlers & DeviceManagerHandlers & CallHandlers, Props>> | Pick<CallClientHandlers, CommonProperties<CallClientHandlers, Props>>;
export const createDefaultHandlersForComponent: <Props>(declarativeCallClient: DeclarativeCallClient, callAgent: CallAgent | undefined, deviceManager: DeviceManager | undefined, call: Call | undefined, _Component: (props: Props) => ReactElement | null) => Pick<{
onHangUp: (options?: HangUpOptions | undefined) => Promise<void> | void;
onMute: () => Promise<void> | void;
onUnmute: () => Promise<void> | void;
onSelectCamera: (deviceId: string) => Promise<void | undefined>;
onSelectMicrophone: (deviceId: string) => Promise<void | undefined>;
onSelectSpeaker: (deviceId: string) => Promise<void | undefined>;
onStartCall: (participants: (CommunicationUserIdentifier | PhoneNumberIdentifier | UnknownIdentifier)[], options?: StartCallOptions | undefined) => Call | undefined;
onStartLocalVideo: (callId: string, deviceId: string, options: CreateViewOptions) => Promise<void>;
onStopLocalVideo: (callId: string) => Promise<void> | void;
onStartScreenShare: () => Promise<void> | void;
onStopScreenShare: () => Promise<void> | void;
onToggleLocalVideo: (callId: string, videoDeviceInfo: any, options: any) => Promise<void> | void;
onToggleMicrophone: () => Promise<void> | void;
onToggleScreenShare: () => Promise<void> | void;
}, CommonProperties<{
onHangUp: (options?: HangUpOptions | undefined) => Promise<void> | void;
onMute: () => Promise<void> | void;
onUnmute: () => Promise<void> | void;
onSelectCamera: (deviceId: string) => Promise<void | undefined>;
onSelectMicrophone: (deviceId: string) => Promise<void | undefined>;
onSelectSpeaker: (deviceId: string) => Promise<void | undefined>;
onStartCall: (participants: (CommunicationUserIdentifier | PhoneNumberIdentifier | UnknownIdentifier)[], options?: StartCallOptions | undefined) => Call | undefined;
onStartLocalVideo: (callId: string, deviceId: string, options: CreateViewOptions) => Promise<void>;
onStopLocalVideo: (callId: string) => Promise<void> | void;
onStartScreenShare: () => Promise<void> | void;
onStopScreenShare: () => Promise<void> | void;
onToggleLocalVideo: (callId: string, videoDeviceInfo: any, options: any) => Promise<void> | void;
onToggleMicrophone: () => Promise<void> | void;
onToggleScreenShare: () => Promise<void> | void;
}, Props>>;
// @public
export type DeviceManagerHandlers = {
getCameras(): Promise<VideoDeviceInfo[]>;
};
// @public (undocumented)
export const microphoneButtonSelector: reselect.OutputParametricSelector<callingDeclarative.CallClientState, BaseSelectorProps, {
checked: boolean;
}, (res: callingDeclarative.Call | undefined) => {
checked: boolean;
}>;
// @public (undocumented)
export const optionsButtonSelector: reselect.OutputParametricSelector<callingDeclarative.CallClientState, BaseSelectorProps, {
microphones: AudioDeviceInfo[];
speakers: AudioDeviceInfo[];
cameras: VideoDeviceInfo[];
selectedMicrophone: AudioDeviceInfo | undefined;
selectedSpeaker: AudioDeviceInfo | undefined;
selectedCamera: VideoDeviceInfo | undefined;
}, (res1: callingDeclarative.DeviceManagerState, res2: callingDeclarative.Call | undefined) => {
microphones: AudioDeviceInfo[];
speakers: AudioDeviceInfo[];
cameras: VideoDeviceInfo[];
selectedMicrophone: AudioDeviceInfo | undefined;
selectedSpeaker: AudioDeviceInfo | undefined;
selectedCamera: VideoDeviceInfo | undefined;
}>;
// @public (undocumented)
export const screenShareButtonSelector: reselect.OutputParametricSelector<callingDeclarative.CallClientState, BaseSelectorProps, {
checked: boolean | undefined;
}, (res: callingDeclarative.Call | undefined) => {
checked: boolean | undefined;
}>;
// (No @packageDocumentation comment for this package)

Просмотреть файл

@ -18,3 +18,6 @@ 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 getCall = (state: CallClientState, props: BaseSelectorProps): Call | undefined =>
state.calls.get(props.callId);

Просмотреть файл

@ -0,0 +1,40 @@
// © Microsoft Corporation. All rights reserved.
// @ts-ignore
import * as reselect from 'reselect';
// @ts-ignore
import * as callingDeclarative from '@azure/acs-calling-declarative';
// @ts-ignore
import { BaseSelectorProps } from './baseSelectors';
import { getCall, getDeviceManager } from './baseSelectors';
// @ts-ignore
import { AudioDeviceInfo, VideoDeviceInfo } from '@azure/communication-calling';
export const microphoneButtonSelector = reselect.createSelector([getCall], (call) => {
return {
checked: !call?.isMuted
};
});
export const cameraButtonSelector = reselect.createSelector([getCall], (call) => {
return {
checked: !!call?.localVideoStreams.find((stream) => stream.mediaStreamType === 'Video')
};
});
export const screenShareButtonSelector = reselect.createSelector([getCall], (call) => {
return {
checked: call?.isScreenSharingOn
};
});
export const optionsButtonSelector = reselect.createSelector([getDeviceManager, getCall], (deviceManager, call) => {
return {
microphones: deviceManager.microphones,
speakers: deviceManager.speakers,
cameras: deviceManager.cameras,
selectedMicrophone: deviceManager.selectedMicrophone,
selectedSpeaker: deviceManager.selectedSpeaker,
selectedCamera: call?.localVideoStreams.find((stream) => stream.mediaStreamType === 'Video')?.source
};
});

Просмотреть файл

@ -3,11 +3,11 @@
import { CallAgent, CallAgentOptions, DeviceManager } from '@azure/communication-calling';
import { CommunicationTokenCredential } from '@azure/communication-common';
import { ReactElement } from 'react';
import { CallClientHandlers, createDefaultHandlersForComponent } from './createHandlers';
import { DefaultHandlers, createDefaultHandlersForComponent } from './createHandlers';
import { DeclarativeCallClient } from '@azure/acs-calling-declarative';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function TestCallClientComponent(props: CallClientHandlers): ReactElement | null {
function TestCallClientComponent(props: DefaultHandlers): ReactElement | null {
return null;
}
@ -43,7 +43,7 @@ describe('createHandlers', () => {
undefined,
TestCallClientComponent
);
console.log(handlers);
expect(handlers).toBeDefined();
expect(Object.keys(handlers).length > 0).toBe(true);
});

Просмотреть файл

@ -5,75 +5,111 @@ import {
Call,
StartCallOptions,
HangUpOptions,
VideoDeviceInfo
LocalVideoStream,
CreateViewOptions
} from '@azure/communication-calling';
import { CommunicationUserIdentifier, PhoneNumberIdentifier, UnknownIdentifier } from '@azure/communication-common';
import { DeclarativeCallClient } from '@azure/acs-calling-declarative';
import { ReactElement } from 'react';
import memoizeOne from 'memoize-one';
/**
* Defines all handlers associated with {@Link @azure/communication-calling#CallClient}.
*/
export type CallClientHandlers = {
getDeviceManager: () => Promise<DeviceManager>;
};
export type DefaultHandlers = ReturnType<typeof createDefaultHandlers>;
/**
* Defines all handlers associated with {@Link @azure/communication-calling#CallAgent}.
*/
export type CallAgentHandlers = {
onStartCall(
participants: (CommunicationUserIdentifier | PhoneNumberIdentifier | UnknownIdentifier)[],
options?: StartCallOptions
): Call;
};
/**
* Defines all handlers associated with {@Link @azure/communication-calling#DeviceManager}.
*/
export type DeviceManagerHandlers = {
getCameras(): Promise<VideoDeviceInfo[]>;
};
/**
* Defines all handlers associated with {@Link @azure/communication-calling#Call}.
*/
export type CallHandlers = {
onHangUp(options?: HangUpOptions): Promise<void>;
};
const createCallClientDefaultHandlers = memoizeOne(
(declarativeCallClient: DeclarativeCallClient): CallClientHandlers => {
return {
getDeviceManager: () => declarativeCallClient.getDeviceManager()
const createDefaultHandlers = memoizeOne(
(
callClient: DeclarativeCallClient,
callAgent: CallAgent | undefined,
deviceManager: DeviceManager | undefined,
call: Call | undefined
) => {
const onStartLocalVideo = async (callId: string, deviceId: string, options: CreateViewOptions): Promise<void> => {
if (!deviceManager) return;
const devices = await deviceManager.getCameras();
const selected = devices.find((device) => device.id === deviceId);
if (!selected) return;
const stream = new LocalVideoStream(selected);
return callClient.startRenderVideo(callId, stream, options);
};
}
);
const createCallAgentDefaultHandlers = memoizeOne(
(declarativeCallAgent: CallAgent): CallAgentHandlers => {
return {
onStartCall: (
participants: (CommunicationUserIdentifier | PhoneNumberIdentifier | UnknownIdentifier)[],
options?: StartCallOptions
): Call => declarativeCallAgent.startCall(participants, options)
const onStopLocalVideo = (callId: string): Promise<void> | void => {
const call = callClient.state.calls.get(callId);
const stream = call?.localVideoStreams.find((stream) => stream.mediaStreamType === 'Video');
if (!stream) return;
return callClient.stopRenderVideo(callId, stream);
};
}
);
const createDeviceManagerDefaultHandlers = memoizeOne(
(declarativeDeviceManager: DeviceManager): DeviceManagerHandlers => {
return {
getCameras: (): Promise<VideoDeviceInfo[]> => declarativeDeviceManager.getCameras()
const onToggleLocalVideo = (callId: string, videoDeviceInfo, options): Promise<void> | void => {
const call = callClient.state.calls.get(callId);
const stream = call?.localVideoStreams.find((stream) => stream.mediaStreamType === 'Video');
if (stream) {
return onStopLocalVideo(callId);
} else {
return onStartLocalVideo(callId, videoDeviceInfo, options);
}
};
}
);
const createCallDefaultHandlers = memoizeOne(
(declarativeCall: Call): CallHandlers => {
const onStartCall = (
participants: (CommunicationUserIdentifier | PhoneNumberIdentifier | UnknownIdentifier)[],
options?: StartCallOptions
): Call | undefined => {
return callAgent ? callAgent.startCall(participants, options) : undefined;
};
const onSelectMicrophone = async (deviceId: string): Promise<void | undefined> => {
if (!deviceManager) return;
const devices = await deviceManager.getMicrophones();
const selected = devices.find((device) => device.id === deviceId);
if (!selected) return;
return deviceManager.selectMicrophone(selected);
};
const onSelectSpeaker = async (deviceId: string): Promise<void | undefined> => {
if (!deviceManager) return;
const devices = await deviceManager.getSpeakers();
const selected = devices.find((device) => device.id === deviceId);
if (!selected) return;
return deviceManager.selectMicrophone(selected);
};
const onSelectCamera = async (deviceId: string): Promise<void | undefined> => {
if (!call || !deviceManager) return;
const devices = await deviceManager.getCameras();
const selected = devices.find((device) => device.id === deviceId);
const stream = call.localVideoStreams.find((stream) => stream.mediaStreamType === 'Video');
if (!selected || !stream) return;
return stream.switchSource(selected);
};
const onMute = (): Promise<void> | void => call?.mute();
const onUnmute = (): Promise<void> | void => call?.unmute();
const onToggleMicrophone = (): Promise<void> | void => (call?.isMuted ? call?.unmute() : call?.mute());
const onStartScreenShare = (): Promise<void> | void => call?.startScreenSharing();
const onStopScreenShare = (): Promise<void> | void => call?.stopScreenSharing();
const onToggleScreenShare = (): Promise<void> | void =>
call?.isScreenSharingOn ? onStopScreenShare() : onStartScreenShare();
const onHangUp = (options?: HangUpOptions): Promise<void> | void => call?.hangUp(options);
return {
onHangUp: (options?: HangUpOptions): Promise<void> => declarativeCall.hangUp(options)
onHangUp,
onMute,
onUnmute,
onSelectCamera,
onSelectMicrophone,
onSelectSpeaker,
onStartCall,
onStartLocalVideo,
onStopLocalVideo,
onStartScreenShare,
onStopScreenShare,
onToggleLocalVideo,
onToggleMicrophone,
onToggleScreenShare
};
}
);
@ -82,7 +118,7 @@ const createCallDefaultHandlers = memoizeOne(
* Type guard for common properties between two types.
*/
export type CommonProperties<A, B> = {
[P in keyof A & keyof B]: A[P] extends B[P] ? (A[P] extends B[P] ? P : never) : never;
[P in keyof A & keyof B]: A[P] extends B[P] ? P : never;
}[keyof A & keyof B];
type Common<A, B> = Pick<A, CommonProperties<A, B>>;
@ -105,18 +141,5 @@ export const createDefaultHandlersForComponent = <Props>(
callAgent: CallAgent | undefined,
deviceManager: DeviceManager | undefined,
call: Call | undefined,
_: (props: Props) => ReactElement | null
):
| Common<CallClientHandlers & CallAgentHandlers & DeviceManagerHandlers & CallHandlers, Props>
| Common<CallClientHandlers, Props> => {
const callClientHandlers = createCallClientDefaultHandlers(declarativeCallClient);
const callAgentHandlers = callAgent ? createCallAgentDefaultHandlers(callAgent) : undefined;
const deviceManagerHandlers = deviceManager ? createDeviceManagerDefaultHandlers(deviceManager) : undefined;
const callHandlers = call ? createCallDefaultHandlers(call) : undefined;
return {
...callClientHandlers,
...callAgentHandlers,
...deviceManagerHandlers,
...callHandlers
};
};
_Component: (props: Props) => ReactElement | null
): Common<DefaultHandlers, Props> => createDefaultHandlers(declarativeCallClient, callAgent, deviceManager, call);

Просмотреть файл

@ -1,10 +1,5 @@
// © Microsoft Corporation. All rights reserved.
export { createDefaultHandlersForComponent } from './handlers/createHandlers';
export type { BaseSelectorProps } from './baseSelectors';
export type {
CallClientHandlers,
CallAgentHandlers,
DeviceManagerHandlers,
CallHandlers,
CommonProperties
} from './handlers/createHandlers';
export type { CommonProperties } from './handlers/createHandlers';
export * from './callControlSelectors';