Listing, muting and unmuting participants

Now that most of the single member functions have been written, we will works towards displaying and managing participants in the call.

In the Writing a backend to proxy SignalWire Video REST API part, we have already written the backend to give appropriate permissions to the user depending on if they are moderator or not. Now, in the frontend, we will make use of those permissions.

Listing members

The RoomSession object provides a getMembers() method which returns the details of all members in the call.

await room.getMembers()

/*
{
  "members": [
    {
      "visible": true,
      "room_session_id": "fde15619-13c1-4cb5-899d-96afaca2c52a",
      "input_volume": 0,
      "id": "1bf4d4fb-a3e4-4d46-80a8-3ebfdceb2a60",
      "input_sensitivity": 50,
      "output_volume": 0,
      "audio_muted": false,
      "on_hold": false,
      "name": "Mark",
      "deaf": false,
      "video_muted": false,
      "room_id": "aae25822-892c-4832-b0b3-34aac3a0e8d1",
      "type": "member"
    },
    {
      "visible": true,
      "room_session_id": "fde15619-13c1-4cb5-899d-96afaca2c52a",
      "input_volume": 0,
      "id": "e0c5be44-d6c7-438f-8cda-f859a1a0b1e7",
      "input_sensitivity": 50,
      "output_volume": 0,
      "audio_muted": false,
      "on_hold": false,
      "name": "David",
      "deaf": false,
      "video_muted": false,
      "room_id": "aae25822-892c-4832-b0b3-34aac3a0e8d1",
      "type": "member"
    }
  ]
}
*/

There will be an entry in the members array for each member in the call, having all the information about the member. The member's id can be used to uniquely track the user, and their name will be used to list them in the UI.

Updating the list of members

Having a static array of members is not enough. We need to keep the array up-to-date as users come and go, or as their attributes are updated. Thankfully, the SDK provides really simple way of keeping track of users. It does so using room events. Learn more about them in the SDK docs.

In fact, we won't even need the room.getMembers() method introduced above. We'll get by just fine just patching and updating the list of members when the events below fire.

  1. room.joined event

This event fires when you first join the room. The list of members present when you first join the room will are listed in Event.room.members as evident in the example below. We will use this event to initialise the list of members as we join.

The event parameter also has a member_id parameter, which lists the unique ID of current user. We need to use this information later, so we will keep it safe.

room.on('room.joined', e=>{
    console.log(e);
  
  thisMemberId.current = e.member_id;
    memberList.current = e.room.members;
    let thisMember = memberList.current.find(
        (m) => m.id === e.member_id
    );

    onRoomUpdate({ thisMemberId: e.member_id, member: thisMember });
    onMemberListUpdate(e.room.members);
  
    eventLogger("You have joined the room.");
})

/*
{
  "room": {
    "room_session_id": string,
    "logos_visible": boolean,
    "members": Array<{
      "visible": boolean,
      "room_session_id": string,
      "input_volume": number,
      "id": string,
      "input_sensitivity": number,
      "output_volume": number,
      "audio_muted": boolean,
      "on_hold": boolean,
      "name": string,
      "deaf": boolean,
      "video_muted": boolean,
      "room_id": string,
      "type": string
    }>,
    "blind_mode": boolean,
    "recording": boolean,
    "silent_mode": boolean,
    "name": string,
    "hide_video_muted": boolean,
    "locked": boolean,
    "meeting_mode": boolean,
    "room_id": string,
    "event_channel": string
  },
  "call_id": string,
  "member_id": string
}
*/
  1. member.joined event

This event is fired when new members join the video call. We use this event to add a new member to the current list of members.

room.on('member.joined', e=>{
    console.log(e);
  memberList.current.push(e.member);
  onMemberListUpdate(memberList.current);
})

/*
{
  "room_session_id": string,
  "room_id": string,
  "member": {
    "visible": boolean,
    "room_session_id": string,
    "input_volume": number,
    "id": string,
    "scope_id": string,
    "input_sensitivity": number,
    "output_volume": number,
    "audio_muted": boolean,
    "on_hold": boolean,
    "name": string,
    "deaf": boolean,
    "video_muted": boolean,
    "room_id": string,
    "type": string
  }
}
*/
  1. member.updated event

When the attributes of any member change, all members of the conference are notified of it via the member.updated event. Changes like a member's video being muting or them pressing the deaf button will fire the member.updated event in all participants. The event parameters includes an object with the list of changed attributes.

room.on("member.updated", async (e) => {
      console.log(e)
    let updatedMember = memberList.current.find(
        (x) => x.id === e.member.id
    );
 
    if (updatedMember === undefined) return;
    updatedMember = {
        ...updatedMember,
        ...e.member
    };

    let newMemberList = memberList.current.filter(
        (x) => x.id !== e.member.id
    );
    newMemberList.push(updatedMember);
    memberList.current = newMemberList;

    onMemberListUpdate([...memberList.current]);
});

/*
{
  "room_session_id": string,
  "room_id": string,
  "member": {
    "updated": Array<string>,
    "room_session_id": string,
    "id": string,
    "room_id": string,
    "type": string
  }
}
*/
  1. member.left event

The member.left event is fired when a member leaves the room. The event parameter includes the ID of the member that left, but not their name.

room.on("member.left", async (e) => {
    console.log(e);
  
    let memberThatLeft = memberList.current.find(
        (m) => m.id === e.member.id
    );
    let remainingMembers = memberList.current.filter(
        (m) => m.id !== e.member.id
    );

    if (memberThatLeft === undefined) return;
    eventLogger(memberThatLeft?.name + " has left the room.");

    if (thisMemberId.current === memberThatLeft?.id) {
        console.log("It is you who has left the room");
        onRoomUpdate({
            left: true
        });
    }

    memberList.current = remainingMembers;
    onMemberListUpdate(memberList.current);
    console.log(memberList.current);
});

/*
{
  "room_session_id": string,
  "room_id": string,
  "member": {
    "room_session_id": string,
    "id": string,
    "room_id": string,
    "type": string
  }
}
*/

Using all these events together, we can maintain an up-to-date list of the members in the call. We subscribe to these events in the component, as we want to keep most SDK specific code inside this component.

The component keeps its parent component up to date about the member list using a new event onMemberListUpdate().

Muting and Unmuting

To mute either the audio or video of members, we use the following methods from the Room object.

  1. Room.videoMute({memberId: string})
  2. Room.videoUnmute({memberId: string})
  3. Room.audioMute({memberId: string})
  4. Room.audioUnmute({memberId: string})

To mute/unmute oneself, we can simply call one of these methods without any parameter, like so: await room.videoMute().

Permissions required

It is important that we have these permissions to be able to mute or unmute oneself.

  1. room.self.audio_mute, room.self.audio_unmute: to mute and unmute one's audio
  2. room.member.audio_mute, room.member.audio_unmute: to mute and unmute another member's audio
  3. room.self.video_mute, room.self.video_unmute: to mute/unmute one's video
  4. room.member.video_mute, room.member.video_unmute: to mute/unmute another member's video

Putting it all together

In the CodeSandbox below is the frontend that we have written so far with a few additions.

The web-app above is served by the backend below (which we made in the Writing a backend to proxy SignalWire Video REST API part). Please make sure the backend is active before trying to use the frontend, because the backend supplies the access token for the frontend to join.

More resources:

  1. SignalWire Video SDK reference
  2. SignalWire Video REST API
  3. Backend on CodeSandbox
  4. Frontend on CodeSandbox
  5. GitHub Repo

Did this page help you?