Pages Menu
TwitterRssFacebook

Posted by on Mar 14, 2021 in Azure Communication Services, Development

Learn Azure Communication Services Day 13 – Managing the Participant List

Learn Azure Communication Services Day 13 – Managing the Participant List

This blog post is part of a series called “Learn ACS“, all about Microsoft Azure Communication Services. The series covers a high-level overview of capabilities and considerations, then dives into the development detail of using ACS in your application. Find the rest of the posts in the series at learnACS.dev.

Today, we’re going to look at how to read the participant list. Azure Communication Services provides an array of remote participants (so, not including you) in a call, plus events for when that array changes (when someone joins or leaves). I’ve taken the Day 8 sample (Teams Meeting Interop join) and built on it because it’s easier to drop in and our of a Teams meeting and see the participant list updated in Azure Communication Services.

Because people can join and leave at any time, most implementations will use the event remoteParticipantsUpdated to subscribe to any changes. There are two approaches you can take. The first is to query the arrays of added and removed participants which are passed to the callback function. These two arrays are passed every time and describe any changes to the participant list. You can use this information to either announce joiners/leavers or keep an existing participant list up to date.

The second approach is to get the full, updated participant list from call.remoteParticipants and rebuild it from there, overwriting anything you previously had.

I’ve used both approaches in the sample code: approach 1 to announce new joiners/leavers, and approach 2 to display a basic participant list.

If you want to dig deeper then check out all the other properties and events associated with a remoteParticipant.

Here’s the code: don’t forget, on Line 19, replace the placeholder text with the full URL of your Azure Function created in Day 3, including the code parameter:

index.html

<!DOCTYPE html>
<html>
<head>
<title>Learn ASC Day 13 - Managing the Participant List</title>
</head>
<body>
<h2>Learn ASC Day 13 - Managing the Participant List</h2>
<p>Call state <span style="font-weight: bold" id="call-state">-</span></p>
<input id="destination-group-input" type="text" placeholder="Microsoft Teams meeting join URL (https://teams.microsoft.com/l/meetup-join/....)"
style="margin-bottom:1em; width: 600px;" />
<div>
<button id="connect-button" type="button" disabled="false">
Connect
</button>
<button id="disconnect-button" type="button" disabled="true">
Disconnect
</button>
</div>
<div>
<ul id="ParticipantList">
</ul>
</div>
<hr/>
<p style="font-family: sans-serif">Hi, I'm Tom! I hope you found this code sample useful. This sample code comes from a series of blog posts about Azure Communication Services. Links for all blog posts can be found at <a href="https://learnacs.dev" target="_blank" rel="noopener">learnACS.dev</a>. I blog at <a href="https://blog.thoughtstuff.co.uk">thoughtstuff.co.uk</a>. You can also <a href="https://www.youtube.com/c/TomMorganTS?sub_confirmation=1" target="_blank" rel="noopener">subscribe to my YouTube channel</a> for videos about ACS (and much more!). </p>
<h4>Disclaimer: This is a sample. It’s not meant for you to take and use without fully understanding what it’s doing. It’s definitely not meant for production use. You should understand the risks of hosting your own ACS instance and associated web-based entry point on the public internet before proceeding. If you end up sharing your access tokens, or there’s a bug in the code and you end up with a huge hosting bill, or find yourself unwittingly hosting other people’s rooms, you’re on your own. This sample code is provided under the <a href="https://opensource.org/licenses/MIT">MIT license</a>, which you should read in full (it’s 21 LOC).</h4>
<script src="./bundle.js"></script>
</body>
</html>

client.js

import { CallClient, CallAgent } from "@azure/communication-calling";
import { AzureCommunicationTokenCredential } from '@azure/communication-common';
const connectButton = document.getElementById('connect-button');
const disconnectButton = document.getElementById('disconnect-button');
const callStateElement = document.getElementById('call-state');
const destinationGroupElement = document.getElementById('destination-group-input');
const participantListElement = document.getElementById('ParticipantList');
let call;
let callAgent;
let callClient;
async function init() {
callClient = new CallClient();
//get an access token to use
const response = await fetch('YOUR ACS TOKEN ISSUING WEB FUNCTION URL HERE (WITH THE CODE). SEE DAY 3');
const responseJson = await response.json();
const token = responseJson.value.item2.token;
const tokenCredential = new AzureCommunicationTokenCredential(token);
callAgent = await callClient.createCallAgent(tokenCredential);
connectButton.disabled = false;
}
init();
connectButton.addEventListener("click", () => {
const destinationToCall = { meetingLink: destinationGroupElement.value};
call = callAgent.join(destinationToCall);
call.on('stateChanged', () => {
callStateElement.innerText = call.state;
});
call.on('remoteParticipantsUpdated', (updateEvent) => {
//approach 1
alert(updateEvent.added.length + " added, " + updateEvent.removed.length + " removed");
//approach 2
participantListElement.innerHTML = "";
call.remoteParticipants.forEach(function(participant)
{
var el = document.createElement("li");
el.appendChild(document.createTextNode(participant.displayName + " (" + participant.identifier.microsoftTeamsUserId + ")"));
participantListElement.appendChild(el);
});
});
// toggle button states
disconnectButton.disabled = false;
connectButton.disabled = true;
});
disconnectButton.addEventListener("click", async () => {
await call.hangUp();
// toggle button states
disconnectButton.disabled = true;
connectButton.disabled = false;
callStateElement.innerText = '-';
});

Testing it out

Similar to previous days, this one is relatively straightforward to test out. As before, use a command-line to run this command then browse to the sample application in your browser (usually http://localhost:8080):

npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map

Try joining a Teams meeting, either an empty one, or one that already has people in it. Try joining and leaving that meeting using the Teams client to see the effect on the participant list, and the alerts about joiners and leavers.

What’s the code doing?

Everything new is happening within the event handler for call.remoteParticipantsUpdated. Approach 1 is to look at the added and removed arrays, each of which contain a collection of zero or more RemoteParticipant objects.

Approach 2 is to rebuild a participant list from call.remoteParticipants, which contains a full array of all RemoteParticipant objects.


Today, we explored the participant list in Azure Communication Services. Tomorrow we’re going to look at showing video and screen share content. Links for all blog posts can be found at learnACS.dev. You can also subscribe to my YouTube channel for videos about ACS (and much more!).

Written by Tom Morgan

Tom is a Microsoft Teams Platform developer and Microsoft MVP who has been blogging for over a decade. Find out more.
Buy the book: Building and Developing Apps & Bots for Microsoft Teams. Now available to purchase online with free updates.

Post a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.