From d45bbdd5dc4d4d7ea1a50799b2fc1a001c092f04 Mon Sep 17 00:00:00 2001 From: Swapnil Madavi Date: Tue, 30 Dec 2025 02:35:49 +0530 Subject: [PATCH] docs(realtimekit): add how-to page explaining participant management in a session --- .../realtimekit/core/end-a-session.mdx | 2 +- ...manage-other-participants-in-a-session.mdx | 245 ++++++++++++++++++ .../realtimekit/core/stage-management.mdx | 2 +- .../realtimekit/core/waiting-room.mdx | 2 +- .../disable-all-participants-video-steps.mdx | 61 +++++ .../web/disable-participant-video-steps.mdx | 60 +++++ .../web/mute-all-participants-steps.mdx | 61 +++++ .../web/mute-participant-steps.mdx | 62 +++++ .../realtimekit/web/pin-participant-steps.mdx | 69 +++++ .../web/remove-all-participants-steps.mdx | 46 ++++ .../web/remove-participant-steps.mdx | 59 +++++ .../web/select-a-remote-participant.mdx | 15 ++ .../web/unpin-participant-steps.mdx | 67 +++++ 13 files changed, 748 insertions(+), 3 deletions(-) create mode 100644 src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/disable-all-participants-video-steps.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/disable-participant-video-steps.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/mute-all-participants-steps.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/mute-participant-steps.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/pin-participant-steps.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/remove-all-participants-steps.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/remove-participant-steps.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/select-a-remote-participant.mdx create mode 100644 src/content/partials/realtime/realtimekit/web/unpin-participant-steps.mdx diff --git a/src/content/docs/realtime/realtimekit/core/end-a-session.mdx b/src/content/docs/realtime/realtimekit/core/end-a-session.mdx index 7b84034fc074a3..24b35c0344c21a 100644 --- a/src/content/docs/realtime/realtimekit/core/end-a-session.mdx +++ b/src/content/docs/realtime/realtimekit/core/end-a-session.mdx @@ -3,7 +3,7 @@ pcx_content_type: how-to title: End a session slug: realtime/realtimekit/core/end-a-session sidebar: - order: 6 + order: 7 --- import { Render } from "~/components"; diff --git a/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx b/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx new file mode 100644 index 00000000000000..3e4d70fbae687a --- /dev/null +++ b/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx @@ -0,0 +1,245 @@ +--- +title: Manage other participants in a session +pcx_content_type: how-to +slug: realtime/realtimekit/core/manage-other-participants-in-a-session +sidebar: + order: 6 +--- + +import { Render } from "~/components"; +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; + +Use RealtimeKit host controls to manage other participants in a live session. You can mute audio or video, pin a participant, or remove participants from the session. +These actions require specific host control permissions enabled in the local participant's [Preset](/realtime/realtimekit/concepts/preset/). +Before you show UI controls or call these methods, verify that the local participant has the necessary permissions. +In this guide, the **local participant** refers to the user performing the actions. + + + +## Before you start + +### Prerequisites + +- The local participant (for example, a host or moderator) must have the required **Host Controls** permissions enabled in their preset. For details, refer to [Preset](/realtime/realtimekit/concepts/preset/). + +### Select a remote participant + + + + + + + + + + + + + +## Mute audio + +Mute audio of participants when you need to manage background noise, moderate a classroom or webinar, or prevent interruptions during a session. +This action requires the **Mute Audio** (`disable_participant_audio`) host control permission enabled in the local participant's preset. + +### Mute a participant + +To mute a specific participant's audio: + + + + + + + + + + + + + +### Mute all participants + +This affects all participants, including the local participant. To mute audio for all participants in the session: + + + + + + + + + + + + + +## Disable video + +Disable video of participants when you need to moderate a session, enforce privacy, or prevent unwanted video during a classroom or webinar. +This action requires the **Mute Video** (`disable_participant_video`) host control permission enabled in the local participant's preset. + +### Disable video for a participant + +To disable a specific participant's video: + + + + + + + + + + + + + +### Disable video for all participants + +This affects all participants, including the local participant. To disable video for all participants in the session: + + + + + + + + + + + + + +## Pin participants + +Pin a participant to highlight them, such as a webinar presenter or classroom teacher. This is a session-wide action. All participants will see the pinned participant as the focus. +This action requires the **Pin Participant** (`pin_participant`) host control permission enabled in the local participant's preset. + +:::note +Only one participant can be pinned at a time. Pinning a new participant automatically unpins the previous one. +::: + +### Pin a participant + +To pin a participant in a session: + + + + + + + + + + + + + +### Unpin a participant + +Unpin a participant when you need to undo the highlight and return the session to a standard grid or active speaker view. +To unpin a pinned participant in a session: + + + + + + + + + + + + + +## Remove participants + +Remove participants from the session when you need to moderate disruptive behavior or enforce session rules. +This action requires the **Kick Participants** (`kick_participant`) host control permission enabled in the local participant's preset. + +### Remove a participant + +To remove a specific participant from the session: + + + + + + + + + + + + + +### Remove all participants + +This removes everyone from the session, including the local participant. This ends the session for everyone. + +For a complete end-a-session flow, refer to [End a session](/realtime/realtimekit/core/end-a-session/). + +To remove all participants from the session: + + + + + + + + + + + + + +## Next steps + +- Review how presets control permissions in [Preset](/realtime/realtimekit/concepts/preset/). +- Review error handling details in [Error Codes](/realtime/realtimekit/core/error-codes/). diff --git a/src/content/docs/realtime/realtimekit/core/stage-management.mdx b/src/content/docs/realtime/realtimekit/core/stage-management.mdx index 4c583b933f525e..3d76f7f43b11f6 100644 --- a/src/content/docs/realtime/realtimekit/core/stage-management.mdx +++ b/src/content/docs/realtime/realtimekit/core/stage-management.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: Stage Management slug: realtime/realtimekit/core/stage-management sidebar: - order: 8 + order: 9 --- import { Tabs, TabItem } from "~/components"; diff --git a/src/content/docs/realtime/realtimekit/core/waiting-room.mdx b/src/content/docs/realtime/realtimekit/core/waiting-room.mdx index 3c69bff392523d..6491c5d9318a4c 100644 --- a/src/content/docs/realtime/realtimekit/core/waiting-room.mdx +++ b/src/content/docs/realtime/realtimekit/core/waiting-room.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: Waiting Room slug: realtime/realtimekit/core/waiting-room sidebar: - order: 7 + order: 8 --- import { Tabs, TabItem } from "~/components"; diff --git a/src/content/partials/realtime/realtimekit/web/disable-all-participants-video-steps.mdx b/src/content/partials/realtime/realtimekit/web/disable-all-participants-video-steps.mdx new file mode 100644 index 00000000000000..f0e6ffe776a088 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/disable-all-participants-video-steps.mdx @@ -0,0 +1,61 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to disable other participants' video. + + ```ts + const canDisableVideo = + meeting.self.permissions.canDisableParticipantVideo === true; + if (!canDisableVideo) { + // Disable the control in your UI. + } + ``` + +2. Call `disableAllVideo()`. + + If the local participant does not have the required permission, `disableAllVideo()` throws a `ClientError` with code `1201`. + + ```ts + try { + await meeting.participants.disableAllVideo(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to disable other participants’ video. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds, each participant’s `videoEnabled` becomes `false`, and the SDK emits a `videoUpdate` event. + The local participant also receives `videoUpdate` on `meeting.self`. + + Listen to remote participant updates on the `joined` map: + + ```ts + meeting.participants.joined.on( + "videoUpdate", + (participant, { videoEnabled, videoTrack }) => { + // videoEnabled is false + // Update UI for the participant + }, + ); + ``` + + Listen to local participant update on `meeting.self`: + + ```ts + meeting.self.on("videoUpdate", ({ videoEnabled, videoTrack }) => { + // videoEnabled is false + // Update UI for the local participant + }); + ``` + + diff --git a/src/content/partials/realtime/realtimekit/web/disable-participant-video-steps.mdx b/src/content/partials/realtime/realtimekit/web/disable-participant-video-steps.mdx new file mode 100644 index 00000000000000..d8dd59748e78e9 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/disable-participant-video-steps.mdx @@ -0,0 +1,60 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to disable other participants' video. + + ```ts + const canDisableVideo = + meeting.self.permissions.canDisableParticipantVideo === true; + if (!canDisableVideo) { + // Disable the control in your UI. + } + ``` + +2. Call `disableVideo()` on the target participant. + + If the local participant does not have the required permission, `disableVideo()` throws a `ClientError` with code `1201`. + + ```ts + try { + await participant.disableVideo(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to disable other participants’ video. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds, the target participant's `videoEnabled` becomes `false`, and the SDK emits a `videoUpdate` event. + + **Option A**: Listen on the participant object + + ```ts + participant.on("videoUpdate", ({ videoEnabled, videoTrack }) => { + // videoEnabled is false + // Update UI for the participant + }); + ``` + + **Option B**: Listen on the `joined` map + + ```ts + meeting.participants.joined.on( + "videoUpdate", + (participant, { videoEnabled, videoTrack }) => { + // videoEnabled is false + // Update UI for the participant + }, + ); + ``` + + diff --git a/src/content/partials/realtime/realtimekit/web/mute-all-participants-steps.mdx b/src/content/partials/realtime/realtimekit/web/mute-all-participants-steps.mdx new file mode 100644 index 00000000000000..d92e2f0ac0c348 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/mute-all-participants-steps.mdx @@ -0,0 +1,61 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to mute other participants' audio. + + ```ts + const canMuteAudio = + meeting.self.permissions.canDisableParticipantAudio === true; + if (!canMuteAudio) { + // Disable the control in your UI. + } + ``` + +2. Call `disableAllAudio()`. + + If the local participant does not have the required permission, `disableAllAudio()` throws a `ClientError` with code `1201`. + + ```ts + try { + await meeting.participants.disableAllAudio(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to mute other participants’ audio. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds, each participant’s `audioEnabled` becomes `false`, and the SDK emits an `audioUpdate` event. + The local participant also receives `audioUpdate` on `meeting.self`. + + Listen to remote participant updates on the `joined` map: + + ```ts + meeting.participants.joined.on( + "audioUpdate", + (participant, { audioEnabled, audioTrack }) => { + // audioEnabled is false + // Update UI for the participant + }, + ); + ``` + + Listen to the local participant update on `meeting.self`: + + ```ts + meeting.self.on("audioUpdate", ({ audioEnabled, audioTrack }) => { + // audioEnabled is false + // Update UI for the local participant + }); + ``` + + diff --git a/src/content/partials/realtime/realtimekit/web/mute-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/mute-participant-steps.mdx new file mode 100644 index 00000000000000..4463534ba95ac1 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/mute-participant-steps.mdx @@ -0,0 +1,62 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to mute other participants' audio. + + ```ts + const canMuteAudio = + meeting.self.permissions.canDisableParticipantAudio === true; + if (!canMuteAudio) { + // Disable the control in your UI. + } + ``` + +2. Call `disableAudio()` on the target participant. + + If the local participant does not have the required permission, `disableAudio()` throws a `ClientError` with code `1201`. + + ```ts + try { + await participant.disableAudio(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to mute other participants’ audio. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds, the target participant's `audioEnabled` becomes `false`, and the SDK emits an `audioUpdate` event. + + **Option A**: Listen on the participant object + + ```ts + participant.on("audioUpdate", ({ audioEnabled, audioTrack }) => { + // audioEnabled is false + // Update UI for the participant + }); + ``` + + **Option B**: Listen on the `joined` map + + ```ts + meeting.participants.joined.on( + "audioUpdate", + (participant, { audioEnabled, audioTrack }) => { + if (participant.id === targetParticipantId) { + // audioEnabled is false + // Update UI for the participant + } + }, + ); + ``` + + diff --git a/src/content/partials/realtime/realtimekit/web/pin-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/pin-participant-steps.mdx new file mode 100644 index 00000000000000..c11a34e11d7df0 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/pin-participant-steps.mdx @@ -0,0 +1,69 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to pin participants. + + ```ts + const canPinParticipant = meeting.self.permissions.pinParticipant === true; + if (!canPinParticipant) { + // Disable the control in your UI. + } + ``` + +2. Call `pin()` on the target participant. + + If the local participant does not have the required permission, `pin()` throws a `ClientError` with code `1201`. + + ```ts + try { + await participant.pin(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to pin participants. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds: + - The target participant's `isPinned` becomes true. + - The participant is added to `meeting.participants.pinned`. + - The SDK emits a `pinned` event. + + **Option A**: Listen on the participant object + + ```ts + participant.on("pinned", (updatedParticipant) => { + // updatedParticipant.isPinned is true + // Update your UI. + }); + ``` + + **Option B**: Listen on the `joined` map + + ```ts + meeting.participants.joined.on("pinned", (updatedParticipant) => { + // updatedParticipant.isPinned is true + // Update your UI. + }); + ``` + + If there was an existing pinned participant before, then the SDK emits an `unpinned` event for that participant. + +4. On the target pinned participant's side, `meeting.self.isPinned` becomes `true` and `meeting.self` emits `pinned`: + + ```ts + meeting.self.on("pinned", (selfParticipant) => { + // Update the local UI to indicate the participant is pinned. + }); + ``` + + diff --git a/src/content/partials/realtime/realtimekit/web/remove-all-participants-steps.mdx b/src/content/partials/realtime/realtimekit/web/remove-all-participants-steps.mdx new file mode 100644 index 00000000000000..61f2b240e32323 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/remove-all-participants-steps.mdx @@ -0,0 +1,46 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to remove participants. + + ```ts + const canKickParticipant = meeting.self.permissions.kickParticipant === true; + if (!canKickParticipant) { + // Disable the control in your UI. + } + ``` + +2. Call `kickAll()`. + + If the local participant does not have the required permission, `kickAll()` throws a `ClientError` with code `1201`. + + ```ts + try { + await meeting.participants.kickAll(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to remove participants. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds, all participants exit the session. On each client, `meeting.self` emits `roomLeft` with state set to `ended`. + + ```ts + meeting.self.on("roomLeft", ({ state }) => { + if (state === "ended") { + // Show a message and navigate the user out of the meeting UI. + } + }); + ``` + + diff --git a/src/content/partials/realtime/realtimekit/web/remove-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/remove-participant-steps.mdx new file mode 100644 index 00000000000000..98c575260f80a0 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/remove-participant-steps.mdx @@ -0,0 +1,59 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to remove participants. + + ```ts + const canKickParticipant = meeting.self.permissions.kickParticipant === true; + if (!canKickParticipant) { + // Disable the control in your UI. + } + ``` + +2. Call `kick()` on the target participant. + + If the local participant does not have the required permission, `kick()` throws a `ClientError` with code `1201`. + + ```ts + try { + await participant.kick(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to remove participants. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds: + - The kicked participant is removed from `meeting.participants.joined`. + - The participant is removed from other participant maps they were in (for example, `meeting.participants.pinned`). + - The SDK emits `participantLeft` on `meeting.participants.joined`. + + ```ts + meeting.participants.joined.on("participantLeft", (participant) => { + // Remove the participant tile from the UI. + }); + ``` + + Other participants in the session also observe the participant leaving through `participantLeft`. + +4. On the removed participant's side, the session disconnects and `meeting.self` emits `roomLeft` event with state set to `kicked`. + + ```ts + meeting.self.on("roomLeft", ({ state }) => { + if (state === "kicked") { + // Show a message and navigate the user out of the meeting UI. + } + }); + ``` + + diff --git a/src/content/partials/realtime/realtimekit/web/select-a-remote-participant.mdx b/src/content/partials/realtime/realtimekit/web/select-a-remote-participant.mdx new file mode 100644 index 00000000000000..c73d3efc5c4db5 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/select-a-remote-participant.mdx @@ -0,0 +1,15 @@ +--- +{} +--- + +To perform actions on a specific participant, you first need to retrieve their participant object. +Remote participants (other participants) are available in `meeting.participants`. The local participant is available in `meeting.self`. +Refer to [Meeting Object Explained](/realtime/realtimekit/core/meeting-object-explained/) for details. + +```ts +const joinedParticipants = meeting.participants.joined.toArray(); +const participant = joinedParticipants[0]; +if (!participant) { + // No remote participants are currently joined. +} +``` diff --git a/src/content/partials/realtime/realtimekit/web/unpin-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/unpin-participant-steps.mdx new file mode 100644 index 00000000000000..4c946dbcdd9ee8 --- /dev/null +++ b/src/content/partials/realtime/realtimekit/web/unpin-participant-steps.mdx @@ -0,0 +1,67 @@ +--- +{} +--- + +import { Steps } from "~/components"; + + + +1. Check that the local participant has permission to unpin participants. + + ```ts + const canUnpinParticipant = meeting.self.permissions.pinParticipant === true; + if (!canUnpinParticipant) { + // Disable the control in your UI. + } + ``` + +2. Call `unpin()` on the target participant. + + If the local participant does not have the required permission, `unpin()` throws a `ClientError` with code `1201`. + + ```ts + try { + await participant.unpin(); + } catch (err: any) { + if (err?.code === 1201) { + // The local participant does not have permission to unpin participants. + return; + } + throw err; + } + ``` + +3. Handle the result by listening for updates. + + After the call succeeds: + - The target participant's `isPinned` becomes `false`. + - The participant is removed from `meeting.participants.pinned`. + - The SDK emits an `unpinned` event. + + **Option A**: Listen on the participant object + + ```ts + participant.on("unpinned", (updatedParticipant) => { + // updatedParticipant.isPinned is false + // Update your UI. + }); + ``` + + **Option B**: Listen on the `joined` map + + ```ts + meeting.participants.joined.on("unpinned", (updatedParticipant) => { + // updatedParticipant.isPinned is false + // Update your UI. + }); + ``` + +4. On the target unpinned participant's side, `meeting.self.isPinned` becomes `false` and `meeting.self` emits `unpinned`: + + ```ts + meeting.self.on("unpinned", (selfParticipant) => { + // Update the local UI to indicate the participant is no longer pinned. + }); + ``` + +