import { AudioDeviceInfo , DeviceAccess , DeviceManager , VideoDeviceInfo } from '@azure/communication-calling' ; import { CallContext } from './CallContext' ; /**
 * Defines the additional methods added by the stateful on top of  {@link @azure/communication-calling#DeviceManager} .
 *
 *  public export interface StatefulDeviceManager extends DeviceManager { /**
     * Sets the selectedCamera in the  DeviceManagerState . This is completely developer driven and is not tied in
     * any way to  {@link @azure/communication-calling#DeviceManager} . It is entirely contained in
     *  StatefulDeviceManager . See also  DeviceManagerState . selectedCamera .
      selectCamera : ( VideoDeviceInfo ) => void ; } /**
 * ProxyDeviceManager proxies DeviceManager and subscribes to all events that affect device manager state. State updates
 * are set on the provided context. Also any queries for state are proxied and stored in state as well. Only one device
 * manager should exist for a given CallClient so if CallClient.getDeviceManager is called multiple times, either a
 * cached ProxyDeviceManager should be returned or the existing ProxyDeviceManager should be destructed via destructor()
 * and a new ProxyDeviceManager created.
 */ class ProxyDeviceManager implements ProxyHandler < DeviceManager > { private _deviceManager : DeviceManager ; private _context : CallContext ; constructor ( deviceManager , context ) { this . _deviceManager = deviceManager ; this . _context = context ; this . setDeviceManager ( ) ; this . subscribe ( ) ; } private setDeviceManager = ( ) : void => { this . _context . setDeviceManagerIsSpeakerSelectionAvailable ( this . _deviceManager . isSpeakerSelectionAvailable ) ; this . _context . setDeviceManagerSelectedMicrophone ( this . _deviceManager . selectedMicrophone ) ; this . _context . setDeviceManagerSelectedSpeaker ( this . _deviceManager . selectedSpeaker ) ; } ; private subscribe = ( ) : void => { this . _deviceManager . on ( 'videoDevicesUpdated' , this . videoDevicesUpdated ) ; this . _deviceManager . on ( 'audioDevicesUpdated' , this . audioDevicesUpdated ) ; this . _deviceManager . on ( 'selectedMicrophoneChanged' , this . selectedMicrophoneChanged ) ; this . _deviceManager . on ( 'selectedSpeakerChanged' , this . selectedSpeakerChanged ) ; } ; /**
     * This is used to unsubscribe DeclarativeDeviceManager from the DeviceManager events.
     */ public unsubscribe = ( ) : void => { this . _deviceManager . off ( 'videoDevicesUpdated' , this . videoDevicesUpdated ) ; this . _deviceManager . off ( 'audioDevicesUpdated' , this . audioDevicesUpdated ) ; this . _deviceManager . off ( 'selectedMicrophoneChanged' , this . selectedMicrophoneChanged ) ; this . _deviceManager . off ( 'selectedSpeakerChanged' , this . selectedSpeakerChanged ) ; } ; param videoDeviceInfo public selectCamera = ( videoDeviceInfo ) : void => { this . _context . setDeviceManagerSelectedCamera ( videoDeviceInfo ) ; } ; private videoDevicesUpdated = async ( ) : Promise < void > => { this . _context . setDeviceManagerCameras ( dedupeById ( await this . _deviceManager . getCameras ( ) ) ) ; } ; private audioDevicesUpdated = async ( ) : Promise < void > => { this . _context . setDeviceManagerMicrophones ( dedupeById ( await this . _deviceManager . getMicrophones ( ) ) ) ; this . _context . setDeviceManagerSpeakers ( dedupeById ( await this . _deviceManager . getSpeakers ( ) ) ) ; } ; private selectedMicrophoneChanged = ( ) : void => { this . _context . setDeviceManagerSelectedMicrophone ( this . _deviceManager . selectedMicrophone ) ; } ; private selectedSpeakerChanged = ( ) : void => { this . _context . setDeviceManagerSelectedSpeaker ( this . _deviceManager . selectedSpeaker ) ; } ; public get < P extends keyof DeviceManager > ( target , prop ) { switch ( prop ) { case 'getCameras' : { return this . _context . withAsyncErrorTeedToState ( ( ) : Promise < VideoDeviceInfo [ ] > => { return target . getCameras ( ) . then ( ( cameras ) => { this . _context . setDeviceManagerCameras ( dedupeById ( cameras ) ) ; return cameras ; } ) ; } , 'DeviceManager.getCameras' ) ; } case 'getMicrophones' : { return this . _context . withAsyncErrorTeedToState ( ( ) : Promise < AudioDeviceInfo [ ] > => { return target . getMicrophones ( ) . then ( ( microphones ) => { this . _context . setDeviceManagerMicrophones ( dedupeById ( microphones ) ) ; return microphones ; } ) ; } , 'DeviceManager.getMicrophones' ) ; } case 'getSpeakers' : { return this . _context . withAsyncErrorTeedToState ( ( ) : Promise < AudioDeviceInfo [ ] > => { return target . getSpeakers ( ) . then ( ( speakers ) => { this . _context . setDeviceManagerSpeakers ( dedupeById ( speakers ) ) ; return speakers ; } ) ; } , 'DeviceManager.getSpeakers' ) ; } case 'selectMicrophone' : { return this . _context . withAsyncErrorTeedToState ( ( ... args ) : Promise < void > => { return target . selectMicrophone ( ... args ) . then ( ( ) => { this . _context . setDeviceManagerSelectedMicrophone ( target . selectedMicrophone ) ; } ) ; } , 'DeviceManager.selectMicrophone' ) ; } case 'selectSpeaker' : { return this . _context . withAsyncErrorTeedToState ( ( ... args ) : Promise < void > => { return target . selectSpeaker ( ... args ) . then ( ( ) => { this . _context . setDeviceManagerSelectedSpeaker ( target . selectedSpeaker ) ; } ) ; } , 'DeviceManager.selectSpeaker' ) ; } case 'askDevicePermission' : { return this . _context . withAsyncErrorTeedToState ( ( ... args ) : Promise < DeviceAccess > => { return target . askDevicePermission ( ... args ) . then ( ( deviceAccess ) => { this . _context . setDeviceManagerDeviceAccess ( deviceAccess ) ; this . setDeviceManager ( ) ; return deviceAccess ; } ) ; } , 'DeviceManager.askDevicePermission' ) ; } default : return Reflect . get ( target , prop ) ; } } } /** Helper function to dedupe duplicate audio and video devices obtained from SDK */ const dedupeById = < T extends { id : string ; } > ( devices ) : T [ ] => { const ids = new Set ( ) ; const uniqueDevices = [ ] ; devices . forEach ( ( device ) => { if ( ! ids . has ( device . id ) ) { uniqueDevices . push ( device ) ; ids . add ( device . id ) ; } } ) ; return uniqueDevices ; } ; param deviceManager param context export const deviceManagerDeclaratify = ( deviceManager , context ) : DeviceManager => { const proxyDeviceManager = new ProxyDeviceManager ( deviceManager , context ) ; Object . defineProperty ( deviceManager , 'unsubscribe' , { configurable : false , value : ( ) => proxyDeviceManager . unsubscribe ( ) } ) ; Object . defineProperty ( deviceManager , 'selectCamera' , { configurable : false , value : ( videoDeviceInfo ) => proxyDeviceManager . selectCamera ( videoDeviceInfo ) } ) ; return new Proxy ( deviceManager , proxyDeviceManager ) as StatefulDeviceManager ; } ;