import { Meta } from '@storybook/addon-docs';
We are sharing some of our recommended best practices when handling state with Azure Communication Services UI Library.
Managing the complex state of a calling or chat sesion can be challenging. This was a complex problem we found we were in and we think many of our developer community will run into this challenge themselves. We wanted to find a convenient way to store this information on the client in a way that plays nicely with our components we are offering. By using the stateful client for calling or chat, you are on our recommended path for powering your own communications experience using Azure Communication Services.
If you are using our provided UI Components, usePropsFor
will help you get integrated with the underlying functionality quickly. This provides a series of properties our
provided components are expecting from the stateful client to render an experience your users will be expecting. This is the fastest way to get your experience up and running with Azure Communication Services.
Today you can use useSelector
to connect your own components to create your own experiences! We use useSelector
under the hood when you leverage our usePropsFor
hook.
We wanted to ensure that developers wanting to create custom components had the same development path that we were using as well. Using this development path allows
you to start on the path that we use as well to create amazing components.
When using Stateful Calling, the call related state will be automatically retrieved for you.
However, this is not the case for DeviceManager
state.
To be able to retrieve DeviceManager
state from the StatefulCallClient
’s state, you must trigger the appropriate APIs to populate the DeviceManager
state.
DeviceManager.askPermission()
– this must be called to ask for device permissions from the user. The result of their decisions will be stored in DeviceManagerState.deviceAccess.DeviceManager.getCameras()
– When called, the result will be stored in DeviceManagerState.cameras.DeviceManager.getMicrophones()
– When called, the result will be stored in DeviceManagerState.microphones.DeviceManager.getSpeakers()
– When called, the result will be stored in DeviceManagerState.speakers
To make Stateful Calling easier to use we’ve added a property to the DeviceManagerState
called selectedCamera
.
This is not a proxy of the SDK state and is completely controlled by you.
It is populated when you set it and updated when you update it. It has no bearing on the function of the SDK and not automatically used by the SDK.
To allow developers to generate a HTMLElement/view of a Stateful VideoStream, we provide a createView and disposeView API. These APIs are located in StatefulCallClient and should only be used for scenarios where developers want to generate their own components to use in conjunction with the UI Library. There are three scenarios to using these APIs:
-
Rendering a RemoteVideoStreamState You are required to call
createView
with a validcallId
,participantIdentifier
, andremoteVideoStreamState
. ThecreateView
is async and if your architecture can support awaiting for it we recommend you do that. Once thecreateView
completes the view will be stored underCallState.RemoteParticipants.[your remote participant].videoStream.[your videostream].view
. If you cannot await the result ofcreateView
, it is fine to callcreateView
again,createView
will no-op the second call. Once the view has been created, the call state will be automatically updated and you can check the state for the view.CreateView
will not allow creating multiple view of the same stream. -
Rendering a LocalVideoStreamState in a Call Same as #1 except this time
createView
only requires a validcallId
andLocalVideoStreamState
. Note that this will only work if theLocalVideoStream
is already part of the call (you’ll have to first callCall.startVideo
to add theLocalVideoStream
to the Call). Everything else is the same as #1 after this except the view can be found inCallState.localVideoStreams.(your videostream).view
. -
Rendering a LocalVideoStreamState when not in a call This scenario supports the case if you want to see a video render without adding that stream to the call, for instance
LocalPreview
scenario. In this casecreateView
requires only a validLocalVideoStreamState
.CreateView
will put the created view underDeviceManagerState.unparentedViews.(your videostream).view
. You can access this Map using your originalLocalVideoStreamState
as the key.
To debug createView
and disposeView
, they emit console.warns
when used incorrectly that can be tracked.
One major gotcha with using Stateful Calling is that callId
may change and callId
is used to look up data in Stateful state.
If you have an old callId and the callId changed, you will not be able to access or find CallState
in CallClientState
.
To work around, we recommend you do not cache the callId
and instead cache the entire call object.
The id in the call object will be kept up to date and when ever you access CallClientState
use the call.id
.
To make Stateful Calling easier to use we added a screenshareRemoteParticipant
property to the state of any Call
.
If there is a remote participant screensharing, you can easily access the participant using CallState.screenShareRemoteParticipant
:
const remoteScreenshareParticipant = statefulCallClient.getState().calls[CALL_ID].screenShareRemoteParticipant;
This property will be undefined if there are no remote participants screensharing. Note that this only applies to remote screenshares and not local screenshares.
Stateful Calling will manage transcription/recording for you.
You only need to simply check the states under CallState.transcription
and CallState.recording
.